planify 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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