plan_features 0.1.0 → 0.2.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 +4 -4
- data/README.md +103 -2
- data/lib/plan_features/feature.rb +1 -1
- data/lib/plan_features/pricing.rb +7 -24
- data/lib/plan_features/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 07bd254cc2719b84d37ac6a37f197e7c90e8c7ac54c5a27d92b4599a63773f95
|
|
4
|
+
data.tar.gz: a7efa17ace3fa438a0da2df4e7928558d9f72aad8f941c768f73a0d858a58afc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a1fea6c3e978ace802ff4658ec1f692ae9035bb68f84a6f19ade4c3b51c79d46ed399aeecdd019e1d850c8232fcd34b06717e14ca23aa419b92f97ab2db6ea65
|
|
7
|
+
data.tar.gz: d970c2cb9c7b452f8f48780cc7eb763075a602bf01cca2fd91cc0191f00327f881d631eb880f590a49eff65a9ea8eb873556764df5100524c6b60cb7da26ae89
|
data/README.md
CHANGED
|
@@ -1,10 +1,107 @@
|
|
|
1
1
|
# PlanFeatures
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
This is a gem for Rails to help you with which features are available for which plans in your paid app.
|
|
4
|
+
|
|
5
|
+
The idea is that you have a set of plans and each plan has a set of features. We want an easy way to list all available
|
|
6
|
+
plans and their features and we want the ability to inherit features from previous plans. Let's say you have a Free plan
|
|
7
|
+
with Feature A and Feature B. When you upgrade to the Paid plan which gives you Feature C and Feature D we still want the
|
|
8
|
+
checks for Feature A and Feature B to return true.
|
|
3
9
|
|
|
4
10
|
## Usage
|
|
5
|
-
|
|
11
|
+
|
|
12
|
+
Create a new file `config/plans.yml` or whatever and create a new initializer `config/initializers/plan_features.rb` with:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
PlanFeatures.configure do |config|
|
|
16
|
+
config.plans_file_path = Rails.root.join("config", "plans.yml")
|
|
17
|
+
end
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Plan configuration
|
|
21
|
+
|
|
22
|
+
Your `plans.yml` could look something like this:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
free:
|
|
26
|
+
name: "Free"
|
|
27
|
+
features:
|
|
28
|
+
feature_a:
|
|
29
|
+
description: "Feature A"
|
|
30
|
+
feature_b:
|
|
31
|
+
description: "Feature B"
|
|
32
|
+
|
|
33
|
+
paid:
|
|
34
|
+
name: "Paid"
|
|
35
|
+
features:
|
|
36
|
+
feature_c:
|
|
37
|
+
description: "Feature C"
|
|
38
|
+
feature_d:
|
|
39
|
+
description: "Feature D"
|
|
40
|
+
prices:
|
|
41
|
+
monthly:
|
|
42
|
+
product: STRIPE_ID
|
|
43
|
+
amount: 500
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This sets up two plans where the `free` plan has the features A and B and the paid plan has the features C and D.
|
|
47
|
+
|
|
48
|
+
## Feature inheritance from previous plans
|
|
49
|
+
|
|
50
|
+
We wouldn't want the paid user to lose access to the features from the Free plan. So let's go ahead and set the previous plan
|
|
51
|
+
on the `paid` plan definition to `free`:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
paid:
|
|
55
|
+
name: "Paid"
|
|
56
|
+
previous: free
|
|
57
|
+
features:
|
|
58
|
+
# features from the above example
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Now it'll inherit the features defined in the Free plan.
|
|
62
|
+
|
|
63
|
+
We can check if a feature is available in our code:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
plan = PricingFeatures::Pricing.find_by_identifier(:paid)
|
|
67
|
+
plan.has_feature?(:feature_c)
|
|
68
|
+
# => true
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Features with limits
|
|
72
|
+
|
|
73
|
+
Sometimes features aren't just boolean features. You might be able to create "posts", but on the free plan you can only create 2, but on the paid plan you can create let's say 50. We can define that like so:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
free:
|
|
77
|
+
name: "Free"
|
|
78
|
+
features:
|
|
79
|
+
posts:
|
|
80
|
+
description: "2 posts"
|
|
81
|
+
limit: 2
|
|
82
|
+
|
|
83
|
+
paid:
|
|
84
|
+
name: "Paid"
|
|
85
|
+
features:
|
|
86
|
+
posts:
|
|
87
|
+
description: "50 posts"
|
|
88
|
+
limit: 50
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
In your code you can check for the limit of a feature with:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
free_plan = PlanFeatures::Pricing.find_by_identifier(:free)
|
|
95
|
+
free_plan.limit_for(:posts)
|
|
96
|
+
# => 2
|
|
97
|
+
|
|
98
|
+
paid_plan = PlanFeatures::Pricing.find_by_identifier(:paid)
|
|
99
|
+
paid_plan.limit_for(:posts)
|
|
100
|
+
# => 50
|
|
101
|
+
```
|
|
6
102
|
|
|
7
103
|
## Installation
|
|
104
|
+
|
|
8
105
|
Add this line to your application's Gemfile:
|
|
9
106
|
|
|
10
107
|
```ruby
|
|
@@ -12,17 +109,21 @@ gem "plan_features"
|
|
|
12
109
|
```
|
|
13
110
|
|
|
14
111
|
And then execute:
|
|
112
|
+
|
|
15
113
|
```bash
|
|
16
114
|
$ bundle
|
|
17
115
|
```
|
|
18
116
|
|
|
19
117
|
Or install it yourself as:
|
|
118
|
+
|
|
20
119
|
```bash
|
|
21
120
|
$ gem install plan_features
|
|
22
121
|
```
|
|
23
122
|
|
|
24
123
|
## Contributing
|
|
124
|
+
|
|
25
125
|
Contribution directions go here.
|
|
26
126
|
|
|
27
127
|
## License
|
|
128
|
+
|
|
28
129
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -7,11 +7,9 @@ module PlanFeatures
|
|
|
7
7
|
include ActiveModel::Model
|
|
8
8
|
|
|
9
9
|
def self.boosts
|
|
10
|
-
|
|
11
|
-
if File.exist?(Rails.root.join("config", "account_boosts.yml"))
|
|
10
|
+
@boosts ||= if File.exist?(Rails.root.join("config", "account_boosts.yml"))
|
|
12
11
|
YAML.load_file(Rails.root.join("config", "account_boosts.yml"))
|
|
13
12
|
end
|
|
14
|
-
end
|
|
15
13
|
end
|
|
16
14
|
|
|
17
15
|
def self.find_by_identifier(id)
|
|
@@ -64,8 +62,8 @@ module PlanFeatures
|
|
|
64
62
|
end
|
|
65
63
|
end
|
|
66
64
|
|
|
67
|
-
def
|
|
68
|
-
features.dig(feature.to_s, "
|
|
65
|
+
def limit_for(feature)
|
|
66
|
+
features.dig(feature.to_s, "limit")
|
|
69
67
|
end
|
|
70
68
|
|
|
71
69
|
def free?
|
|
@@ -76,27 +74,11 @@ module PlanFeatures
|
|
|
76
74
|
prices&.any?
|
|
77
75
|
end
|
|
78
76
|
|
|
79
|
-
def pricing_id(interval = "monthly")
|
|
80
|
-
if interval == "monthly"
|
|
81
|
-
monthly_pricing_id
|
|
82
|
-
else
|
|
83
|
-
yearly_pricing_id
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
77
|
def pricing_id(name: :monthly)
|
|
88
78
|
return nil if prices.nil?
|
|
89
79
|
prices[name.to_s]["product"]
|
|
90
80
|
end
|
|
91
81
|
|
|
92
|
-
def monthly_pricing_id
|
|
93
|
-
pricing_id(name: :monthly)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def yearly_pricing_id
|
|
97
|
-
pricing_id(name: :yearly)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
82
|
def price(name: :monthly)
|
|
101
83
|
return nil if prices.nil?
|
|
102
84
|
prices[name.to_s]["amount"] / 100
|
|
@@ -110,11 +92,12 @@ module PlanFeatures
|
|
|
110
92
|
def display_features
|
|
111
93
|
features.map do |identifier, attributes|
|
|
112
94
|
PricingFeatures::Feature.new(
|
|
95
|
+
identifier: identifier,
|
|
113
96
|
description: attributes["description"],
|
|
114
|
-
|
|
115
|
-
hidden: attributes["hidden"]
|
|
97
|
+
limit: attributes["limit"],
|
|
98
|
+
hidden: attributes["hidden"]
|
|
116
99
|
)
|
|
117
|
-
end.compact.filter{|f| !f.hidden? }
|
|
100
|
+
end.compact.filter { |f| !f.hidden? }
|
|
118
101
|
end
|
|
119
102
|
end
|
|
120
103
|
end
|