profitable 0.2.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 506d907bbcbaa811d4986d49fffdc7f1874c77528fb99996f49621834a15e263
4
- data.tar.gz: 6e37aaa616a3238b85cacfd4d3b9373781b4632fb4e4e36ad7946a8d71fa066e
3
+ metadata.gz: f24a9849fbf5f3d6b87beaa3f0a4100d18a45ccb2b585e8e48b417d303759593
4
+ data.tar.gz: 7c8523a9547f30468ee7517a92af604aa66c7bcc269e6292200d8722f83c30ff
5
5
  SHA512:
6
- metadata.gz: aafa273d6430cd1e4c30dada038bdc83a9ecacec8c2d9be0f9f6bad642dc818399dbb494876244eb50c96c1aa3af7932ef43f56e839ab11a8b8f036a375f822f
7
- data.tar.gz: 7dca54330874ae397c77b2400364429509dffc5de96a2096fe963984455ff4c46e67849c5305218086684beb7b1d5300815f49af252ba9e875c028c42b717c1a
6
+ metadata.gz: 4b79c94e7288c859f7419a8afe27c4352db1be8cff2c269b9d9d57348761a59e5f3a48cfe4b601496a1d4642b88fd5e2e5f349a1733f133488bb47a153b6a453
7
+ data.tar.gz: 216578eb9882a946fed3818c7d9301449727cd2b1bc64b282e90e1d8cf056b2f05ff801e49748ec46cf89a008771d796ec65daf9ce99d7ed058da5fa0d284e01
data/.simplecov ADDED
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SimpleCov configuration file (auto-loaded before test suite)
4
+ # This keeps test_helper.rb clean and follows best practices
5
+
6
+ SimpleCov.start do
7
+ # Use SimpleFormatter for terminal-only output (no HTML generation)
8
+ formatter SimpleCov::Formatter::SimpleFormatter
9
+
10
+ # Track coverage for the lib directory (gem source code)
11
+ add_filter "/test/"
12
+
13
+ # Exclude Rails engine components that require integration testing
14
+ # These are tested via Appraisal with a full Rails app
15
+ add_filter "/lib/profitable/engine.rb"
16
+ add_filter "/app/"
17
+
18
+ # Exclude the main profitable.rb entry point - it loads the engine and
19
+ # defines the Profitable module. Our unit tests use a test-specific
20
+ # Profitable module (defined in test_helper.rb) to avoid Rails dependencies.
21
+ # The core logic is tested via the individual lib/profitable/*.rb files.
22
+ add_filter "/lib/profitable.rb"
23
+
24
+ # Track the lib directory (core gem logic)
25
+ track_files "lib/**/*.rb"
26
+
27
+ # Enable branch coverage for more detailed metrics
28
+ enable_coverage :branch
29
+
30
+ # Set minimum coverage threshold for the core calculation logic
31
+ # The Rails engine/controllers are tested separately via Appraisal
32
+ minimum_coverage line: 80, branch: 70
33
+
34
+ # Disambiguate parallel test runs
35
+ command_name "Job #{ENV['TEST_ENV_NUMBER']}" if ENV['TEST_ENV_NUMBER']
36
+ end
37
+
38
+ # Print coverage summary to terminal after tests complete
39
+ SimpleCov.at_exit do
40
+ SimpleCov.result.format!
41
+ puts "\n" + "=" * 60
42
+ puts "COVERAGE SUMMARY"
43
+ puts "=" * 60
44
+ puts "Line Coverage: #{SimpleCov.result.covered_percent.round(2)}%"
45
+ puts "Branch Coverage: #{SimpleCov.result.coverage_statistics[:branch]&.percent&.round(2) || 'N/A'}%"
46
+ puts "=" * 60
47
+ end
data/AGENTS.md ADDED
@@ -0,0 +1,5 @@
1
+ # AGENTS.md
2
+
3
+ This file provides guidance to AI Agents (like OpenAI's Codex, Cursor Agent, Claude Code, etc) when working with code in this repository.
4
+
5
+ Please go ahead and read the full context for this project at `.cursor/rules/0-overview.mdc` and `.cursor/rules/1-quality.mdc` now. Also read the README for a good overview of the project.
data/Appraisals ADDED
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Test minimum supported Rails version (with latest Pay)
4
+ appraise "rails-7.2" do
5
+ gem "rails", "~> 7.2.0"
6
+ gem "pay", "~> 11.0"
7
+ gem "stripe", "~> 18.0"
8
+ end
9
+
10
+ # Test latest Rails version (with latest Pay)
11
+ appraise "rails-8.1" do
12
+ gem "rails", "~> 8.1.0"
13
+ gem "pay", "~> 11.0"
14
+ gem "stripe", "~> 18.0"
15
+ end
16
+
17
+ # Test against Pay 7.x (original minimum supported version)
18
+ appraise "pay-7.3" do
19
+ gem "pay", "~> 7.3.0"
20
+ gem "stripe", "~> 12.0"
21
+ gem "rails", "~> 8.1.0"
22
+ end
23
+
24
+ # Test against Pay 8.x
25
+ appraise "pay-8.3" do
26
+ gem "pay", "~> 8.3.0"
27
+ gem "stripe", "~> 13.0"
28
+ gem "rails", "~> 8.1.0"
29
+ end
30
+
31
+ # Test against Pay 9.x
32
+ appraise "pay-9.0" do
33
+ gem "pay", "~> 9.0.0"
34
+ gem "stripe", "~> 13.0"
35
+ gem "rails", "~> 8.1.0"
36
+ end
37
+
38
+ # Test against Pay 10.x (newly supported version with object column)
39
+ appraise "pay-10.0" do
40
+ gem "pay", "~> 10.0.0"
41
+ gem "stripe", "~> 15.0"
42
+ gem "rails", "~> 8.1.0"
43
+ end
44
+
45
+ # Test against Pay 11.x (latest version as of 2025)
46
+ appraise "pay-11.0" do
47
+ gem "pay", "~> 11.0"
48
+ gem "stripe", "~> 18.0"
49
+ gem "rails", "~> 8.1.0"
50
+ end
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # `profitable`
2
2
 
3
+ ## [0.4.0] - 2026-02-10
4
+ - Add monthly summary (12mo) and daily summary (30d) tables to dashboard
5
+ - Add `period_data` method for efficient batch computation of period metrics
6
+ - Fix `new_mrr` counting incomplete/unpaid subscriptions (now only counts active)
7
+ - Fix `new_subscribers` not filtering out trialing/paused subscriptions
8
+ - DRY up period methods (churn, churned_customers, new_mrr, etc.) via `_in_period` delegation
9
+ - Optimize dashboard from ~176 to 38 queries (batch summary queries, precompute in controller)
10
+
11
+ ## [0.3.0] - 2026-01-01
12
+ - Add Pay v10+ support, comprehensive Minitest test suite, and 16 critical bugfixes re: wrong calculations
13
+
3
14
  ## [0.2.3] - 2024-09-01
4
15
 
5
16
  - Fix the `time_to_next_mrr_milestone` estimation and make it accurate to the day
data/CLAUDE.md ADDED
@@ -0,0 +1,5 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ Please go ahead and read the full context for this project at `.cursor/rules/0-overview.mdc` and `.cursor/rules/1-quality.mdc` now. Also read the README for a good overview of the project.
data/README.md CHANGED
@@ -1,8 +1,11 @@
1
- # 💸 `profitable` - SaaS metrics for your Rails app
1
+ # 💸 `profitable` - MRR dashboard & SaaS metrics for your Rails app
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/profitable.svg)](https://badge.fury.io/rb/profitable)
3
+ [![Gem Version](https://badge.fury.io/rb/profitable.svg)](https://badge.fury.io/rb/profitable) [![Build Status](https://github.com/rameerez/profitable/workflows/Tests/badge.svg)](https://github.com/rameerez/profitable/actions)
4
4
 
5
- Calculate the MRR, ARR, churn, LTV, ARPU, total revenue & estimated valuation of your `pay`-powered Rails SaaS app, and display them in a simple dashboard.
5
+ > [!TIP]
6
+ > **🚀 Ship your next Rails app 10x faster!** I've built **[RailsFast](https://railsfast.com/?ref=profitable)**, a production-ready Rails boilerplate template that comes with everything you need to launch a software business in days, not weeks. Go [check it out](https://railsfast.com/?ref=profitable)!
7
+
8
+ `profitable` allows you to calculate the MRR, ARR, churn, LTV, ARPU, total revenue & estimated valuation of your `pay`-powered Rails SaaS app, and display them in a simple dashboard. It also provides handy methods you can use independently if you don't want the full dashboard.
6
9
 
7
10
  ![Profitable gem main dashboard](profitable.webp)
8
11
 
@@ -12,7 +15,7 @@ Calculate the MRR, ARR, churn, LTV, ARPU, total revenue & estimated valuation of
12
15
 
13
16
  Usually, you would look into your Stripe Dashboard or query the Stripe API to know your MRR / ARR / churn – but when you're using `pay`, you already have that data available and auto synced to your own database. So we can leverage it to make handy, composable ActiveRecord queries that you can reuse in any part of your Rails app (dashboards, internal pages, reports, status messages, etc.)
14
17
 
15
- Think doing something like: `"Your app is currently at $#{Profitable.mrr} MRR Estimated to be worth $#{Profitable.valuation_estimate("3x")} at a 3x valuation"`
18
+ Think doing something like: `"Current MRR: $#{Profitable.mrr}"` or `"Your app is worth $#{Profitable.valuation_estimate("3x")} at a 3x valuation"`
16
19
 
17
20
  ## Installation
18
21
 
@@ -133,7 +136,56 @@ Profitable.mrr # => 123456
133
136
 
134
137
  ## Development
135
138
 
136
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
139
+ After checking out the repo, install dependencies:
140
+
141
+ ```bash
142
+ bundle install
143
+ ```
144
+
145
+ ### Running Tests
146
+
147
+ The gem includes a Minitest test suite. Run it with:
148
+
149
+ ```bash
150
+ # Run all tests
151
+ bundle exec rake test
152
+ ```
153
+
154
+ ### Testing Against Multiple Pay Gem Versions
155
+
156
+ This gem uses [Appraisal](https://github.com/thoughtbot/appraisal) to test against multiple versions of the Pay gem, ensuring compatibility across Pay 7.x through 11.x.
157
+
158
+ **Generate appraisal gemfiles:**
159
+
160
+ ```bash
161
+ bundle exec appraisal install
162
+ ```
163
+
164
+ **Run tests against a specific Pay version:**
165
+
166
+ ```bash
167
+ # Test against Pay 10.x
168
+ bundle exec appraisal pay-10.0 rake test
169
+
170
+ # Test against Pay 11.x
171
+ bundle exec appraisal pay-11.0 rake test
172
+ ```
173
+
174
+ **Run tests against all Pay versions:**
175
+
176
+ ```bash
177
+ bundle exec appraisal rake test
178
+ ```
179
+
180
+ ### Database Compatibility
181
+
182
+ Tests run on SQLite by default, but the gem supports:
183
+ - PostgreSQL (9.3+)
184
+ - MySQL (5.7.9+)
185
+ - MariaDB (10.2.7+)
186
+ - SQLite (3.9.0+)
187
+
188
+ The gem automatically detects your database adapter and uses the appropriate JSON query syntax.
137
189
 
138
190
  To install this gem onto your local machine, run `bundle exec rake install`.
139
191
 
data/Rakefile CHANGED
@@ -1,4 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
- task default: %i[]
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ t.warning = false
11
+ end
12
+
13
+ task default: :test
@@ -1,12 +1,24 @@
1
1
  module Profitable
2
2
  class DashboardController < BaseController
3
3
  def index
4
- end
4
+ @mrr = Profitable.mrr
5
+ @mrr_growth_rate = Profitable.mrr_growth_rate
6
+ @total_customers = Profitable.total_customers
7
+ @all_time_revenue = Profitable.all_time_revenue
8
+ @estimated_valuation = Profitable.estimated_valuation
9
+ @average_revenue_per_customer = Profitable.average_revenue_per_customer
10
+ @lifetime_value = Profitable.lifetime_value
5
11
 
6
- private
12
+ @show_milestone = @mrr_growth_rate > 0
13
+ @milestone_message = Profitable.time_to_next_mrr_milestone if @show_milestone
7
14
 
8
- def test
9
- end
15
+ @monthly_summary = Profitable.monthly_summary(months: 12)
16
+ @daily_summary = Profitable.daily_summary(days: 30)
10
17
 
18
+ @periods = [24.hours, 7.days, 30.days]
19
+ @period_data = @periods.each_with_object({}) do |period, hash|
20
+ hash[period] = Profitable.period_data(in_the_last: period)
21
+ end
22
+ end
11
23
  end
12
24
  end
@@ -1,4 +1,9 @@
1
1
  <style>
2
+ main h2.title {
3
+ font-size: 1.25rem;
4
+ margin: 64px 0 12px 0;
5
+ }
6
+
2
7
  .card-grid {
3
8
  display: flex;
4
9
  flex-wrap: wrap;
@@ -32,8 +37,8 @@
32
37
 
33
38
  <header>
34
39
  <h1>💸 <%= Rails.application.class.module_parent_name %></h1>
35
- <% if Profitable.mrr_growth_rate > 0 %>
36
- <p><%= Profitable.time_to_next_mrr_milestone %></p>
40
+ <% if @show_milestone %>
41
+ <p><%= @milestone_message %></p>
37
42
  <% end %>
38
43
  </header>
39
44
 
@@ -41,69 +46,197 @@
41
46
 
42
47
  <div class="card-grid">
43
48
  <div class="card">
44
- <h2><%= Profitable.total_customers.to_readable %></h2>
49
+ <h2><%= @total_customers.to_readable %></h2>
45
50
  <p>total customers</p>
46
51
  </div>
47
52
  <div class="card">
48
- <h2><%= Profitable.mrr.to_readable %></h2>
53
+ <h2><%= @mrr.to_readable %></h2>
49
54
  <p>MRR</p>
50
55
  </div>
51
56
  <div class="card">
52
- <h2><%= Profitable.estimated_valuation.to_readable %></h2>
57
+ <h2><%= @estimated_valuation.to_readable %></h2>
53
58
  <p>Valuation at 3x ARR</p>
54
59
  </div>
55
60
  <div class="card">
56
- <h2><%= Profitable.mrr_growth_rate.to_readable %></h2>
61
+ <h2><%= @mrr_growth_rate.to_readable %></h2>
57
62
  <p>MRR growth rate</p>
58
63
  </div>
59
64
  <div class="card">
60
- <h2><%= Profitable.average_revenue_per_customer.to_readable %></h2>
65
+ <h2><%= @average_revenue_per_customer.to_readable %></h2>
61
66
  <p>ARPC</p>
62
67
  </div>
63
68
  <div class="card">
64
- <h2><%= Profitable.lifetime_value.to_readable %></h2>
69
+ <h2><%= @lifetime_value.to_readable %></h2>
65
70
  <p>LTV</p>
66
71
  </div>
67
72
  <div class="card">
68
- <h2><%= Profitable.all_time_revenue.to_readable %></h2>
73
+ <h2><%= @all_time_revenue.to_readable %></h2>
69
74
  <p>All-time revenue</p>
70
75
  </div>
71
76
  </div>
72
77
 
73
- <% [24.hours, 7.days, 30.days].each do |period| %>
78
+ <h2 class="title">Monthly summary</h2>
79
+ <small>(last 12 months)</small>
80
+
81
+ <style>
82
+ .summary-table {
83
+ width: 100%;
84
+ border-collapse: collapse;
85
+ margin-top: 16px;
86
+ background-color: var(--bg);
87
+ border: 1px solid var(--border);
88
+ border-radius: var(--standard-border-radius);
89
+ overflow: hidden;
90
+ }
91
+
92
+ .summary-table th,
93
+ .summary-table td {
94
+ padding: 12px 16px;
95
+ text-align: left;
96
+ border-bottom: 1px solid var(--border);
97
+ }
98
+
99
+ .summary-table th {
100
+ font-weight: 600;
101
+ background-color: var(--bg);
102
+ position: sticky;
103
+ top: 0;
104
+ }
105
+
106
+ .summary-table tr:last-child td {
107
+ border-bottom: none;
108
+ }
109
+
110
+ .summary-table .positive {
111
+ color: #10b981;
112
+ }
113
+
114
+ .summary-table .negative {
115
+ color: #ef4444;
116
+ }
117
+
118
+ .summary-table .muted {
119
+ color: #6b7280;
120
+ font-size: 0.875em;
121
+ }
122
+
123
+ .summary-table tbody tr:hover {
124
+ background-color: rgba(0, 0, 0, 0.02);
125
+ }
126
+ </style>
127
+
128
+ <table class="summary-table">
129
+ <thead>
130
+ <tr>
131
+ <th>Month</th>
132
+ <th>New</th>
133
+ <th>Churned</th>
134
+ <th>Net</th>
135
+ <th>Churn %</th>
136
+ </tr>
137
+ </thead>
138
+ <tbody>
139
+ <% @monthly_summary.reverse.each do |month_data| %>
140
+ <tr>
141
+ <td><strong><%= month_data[:month_date].strftime('%b %Y') %></strong></td>
142
+ <td>
143
+ <span class="positive">+<%= month_data[:new_subscribers] %></span>
144
+ <span class="muted">(~<%= number_to_currency(month_data[:new_mrr] / 100.0, precision: 0) %>)</span>
145
+ </td>
146
+ <td>
147
+ <% if month_data[:churned_subscribers] > 0 %>
148
+ <span class="negative">-<%= month_data[:churned_subscribers] %></span>
149
+ <span class="muted">(~<%= number_to_currency(month_data[:churned_mrr] / 100.0, precision: 0) %>)</span>
150
+ <% else %>
151
+ <span>0</span>
152
+ <% end %>
153
+ </td>
154
+ <td>
155
+ <% if month_data[:net_subscribers] > 0 %>
156
+ <span class="positive">+<%= month_data[:net_subscribers] %></span>
157
+ <span class="muted">(~+<%= number_to_currency(month_data[:net_mrr] / 100.0, precision: 0) %>)</span>
158
+ <% elsif month_data[:net_subscribers] < 0 %>
159
+ <span class="negative"><%= month_data[:net_subscribers] %></span>
160
+ <span class="muted">(~<%= number_to_currency(month_data[:net_mrr] / 100.0, precision: 0) %>)</span>
161
+ <% else %>
162
+ <span>0</span>
163
+ <% end %>
164
+ </td>
165
+ <td><%= month_data[:churn_rate] %>%</td>
166
+ </tr>
167
+ <% end %>
168
+ </tbody>
169
+ </table>
170
+
171
+ <h2 class="title">Daily summary</h2>
172
+ <small>(last 30 days)</small>
173
+
174
+ <table class="summary-table">
175
+ <thead>
176
+ <tr>
177
+ <th>Date</th>
178
+ <th>New Subscribers</th>
179
+ <th>Churned</th>
180
+ </tr>
181
+ </thead>
182
+ <tbody>
183
+ <% @daily_summary.reverse.each do |day_data| %>
184
+ <tr>
185
+ <td><strong><%= day_data[:date].strftime('%b %-d, %Y') %></strong></td>
186
+ <td>
187
+ <% if day_data[:new_subscribers] > 0 %>
188
+ <span class="positive">+<%= day_data[:new_subscribers] %></span>
189
+ <% else %>
190
+ <span>0</span>
191
+ <% end %>
192
+ </td>
193
+ <td>
194
+ <% if day_data[:churned_subscribers] > 0 %>
195
+ <span class="negative">-<%= day_data[:churned_subscribers] %></span>
196
+ <% else %>
197
+ <span>0</span>
198
+ <% end %>
199
+ </td>
200
+ </tr>
201
+ <% end %>
202
+ </tbody>
203
+ </table>
204
+
205
+ <% @periods.each do |period| %>
74
206
  <% period_short = period.inspect.gsub("days", "d").gsub("hours", "h").gsub(" ", "") %>
207
+ <% data = @period_data[period] %>
75
208
 
76
- <h2>Last <%= period.inspect %></h2>
209
+ <h2 class="title">Last <%= period.inspect %></h2>
77
210
 
78
211
  <div class="card-grid">
79
212
  <div class="card">
80
- <h2><%= Profitable.new_customers(in_the_last: period).to_readable %></h2>
213
+ <h2><%= data[:new_customers].to_readable %></h2>
81
214
  <p>new customers (<%= period_short %>)</p>
82
215
  </div>
83
216
  <div class="card">
84
- <h2><%= Profitable.churned_customers(in_the_last: period).to_readable %></h2>
217
+ <h2><%= data[:churned_customers].to_readable %></h2>
85
218
  <p>churned customers (<%= period_short %>)</p>
86
219
  </div>
87
220
  <div class="card">
88
- <h2><%= Profitable.churn(in_the_last: period).to_readable %></h2>
221
+ <h2><%= data[:churn].to_readable %></h2>
89
222
  <p>churn (<%= period_short %>)</p>
90
223
  </div>
91
224
 
92
225
  <div class="card">
93
- <h2><%= Profitable.new_mrr(in_the_last: period).to_readable %></h2>
226
+ <h2><%= data[:new_mrr].to_readable %></h2>
94
227
  <p>new MRR (<%= period_short %>)</p>
95
228
  </div>
96
229
  <div class="card">
97
- <h2><%= Profitable.churned_mrr(in_the_last: period).to_readable %></h2>
230
+ <h2><%= data[:churned_mrr].to_readable %></h2>
98
231
  <p>churned MRR (<%= period_short %>)</p>
99
232
  </div>
100
233
  <div class="card">
101
- <h2><%= Profitable.mrr_growth(in_the_last: period).to_readable %></h2>
234
+ <h2><%= data[:mrr_growth].to_readable %></h2>
102
235
  <p>MRR growth (<%= period_short %>)</p>
103
236
  </div>
104
237
 
105
238
  <div class="card">
106
- <h2><%= Profitable.revenue_in_period(in_the_last: period).to_readable %></h2>
239
+ <h2><%= data[:revenue].to_readable %></h2>
107
240
  <p>total revenue (<%= period_short %>)</p>
108
241
  </div>
109
242
 
@@ -0,0 +1,23 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "pay", "~> 10.0.0"
7
+ gem "stripe", "~> 15.0"
8
+ gem "rails", "~> 8.1.0"
9
+
10
+ group :development, :test do
11
+ gem "appraisal"
12
+ gem "minitest", "~> 6.0"
13
+ gem "minitest-mock"
14
+ gem "minitest-reporters"
15
+ gem "rack-test"
16
+ gem "simplecov", require: false
17
+ gem "sqlite3", ">= 2.1"
18
+ gem "mocha", "~> 2.1"
19
+ gem "activerecord", ">= 7.0"
20
+ gem "actionview", ">= 7.0"
21
+ end
22
+
23
+ gemspec path: "../"
@@ -0,0 +1,23 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "pay", "~> 11.0"
7
+ gem "stripe", "~> 18.0"
8
+ gem "rails", "~> 8.1.0"
9
+
10
+ group :development, :test do
11
+ gem "appraisal"
12
+ gem "minitest", "~> 6.0"
13
+ gem "minitest-mock"
14
+ gem "minitest-reporters"
15
+ gem "rack-test"
16
+ gem "simplecov", require: false
17
+ gem "sqlite3", ">= 2.1"
18
+ gem "mocha", "~> 2.1"
19
+ gem "activerecord", ">= 7.0"
20
+ gem "actionview", ">= 7.0"
21
+ end
22
+
23
+ gemspec path: "../"
@@ -0,0 +1,23 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "pay", "~> 7.3.0"
7
+ gem "stripe", "~> 12.0"
8
+ gem "rails", "~> 8.1.0"
9
+
10
+ group :development, :test do
11
+ gem "appraisal"
12
+ gem "minitest", "~> 6.0"
13
+ gem "minitest-mock"
14
+ gem "minitest-reporters"
15
+ gem "rack-test"
16
+ gem "simplecov", require: false
17
+ gem "sqlite3", ">= 2.1"
18
+ gem "mocha", "~> 2.1"
19
+ gem "activerecord", ">= 7.0"
20
+ gem "actionview", ">= 7.0"
21
+ end
22
+
23
+ gemspec path: "../"
@@ -0,0 +1,23 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "pay", "~> 8.3.0"
7
+ gem "stripe", "~> 13.0"
8
+ gem "rails", "~> 8.1.0"
9
+
10
+ group :development, :test do
11
+ gem "appraisal"
12
+ gem "minitest", "~> 6.0"
13
+ gem "minitest-mock"
14
+ gem "minitest-reporters"
15
+ gem "rack-test"
16
+ gem "simplecov", require: false
17
+ gem "sqlite3", ">= 2.1"
18
+ gem "mocha", "~> 2.1"
19
+ gem "activerecord", ">= 7.0"
20
+ gem "actionview", ">= 7.0"
21
+ end
22
+
23
+ gemspec path: "../"
@@ -0,0 +1,23 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "pay", "~> 9.0.0"
7
+ gem "stripe", "~> 13.0"
8
+ gem "rails", "~> 8.1.0"
9
+
10
+ group :development, :test do
11
+ gem "appraisal"
12
+ gem "minitest", "~> 6.0"
13
+ gem "minitest-mock"
14
+ gem "minitest-reporters"
15
+ gem "rack-test"
16
+ gem "simplecov", require: false
17
+ gem "sqlite3", ">= 2.1"
18
+ gem "mocha", "~> 2.1"
19
+ gem "activerecord", ">= 7.0"
20
+ gem "actionview", ">= 7.0"
21
+ end
22
+
23
+ gemspec path: "../"
@@ -0,0 +1,23 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "rails", "~> 7.2.0"
7
+ gem "pay", "~> 11.0"
8
+ gem "stripe", "~> 18.0"
9
+
10
+ group :development, :test do
11
+ gem "appraisal"
12
+ gem "minitest", "~> 6.0"
13
+ gem "minitest-mock"
14
+ gem "minitest-reporters"
15
+ gem "rack-test"
16
+ gem "simplecov", require: false
17
+ gem "sqlite3", ">= 2.1"
18
+ gem "mocha", "~> 2.1"
19
+ gem "activerecord", ">= 7.0"
20
+ gem "actionview", ">= 7.0"
21
+ end
22
+
23
+ gemspec path: "../"
@@ -0,0 +1,23 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "rails", "~> 8.1.0"
7
+ gem "pay", "~> 11.0"
8
+ gem "stripe", "~> 18.0"
9
+
10
+ group :development, :test do
11
+ gem "appraisal"
12
+ gem "minitest", "~> 6.0"
13
+ gem "minitest-mock"
14
+ gem "minitest-reporters"
15
+ gem "rack-test"
16
+ gem "simplecov", require: false
17
+ gem "sqlite3", ">= 2.1"
18
+ gem "mocha", "~> 2.1"
19
+ gem "activerecord", ">= 7.0"
20
+ gem "actionview", ">= 7.0"
21
+ end
22
+
23
+ gemspec path: "../"