How do I set ActiveRecord limits by subscription plan type in a Rails 4.2 application?

I have a multi-tenant Rails 4.2 application using Devise for Authentication. I have subscription plans as follows: Individual {Monthly and Yearly}, Business {Monthly and Yearly}. Individual plan has a limit of 5 users and 5 plans. The business plan has unlimited users and plans.

I am trying to set up a way to prevent users of individual plans from creating new Plan and User records when they have reached their limit.

I tried this: in rails, how to limit the number of mails stored in the database before asking to update my account

My code looked like the "plan" model:

before_save :check_plan_quota
private

def check_plan_quota
if self.account.plans.count >= 5
  @quota_warning = "Plan limit reached."
  return false
 end
end

      

And I had this in my "dashboard" controller (because plans are created and listed here):

before_filter :check_quota
  private

 def check_quota
  if Plan.count >= 5
    @quota_warning = "Plan limit reached."
  end
end

      

I managed to get it to work where the user could not add more entries once they reached 5 plans and also made the "add plan" button disappear based on @quota_warning.

          <p>
      <% if @quota_warning.present? %>
       <button type="button" class="btn btn-default"> You have <%= Plan.count %> Plans </button> &nbsp;&nbsp; - &nbsp;&nbsp; <button type="button" class="btn btn-secondary"> <%= @quota_warning %></button> 
       <% else %>
        <a href="/plans/new" class="btn btn-primary" data-toggle="modal" data-target="#styledModal">New Plan</a> &nbsp;&nbsp; - &nbsp;&nbsp; <button type="button" class="btn btn-default"> You have <%= Plan.count %> Plans </button>
        <% end %> 
      </p>

      

BUT This limited all users to only 5 plans and tried as best I can to get an "if" statement that would apply this limitation only to users of individual monthly or yearly plans.

Then I tried this on a "custom" controller.

before_filter :check_user_limit, :only => :create

def check_user_limit
  redirect_to new_user_url if current_account.reached_user_limit?
end

      

And in the "account" model I put.

class Account < ActiveRecord::Base

has_many :users, dependent: :destroy, :inverse_of => :account
accepts_nested_attributes_for :users

has_one :admin, -> { where(admin: true) }, :class_name => "User"
accepts_nested_attributes_for :admin

:user_limit => Proc.new {|a| a.users.count }

has_many :plans, dependent: :destroy, :inverse_of => :account
accepts_nested_attributes_for :plans

:plan_limit => Proc.new {|a| a.plans.count }

      

The user_limit and plan_limit fields have been added to the account column in the database, so every account must have these restrictions set for registration. As you can imagine, the above code cannot be used, but being a newbie in rails I don't know how to use it better or even if it is the best route.

The first way I tried seemed simple, but I couldn't get it to work with something like

if account.user.plan.stripe_id = 'monthly' or 'yearly and Plan.count >= 5
@quota_warning = "Limit reached"
end

      

My application structure looks like this:

  • Account - has all the functions and the user.
  • User - Owns the account and has a subscription via User.plan, but the functions are not owned by the user. I don't want user-specific functions to be done using own_to.

The only limit I need to focus on is individual accounts as pro accounts have no limit.

It seems like using the user_limit and plan_limit code in the model and a simple creation check in the controller is slightly better than the other, but I can't seem to get it to work. I'm not sure if I like db fields too. I understand what the Proc.new code is doing, but not what I can apply it to. I tried to apply it to the end of has_one: admin but got the following error:

Unknown key: :user_limit. Valid keys are: :class_name, :class, :foreign_key, :validate, :autosave, :dependent, :primary_key, :inverse_of, :required, :as, :foreign_type

      

I'm not sure if there is enough information to help me, but if I need to add more information, please let me know.

+3


source to share


1 answer


I would break this down in methods / pieces of bite size logic and then build the functionality you want based on that. Something like:

INDIVIDUAL_USER_LIMIT = 5
INDIVIDUAL_PLAN_LIMIT = 5

def individual_plan?
  self.plan.type == :individual # or some such logic.
end

def over_user_limit?
  self.individual_plan? && self.users.count > INDIVIDUAL_USER_LIMIT
end

def over_plan_limit?
  self.individual_plan? && self.plans.count > INDIVIDUAL_PLANS_LIMIT
end

      

It might also be worth taking your time to have a look at the implementation of a custom validator that looks like



class UserLimitValidator < ActiveModel::Validator
  def validate(account)
    if account.over_user_limit?
      account.errors[:users] << 'Too many users for your plan!'
    end
  end
end

      

And let me confirm that the models you save have certain limitations on them.

As you know, when implementing this, be careful when calling self.plans.count

or self.users.count

as it makes an additional query to the database and can cause some nasty performance issues at scale such as the N + 1 query problem .

+2


source







All Articles