Rails Account Plan Model Best Practices

I'm working on a rails application that will allow customers to sign up for an account with a particular plan (i.e. Free, Basic, Premium, etc) that defines what features are available, etc. Pretty standard stuff you see everywhere, however, not something I've implemented before.

I'm struggling with trying to figure out "the rails way" - if there is one. Are there best practices you guys typically follow when implementing something like this. Don't want to reinvent the wheel here.

The direction I've started down is something like this:

class Account < ActiveRecord::Base   belongs_to :subscription_plan   delegate :limit, :to => :subscription_plan         delegate :feature_allowed, :to => :subscription_plan

end

# Table name: subscription_plans

I'm working on a rails application that will allow customers to sign up for an account with a particular plan (i.e. Free, Basic, Premium, etc) that defines what features are available, etc. Pretty standard stuff you see everywhere, however, not something I've implemented before.

I'm struggling with trying to figure out "the rails way" - if there is one. Are there best practices you guys typically follow when implementing something like this. Don't want to reinvent the wheel here.

The direction I've started down is something like this:

class Account < ActiveRecord::Base         belongs_to :subscription_plan         delegate :limit, :to => :subscription_plan         delegate :feature_allowed, :to => :subscription_plan

end

# Table name: subscription_plans # # id :integer not null, primary key # plan_name :string(255) # limit :integer # feature_allowed :boolean class SubscriptionPlan < ActiveRecord::Base         has_many :accounts end

But I'm not sure if making the SubscriptionPlan an active record class and defining all the plans in the database is the best way to go, or whether it makes more sense to create something like FreeSubscriptionPlan, BasicSubscriptionPlan, PremiumSubscriptionPlan, etc as just regular classes.

Anybody with thoughts on this or a pointer to some good resources?

Robin,

You can always define a plan structure as a quick hash in environment.rb instead of using heavy duty AR classes:

  PLANS = {     :free => { :cents => 0, :name => 'Free', :max_pics => 10 },     :basic => { :cents => 2900, :name => 'Basic', :max_pics => 500 },     :plus => { :cents => 4900, :name => 'Plus', :max_pics => 1000 },     :premium => { :cents => 9900, :name => 'Premium', :max_pics => 5000 }   }

Then you can use the hash to lookup names based on cents and so forth...

I would recommend allowing accounts be able to have multiple subscriptions:

class Account < AR   has_many :subscriptions   has_one :current_subscription, { :class_name => 'Subscription', :order => 'id desc' } end

This way you can track subscription history.

Hope this helps,

Thanks Zack, that's helpful. I hadn't thought of just using a hash. So, I assume that in your example, Subscription is still an AR class, with an attribute for which plan the account selected?

Yes, Subscription is an AR class representing a subscription of the customer, either past or present. So if a customer changed plans five times during the year they would have 5+1 (original) subscriptions. That's why it's a has_many relationship.

To keep it simple you can just map the Subscription cents attribute to the PLAN hash.

Cool, thanks Zack.

Not to step on anyones toes, but I would recommend against defining a PLAN hash in environment.rb. In doing so, a constant is essentially being used as a global variable. A better idea would be to encapsulate such data into your domain model appropriately. Not knowing exactly what that is, I'd suggest an easy start is something similar to the following (a few things removed for brevity):

class Account < ActiveRecord::Base   class_inheritable_accessor :plan_types   self.plan_types = { :free => { :cents => 0, :name => 'Free',    :max_pics => 10 },                       :basic => { :cents => 2900, :name => 'Basic', :max_pics => 500 },                       :plus => { :cents => 4900, :name => 'Plus',    :max_pics => 1000 },                       :premium => { :cents => 9900, :name => 'Premium', :max_pics => 5000 } }

end

Furthermore, you could also define your own non-AR-based Plan model that reads the above data from a YAML file rather than having it in your code, and so forth. I would suggest you investigate that approach, personally, if you feel just using an AR model for the plan data isn't appropriate.

- Gabriel

I think the answer depends on two questions: - How dynamic will the number of subscription plans be? - Who will control the plans (name, feature set, etc)?

If the number of plans could vary wildly over time and an end user needs to have full control over them then the very flexible approach you're outlining makes sense. It's really no different than a standard role-based authorization with the 'plan' taking the place of the role. You could allow the end user ultimate control -- allowing them to authorize controller/action for a particular plan -- and not have to worry about coding authorization to a feature.

If the number of plans is more stable and the development team will be involved with the evolving plans then you could go with a static array or hash to provide the names and db values for the plans. If you don't envision the plans containing much more than the limit and allowed flag you describe then you may not need more than that.

If you expect more features to be added to the list of allowed/ disallowed for a plan you'll need to add some kind of association between the plan and the feature(s). That is, you're better to build a table of AllowedFeatures and using a join or has/belongs relationship to the SubscriptionPlan. The SubscriptionPlan could use a method to respond to the (delegated) 'feature_allowed', looking up the feature through it's collection of AllowedFeatures.