saucy 0.2.21 → 0.2.24
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.
- data/app/views/plans/_plan.html.erb +23 -13
- data/app/views/plans/edit.html.erb +3 -0
- data/lib/generators/saucy/features/templates/features/manage_plan.feature +1 -0
- data/lib/generators/saucy/features/templates/features/trial_plans.feature +16 -7
- data/lib/saucy/subscription.rb +14 -5
- data/spec/models/subscription_spec.rb +39 -0
- metadata +28 -28
@@ -1,20 +1,30 @@
|
|
1
1
|
<p class="name"><%= plan.name %></p>
|
2
|
-
<p class="price"
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
<% end %>
|
8
|
-
<% end %>
|
9
|
-
<% plan.limits.boolean.each do |limit| %>
|
10
|
-
<% if limit.allowed? -%>
|
11
|
-
<%= content_tag_for :li, limit, :class => limit.name do %>
|
12
|
-
<%= limit.name %>
|
13
|
-
<% end %>
|
14
|
-
<% end -%>
|
2
|
+
<p class="price">
|
3
|
+
<% if plan.billed? %>
|
4
|
+
$<%= plan.price %>/month
|
5
|
+
<% else %>
|
6
|
+
Free
|
15
7
|
<% end %>
|
8
|
+
<% if plan.trial? %>
|
9
|
+
Trial
|
10
|
+
<% end -%>
|
11
|
+
</p>
|
12
|
+
<ul class="features">
|
16
13
|
<% if plan.trial? -%>
|
17
14
|
<li class="trial"><%= t(".trial_will_expire",
|
18
15
|
:default => "Your trial will expire after 30 days.") %></li>
|
16
|
+
<% else -%>
|
17
|
+
<% plan.limits.numbered.each do |limit| %>
|
18
|
+
<%= content_tag_for :li, limit, :class => limit.name do %>
|
19
|
+
<%= pluralize(limit.value, limit.name.singularize) %>
|
20
|
+
<% end %>
|
21
|
+
<% end %>
|
22
|
+
<% plan.limits.boolean.each do |limit| %>
|
23
|
+
<% if limit.allowed? -%>
|
24
|
+
<%= content_tag_for :li, limit, :class => limit.name do %>
|
25
|
+
<%= limit.name %>
|
26
|
+
<% end %>
|
27
|
+
<% end -%>
|
28
|
+
<% end %>
|
19
29
|
<% end -%>
|
20
30
|
</ul>
|
@@ -8,6 +8,9 @@
|
|
8
8
|
<%= content_tag_for :li, plan, :class => "#{plan.name.parameterize} #{'disabled' if !@account.can_change_plan_to?(plan)}" do %>
|
9
9
|
<%= form.label :plan_id, render(plan), :value => plan.id %>
|
10
10
|
<%= form.radio_button :plan_id, plan.id, :disabled => !@account.can_change_plan_to?(plan), "data-free" => plan.free? %>
|
11
|
+
<% if !@account.can_change_plan_to?(plan) -%>
|
12
|
+
<p class="disabled"><%= t(".plan-disabled", :default => "Too big for %{name}.", :name => plan.name) %></p>
|
13
|
+
<% end -%>
|
11
14
|
<% end %>
|
12
15
|
<% end -%>
|
13
16
|
<% end %>
|
@@ -116,6 +116,7 @@ Feature: Manage Plan
|
|
116
116
|
And I follow "Upgrade"
|
117
117
|
Then I should see "Upgrade Your Plan"
|
118
118
|
And the "Free" plan should be disabled
|
119
|
+
And I should see "Too big for Free."
|
119
120
|
And I should see "1 user" within ".basic"
|
120
121
|
And I should see "0 users" within ".free"
|
121
122
|
And I should see "1 project" within ".free"
|
@@ -3,14 +3,21 @@ Feature: Trial plans
|
|
3
3
|
Background:
|
4
4
|
Given the following plan exists:
|
5
5
|
| name | price | trial |
|
6
|
-
|
|
6
|
+
| Temp | 0 | true |
|
7
7
|
| Eternal | 0 | false |
|
8
|
+
And the following limits exist:
|
9
|
+
| plan | name | value | value_type |
|
10
|
+
| name: Temp | users | 2 | number |
|
11
|
+
| name: Eternal | users | 2 | number |
|
8
12
|
|
9
13
|
Scenario: Sign up for a trial plan
|
10
14
|
When I go to the list of plans page
|
11
|
-
And I follow "
|
15
|
+
And I follow "Temp"
|
16
|
+
Then I should see "Free"
|
17
|
+
And I should see "Trial"
|
12
18
|
And I should see "Your trial will expire after 30 days"
|
13
|
-
|
19
|
+
But I should not see "users"
|
20
|
+
When I fill in "Email" with "email@person.com"
|
14
21
|
And I fill in "Password" with "password"
|
15
22
|
And I fill in "Confirm password" with "password"
|
16
23
|
And I fill in "Your name" with "Robot"
|
@@ -20,24 +27,26 @@ Feature: Trial plans
|
|
20
27
|
Then I should see "created"
|
21
28
|
|
22
29
|
Scenario: use an account during the trial
|
23
|
-
Given a "
|
30
|
+
Given a "Temp" account exists with a name of "Test" created 29 days ago
|
24
31
|
And I am signed in as an admin of the "Test" account
|
25
32
|
When I go to the projects page for the "Test" account
|
26
33
|
Then I should see "Test"
|
27
34
|
And I should not see "expired"
|
28
35
|
|
29
36
|
Scenario: Try to use an expired trial plan
|
30
|
-
Given a "
|
37
|
+
Given a "Temp" account exists with a name of "Test" created 30 days ago
|
31
38
|
And I am signed in as an admin of the "Test" account
|
32
39
|
When I go to the projects page for the "Test" account
|
33
40
|
Then I should be on the upgrade plan page for the "Test" account
|
34
41
|
And I should see "expired"
|
35
|
-
And the "
|
42
|
+
And the "Temp" plan should be disabled
|
36
43
|
|
37
44
|
Scenario: Sign up for a non-trial plan
|
38
45
|
When I go to the list of plans page
|
39
46
|
And I follow "Eternal"
|
40
|
-
Then I should
|
47
|
+
Then I should see "users"
|
48
|
+
But I should not see "Trial"
|
49
|
+
And I should not see "expire"
|
41
50
|
|
42
51
|
Scenario: Use a non-trial plan forever
|
43
52
|
Given an "Eternal" account exists with a name of "Test" created 30 days ago
|
data/lib/saucy/subscription.rb
CHANGED
@@ -17,6 +17,9 @@ module Saucy
|
|
17
17
|
|
18
18
|
attr_accessor *CUSTOMER_ATTRIBUTES.keys
|
19
19
|
|
20
|
+
CUSTOMER_ATTRIBUTES.keys.each do |attribute|
|
21
|
+
validates_presence_of attribute, :if => :switching_to_billed?
|
22
|
+
end
|
20
23
|
before_create :create_customer
|
21
24
|
before_create :create_subscription, :if => :billed?
|
22
25
|
after_destroy :destroy_customer
|
@@ -104,16 +107,18 @@ module Saucy
|
|
104
107
|
|
105
108
|
def update_customer(attributes)
|
106
109
|
set_customer_attributes(attributes)
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
+
if valid?
|
111
|
+
result = Braintree::Customer.update(customer_token, customer_attributes)
|
112
|
+
handle_customer_result(result)
|
113
|
+
result.success?
|
114
|
+
end
|
110
115
|
end
|
111
116
|
|
112
117
|
def save_subscription
|
113
|
-
if subscription
|
118
|
+
if subscription
|
114
119
|
Braintree::Subscription.update(subscription_token, :plan_id => plan_id)
|
115
120
|
elsif plan.billed?
|
116
|
-
create_subscription
|
121
|
+
valid? && create_subscription
|
117
122
|
end
|
118
123
|
end
|
119
124
|
|
@@ -200,6 +205,10 @@ module Saucy
|
|
200
205
|
false
|
201
206
|
end
|
202
207
|
end
|
208
|
+
|
209
|
+
def switching_to_billed?
|
210
|
+
plan_id && plan.billed? && subscription_token.blank?
|
211
|
+
end
|
203
212
|
end
|
204
213
|
|
205
214
|
module ClassMethods
|
@@ -9,6 +9,7 @@ describe Account do
|
|
9
9
|
:cardholder_name => "Ralph Robot",
|
10
10
|
:billing_email => "ralph@example.com",
|
11
11
|
:card_number => "4111111111111112",
|
12
|
+
:verification_code => "100",
|
12
13
|
:expiration_month => 5,
|
13
14
|
:expiration_year => 2012,
|
14
15
|
:plan => Factory(:paid_plan))
|
@@ -51,6 +52,44 @@ describe Account do
|
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
55
|
+
describe Account, "given free and paid plans" do
|
56
|
+
let(:free) { Factory(:plan, :price => 0) }
|
57
|
+
let(:paid) { Factory(:plan, :price => 1) }
|
58
|
+
|
59
|
+
it "doesn't switch from free to paid without credit card info" do
|
60
|
+
account = Factory(:account, :plan => free)
|
61
|
+
account = Account.find(account.id)
|
62
|
+
|
63
|
+
result = account.save_customer_and_subscription!(:plan_id => paid.id)
|
64
|
+
|
65
|
+
result.should be_false
|
66
|
+
account.reload.plan.should == free
|
67
|
+
Saucy::Subscription::CUSTOMER_ATTRIBUTES.keys.each do |attribute|
|
68
|
+
account.errors[attribute].should_not be_blank
|
69
|
+
end
|
70
|
+
FakeBraintree.customers[account.customer_token].should_not be_nil
|
71
|
+
FakeBraintree.customers[account.customer_token]["credit_cards"].should be_blank
|
72
|
+
end
|
73
|
+
|
74
|
+
it "requires a billing email when upgrading to a paid plan" do
|
75
|
+
account = Factory(:account, :plan => free, :card_number => '123')
|
76
|
+
account.reload
|
77
|
+
account.plan = paid
|
78
|
+
|
79
|
+
account.should validate_presence_of(:billing_email)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "requires a billing email for paid plans" do
|
83
|
+
account = Factory.build(:account, :plan => paid, :card_number => '123')
|
84
|
+
account.should validate_presence_of(:billing_email)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "doesn't require a billing email for free plans" do
|
88
|
+
account = Factory.build(:account, :plan => free)
|
89
|
+
account.should_not validate_presence_of(:billing_email)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
54
93
|
describe Account, "with a paid plan" do
|
55
94
|
subject do
|
56
95
|
Factory(:account,
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: saucy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 39
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 24
|
10
|
+
version: 0.2.24
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- thoughtbot, inc.
|
@@ -17,14 +17,13 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2011-02-
|
20
|
+
date: 2011-02-15 00:00:00 -05:00
|
21
21
|
default_executable:
|
22
22
|
dependencies:
|
23
23
|
- !ruby/object:Gem::Dependency
|
24
|
-
type: :runtime
|
25
|
-
prerelease: false
|
26
24
|
name: formtastic
|
27
|
-
|
25
|
+
prerelease: false
|
26
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
28
27
|
none: false
|
29
28
|
requirements:
|
30
29
|
- - ">="
|
@@ -34,12 +33,12 @@ dependencies:
|
|
34
33
|
- 1
|
35
34
|
- 2
|
36
35
|
version: "1.2"
|
37
|
-
requirement: *id001
|
38
|
-
- !ruby/object:Gem::Dependency
|
39
36
|
type: :runtime
|
40
|
-
|
37
|
+
version_requirements: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
41
39
|
name: railties
|
42
|
-
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
43
42
|
none: false
|
44
43
|
requirements:
|
45
44
|
- - ">="
|
@@ -50,12 +49,12 @@ dependencies:
|
|
50
49
|
- 0
|
51
50
|
- 3
|
52
51
|
version: 3.0.3
|
53
|
-
requirement: *id002
|
54
|
-
- !ruby/object:Gem::Dependency
|
55
52
|
type: :runtime
|
56
|
-
|
53
|
+
version_requirements: *id002
|
54
|
+
- !ruby/object:Gem::Dependency
|
57
55
|
name: braintree
|
58
|
-
|
56
|
+
prerelease: false
|
57
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
59
58
|
none: false
|
60
59
|
requirements:
|
61
60
|
- - ">="
|
@@ -66,12 +65,12 @@ dependencies:
|
|
66
65
|
- 6
|
67
66
|
- 2
|
68
67
|
version: 2.6.2
|
69
|
-
requirement: *id003
|
70
|
-
- !ruby/object:Gem::Dependency
|
71
68
|
type: :runtime
|
72
|
-
|
69
|
+
version_requirements: *id003
|
70
|
+
- !ruby/object:Gem::Dependency
|
73
71
|
name: sham_rack
|
74
|
-
|
72
|
+
prerelease: false
|
73
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
75
74
|
none: false
|
76
75
|
requirements:
|
77
76
|
- - "="
|
@@ -82,12 +81,12 @@ dependencies:
|
|
82
81
|
- 3
|
83
82
|
- 3
|
84
83
|
version: 1.3.3
|
85
|
-
requirement: *id004
|
86
|
-
- !ruby/object:Gem::Dependency
|
87
84
|
type: :runtime
|
88
|
-
|
85
|
+
version_requirements: *id004
|
86
|
+
- !ruby/object:Gem::Dependency
|
89
87
|
name: sinatra
|
90
|
-
|
88
|
+
prerelease: false
|
89
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
91
90
|
none: false
|
92
91
|
requirements:
|
93
92
|
- - "="
|
@@ -98,12 +97,12 @@ dependencies:
|
|
98
97
|
- 1
|
99
98
|
- 2
|
100
99
|
version: 1.1.2
|
101
|
-
|
100
|
+
type: :runtime
|
101
|
+
version_requirements: *id005
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
|
-
type: :development
|
104
|
-
prerelease: false
|
105
103
|
name: aruba
|
106
|
-
|
104
|
+
prerelease: false
|
105
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
107
106
|
none: false
|
108
107
|
requirements:
|
109
108
|
- - "="
|
@@ -114,7 +113,8 @@ dependencies:
|
|
114
113
|
- 2
|
115
114
|
- 6
|
116
115
|
version: 0.2.6
|
117
|
-
|
116
|
+
type: :development
|
117
|
+
version_requirements: *id006
|
118
118
|
description: Clearance-based Rails engine for Software as a Service (Saas) that provides account and project management
|
119
119
|
email: support@thoughtbot.com
|
120
120
|
executables: []
|