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

Methods included from JsonHelpers

json_extract

Class Method Details

.active_subscribersObject

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_revenueObject



47
48
49
# File 'lib/profitable/metrics.rb', line 47

def all_time_revenue
  NumericResult.new(calculate_all_time_revenue)
end

.arrObject

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_customerObject



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_valueObject



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

.mrrObject

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_milestoneObject



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_customersObject

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_subscribersObject

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

.ttmObject

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_revenueObject

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