Forum: Ruby on Rails Rails Account Plan Model Best Practices

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
(Guest)
on 2007-04-25 07:52
(Received via mailing list)
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?
Zack C. (Guest)
on 2007-04-25 08:12
(Received via mailing list)
>
> #
> 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
(Guest)
on 2007-04-25 18:37
(Received via mailing list)
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?
Zack C. (Guest)
on 2007-04-25 22:42
(Received via mailing list)
On 4/25/07, removed_email_address@domain.invalid 
<removed_email_address@domain.invalid> 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
(Guest)
on 2007-04-26 01:08
(Received via mailing list)
Cool, thanks Zack.
Gabriel Gironda (Guest)
on 2007-04-26 23:43
(Received via mailing list)
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
AndyV (Guest)
on 2007-04-26 23:55
(Received via mailing list)
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, "removed_email_address@domain.invalid" 
<removed_email_address@domain.invalid>
This topic is locked and can not be replied to.