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

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?

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?

On 4/25/07, [email protected] [email protected] wrote:

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.


Zack C.
http://depixelate.com

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,


Zack C.
http://depixelate.com

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

Cool, thanks Zack.

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.

On Apr 24, 11:51 pm, “[email protected][email protected]