planify 1.0.0 → 1.0.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 +7 -0
- data/Gemfile +3 -1
- data/README.md +12 -6
- data/lib/planify/plan.rb +37 -0
- data/lib/planify/plans.rb +10 -0
- data/lib/planify/user.rb +0 -3
- data/lib/planify/version.rb +1 -1
- data/spec/planify/integrations/rails_spec.rb +59 -0
- data/spec/planify/plan_spec.rb +63 -0
- data/spec/planify/plans_spec.rb +13 -0
- data/spec/planify/util/class_helper_spec.rb +56 -0
- metadata +15 -23
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 895a08ebad3001a3d2f93ac311c480fa4737c9d2
|
4
|
+
data.tar.gz: defff6ce05ca94f201d38ff14cb8c48401ded5ec
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c33faa819fc593900eda98752c7531f4db1cfb2de3cef927c46af771a0910f6ce24f517e1a1ebf7d33ca5fc7f1fc286492d77487577f81e5b3d5db85aca0740c
|
7
|
+
data.tar.gz: be9e24e3f82f37c9ae085ae6c1b5eec7e65d205d72936e499d3c585038dd36c59361c26fbad2464c839e73fd14e45ef2fae991e45228a66306a3787875c13bfa
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Planify [](http://travis-ci.org/maildropr/planify) [](https://codeclimate.com/github/maildropr/planify) [](https://coveralls.io/r/maildropr/planify)
|
1
|
+
Planify [](http://travis-ci.org/maildropr/planify) [](https://codeclimate.com/github/maildropr/planify) [](https://coveralls.io/r/maildropr/planify) [](http://badge.fury.io/rb/planify)
|
2
2
|
========
|
3
3
|
|
4
4
|
Make subscription plans and enforce their limits with Planify.
|
@@ -47,9 +47,16 @@ Plans hold information about how many instances of a `Limitable` can be created,
|
|
47
47
|
```ruby
|
48
48
|
# config/initializers/plans.rb
|
49
49
|
Planify::Plans.define :starter do
|
50
|
+
price 5.00 # Costs 5 dollars
|
51
|
+
description "The perfect plan to get you started"
|
52
|
+
|
50
53
|
max Widget, 100 # Can only create up to 100 widgets before needing to upgrade
|
51
54
|
|
52
|
-
feature :ajax_search
|
55
|
+
feature :ajax_search # This plan supports ajax search
|
56
|
+
enable_feature :live_reload # And live reloading
|
57
|
+
|
58
|
+
disable_feature :guest_account # But no guest accounts
|
59
|
+
feature :support_tickets, false # And no support tickets
|
53
60
|
end
|
54
61
|
```
|
55
62
|
|
@@ -77,7 +84,7 @@ You can also assign user-specific overrides to plan limits and features:
|
|
77
84
|
# This user has half the widgets and no ajax-search
|
78
85
|
@user.has_plan :starter do
|
79
86
|
max Widget, 50
|
80
|
-
|
87
|
+
disable_feature :ajax_search
|
81
88
|
end
|
82
89
|
```
|
83
90
|
|
@@ -90,10 +97,9 @@ After creating your Limitables, Plans, and User, you are ready to start enforcin
|
|
90
97
|
|
91
98
|
def create
|
92
99
|
@user = current_user
|
93
|
-
@widget = Widget.create(params[:widget])
|
94
100
|
|
95
|
-
if @user.can_create?
|
96
|
-
@widget.
|
101
|
+
if @user.can_create? Widget # User has not hit their Widget cap
|
102
|
+
@widget = Widget.create(params[:widget])
|
97
103
|
@user.created :widget
|
98
104
|
end
|
99
105
|
end
|
data/lib/planify/plan.rb
CHANGED
@@ -24,10 +24,25 @@ module Planify
|
|
24
24
|
@limits.get(limitable)
|
25
25
|
end
|
26
26
|
|
27
|
+
# Defines a feature of this plan
|
28
|
+
# @param [String,Symbol] feature_name The name of the feature
|
29
|
+
# @param [Boolean] enabled Sets availability of the feature for this plan
|
27
30
|
def feature(feature_name, enabled = true)
|
28
31
|
@features[feature_name.to_sym] = enabled
|
29
32
|
end
|
30
33
|
|
34
|
+
# Enables a feature on this plan.
|
35
|
+
# @param [String,Symbol] feature_name The name of the feature to enable
|
36
|
+
def enable_feature(feature_name)
|
37
|
+
feature(feature_name, true)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Disables a feature on this plan.
|
41
|
+
# @param [String,Symbol] feature_name The name of the feature to disable
|
42
|
+
def disable_feature(feature_name)
|
43
|
+
feature(feature_name, false)
|
44
|
+
end
|
45
|
+
|
31
46
|
# Boolean method for determining if a certain feature is enabled in this plan
|
32
47
|
# @param [String,Symbol] feature the feature to check
|
33
48
|
# @return [Boolean] +true+ if +feature+ is enabled, +false+ if +feature+ is disabled or undefined.
|
@@ -42,6 +57,28 @@ module Planify
|
|
42
57
|
!feature_enabled?(feature)
|
43
58
|
end
|
44
59
|
|
60
|
+
# Sets or returns the price of this plan.
|
61
|
+
# When called without arguments, it returns the price.
|
62
|
+
# When called with arguments, the price is set to the first argument provided.
|
63
|
+
def price(*args)
|
64
|
+
unless args.empty?
|
65
|
+
@price = args.first
|
66
|
+
end
|
67
|
+
|
68
|
+
@price ||= 0.00
|
69
|
+
end
|
70
|
+
|
71
|
+
# Sets or returns the description of this plan
|
72
|
+
# When called without arguments, it returns the description
|
73
|
+
# When called with arguments, the description is set to the first argument provided.
|
74
|
+
def description(*args)
|
75
|
+
unless args.empty?
|
76
|
+
@description = args.first
|
77
|
+
end
|
78
|
+
|
79
|
+
@description ||= nil
|
80
|
+
end
|
81
|
+
|
45
82
|
# Returns a duplicate instance of this plan
|
46
83
|
# @return [Planify::Plan] an exact copy of this plan
|
47
84
|
def dup
|
data/lib/planify/plans.rb
CHANGED
@@ -3,6 +3,9 @@ module Planify
|
|
3
3
|
|
4
4
|
@plans = ActiveSupport::HashWithIndifferentAccess.new
|
5
5
|
|
6
|
+
# Defines a new plan, evaluating the given block on the new plan instance.
|
7
|
+
# @param [String, Symbol] name The name of the plan
|
8
|
+
# @return [Planify::Plan] the new plan
|
6
9
|
def self.define(name, &block)
|
7
10
|
plan = Plan.new
|
8
11
|
plan.instance_eval(&block) if block_given?
|
@@ -10,6 +13,10 @@ module Planify
|
|
10
13
|
@plans[name] = plan
|
11
14
|
end
|
12
15
|
|
16
|
+
# Gets a plan by name
|
17
|
+
# @param [String,Symbol] name The name of the plan
|
18
|
+
# @return [Planify::Plan] the plan represented by +name+
|
19
|
+
# @raise [ArgumentError] if a plan named +name+ is not defined
|
13
20
|
def self.get(name)
|
14
21
|
begin
|
15
22
|
@plans.fetch(name)
|
@@ -18,10 +25,13 @@ module Planify
|
|
18
25
|
end
|
19
26
|
end
|
20
27
|
|
28
|
+
# Returns the list of plans
|
29
|
+
# @return [Array] an array of Planify::Plan instances
|
21
30
|
def self.all
|
22
31
|
@plans
|
23
32
|
end
|
24
33
|
|
34
|
+
# Removes all currently defined plans
|
25
35
|
def self.clear
|
26
36
|
@plans.clear
|
27
37
|
end
|
data/lib/planify/user.rb
CHANGED
@@ -19,12 +19,9 @@ module Planify
|
|
19
19
|
plan_info = PlanInfo.new(name: plan_name)
|
20
20
|
|
21
21
|
if block_given?
|
22
|
-
plan = plan.dup
|
23
22
|
configuration = Planify::Plan.new
|
24
23
|
configuration.instance_eval &block
|
25
24
|
|
26
|
-
plan.merge! configuration
|
27
|
-
|
28
25
|
plan_info.limit_overrides = configuration.limits.all
|
29
26
|
plan_info.feature_overrides = configuration.features
|
30
27
|
end
|
data/lib/planify/version.rb
CHANGED
@@ -0,0 +1,59 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "planify/integrations/rails"
|
3
|
+
|
4
|
+
METHODS = [
|
5
|
+
:enforce_limit!,
|
6
|
+
:limit_exceeded!
|
7
|
+
]
|
8
|
+
|
9
|
+
module ActionController
|
10
|
+
class Base
|
11
|
+
def self.helper(*args); end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Planify::Integrations::Rails do
|
16
|
+
subject { ActionController::Base.send(:include, Planify::Integrations::Rails) }
|
17
|
+
|
18
|
+
describe ".included" do
|
19
|
+
it "it adds controller methods to ActionController::Base" do
|
20
|
+
METHODS.each do |method|
|
21
|
+
subject.should respond_to method
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ".enforce_limit!" do
|
27
|
+
let(:user) { User.new }
|
28
|
+
before do
|
29
|
+
Planify::Plans.define :starter do
|
30
|
+
max Post, 1
|
31
|
+
end
|
32
|
+
|
33
|
+
user.has_plan :starter
|
34
|
+
end
|
35
|
+
|
36
|
+
context "user is over their quota" do
|
37
|
+
before { user.created Post }
|
38
|
+
|
39
|
+
it "calls limit_exceeded!" do
|
40
|
+
subject.should_receive(:limit_exceeded!)
|
41
|
+
subject.enforce_limit! user, Post
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "user is under their quota" do
|
46
|
+
it "returns nil" do
|
47
|
+
subject.should_not_receive(:limit_exceeded!)
|
48
|
+
subject.enforce_limit!(user, Post).should be_nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe ".limit_exceeded!" do
|
54
|
+
it "raises exception" do
|
55
|
+
expect { subject.limit_exceeded! }.to raise_error
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/spec/planify/plan_spec.rb
CHANGED
@@ -34,6 +34,20 @@ describe Planify::Plan do
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
+
describe ".enable_feature" do
|
38
|
+
it "enables the named feature" do
|
39
|
+
subject.enable_feature(:live_reload)
|
40
|
+
subject.feature_enabled?(:live_reload).should be_true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ".disable_feature" do
|
45
|
+
it "disables the named feature" do
|
46
|
+
subject.disable_feature(:live_reload)
|
47
|
+
subject.feature_enabled?(:live_reload).should be_false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
37
51
|
describe ".feature_enabled?" do
|
38
52
|
before { subject.feature(:ajax_search) }
|
39
53
|
|
@@ -82,6 +96,55 @@ describe Planify::Plan do
|
|
82
96
|
end
|
83
97
|
end
|
84
98
|
|
99
|
+
|
100
|
+
describe ".price" do
|
101
|
+
context "with arguments" do
|
102
|
+
it "sets the current price" do
|
103
|
+
subject.price(10.00)
|
104
|
+
|
105
|
+
subject.price.should == 10.00
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "without arguments" do
|
110
|
+
before { subject.price(15.00) }
|
111
|
+
|
112
|
+
it "returns the current price" do
|
113
|
+
subject.price.should == 15.00
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when no price is set" do
|
118
|
+
it "returns zero" do
|
119
|
+
Planify::Plan.new.price.should == 0.00
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe ".description" do
|
125
|
+
context "with arguments" do
|
126
|
+
it "sets the plan description" do
|
127
|
+
subject.description "The best plan ever!"
|
128
|
+
|
129
|
+
subject.description.should == "The best plan ever!"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "without arguments" do
|
134
|
+
before { subject.description("Plan Description") }
|
135
|
+
|
136
|
+
it "returns the plan description" do
|
137
|
+
subject.description.should == "Plan Description"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context "when no description is set" do
|
142
|
+
it "returns nil" do
|
143
|
+
Planify::Plan.new.description.should be_nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
85
148
|
describe ".merge!" do
|
86
149
|
|
87
150
|
let(:pro_plan) do
|
data/spec/planify/plans_spec.rb
CHANGED
@@ -46,6 +46,19 @@ describe Planify::Plans do
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
describe ".all" do
|
50
|
+
before { Planify::Plans.clear }
|
51
|
+
|
52
|
+
it "returns all defined plans" do
|
53
|
+
Planify::Plans.all.should be_empty
|
54
|
+
|
55
|
+
Planify::Plans.define :plan1
|
56
|
+
Planify::Plans.define :plan2
|
57
|
+
|
58
|
+
Planify::Plans.all.size.should == 2
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
49
62
|
describe ".clear" do
|
50
63
|
before { Planify::Plans.define :starter }
|
51
64
|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Planify::ClassHelper do
|
4
|
+
subject { Object.new.extend(Planify::ClassHelper) }
|
5
|
+
|
6
|
+
describe ".normalize_class" do
|
7
|
+
context "with a class constant" do
|
8
|
+
it "returns a string representation" do
|
9
|
+
subject.normalize_class(Post).should == "Post"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "with a class instance" do
|
14
|
+
it "returns a string representation of the instance's class" do
|
15
|
+
subject.normalize_class(Post.new).should == "Post"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "with a module" do
|
20
|
+
it "returns a string representation of the module's name" do
|
21
|
+
module Foo; end
|
22
|
+
subject.normalize_class(Foo).should == "Foo"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "with a string" do
|
27
|
+
context "string represents a constant name" do
|
28
|
+
it "returns itself" do
|
29
|
+
subject.normalize_class("Post").should == "Post"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "string does not represent a constant name" do
|
34
|
+
it "raises NameError" do
|
35
|
+
expect { subject.normalize_class("Not::ARealClass") }.to raise_exception(NameError)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "with a symbol" do
|
41
|
+
context "symbol represents a constant name" do
|
42
|
+
it "returns a string representation of the symbol" do
|
43
|
+
subject.normalize_class(:post).should == "Post"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "symbol does not represent a constant name" do
|
48
|
+
it "raises NameError" do
|
49
|
+
expect { subject.normalize_class(:not_a_class) }.to raise_exception(NameError)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
metadata
CHANGED
@@ -1,46 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: planify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Kyle Dayton
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-08-20 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: mongoid
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: 3.0.0
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '>='
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: 3.0.0
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: activesupport
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - '>='
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - '>='
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
description: A Mongoid plugin for managing subscription plans and features
|
@@ -72,51 +67,48 @@ files:
|
|
72
67
|
- spec/config/mongoid.yml
|
73
68
|
- spec/models/post.rb
|
74
69
|
- spec/models/user.rb
|
70
|
+
- spec/planify/integrations/rails_spec.rb
|
75
71
|
- spec/planify/limitations_spec.rb
|
76
72
|
- spec/planify/plan_overrides_spec.rb
|
77
73
|
- spec/planify/plan_spec.rb
|
78
74
|
- spec/planify/plans_spec.rb
|
79
75
|
- spec/planify/user/plan_info_spec.rb
|
80
76
|
- spec/planify/user_spec.rb
|
77
|
+
- spec/planify/util/class_helper_spec.rb
|
81
78
|
- spec/spec_helper.rb
|
82
79
|
homepage: http://github.com/kdayton-/planify
|
83
80
|
licenses: []
|
81
|
+
metadata: {}
|
84
82
|
post_install_message:
|
85
83
|
rdoc_options: []
|
86
84
|
require_paths:
|
87
85
|
- lib
|
88
86
|
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
87
|
requirements:
|
91
|
-
- -
|
88
|
+
- - '>='
|
92
89
|
- !ruby/object:Gem::Version
|
93
90
|
version: '0'
|
94
|
-
segments:
|
95
|
-
- 0
|
96
|
-
hash: 654973981249883969
|
97
91
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
-
none: false
|
99
92
|
requirements:
|
100
|
-
- -
|
93
|
+
- - '>='
|
101
94
|
- !ruby/object:Gem::Version
|
102
95
|
version: '0'
|
103
|
-
segments:
|
104
|
-
- 0
|
105
|
-
hash: 654973981249883969
|
106
96
|
requirements: []
|
107
97
|
rubyforge_project:
|
108
|
-
rubygems_version:
|
98
|
+
rubygems_version: 2.0.3
|
109
99
|
signing_key:
|
110
|
-
specification_version:
|
100
|
+
specification_version: 4
|
111
101
|
summary: A Mongoid plugin for managing subscription plans and features
|
112
102
|
test_files:
|
113
103
|
- spec/config/mongoid.yml
|
114
104
|
- spec/models/post.rb
|
115
105
|
- spec/models/user.rb
|
106
|
+
- spec/planify/integrations/rails_spec.rb
|
116
107
|
- spec/planify/limitations_spec.rb
|
117
108
|
- spec/planify/plan_overrides_spec.rb
|
118
109
|
- spec/planify/plan_spec.rb
|
119
110
|
- spec/planify/plans_spec.rb
|
120
111
|
- spec/planify/user/plan_info_spec.rb
|
121
112
|
- spec/planify/user_spec.rb
|
113
|
+
- spec/planify/util/class_helper_spec.rb
|
122
114
|
- spec/spec_helper.rb
|