Module: PricingPlans::PlanOwner::HasManyInterceptor

Defined in:
lib/pricing_plans/plan_owner.rb

Instance Method Summary collapse

Instance Method Details

#has_many(name, scope = nil, **options, &extension) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/pricing_plans/plan_owner.rb', line 61

def has_many(name, scope = nil, **options, &extension)
  limited_opts = options.delete(:limited_by_pricing_plans)
  reflection = super(name, scope, **options, &extension)

  if limited_opts
    config = limited_opts == true ? {} : limited_opts.dup
    limit_key = (config.delete(:limit_key) || name).to_sym
    per = config.delete(:per)
    error_after_limit = config.delete(:error_after_limit)
    count_scope = config.delete(:count_scope)

    # Define English-y sugar methods on the plan owner immediately
    PricingPlans::PlanOwner.define_limit_sugar_methods(self, limit_key)

    begin
      assoc_reflection = reflect_on_association(name)
      child_klass = assoc_reflection.klass
      foreign_key = assoc_reflection.foreign_key.to_s

      # Find the child's belongs_to backref to this plan owner
      inferred_owner = child_klass.reflections.values.find { |r| r.macro == :belongs_to && r.foreign_key.to_s == foreign_key }&.name
      # If foreign_key doesn't match (e.g., child uses :organization), prefer association matching owner class semantic name
      owner_name_sym = self.name.underscore.to_sym
      inferred_owner ||= (child_klass.reflections.key?(owner_name_sym.to_s) ? owner_name_sym : nil)
      # Common conventions fallback
      inferred_owner ||= %i[organization account user team company workspace tenant].find { |cand| child_klass.reflections.key?(cand.to_s) }
      # Final fallback to underscored class name
      inferred_owner ||= owner_name_sym

      child_klass.include PricingPlans::Limitable unless child_klass.ancestors.include?(PricingPlans::Limitable)
      child_klass.limited_by_pricing_plans(
        limit_key,
        plan_owner: inferred_owner,
        per: per,
        error_after_limit: error_after_limit,
        count_scope: count_scope
      )
    rescue StandardError
      # If child class cannot be resolved yet, register for later resolution
      PricingPlans::AssociationLimitRegistry.register(
        plan_owner_class: self,
        association_name: name,
        options: { limit_key: limit_key, per: per, error_after_limit: error_after_limit, count_scope: count_scope }
      )
    end
  end

  reflection
end