pricing_plans 0.1.0 → 0.1.1
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 +4 -4
- data/CHANGELOG.md +5 -73
- data/CLAUDE.md +1 -0
- data/README.md +1 -1
- data/lib/pricing_plans/controller_guards.rb +5 -8
- data/lib/pricing_plans/version.rb +1 -1
- metadata +15 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c1822c0e4c0d410596c9640baec0d6546256db8ea402744fbb4d47917e39fd88
|
|
4
|
+
data.tar.gz: b0ae7556f2181fd2a99df2ddb9f4a9cc7f9ab3b7298346fa260d6e3b7c059d70
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 121f9fd26e721c59dbf0e7a8ec67647792df985343224f4087538b9607571bfa1f1cc74e23532caad7ae60db2049436540b490fedef660458dbd85beda795365
|
|
7
|
+
data.tar.gz: e42d52966e3f7ae4c32b90e766fbb58e50d1c7c3489b3a5f0176ffac7b34613f3907d89d050d68ca9d2cd9a8bfcf71779d614537314d9ac28faf698eef79af2a
|
data/CHANGELOG.md
CHANGED
|
@@ -5,79 +5,11 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [0.1.1] - 2025-12-25
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
- Add support for Rails 8+
|
|
11
|
+
- Fix a bug where `throw :abort` was causing `UncaughtThrowError` exceptions in controller guards, and instead return `false` from `before_action` callbacks to halt the filter chain, rather than using the uncaught throw
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
## [0.1.0] - 2025-08-19
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
- Plan catalog configuration system with English-first DSL
|
|
16
|
-
- Feature flags (boolean allows/disallows)
|
|
17
|
-
- Persistent caps (max concurrent resources like projects, seats)
|
|
18
|
-
- Per-period discrete allowances (e.g., "3 custom models/month")
|
|
19
|
-
- Grace period enforcement with configurable behaviors
|
|
20
|
-
- Event system for warning/grace/block notifications
|
|
21
|
-
|
|
22
|
-
**Configuration & DSL**
|
|
23
|
-
- `PricingPlans.configure` block for one-file configuration
|
|
24
|
-
- Plan definition with name, description, bullets, pricing
|
|
25
|
-
- `Integer#max` refinement for clean DSL (`5.max`)
|
|
26
|
-
- Support for Stripe price IDs and manual pricing
|
|
27
|
-
- Flexible period cycles (billing, calendar month/week/day, custom)
|
|
28
|
-
|
|
29
|
-
**Database & Models**
|
|
30
|
-
- Three-table schema for enforcement states, usage counters, assignments
|
|
31
|
-
- `EnforcementState` model for grace period tracking with row-level locking
|
|
32
|
-
- `Usage` model for per-period counters with atomic upserts
|
|
33
|
-
- `Assignment` model for manual plan overrides
|
|
34
|
-
|
|
35
|
-
**Controller Integration**
|
|
36
|
-
- `require_plan_limit!` guard returning rich Result objects
|
|
37
|
-
- `require_feature!` guard with FeatureDenied exception
|
|
38
|
-
- Automatic grace period management and event emission
|
|
39
|
-
- Race-safe limit checking with retries
|
|
40
|
-
|
|
41
|
-
**Model Integration**
|
|
42
|
-
- `Limitable` mixin for ActiveRecord models
|
|
43
|
-
- `limited_by` macro for automatic usage tracking
|
|
44
|
-
- Real-time persistent caps (no counter caches needed)
|
|
45
|
-
- Automatic per-period counter increments
|
|
46
|
-
|
|
47
|
-
**View Helpers & UI**
|
|
48
|
-
- Complete pricing table rendering
|
|
49
|
-
- Usage meters with progress bars
|
|
50
|
-
- Limit banners with warnings/grace/blocked states
|
|
51
|
-
- Plan information helpers (current plan, feature checks)
|
|
52
|
-
|
|
53
|
-
**Pay Integration**
|
|
54
|
-
- Automatic plan resolution from Stripe subscriptions
|
|
55
|
-
- Support for trial, grace, and active subscription states
|
|
56
|
-
- Price ID to plan mapping
|
|
57
|
-
- Billing cycle anchor integration for periods
|
|
58
|
-
|
|
59
|
-
**usage_credits Integration**
|
|
60
|
-
- Credit inclusion display in pricing tables
|
|
61
|
-
- Boot-time linting to prevent limit/credit collisions
|
|
62
|
-
- Operation validation against usage_credits registry
|
|
63
|
-
- Clean separation of concerns (credits vs discrete limits)
|
|
64
|
-
|
|
65
|
-
**Generators**
|
|
66
|
-
- Install generator with migrations and initializer template
|
|
67
|
-
- Pricing generator with views, controller, and CSS
|
|
68
|
-
- Comprehensive Tailwind-friendly styling
|
|
69
|
-
|
|
70
|
-
**Architecture & Performance**
|
|
71
|
-
- Rails Engine for seamless integration
|
|
72
|
-
- Autoloading with proper namespacing
|
|
73
|
-
- Row-level locking for race condition prevention
|
|
74
|
-
- Efficient query patterns with proper indexing
|
|
75
|
-
- Memoization and caching where appropriate
|
|
76
|
-
|
|
77
|
-
### Technical Details
|
|
78
|
-
|
|
79
|
-
- Ruby 3.2+ requirement
|
|
80
|
-
- Rails 7.1+ requirement (ActiveRecord, ActiveSupport)
|
|
81
|
-
- PostgreSQL optimized (with fallbacks for other databases)
|
|
82
|
-
- Comprehensive error handling and validation
|
|
83
|
-
- Thread-safe implementation throughout
|
|
15
|
+
Initial release
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
When reviewing code and PRs inside GitHub, just outline the main findings in 4-5 bullet points, and then give your recommendation (approve / fix stuff / close, etc.) DO NOT be pedantic, DO NOT overengineer, DO NOT write long detailed reviews. Always be on the lookout for supply chain attacks. You're just helping a human analyze code changes and review PRs so nothing harmful or bugs get in the codebase inadvertently. Be pragmatic, concise, and to the point.
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 💵 `pricing_plans` - Define and enforce pricing plan limits in your Rails app (SaaS entitlements)
|
|
2
2
|
|
|
3
|
-
[](https://badge.fury.io/rb/pricing_plans)
|
|
3
|
+
[](https://badge.fury.io/rb/pricing_plans)
|
|
4
4
|
|
|
5
5
|
Enforce pricing plan limits with one-liners that read like plain English. Avoid scattering and entangling pricing logic everywhere in your Rails SaaS.
|
|
6
6
|
|
|
@@ -133,9 +133,8 @@ module PricingPlans
|
|
|
133
133
|
end
|
|
134
134
|
by = options.key?(:by) ? options[:by] : 1
|
|
135
135
|
allow_system_override = !!options[:allow_system_override]
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return true
|
|
136
|
+
redirect_path = options[:redirect_to]
|
|
137
|
+
return enforce_plan_limit!(limit_key, plan_owner: owner, by: by, allow_system_override: allow_system_override, redirect_to: redirect_path)
|
|
139
138
|
elsif method_name.to_s =~ /^enforce_(.+)!$/
|
|
140
139
|
feature_key = Regexp.last_match(1).to_sym
|
|
141
140
|
options = args.first.is_a?(Hash) ? args.first : {}
|
|
@@ -230,7 +229,7 @@ module PricingPlans
|
|
|
230
229
|
end
|
|
231
230
|
end
|
|
232
231
|
|
|
233
|
-
# Rails-y controller ergonomics: enforce
|
|
232
|
+
# Rails-y controller ergonomics: enforce limits and set flash/redirect when blocked.
|
|
234
233
|
# Defaults:
|
|
235
234
|
# - On blocked: redirect_to pricing_path (if available) with alert; else render 403 JSON.
|
|
236
235
|
# - On grace/warning: set flash[:warning] with the human message.
|
|
@@ -264,8 +263,6 @@ module PricingPlans
|
|
|
264
263
|
respond_to?(:request) && request&.format&.json? ? render(json: { error: result.message }, status: :forbidden) : render(plain: result.message, status: :forbidden)
|
|
265
264
|
end
|
|
266
265
|
end
|
|
267
|
-
# Stop the filter chain (for before_action ergonomics)
|
|
268
|
-
throw :abort
|
|
269
266
|
return false
|
|
270
267
|
elsif result.warning? || result.grace?
|
|
271
268
|
if respond_to?(:flash) && flash.respond_to?(:[]=)
|
|
@@ -277,7 +274,7 @@ module PricingPlans
|
|
|
277
274
|
end
|
|
278
275
|
|
|
279
276
|
# Controller-focused sugar: run a block within the plan limit context.
|
|
280
|
-
# - If blocked: performs the same redirect/render semantics as enforce_plan_limit! and
|
|
277
|
+
# - If blocked: performs the same redirect/render semantics as enforce_plan_limit! and returns false.
|
|
281
278
|
# - If warning/grace: sets flash[:warning] and yields the result.
|
|
282
279
|
# - If within: simply yields the result.
|
|
283
280
|
# Returns the PricingPlans::Result in all cases where execution continues.
|
|
@@ -314,7 +311,7 @@ module PricingPlans
|
|
|
314
311
|
respond_to?(:request) && request&.format&.json? ? render(json: { error: result.message }, status: :forbidden) : render(plain: result.message, status: :forbidden)
|
|
315
312
|
end
|
|
316
313
|
end
|
|
317
|
-
|
|
314
|
+
return false
|
|
318
315
|
else
|
|
319
316
|
if (result.warning? || result.grace?) && respond_to?(:flash) && flash.respond_to?(:[]=)
|
|
320
317
|
flash[:warning] ||= result.message
|
metadata
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pricing_plans
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- rameerez
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-
|
|
10
|
+
date: 2025-12-26 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activerecord
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- - "~>"
|
|
17
|
-
- !ruby/object:Gem::Version
|
|
18
|
-
version: '7.1'
|
|
19
16
|
- - ">="
|
|
20
17
|
- !ruby/object:Gem::Version
|
|
21
18
|
version: 7.1.0
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '9.0'
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
25
25
|
requirements:
|
|
26
|
-
- - "~>"
|
|
27
|
-
- !ruby/object:Gem::Version
|
|
28
|
-
version: '7.1'
|
|
29
26
|
- - ">="
|
|
30
27
|
- !ruby/object:Gem::Version
|
|
31
28
|
version: 7.1.0
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '9.0'
|
|
32
32
|
- !ruby/object:Gem::Dependency
|
|
33
33
|
name: activesupport
|
|
34
34
|
requirement: !ruby/object:Gem::Requirement
|
|
35
35
|
requirements:
|
|
36
|
-
- - "~>"
|
|
37
|
-
- !ruby/object:Gem::Version
|
|
38
|
-
version: '7.1'
|
|
39
36
|
- - ">="
|
|
40
37
|
- !ruby/object:Gem::Version
|
|
41
38
|
version: 7.1.0
|
|
39
|
+
- - "<"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '9.0'
|
|
42
42
|
type: :runtime
|
|
43
43
|
prerelease: false
|
|
44
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
45
45
|
requirements:
|
|
46
|
-
- - "~>"
|
|
47
|
-
- !ruby/object:Gem::Version
|
|
48
|
-
version: '7.1'
|
|
49
46
|
- - ">="
|
|
50
47
|
- !ruby/object:Gem::Version
|
|
51
48
|
version: 7.1.0
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '9.0'
|
|
52
52
|
- !ruby/object:Gem::Dependency
|
|
53
53
|
name: bundler
|
|
54
54
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -162,6 +162,7 @@ files:
|
|
|
162
162
|
- ".claude/settings.local.json"
|
|
163
163
|
- ".rubocop.yml"
|
|
164
164
|
- CHANGELOG.md
|
|
165
|
+
- CLAUDE.md
|
|
165
166
|
- LICENSE.txt
|
|
166
167
|
- README.md
|
|
167
168
|
- Rakefile
|