Class: PricingPlans::GraceManager

Inherits:
Object
  • Object
show all
Extended by:
ExceededStateUtils
Defined in:
lib/pricing_plans/grace_manager.rb

Class Method Summary collapse

Methods included from ExceededStateUtils

clear_exceeded_flags!, exceeded_now?

Class Method Details

.grace_active?(plan_owner, limit_key) ⇒ Boolean

Returns:

  • (Boolean)


37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/pricing_plans/grace_manager.rb', line 37

def grace_active?(plan_owner, limit_key)
  state = fresh_state_or_nil(plan_owner, limit_key)
  return false unless state&.exceeded?

  plan = PlanResolver.effective_plan_for(plan_owner)
  limit_config = plan&.limit_for(limit_key)
  unless currently_exceeded?(plan_owner, limit_key, limit_config)
    clear_exceeded_flags!(state)
    return false
  end

  !state.grace_expired?
end

.grace_ends_at(plan_owner, limit_key) ⇒ Object



135
136
137
138
# File 'lib/pricing_plans/grace_manager.rb', line 135

def grace_ends_at(plan_owner, limit_key)
  state = fresh_state_or_nil(plan_owner, limit_key)
  state&.grace_ends_at
end

.mark_blocked!(plan_owner, limit_key) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/pricing_plans/grace_manager.rb', line 81

def mark_blocked!(plan_owner, limit_key)
  with_lock(plan_owner, limit_key) do |state|
    state = ensure_fresh_state_for_current_window!(state, plan_owner, limit_key)
    return state if state.blocked?

    state.update!(blocked_at: Time.current)

    # Emit block event
    emit_block_event(plan_owner, limit_key)

    state
  end
end

.mark_exceeded!(plan_owner, limit_key, grace_period: nil) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/pricing_plans/grace_manager.rb', line 7

def mark_exceeded!(plan_owner, limit_key, grace_period: nil)
  with_lock(plan_owner, limit_key) do |state|
    # Ensure state is for the current window for per-period limits
    state = ensure_fresh_state_for_current_window!(state, plan_owner, limit_key)

    return state if state.exceeded?

    plan = PlanResolver.effective_plan_for(plan_owner)
    limit_config = plan&.limit_for(limit_key)

    grace_period ||= limit_config&.dig(:grace) || 7.days

    state.update!(
      exceeded_at: Time.current,
      data: state.data.merge(
        "grace_period" => grace_period.to_i,

        # Track window for per-period limits
        "window_start_epoch" => current_window_start_if_per(limit_config, plan_owner, limit_key)&.to_i,
        "window_end_epoch" => current_window_end_if_per(limit_config, plan_owner, limit_key)&.to_i
      )
    )

    # Emit grace start event
    emit_grace_start_event(plan_owner, limit_key, state.grace_ends_at)

    state
  end
end

.maybe_emit_warning!(plan_owner, limit_key, threshold) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/pricing_plans/grace_manager.rb', line 95

def maybe_emit_warning!(plan_owner, limit_key, threshold)
  with_lock(plan_owner, limit_key) do |state|
    state = ensure_fresh_state_for_current_window!(state, plan_owner, limit_key)
    last_threshold = state.last_warning_threshold || 0.0

    # Only emit if this is a higher threshold than last time
    if threshold > last_threshold
      plan = PlanResolver.effective_plan_for(plan_owner)
      limit_config = plan&.limit_for(limit_key)
      window_start_epoch = nil
      window_end_epoch = nil
      if limit_config && limit_config[:per]
        period_start, period_end = PeriodCalculator.window_for(plan_owner, limit_key)
        window_start_epoch = period_start.to_i
        window_end_epoch = period_end.to_i
      end

      state.update!(
        last_warning_threshold: threshold,
        last_warning_at: Time.current,
        data: state.data.merge(
          "window_start_epoch" => window_start_epoch,
          "window_end_epoch" => window_end_epoch
        )
      )

      emit_warning_event(plan_owner, limit_key, threshold)
    end

    state
  end
end

.reset_state!(plan_owner, limit_key) ⇒ Object



128
129
130
131
132
133
# File 'lib/pricing_plans/grace_manager.rb', line 128

def reset_state!(plan_owner, limit_key)
  state = find_state(plan_owner, limit_key)
  return unless state

  state.destroy!
end

.should_block?(plan_owner, limit_key) ⇒ Boolean

Returns:

  • (Boolean)


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/pricing_plans/grace_manager.rb', line 51

def should_block?(plan_owner, limit_key)
  plan = PlanResolver.effective_plan_for(plan_owner)
  limit_config = plan&.limit_for(limit_key)
  return false unless limit_config

  after_limit = limit_config[:after_limit]
  return false if after_limit == :just_warn

  # Only block when usage has reached or exceeded the configured limit
  limit_amount = limit_config[:to]
  return false if limit_amount == :unlimited
  current_usage = LimitChecker.current_usage_for(plan_owner, limit_key, limit_config)
  exceeded = exceeded_now?(current_usage, limit_amount, after_limit: after_limit)

  unless exceeded
    if (state = fresh_state_or_nil(plan_owner, limit_key))
      clear_exceeded_flags!(state)
    end
    return false
  end

  return true if after_limit == :block_usage

  # For :grace_then_block, check if grace period expired
  state = fresh_state_or_nil(plan_owner, limit_key)
  return false unless state&.exceeded?

  state.grace_expired?
end