Search code examples
ruby-on-railsdeviseassociations

Manage newsletter subscribers from one or two models? - Rails 5


I want my Rails app to send a newsletter. I first implemented it so that Users can subscribe and receive the newsletter. But now I'd like people who are not a user to be able to join the newsletter as well. Therefore I created a new model: NewsletterSubscribers.

The thing I'm running in now though is that I want to make sure that whenever someone first joined the newsletter and later on signs up for an account, he doesn't get the newsletter twice.

Im doubting how to go forward with this. I see two options:

  • Send it to NewsLetterSubscribers and then before sending it to the Users, check if their email is present in a record of the NewsletterSubscriber table. And if it is present, not send it to that email again. My worry though is that this is very slow. Also it feels like bad practice to manage my newsletter subscription with two models.
  • To manage the newsletter only from the NewsletterSubscriber model. Everyone can subscribe (visitors and users). This would simplify it a lot, but I want to be able to check whether a user is subscribed to the newsletter or not in my views. So then I would need a belongs_to - has_many association between NewsletterSubscriber and Users. But then visitors can not subscribe anymore because of the validations that come with this association.

As you can read, I'm stuck... Does anyone have an idea how to best approach this?

ps. I use Devise with Twitter omniauth to authenticate users. Wanted to let you know, in case this simplifies or complicates a possible solution.


Solution

  • You can just disable the validation generated by belongs_to by passing the optional: true option:

    class User < ApplicationRecord
      has_many :newsletter_subscriptions
      # or 
      has_one :newsletter_subscription
    end
    
    class NewsletterSubscription < ApplicationRecord
      belongs_to :user, optional: :true
    end
    

    Another way you might want to consider is using Single Table Inheritance (STI) which will let you create different logic for guest and registered subscriptions while still using a single table. You can set this up by adding a string column named type to the newsletter_subscriptions table.

    # rails g model NewsletterSubscription type:string email:string user:references
    class NewsletterSubscription < ApplicationRecord
      # shared logic
    end
    
    
    # app/models/users/newsletter_subscription.rb
    module Users
      class NewsletterSubscription < ::NewsletterSubscription
        belongs_to :user
        delegate :email, to: :user
      end
    end
    
    # app/models/guests/newsletter_subscription.rb
    module Guests
      class NewsletterSubscription < ::NewsletterSubscription
        validates_presence_of :email
      end
    end