Module: PricingPlans::PlanOwner
- Defined in:
- lib/pricing_plans/plan_owner.rb
Overview
Mix-in for the configured plan owner class (e.g., Organization)
Provides readable, owner-centric helpers.
Defined Under Namespace
Modules: ClassMethods, HasManyInterceptor
Class Method Summary
collapse
Instance Method Summary
collapse
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, &block) ⇒ Object
Syntactic sugar for feature checks:
Allows calls like plan_owner.plan_allows_api_access? which map to
plan_owner.plan_allows?(:api_access).
269
270
271
272
273
274
275
|
# File 'lib/pricing_plans/plan_owner.rb', line 269
def method_missing(method_name, *args, &block)
if (m = method_name.to_s.match(/^plan_allows_(.+)\?$/))
feature_key = m[1].to_sym
return plan_allows?(feature_key)
end
super
end
|
Class Method Details
.define_limit_sugar_methods(plan_owner_class, limit_key) ⇒ Object
Define English-y sugar methods on the plan owner for a specific limit key.
Idempotent: skips if methods already exist.
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
# File 'lib/pricing_plans/plan_owner.rb', line 14
def self.define_limit_sugar_methods(plan_owner_class, limit_key)
key = limit_key.to_sym
within_m = :"#{key}_within_plan_limits?"
remaining_m = :"#{key}_remaining"
percent_m = :"#{key}_percent_used"
grace_active_m = :"#{key}_grace_active?"
grace_ends_m = :"#{key}_grace_ends_at"
blocked_m = :"#{key}_blocked?"
unless plan_owner_class.method_defined?(within_m)
plan_owner_class.define_method(within_m) do |by: 1|
LimitChecker.within_limit?(self, key, by: by)
end
end
unless plan_owner_class.method_defined?(remaining_m)
plan_owner_class.define_method(remaining_m) do
LimitChecker.plan_limit_remaining(self, key)
end
end
unless plan_owner_class.method_defined?(percent_m)
plan_owner_class.define_method(percent_m) do
LimitChecker.plan_limit_percent_used(self, key)
end
end
unless plan_owner_class.method_defined?(grace_active_m)
plan_owner_class.define_method(grace_active_m) do
GraceManager.grace_active?(self, key)
end
end
unless plan_owner_class.method_defined?(grace_ends_m)
plan_owner_class.define_method(grace_ends_m) do
GraceManager.grace_ends_at(self, key)
end
end
unless plan_owner_class.method_defined?(blocked_m)
plan_owner_class.define_method(blocked_m) do
GraceManager.should_block?(self, key)
end
end
end
|
.included(base) ⇒ Object
7
8
9
10
|
# File 'lib/pricing_plans/plan_owner.rb', line 7
def self.included(base)
base.extend(ClassMethods)
base.singleton_class.prepend HasManyInterceptor
end
|
Instance Method Details
#any_grace_active_for?(*limit_keys) ⇒ Boolean
Aggregate helpers across multiple limit keys
322
323
324
|
# File 'lib/pricing_plans/plan_owner.rb', line 322
def any_grace_active_for?(*limit_keys)
limit_keys.flatten.any? { |k| GraceManager.grace_active?(self, k) }
end
|
#approaching_limit?(limit_key, at: nil) ⇒ Boolean
378
379
380
|
# File 'lib/pricing_plans/plan_owner.rb', line 378
def approaching_limit?(limit_key, at: nil)
PricingPlans.approaching_limit?(self, limit_key, at: at)
end
|
#assign_pricing_plan!(plan_key, source: "manual") ⇒ Object
244
245
246
247
248
249
250
251
|
# File 'lib/pricing_plans/plan_owner.rb', line 244
def assign_pricing_plan!(plan_key, source: "manual")
LegacyPlanAssignmentApi.create_or_update_override!(
self,
plan_key,
source: source,
called_method_name: "assign_pricing_plan!"
)
end
|
#attention_required_for_limit?(limit_key) ⇒ Boolean
374
375
376
|
# File 'lib/pricing_plans/plan_owner.rb', line 374
def attention_required_for_limit?(limit_key)
PricingPlans.attention_required?(self, limit_key)
end
|
#clear_pricing_plan_override! ⇒ Object
#current_pricing_plan ⇒ Object
#current_pricing_plan_resolution ⇒ Object
201
202
203
|
# File 'lib/pricing_plans/plan_owner.rb', line 201
def current_pricing_plan_resolution
PlanResolver.resolution_for(self)
end
|
#current_pricing_plan_source ⇒ Object
205
206
207
|
# File 'lib/pricing_plans/plan_owner.rb', line 205
def current_pricing_plan_source
current_pricing_plan_resolution.source
end
|
#earliest_grace_ends_at_for(*limit_keys) ⇒ Object
326
327
328
329
|
# File 'lib/pricing_plans/plan_owner.rb', line 326
def earliest_grace_ends_at_for(*limit_keys)
times = limit_keys.flatten.map { |k| GraceManager.grace_ends_at(self, k) }.compact
times.min
end
|
#grace_active_for?(limit_key) ⇒ Boolean
Per-limit grace helpers managed by PricingPlans
299
300
301
|
# File 'lib/pricing_plans/plan_owner.rb', line 299
def grace_active_for?(limit_key)
GraceManager.grace_active?(self, limit_key)
end
|
#grace_ends_at_for(limit_key) ⇒ Object
303
304
305
|
# File 'lib/pricing_plans/plan_owner.rb', line 303
def grace_ends_at_for(limit_key)
GraceManager.grace_ends_at(self, limit_key)
end
|
#grace_remaining_days_for(limit_key) ⇒ Object
313
314
315
|
# File 'lib/pricing_plans/plan_owner.rb', line 313
def grace_remaining_days_for(limit_key)
(grace_remaining_seconds_for(limit_key) / 86_400.0).ceil
end
|
#grace_remaining_seconds_for(limit_key) ⇒ Object
307
308
309
310
311
|
# File 'lib/pricing_plans/plan_owner.rb', line 307
def grace_remaining_seconds_for(limit_key)
ends_at = grace_ends_at_for(limit_key)
return 0 unless ends_at
[0, (ends_at - Time.current).to_i].max
end
|
#has_plan_assignment? ⇒ Boolean
214
215
216
|
# File 'lib/pricing_plans/plan_owner.rb', line 214
def has_plan_assignment?
pricing_plan_overridden?
end
|
#limit(limit_key) ⇒ Object
Rails-y wrappers for usage/status (defaults to all configured limits)
332
333
334
|
# File 'lib/pricing_plans/plan_owner.rb', line 332
def limit(limit_key)
PricingPlans.status(self, limits: [limit_key]).first
end
|
#limit_alert(limit_key) ⇒ Object
Returns the pure-data alert view model for a single limit key
388
389
390
|
# File 'lib/pricing_plans/plan_owner.rb', line 388
def limit_alert(limit_key)
PricingPlans.alert_for(self, limit_key)
end
|
#limit_message(limit_key) ⇒ Object
366
367
368
|
# File 'lib/pricing_plans/plan_owner.rb', line 366
def limit_message(limit_key)
PricingPlans.message_for(self, limit_key)
end
|
#limit_overage(limit_key) ⇒ Object
370
371
372
|
# File 'lib/pricing_plans/plan_owner.rb', line 370
def limit_overage(limit_key)
PricingPlans.overage_for(self, limit_key)
end
|
#limit_severity(limit_key) ⇒ Object
View-friendly, English-like convenience helpers (pure data decisions)
362
363
364
|
# File 'lib/pricing_plans/plan_owner.rb', line 362
def limit_severity(limit_key)
PricingPlans.severity_for(self, limit_key)
end
|
#limits(*limit_keys) ⇒ Object
336
337
338
339
|
# File 'lib/pricing_plans/plan_owner.rb', line 336
def limits(*limit_keys)
keys = normalize_limit_keys(limit_keys)
PricingPlans.status(self, limits: keys)
end
|
#limits_message(*limit_keys) ⇒ Object
350
351
352
353
|
# File 'lib/pricing_plans/plan_owner.rb', line 350
def limits_message(*limit_keys)
keys = normalize_limit_keys(limit_keys)
PricingPlans.combine_messages_for(self, *keys)
end
|
#limits_overview(*limit_keys) ⇒ Object
Global overview for banner building
356
357
358
359
|
# File 'lib/pricing_plans/plan_owner.rb', line 356
def limits_overview(*limit_keys)
keys = normalize_limit_keys(limit_keys)
PricingPlans.overview_for(self, *keys)
end
|
#limits_severity(*limit_keys) ⇒ Object
345
346
347
348
|
# File 'lib/pricing_plans/plan_owner.rb', line 345
def limits_severity(*limit_keys)
keys = normalize_limit_keys(limit_keys)
PricingPlans.highest_severity_for(self, *keys)
end
|
#limits_summary(*limit_keys) ⇒ Object
341
342
343
|
# File 'lib/pricing_plans/plan_owner.rb', line 341
def limits_summary(*limit_keys)
limits(*limit_keys)
end
|
#on_free_plan? ⇒ Boolean
209
210
211
212
|
# File 'lib/pricing_plans/plan_owner.rb', line 209
def on_free_plan?
plan = current_pricing_plan || PricingPlans::Registry.default_plan
plan&.free? || false
end
|
#override_pricing_plan!(plan_key, source:) ⇒ Object
#pay_on_grace_period? ⇒ Boolean
293
294
295
296
|
# File 'lib/pricing_plans/plan_owner.rb', line 293
def pay_on_grace_period?
sub = PaySupport.current_subscription_for(self)
!!(sub && sub.respond_to?(:on_grace_period?) && sub.on_grace_period?)
end
|
#pay_on_trial? ⇒ Boolean
288
289
290
291
|
# File 'lib/pricing_plans/plan_owner.rb', line 288
def pay_on_trial?
sub = PaySupport.current_subscription_for(self)
!!(sub && sub.respond_to?(:on_trial?) && sub.on_trial?)
end
|
#pay_subscription_active? ⇒ Boolean
Pay (Stripe) convenience wrappers (return false/nil if Pay not available)
Pay (Stripe) state — billing-facing, NOT used by our enforcement logic
#plan_allows?(feature_key) ⇒ Boolean
261
262
263
264
|
# File 'lib/pricing_plans/plan_owner.rb', line 261
def plan_allows?(feature_key)
plan = current_pricing_plan
plan&.allows_feature?(feature_key) || false
end
|
#plan_assignment ⇒ Object
223
224
225
|
# File 'lib/pricing_plans/plan_owner.rb', line 223
def plan_assignment
pricing_plan_override
end
|
#plan_blocked_for?(limit_key) ⇒ Boolean
317
318
319
|
# File 'lib/pricing_plans/plan_owner.rb', line 317
def plan_blocked_for?(limit_key)
GraceManager.should_block?(self, limit_key)
end
|
#plan_cta ⇒ Object
383
384
385
|
# File 'lib/pricing_plans/plan_owner.rb', line 383
def plan_cta
PricingPlans.cta_for(self)
end
|
#plan_limit_percent_used(limit_key) ⇒ Object
#plan_limit_remaining(limit_key) ⇒ Object
#pricing_plan_overridden? ⇒ Boolean
218
219
220
221
|
# File 'lib/pricing_plans/plan_owner.rb', line 218
def pricing_plan_overridden?
return false unless respond_to?(:id) && id.present?
Assignment.exists?(plan_owner_type: self.class.name, plan_owner_id: id)
end
|
#pricing_plan_override ⇒ Object
227
228
229
230
|
# File 'lib/pricing_plans/plan_owner.rb', line 227
def pricing_plan_override
return nil unless respond_to?(:id) && id.present?
Assignment.find_by(plan_owner_type: self.class.name, plan_owner_id: id)
end
|
#pricing_plan_override_source ⇒ Object
232
233
234
|
# File 'lib/pricing_plans/plan_owner.rb', line 232
def pricing_plan_override_source
pricing_plan_override&.source
end
|
#remove_pricing_plan! ⇒ Object
253
254
255
256
257
258
|
# File 'lib/pricing_plans/plan_owner.rb', line 253
def remove_pricing_plan!
LegacyPlanAssignmentApi.clear_override!(
self,
called_method_name: "remove_pricing_plan!"
)
end
|
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
277
278
279
280
|
# File 'lib/pricing_plans/plan_owner.rb', line 277
def respond_to_missing?(method_name, include_private = false)
return true if method_name.to_s.start_with?("plan_allows_") && method_name.to_s.end_with?("?")
super
end
|
#within_plan_limits?(limit_key, by: 1) ⇒ Boolean
185
186
187
|
# File 'lib/pricing_plans/plan_owner.rb', line 185
def within_plan_limits?(limit_key, by: 1)
LimitChecker.within_limit?(self, limit_key, by: by)
end
|