saucy 0.2.18 → 0.2.20

Sign up to get free protection for your applications and to get access to all the features.
@@ -22,17 +22,17 @@ class InvitationsController < ApplicationController
22
22
  end
23
23
 
24
24
  def show
25
- @invitation = Invitation.find(params[:id])
26
- render
25
+ with_invitation { render }
27
26
  end
28
27
 
29
28
  def update
30
- @invitation = Invitation.find(params[:id])
31
- if @invitation.accept(params[:invitation])
32
- sign_in @invitation.user
33
- redirect_to root_url
34
- else
35
- render :action => 'show'
29
+ with_invitation do
30
+ if @invitation.accept(params[:invitation])
31
+ sign_in @invitation.user
32
+ redirect_to root_url
33
+ else
34
+ render :action => 'show'
35
+ end
36
36
  end
37
37
  end
38
38
 
@@ -41,4 +41,15 @@ class InvitationsController < ApplicationController
41
41
  def assign_projects
42
42
  @projects = current_account.projects_by_name
43
43
  end
44
+
45
+ def with_invitation
46
+ @invitation = Invitation.find_by_code!(params[:id])
47
+ if @invitation.used?
48
+ flash[:error] = t("invitations.show.used",
49
+ :default => "That invitation has already been used.")
50
+ redirect_to root_url
51
+ else
52
+ yield
53
+ end
54
+ end
44
55
  end
@@ -4,13 +4,14 @@ class Invitation < ActiveRecord::Base
4
4
  validates_presence_of :email
5
5
  has_and_belongs_to_many :projects
6
6
 
7
+ before_create :generate_code
7
8
  after_create :deliver_invitation
8
9
 
9
10
  attr_accessor :new_user_name, :new_user_password,
10
11
  :new_user_password_confirmation, :existing_user_password
11
12
  attr_writer :new_user_email, :existing_user_email
12
13
  attr_reader :user
13
- attr_protected :account_id
14
+ attr_protected :account_id, :used
14
15
 
15
16
  validate :validate_accepting_user, :on => :update
16
17
 
@@ -20,12 +21,16 @@ class Invitation < ActiveRecord::Base
20
21
 
21
22
  def accept(attributes)
22
23
  self.attributes = attributes
24
+ self.used = true
23
25
  @user = existing_user || new_user
24
26
  if valid?
25
- @user.save!
26
- @user.memberships.create!(:account => account,
27
- :admin => admin,
28
- :projects => projects)
27
+ transaction do
28
+ save!
29
+ @user.save!
30
+ @user.memberships.create!(:account => account,
31
+ :admin => admin,
32
+ :projects => projects)
33
+ end
29
34
  end
30
35
  end
31
36
 
@@ -37,6 +42,10 @@ class Invitation < ActiveRecord::Base
37
42
  @existing_user_email ||= email
38
43
  end
39
44
 
45
+ def to_param
46
+ code
47
+ end
48
+
40
49
  private
41
50
 
42
51
  def deliver_invitation
@@ -84,4 +93,8 @@ class Invitation < ActiveRecord::Base
84
93
  errors.add(:existing_user_password, "is incorrect")
85
94
  end
86
95
  end
96
+
97
+ def generate_code
98
+ self.code = SecureRandom.hex(8)
99
+ end
87
100
  end
@@ -29,6 +29,8 @@ class CreateSaucyTables < ActiveRecord::Migration
29
29
  table.string :email
30
30
  table.integer :account_id
31
31
  table.boolean :admin
32
+ table.string :code
33
+ table.boolean :used
32
34
  table.datetime :created_at
33
35
  table.datetime :updated_at
34
36
  end
@@ -101,11 +101,11 @@ describe InvitationsController, "invalid create", :as => :account_admin do
101
101
  end
102
102
 
103
103
  describe InvitationsController, "show" do
104
- let(:invitation) { Factory.stub(:invitation) }
104
+ let(:invitation) { Factory.stub(:invitation, :code => 'abc') }
105
105
  let(:account) { invitation.account }
106
106
 
107
107
  before do
108
- Invitation.stubs(:find => invitation)
108
+ Invitation.stubs(:find_by_code! => invitation)
109
109
  get :show, :id => invitation.to_param, :account_id => account.to_param
110
110
  end
111
111
 
@@ -115,19 +115,37 @@ describe InvitationsController, "show" do
115
115
  end
116
116
 
117
117
  it "assigns the invitation" do
118
- Invitation.should have_received(:find).with(invitation.to_param)
118
+ Invitation.should have_received(:find_by_code!).with(invitation.to_param)
119
119
  should assign_to(:invitation).with(invitation)
120
120
  end
121
121
  end
122
122
 
123
+ describe InvitationsController, "show for a used invitation" do
124
+ let(:invitation) { Factory.stub(:invitation, :code => 'abc', :used => true) }
125
+ let(:account) { invitation.account }
126
+
127
+ before do
128
+ Invitation.stubs(:find_by_code! => invitation)
129
+ get :show, :id => invitation.to_param, :account_id => account.to_param
130
+ end
131
+
132
+ it "redirects to the root url" do
133
+ should redirect_to("/")
134
+ end
135
+
136
+ it "sets a flash message" do
137
+ should set_the_flash.to(/used/i)
138
+ end
139
+ end
140
+
123
141
  describe InvitationsController, "valid update" do
124
- let(:invitation) { Factory.stub(:invitation) }
142
+ let(:invitation) { Factory.stub(:invitation, :code => 'abc') }
125
143
  let(:account) { invitation.account }
126
144
  let(:attributes) { 'attributes' }
127
145
  let(:user) { Factory.stub(:user) }
128
146
 
129
147
  before do
130
- Invitation.stubs(:find => invitation)
148
+ Invitation.stubs(:find_by_code! => invitation)
131
149
  invitation.stubs(:accept => true)
132
150
  invitation.stubs(:user => user)
133
151
  put :update, :id => invitation.to_param,
@@ -140,7 +158,7 @@ describe InvitationsController, "valid update" do
140
158
  end
141
159
 
142
160
  it "accepts the invitation" do
143
- Invitation.should have_received(:find).with(invitation.to_param)
161
+ Invitation.should have_received(:find_by_code!).with(invitation.to_param)
144
162
  invitation.should have_received(:accept).with(attributes)
145
163
  end
146
164
 
@@ -150,11 +168,11 @@ describe InvitationsController, "valid update" do
150
168
  end
151
169
 
152
170
  describe InvitationsController, "invalid update" do
153
- let(:invitation) { Factory.stub(:invitation) }
171
+ let(:invitation) { Factory.stub(:invitation, :code => 'abc') }
154
172
  let(:account) { invitation.account }
155
173
 
156
174
  before do
157
- Invitation.stubs(:find => invitation)
175
+ Invitation.stubs(:find_by_code! => invitation)
158
176
  invitation.stubs(:accept => false)
159
177
  put :update, :id => invitation.to_param,
160
178
  :account_id => account.to_param,
@@ -174,3 +192,22 @@ describe InvitationsController, "invalid update" do
174
192
  should assign_to(:invitation).with(invitation)
175
193
  end
176
194
  end
195
+
196
+ describe InvitationsController, "update for a used invitation" do
197
+ let(:invitation) { Factory.stub(:invitation, :code => 'abc', :used => true) }
198
+ let(:account) { invitation.account }
199
+
200
+ before do
201
+ Invitation.stubs(:find_by_code! => invitation)
202
+ put :update, :id => invitation.to_param, :account_id => account.to_param
203
+ end
204
+
205
+ it "redirects to the root url" do
206
+ should redirect_to("/")
207
+ end
208
+
209
+ it "sets a flash message" do
210
+ should set_the_flash.to(/used/i)
211
+ end
212
+ end
213
+
@@ -7,6 +7,7 @@ describe Invitation do
7
7
  it { should have_and_belong_to_many(:projects) }
8
8
 
9
9
  it { should_not allow_mass_assignment_of(:account_id) }
10
+ it { should_not allow_mass_assignment_of(:used) }
10
11
 
11
12
  %w(new_user_name new_user_email new_user_password
12
13
  new_user_password_confirmation existing_user_password).each do |attribute|
@@ -19,9 +20,15 @@ end
19
20
 
20
21
  describe Invitation, "saved" do
21
22
  let(:mail) { stub('invitation', :deliver => true) }
22
- before { InvitationMailer.stubs(:invitation => mail) }
23
23
  subject { Factory(:invitation) }
24
24
  let(:email) { subject.email }
25
+ let(:code) { 'abchex123' }
26
+
27
+ before do
28
+ SecureRandom.stubs(:hex => code)
29
+ InvitationMailer.stubs(:invitation => mail)
30
+ subject
31
+ end
25
32
 
26
33
  it "sends an invitation email" do
27
34
  InvitationMailer.should have_received(:invitation).with(subject)
@@ -39,6 +46,15 @@ describe Invitation, "saved" do
39
46
  it "defauls existing user email to invited email" do
40
47
  subject.existing_user_email.should == subject.email
41
48
  end
49
+
50
+ it "generates a code" do
51
+ SecureRandom.should have_received(:hex).with(8)
52
+ subject.code.should == code
53
+ end
54
+
55
+ it "uses the code in the url" do
56
+ subject.to_param.should == code
57
+ end
42
58
  end
43
59
 
44
60
  describe Invitation, "valid accept for a new user" do
@@ -75,6 +91,10 @@ describe Invitation, "valid accept for a new user" do
75
91
  user.should be_member_of(project)
76
92
  end
77
93
  end
94
+
95
+ it "marks the invitation as used" do
96
+ subject.reload.should be_used
97
+ end
78
98
  end
79
99
 
80
100
  describe Invitation, "invalid accept for a new user" do
@@ -94,6 +114,10 @@ describe Invitation, "invalid accept for a new user" do
94
114
  it "adds error messages" do
95
115
  subject.errors[:new_user_password].should be_present
96
116
  end
117
+
118
+ it "doesn't mark the invitation as used" do
119
+ subject.reload.should_not be_used
120
+ end
97
121
  end
98
122
 
99
123
  describe Invitation, "valid accept for an existing user" do
@@ -115,6 +139,10 @@ describe Invitation, "valid accept for an existing user" do
115
139
  it "adds the user to the account" do
116
140
  account.users.should include(user)
117
141
  end
142
+
143
+ it "marks the invitation as used" do
144
+ subject.reload.should be_used
145
+ end
118
146
  end
119
147
 
120
148
  describe Invitation, "accepting with an invalid password" do
@@ -136,6 +164,108 @@ describe Invitation, "accepting with an invalid password" do
136
164
  end
137
165
  end
138
166
 
167
+ describe Invitation, "saved" do
168
+ let(:mail) { stub('invitation', :deliver => true) }
169
+ subject { Factory(:invitation) }
170
+ let(:email) { subject.email }
171
+ let(:code) { 'abchex123' }
172
+
173
+ before do
174
+ SecureRandom.stubs(:hex => code)
175
+ InvitationMailer.stubs(:invitation => mail)
176
+ subject
177
+ end
178
+
179
+ it "sends an invitation email" do
180
+ InvitationMailer.should have_received(:invitation).with(subject)
181
+ mail.should have_received(:deliver)
182
+ end
183
+
184
+ it "delegates account name" do
185
+ subject.account_name.should == subject.account.name
186
+ end
187
+
188
+ it "defauls new user email to invited email" do
189
+ subject.new_user_email.should == subject.email
190
+ end
191
+
192
+ it "defauls existing user email to invited email" do
193
+ subject.existing_user_email.should == subject.email
194
+ end
195
+
196
+ it "generates a code" do
197
+ SecureRandom.should have_received(:hex).with(8)
198
+ subject.code.should == code
199
+ end
200
+
201
+ it "uses the code in the url" do
202
+ subject.to_param.should == code
203
+ end
204
+ end
205
+
206
+ describe Invitation, "valid accept for a new user" do
207
+ let(:account) { Factory(:account) }
208
+ let(:projects) { [Factory(:project, :account => account)] }
209
+ let(:password) { 'secret' }
210
+ let(:name) { 'Rocket' }
211
+ subject { Factory(:invitation, :account => account, :projects => projects) }
212
+
213
+ let!(:result) do
214
+ subject.accept(:new_user_password => password,
215
+ :new_user_password_confirmation => password,
216
+ :new_user_name => name)
217
+ end
218
+
219
+ let(:user) { subject.user }
220
+
221
+ it "returns true" do
222
+ result.should be_true
223
+ end
224
+
225
+ it "creates a saved, confirmed user" do
226
+ user.should_not be_nil
227
+ user.should be_persisted
228
+ user.name.should == name
229
+ end
230
+
231
+ it "adds the user to the account" do
232
+ account.users.should include(user)
233
+ end
234
+
235
+ it "adds the user to each of the invitation's projects" do
236
+ projects.each do |project|
237
+ user.should be_member_of(project)
238
+ end
239
+ end
240
+
241
+ it "marks the invitation as used" do
242
+ subject.reload.should be_used
243
+ end
244
+ end
245
+
246
+ describe Invitation, "invalid accept for a new user" do
247
+ subject { Factory(:invitation) }
248
+ let!(:result) { subject.accept({}) }
249
+ let(:user) { subject.user }
250
+ let(:account) { subject.account }
251
+
252
+ it "returns false" do
253
+ result.should be_false
254
+ end
255
+
256
+ it "doesn't create a user" do
257
+ user.should be_new_record
258
+ end
259
+
260
+ it "adds error messages" do
261
+ subject.errors[:new_user_password].should be_present
262
+ end
263
+
264
+ it "doesn't mark the invitation as used" do
265
+ subject.reload.should_not be_used
266
+ end
267
+ end
268
+
139
269
  describe Invitation, "accepting with an unknown email" do
140
270
  subject { Factory(:invitation, :email => 'unknown') }
141
271
  let(:account) { subject.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: 51
4
+ hash: 63
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 18
10
- version: 0.2.18
9
+ - 20
10
+ version: 0.2.20
11
11
  platform: ruby
12
12
  authors:
13
13
  - thoughtbot, inc.
@@ -17,13 +17,14 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2011-02-09 00:00:00 -05:00
20
+ date: 2011-02-11 00:00:00 -05:00
21
21
  default_executable:
22
22
  dependencies:
23
23
  - !ruby/object:Gem::Dependency
24
- name: formtastic
24
+ type: :runtime
25
25
  prerelease: false
26
- requirement: &id001 !ruby/object:Gem::Requirement
26
+ name: formtastic
27
+ version_requirements: &id001 !ruby/object:Gem::Requirement
27
28
  none: false
28
29
  requirements:
29
30
  - - ">="
@@ -33,12 +34,12 @@ dependencies:
33
34
  - 1
34
35
  - 2
35
36
  version: "1.2"
36
- type: :runtime
37
- version_requirements: *id001
37
+ requirement: *id001
38
38
  - !ruby/object:Gem::Dependency
39
- name: railties
39
+ type: :runtime
40
40
  prerelease: false
41
- requirement: &id002 !ruby/object:Gem::Requirement
41
+ name: railties
42
+ version_requirements: &id002 !ruby/object:Gem::Requirement
42
43
  none: false
43
44
  requirements:
44
45
  - - ">="
@@ -49,12 +50,12 @@ dependencies:
49
50
  - 0
50
51
  - 3
51
52
  version: 3.0.3
52
- type: :runtime
53
- version_requirements: *id002
53
+ requirement: *id002
54
54
  - !ruby/object:Gem::Dependency
55
- name: braintree
55
+ type: :runtime
56
56
  prerelease: false
57
- requirement: &id003 !ruby/object:Gem::Requirement
57
+ name: braintree
58
+ version_requirements: &id003 !ruby/object:Gem::Requirement
58
59
  none: false
59
60
  requirements:
60
61
  - - ">="
@@ -65,12 +66,12 @@ dependencies:
65
66
  - 6
66
67
  - 2
67
68
  version: 2.6.2
68
- type: :runtime
69
- version_requirements: *id003
69
+ requirement: *id003
70
70
  - !ruby/object:Gem::Dependency
71
- name: sham_rack
71
+ type: :runtime
72
72
  prerelease: false
73
- requirement: &id004 !ruby/object:Gem::Requirement
73
+ name: sham_rack
74
+ version_requirements: &id004 !ruby/object:Gem::Requirement
74
75
  none: false
75
76
  requirements:
76
77
  - - "="
@@ -81,12 +82,12 @@ dependencies:
81
82
  - 3
82
83
  - 3
83
84
  version: 1.3.3
84
- type: :runtime
85
- version_requirements: *id004
85
+ requirement: *id004
86
86
  - !ruby/object:Gem::Dependency
87
- name: sinatra
87
+ type: :runtime
88
88
  prerelease: false
89
- requirement: &id005 !ruby/object:Gem::Requirement
89
+ name: sinatra
90
+ version_requirements: &id005 !ruby/object:Gem::Requirement
90
91
  none: false
91
92
  requirements:
92
93
  - - "="
@@ -97,12 +98,12 @@ dependencies:
97
98
  - 1
98
99
  - 2
99
100
  version: 1.1.2
100
- type: :runtime
101
- version_requirements: *id005
101
+ requirement: *id005
102
102
  - !ruby/object:Gem::Dependency
103
- name: aruba
103
+ type: :development
104
104
  prerelease: false
105
- requirement: &id006 !ruby/object:Gem::Requirement
105
+ name: aruba
106
+ version_requirements: &id006 !ruby/object:Gem::Requirement
106
107
  none: false
107
108
  requirements:
108
109
  - - "="
@@ -113,8 +114,7 @@ dependencies:
113
114
  - 2
114
115
  - 6
115
116
  version: 0.2.6
116
- type: :development
117
- version_requirements: *id006
117
+ requirement: *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: []