Module: Profitable
- Extended by:
- ActionView::Helpers::NumberHelper, JsonHelpers
- Defined in:
- lib/profitable/error.rb,
lib/profitable/engine.rb,
lib/profitable/metrics.rb,
lib/profitable/version.rb,
lib/profitable/json_helpers.rb,
lib/profitable/mrr_calculator.rb,
lib/profitable/numeric_result.rb,
lib/profitable/processors/base.rb,
app/controllers/profitable/base_controller.rb,
lib/profitable/processors/stripe_processor.rb,
lib/profitable/processors/braintree_processor.rb,
app/controllers/profitable/dashboard_controller.rb,
lib/profitable/processors/paddle_billing_processor.rb,
lib/profitable/processors/paddle_classic_processor.rb
Defined Under Namespace
Modules: JsonHelpers, Processors Classes: BaseController, DashboardController, Engine, Error, MrrCalculator, NumericResult
Constant Summary collapse
- TRIAL_SUBSCRIPTION_STATUSES =
Pay exposes some processor-specific status variants beyond the core generic list. We normalize them into business-meaningful groups so current-state metrics, historical event metrics, and churn denominators all behave consistently.
-
Trial statuses bill nothing until the trial actually ends (Stripe: trialing, Lemon Squeezy: on_trial).
-
Churned statuses stop billing at ends_at (Stripe: canceled, Paddle Classic: deleted, Braintree/Lemon Squeezy: expired, legacy Pay: ended/cancelled).
-
Never-billable statuses either failed to start billing (Stripe: incomplete, incomplete_expired, unpaid) or have not started billing yet (Braintree: pending).
-
['trialing', 'on_trial'].freeze
- CHURNED_STATUSES =
['canceled', 'cancelled', 'ended', 'deleted', 'expired'].freeze
- NEVER_BILLABLE_SUBSCRIPTION_STATUSES =
['incomplete', 'incomplete_expired', 'unpaid', 'pending'].freeze
- DEFAULT_PERIOD =
30.days
- MRR_MILESTONES =
[5, 10, 20, 30, 50, 75, 100, 200, 300, 400, 500, 1_000, 2_000, 3_000, 5_000, 10_000, 20_000, 30_000, 50_000, 83_333, 100_000, 250_000, 500_000, 1_000_000, 5_000_000, 10_000_000, 25_000_000, 50_000_000, 75_000_000, 100_000_000]
- VERSION =
"0.6.0"
Constants included from JsonHelpers
JsonHelpers::VALID_JSON_KEY_PATTERN, JsonHelpers::VALID_TABLE_COLUMN_PATTERN
Class Method Summary collapse
-
.active_subscribers ⇒ Object
Customers with subscriptions that are billable right now.
- .all_time_revenue ⇒ Object
-
.arr ⇒ Object
Annual Recurring Revenue (ARR) based on the current recurring base.
- .average_revenue_per_customer ⇒ Object
- .churn(in_the_last: DEFAULT_PERIOD) ⇒ Object
- .churned_customers(in_the_last: DEFAULT_PERIOD) ⇒ Object
- .churned_mrr(in_the_last: DEFAULT_PERIOD) ⇒ Object
- .daily_summary(days: 30) ⇒ Object
- .estimated_arr_valuation(multiplier = nil, at: nil, multiple: nil) ⇒ Object
- .estimated_revenue_run_rate_valuation(multiplier = nil, at: nil, multiple: nil, in_the_last: DEFAULT_PERIOD) ⇒ Object
- .estimated_ttm_revenue_valuation(multiplier = nil, at: nil, multiple: nil) ⇒ Object
-
.estimated_valuation(multiplier = nil, at: nil, multiple: nil) ⇒ Object
Backwards-compatible ARR-multiple heuristic for a quick valuation estimate.
- .lifetime_value ⇒ Object
- .monthly_summary(months: 12) ⇒ Object
-
.mrr ⇒ Object
Monthly Recurring Revenue (MRR) from subscriptions that are billable right now.
-
.mrr_at(date) ⇒ Object
Historical MRR snapshot from subscriptions that were billable at the given date.
- .mrr_growth(in_the_last: DEFAULT_PERIOD) ⇒ Object
- .mrr_growth_rate(in_the_last: DEFAULT_PERIOD) ⇒ Object
-
.new_customers(in_the_last: DEFAULT_PERIOD) ⇒ Object
First-time customers added in the period, based on first monetization date rather than signup date.
-
.new_mrr(in_the_last: DEFAULT_PERIOD) ⇒ Object
Full monthly value of subscriptions that became billable in the period.
-
.new_subscribers(in_the_last: DEFAULT_PERIOD) ⇒ Object
Customers whose subscriptions first became billable in the period.
- .period_data(in_the_last: DEFAULT_PERIOD) ⇒ Object
- .recurring_revenue_in_period(in_the_last: DEFAULT_PERIOD) ⇒ Object
- .recurring_revenue_percentage(in_the_last: DEFAULT_PERIOD) ⇒ Object
-
.revenue_in_period(in_the_last: DEFAULT_PERIOD) ⇒ Object
Historical revenue collected over a rolling period.
- .revenue_run_rate(in_the_last: DEFAULT_PERIOD) ⇒ Object
- .time_to_next_mrr_milestone ⇒ Object
-
.total_customers ⇒ Object
Customers who have actually monetized: either a paid charge or a subscription that has crossed into a billable state.
-
.total_subscribers ⇒ Object
Customers who have ever had a paid subscription.
-
.ttm ⇒ Object
Founder-friendly shorthand for trailing-twelve-month revenue.
-
.ttm_revenue ⇒ Object
Trailing twelve-month revenue reflects actual cash collected in the last year.
Methods included from JsonHelpers
Class Method Details
.active_subscribers ⇒ Object
Customers with subscriptions that are billable right now. Excludes free trials, paused subscriptions, and churned subscriptions.
116 117 118 |
# File 'lib/profitable/metrics.rb', line 116 def active_subscribers NumericResult.new(calculate_active_subscribers, :integer) end |
.all_time_revenue ⇒ Object
47 48 49 |
# File 'lib/profitable/metrics.rb', line 47 def all_time_revenue NumericResult.new(calculate_all_time_revenue) end |
.arr ⇒ Object
Annual Recurring Revenue (ARR) based on the current recurring base. This is today’s MRR annualized, not historical 12-month revenue.
39 40 41 |
# File 'lib/profitable/metrics.rb', line 39 def arr NumericResult.new(calculate_arr) end |
.average_revenue_per_customer ⇒ Object
146 147 148 |
# File 'lib/profitable/metrics.rb', line 146 def average_revenue_per_customer NumericResult.new(calculate_average_revenue_per_customer) end |
.churn(in_the_last: DEFAULT_PERIOD) ⇒ Object
43 44 45 |
# File 'lib/profitable/metrics.rb', line 43 def churn(in_the_last: DEFAULT_PERIOD) NumericResult.new(calculate_churn(in_the_last), :percentage) end |
.churned_customers(in_the_last: DEFAULT_PERIOD) ⇒ Object
132 133 134 |
# File 'lib/profitable/metrics.rb', line 132 def churned_customers(in_the_last: DEFAULT_PERIOD) NumericResult.new(calculate_churned_customers(in_the_last), :integer) end |
.churned_mrr(in_the_last: DEFAULT_PERIOD) ⇒ Object
142 143 144 |
# File 'lib/profitable/metrics.rb', line 142 def churned_mrr(in_the_last: DEFAULT_PERIOD) NumericResult.new(calculate_churned_mrr(in_the_last)) end |
.daily_summary(days: 30) ⇒ Object
170 171 172 |
# File 'lib/profitable/metrics.rb', line 170 def daily_summary(days: 30) calculate_daily_summary(days) end |
.estimated_arr_valuation(multiplier = nil, at: nil, multiple: nil) ⇒ Object
88 89 90 91 |
# File 'lib/profitable/metrics.rb', line 88 def estimated_arr_valuation(multiplier = nil, at: nil, multiple: nil) actual_multiplier = multiplier || at || multiple || 3 NumericResult.new(calculate_estimated_valuation_from(arr.to_i, actual_multiplier)) end |
.estimated_revenue_run_rate_valuation(multiplier = nil, at: nil, multiple: nil, in_the_last: DEFAULT_PERIOD) ⇒ Object
98 99 100 101 |
# File 'lib/profitable/metrics.rb', line 98 def estimated_revenue_run_rate_valuation(multiplier = nil, at: nil, multiple: nil, in_the_last: DEFAULT_PERIOD) actual_multiplier = multiplier || at || multiple || 3 NumericResult.new(calculate_estimated_valuation_from(revenue_run_rate(in_the_last:).to_i, actual_multiplier)) end |
.estimated_ttm_revenue_valuation(multiplier = nil, at: nil, multiple: nil) ⇒ Object
93 94 95 96 |
# File 'lib/profitable/metrics.rb', line 93 def estimated_ttm_revenue_valuation(multiplier = nil, at: nil, multiple: nil) actual_multiplier = multiplier || at || multiple || 3 NumericResult.new(calculate_estimated_valuation_from(ttm_revenue.to_i, actual_multiplier)) end |
.estimated_valuation(multiplier = nil, at: nil, multiple: nil) ⇒ Object
Backwards-compatible ARR-multiple heuristic for a quick valuation estimate. This is intentionally simple and should not be treated as a market appraisal.
84 85 86 |
# File 'lib/profitable/metrics.rb', line 84 def estimated_valuation(multiplier = nil, at: nil, multiple: nil) estimated_arr_valuation(multiplier, at:, multiple:) end |
.lifetime_value ⇒ Object
150 151 152 |
# File 'lib/profitable/metrics.rb', line 150 def lifetime_value NumericResult.new(calculate_lifetime_value) end |
.monthly_summary(months: 12) ⇒ Object
166 167 168 |
# File 'lib/profitable/metrics.rb', line 166 def monthly_summary(months: 12) calculate_monthly_summary(months) end |
.mrr ⇒ Object
Monthly Recurring Revenue (MRR) from subscriptions that are billable right now. This is a current recurring run-rate metric, useful for operating momentum and near-term subscription changes.
28 29 30 |
# File 'lib/profitable/metrics.rb', line 28 def mrr NumericResult.new(MrrCalculator.calculate) end |
.mrr_at(date) ⇒ Object
Historical MRR snapshot from subscriptions that were billable at the given date.
33 34 35 |
# File 'lib/profitable/metrics.rb', line 33 def mrr_at(date) NumericResult.new(calculate_mrr_at(date)) end |
.mrr_growth(in_the_last: DEFAULT_PERIOD) ⇒ Object
154 155 156 |
# File 'lib/profitable/metrics.rb', line 154 def mrr_growth(in_the_last: DEFAULT_PERIOD) NumericResult.new(calculate_mrr_growth(in_the_last)) end |
.mrr_growth_rate(in_the_last: DEFAULT_PERIOD) ⇒ Object
158 159 160 |
# File 'lib/profitable/metrics.rb', line 158 def mrr_growth_rate(in_the_last: DEFAULT_PERIOD) NumericResult.new(calculate_mrr_growth_rate(in_the_last), :percentage) end |
.new_customers(in_the_last: DEFAULT_PERIOD) ⇒ Object
First-time customers added in the period, based on first monetization date rather than signup date.
122 123 124 |
# File 'lib/profitable/metrics.rb', line 122 def new_customers(in_the_last: DEFAULT_PERIOD) NumericResult.new(calculate_new_customers(in_the_last), :integer) end |
.new_mrr(in_the_last: DEFAULT_PERIOD) ⇒ Object
Full monthly value of subscriptions that became billable in the period. This is a flow metric, so it still counts subscriptions that churned later in the same window.
138 139 140 |
# File 'lib/profitable/metrics.rb', line 138 def new_mrr(in_the_last: DEFAULT_PERIOD) NumericResult.new(calculate_new_mrr(in_the_last)) end |
.new_subscribers(in_the_last: DEFAULT_PERIOD) ⇒ Object
Customers whose subscriptions first became billable in the period. Trial starts do not count until the trial ends.
128 129 130 |
# File 'lib/profitable/metrics.rb', line 128 def new_subscribers(in_the_last: DEFAULT_PERIOD) NumericResult.new(calculate_new_subscribers(in_the_last), :integer) end |
.period_data(in_the_last: DEFAULT_PERIOD) ⇒ Object
174 175 176 |
# File 'lib/profitable/metrics.rb', line 174 def period_data(in_the_last: DEFAULT_PERIOD) calculate_period_data(in_the_last) end |
.recurring_revenue_in_period(in_the_last: DEFAULT_PERIOD) ⇒ Object
70 71 72 |
# File 'lib/profitable/metrics.rb', line 70 def recurring_revenue_in_period(in_the_last: DEFAULT_PERIOD) NumericResult.new(calculate_recurring_revenue_in_period(in_the_last)) end |
.recurring_revenue_percentage(in_the_last: DEFAULT_PERIOD) ⇒ Object
74 75 76 |
# File 'lib/profitable/metrics.rb', line 74 def recurring_revenue_percentage(in_the_last: DEFAULT_PERIOD) NumericResult.new(calculate_recurring_revenue_percentage(in_the_last), :percentage) end |
.revenue_in_period(in_the_last: DEFAULT_PERIOD) ⇒ Object
Historical revenue collected over a rolling period. Unlike ARR, this is trailing actual revenue rather than a projection.
66 67 68 |
# File 'lib/profitable/metrics.rb', line 66 def revenue_in_period(in_the_last: DEFAULT_PERIOD) NumericResult.new(calculate_revenue_in_period(in_the_last)) end |
.revenue_run_rate(in_the_last: DEFAULT_PERIOD) ⇒ Object
78 79 80 |
# File 'lib/profitable/metrics.rb', line 78 def revenue_run_rate(in_the_last: DEFAULT_PERIOD) NumericResult.new(calculate_revenue_run_rate(in_the_last)) end |
.time_to_next_mrr_milestone ⇒ Object
162 163 164 |
# File 'lib/profitable/metrics.rb', line 162 def time_to_next_mrr_milestone NumericResult.new(calculate_time_to_next_mrr_milestone, :string) end |
.total_customers ⇒ Object
Customers who have actually monetized: either a paid charge or a subscription that has crossed into a billable state.
105 106 107 |
# File 'lib/profitable/metrics.rb', line 105 def total_customers NumericResult.new(calculate_total_customers, :integer) end |
.total_subscribers ⇒ Object
Customers who have ever had a paid subscription. Trial-only subscriptions do not count.
110 111 112 |
# File 'lib/profitable/metrics.rb', line 110 def total_subscribers NumericResult.new(calculate_total_subscribers, :integer) end |
.ttm ⇒ Object
Founder-friendly shorthand for trailing-twelve-month revenue. We keep the explicit ttm_revenue name as the canonical API because bare “TTM” is ambiguous in finance once profit metrics enter the picture.
60 61 62 |
# File 'lib/profitable/metrics.rb', line 60 def ttm ttm_revenue end |
.ttm_revenue ⇒ Object
Trailing twelve-month revenue reflects actual cash collected in the last year. It complements ARR, which annualizes the current recurring base.
53 54 55 |
# File 'lib/profitable/metrics.rb', line 53 def ttm_revenue revenue_in_period(in_the_last: 12.months) end |