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.
@@ -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
@@ -1,7 +1,9 @@
1
1
  source "https://rubygems.org"
2
2
  gemspec
3
3
 
4
- gem "rake"
4
+ group :development, :test do
5
+ gem "rake"
6
+ end
5
7
 
6
8
  group :test do
7
9
  gem "rspec", "~> 2.14"
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- Planify [![Build Status](https://secure.travis-ci.org/maildropr/planify.png?branch=master)](http://travis-ci.org/maildropr/planify) [![Code Climate](https://codeclimate.com/github/maildropr/planify.png)](https://codeclimate.com/github/maildropr/planify) [![Coverage Status](https://coveralls.io/repos/maildropr/planify/badge.png)](https://coveralls.io/r/maildropr/planify)
1
+ Planify [![Build Status](https://secure.travis-ci.org/maildropr/planify.png?branch=master)](http://travis-ci.org/maildropr/planify) [![Code Climate](https://codeclimate.com/github/maildropr/planify.png)](https://codeclimate.com/github/maildropr/planify) [![Coverage Status](https://coveralls.io/repos/maildropr/planify/badge.png)](https://coveralls.io/r/maildropr/planify) [![Gem Version](https://badge.fury.io/rb/planify.png)](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
- feature :ajax_search, false
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? @widget # User has not hit their Widget cap
96
- @widget.save
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
@@ -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
@@ -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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Planify
2
- VERSION = [1, 0, 0].join(".")
2
+ VERSION = [1, 0, 1].join(".")
3
3
  end
@@ -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
@@ -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
@@ -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.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-07-19 00:00:00.000000000 Z
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: 1.8.25
98
+ rubygems_version: 2.0.3
109
99
  signing_key:
110
- specification_version: 3
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