Module: UsageCredits::PeriodParser

Defined in:
lib/usage_credits/helpers/period_parser.rb

Overview

Handles parsing and normalization of time periods throughout the gem. Converts strings like "1.month" or symbols like :monthly into ActiveSupport::Duration objects.

Constant Summary collapse

VALID_PERIODS =

Canonical periods and their aliases

{
  second: [:second, :seconds],        # 1.second
  minute: [:minute, :minutes],        # 1.minute
  hour: [:hour, :hours, :hourly],     # 1.hour
  day: [:day, :daily],                # 1.day
  week: [:week, :weekly],             # 1.week
  month: [:month, :monthly],          # 1.month
  quarter: [:quarter, :quarterly],    # 3.months
  year: [:year, :yearly, :annually]   # 1.year
}.freeze
MIN_PERIOD =

Deprecated: Use UsageCredits.configuration.min_fulfillment_period instead

1.day

Class Method Summary collapse

Class Method Details

.canonical_unit_for(unit) ⇒ Symbol

Map any alias to its canonical unit method name

Parameters:

  • unit (Symbol)

    The unit symbol (e.g., :hourly, :seconds, :day)

Returns:

  • (Symbol)

    The canonical unit method (e.g., :hour, :second, :day)



96
97
98
99
100
101
102
103
104
# File 'lib/usage_credits/helpers/period_parser.rb', line 96

def canonical_unit_for(unit)
  # Find which canonical unit this alias belongs to
  VALID_PERIODS.each do |canonical, aliases|
    return canonical if aliases.include?(unit)
  end

  # Fallback to the unit itself if not found (shouldn't happen if validation passed)
  unit
end

.min_fulfillment_periodObject

Get the configured minimum fulfillment period



25
26
27
28
29
30
31
32
# File 'lib/usage_credits/helpers/period_parser.rb', line 25

def min_fulfillment_period
  # Use configured value if available, otherwise fall back to default
  if defined?(UsageCredits) && UsageCredits.respond_to?(:configuration)
    UsageCredits.configuration.min_fulfillment_period
  else
    MIN_PERIOD
  end
end

.normalize_period(period) ⇒ Object

Turns things like :monthly into 1.month to always store consistent time periods



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/usage_credits/helpers/period_parser.rb', line 35

def normalize_period(period)
  return nil unless period

  # Handle ActiveSupport::Duration objects directly
  if period.is_a?(ActiveSupport::Duration)
    min_period = min_fulfillment_period
    raise ArgumentError, "Period must be at least #{min_period.inspect}" if period < min_period
    period
  else
    # Convert symbols to canonical durations
    duration = case period
    when *VALID_PERIODS[:second] then 1.second
    when *VALID_PERIODS[:minute] then 1.minute
    when *VALID_PERIODS[:hour] then 1.hour
    when *VALID_PERIODS[:day] then 1.day
    when *VALID_PERIODS[:week] then 1.week
    when *VALID_PERIODS[:month] then 1.month
    when *VALID_PERIODS[:quarter] then 3.months
    when *VALID_PERIODS[:year] then 1.year
    else
      raise ArgumentError, "Unsupported period: #{period}. Supported periods: #{VALID_PERIODS.values.flatten.inspect}"
    end

    min_period = min_fulfillment_period
    raise ArgumentError, "Period must be at least #{min_period.inspect}" if duration < min_period
    duration
  end
end

.parse_period(period_str) ⇒ ActiveSupport::Duration

Parse a period string into an ActiveSupport::Duration

Parameters:

  • period_str (String, ActiveSupport::Duration)

    A string like "1.month" or "1 month" or an existing duration

Returns:

  • (ActiveSupport::Duration)

    The parsed duration

Raises:

  • (ArgumentError)

    If the period string is invalid



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/usage_credits/helpers/period_parser.rb', line 68

def parse_period(period_str)
  return period_str if period_str.is_a?(ActiveSupport::Duration)

  if period_str.to_s =~ /\A(\d+)[.\s](\w+)\z/
    amount = $1.to_i
    unit = $2.singularize.to_sym

    # Validate the unit is supported
    valid_units = VALID_PERIODS.values.flatten
    unless valid_units.include?(unit)
      raise ArgumentError, "Unsupported period unit: #{unit}. Supported units: #{valid_units.inspect}"
    end

    # Map alias to canonical unit (e.g., :hourly -> :hour, :seconds -> :second)
    canonical_unit = canonical_unit_for(unit)

    duration = amount.send(canonical_unit)
    min_period = min_fulfillment_period
    raise ArgumentError, "Period must be at least #{min_period.inspect}" if duration < min_period
    duration
  else
    raise ArgumentError, "Invalid period format: #{period_str}. Expected format: '1.month', '2 months', etc."
  end
end

.valid_period_format?(period_str) ⇒ Boolean

Validates that a period string matches the expected format and units

Returns:

  • (Boolean)


107
108
109
110
111
112
# File 'lib/usage_credits/helpers/period_parser.rb', line 107

def valid_period_format?(period_str)
  parse_period(period_str)
  true
rescue ArgumentError
  false
end