acts_as_approvable 0.1.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/.gitignore +4 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +5 -0
  4. data/Appraisals +22 -0
  5. data/CHANGELOG +76 -0
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +84 -0
  8. data/MIT-LICENSE +2 -2
  9. data/README.md +146 -0
  10. data/Rakefile +90 -7
  11. data/TODO.md +30 -0
  12. data/VERSION +1 -0
  13. data/acts_as_approvable.gemspec +40 -0
  14. data/features/create_approval.feature +36 -0
  15. data/features/destroy_approval.feature +19 -0
  16. data/features/reset_approval.feature +13 -0
  17. data/features/step_definitions/cucumber_steps.rb +132 -0
  18. data/features/support/env.rb +14 -0
  19. data/features/support/large.txt +29943 -0
  20. data/features/support/second_large.txt +31798 -0
  21. data/features/update_approval.feature +48 -0
  22. data/gemfiles/Gemfile.ci +14 -0
  23. data/gemfiles/Gemfile.ci.lock +98 -0
  24. data/gemfiles/mysql2.gemfile +7 -0
  25. data/gemfiles/mysql2.gemfile.lock +86 -0
  26. data/gemfiles/rails2.gemfile +8 -0
  27. data/gemfiles/rails2.gemfile.lock +86 -0
  28. data/gemfiles/rails30.gemfile +9 -0
  29. data/gemfiles/rails30.gemfile.lock +124 -0
  30. data/gemfiles/rails31.gemfile +9 -0
  31. data/gemfiles/rails31.gemfile.lock +135 -0
  32. data/gemfiles/sqlite.gemfile +7 -0
  33. data/generators/acts_as_approvable/USAGE +3 -0
  34. data/generators/acts_as_approvable/acts_as_approvable_generator.rb +81 -0
  35. data/generators/acts_as_approvable/templates/approvals.js +71 -0
  36. data/generators/acts_as_approvable/templates/approvals_controller.rb +91 -0
  37. data/generators/acts_as_approvable/templates/create_approvals.rb +27 -0
  38. data/generators/acts_as_approvable/templates/initializer.rb +3 -0
  39. data/generators/acts_as_approvable/templates/jquery.form.js +101 -0
  40. data/generators/acts_as_approvable/templates/views/erb/_owner_select.html.erb +4 -0
  41. data/generators/acts_as_approvable/templates/views/erb/_table.html.erb +26 -0
  42. data/generators/acts_as_approvable/templates/views/erb/index.html.erb +17 -0
  43. data/generators/acts_as_approvable/templates/views/haml/_owner_select.html.haml +3 -0
  44. data/generators/acts_as_approvable/templates/views/haml/_table.html.haml +19 -0
  45. data/generators/acts_as_approvable/templates/views/haml/index.html.haml +15 -0
  46. data/init.rb +1 -0
  47. data/lib/acts_as_approvable.rb +96 -2
  48. data/lib/acts_as_approvable/approval.rb +205 -11
  49. data/lib/acts_as_approvable/error.rb +34 -0
  50. data/lib/acts_as_approvable/model.rb +60 -0
  51. data/lib/acts_as_approvable/model/class_methods.rb +63 -0
  52. data/lib/acts_as_approvable/model/create_instance_methods.rb +88 -0
  53. data/lib/acts_as_approvable/model/destroy_instance_methods.rb +38 -0
  54. data/lib/acts_as_approvable/model/instance_methods.rb +107 -0
  55. data/lib/acts_as_approvable/model/update_instance_methods.rb +61 -0
  56. data/lib/acts_as_approvable/ownership.rb +141 -0
  57. data/lib/acts_as_approvable/railtie.rb +7 -0
  58. data/lib/acts_as_approvable/version.rb +1 -8
  59. data/lib/generators/acts_as_approvable/USAGE +1 -0
  60. data/lib/generators/acts_as_approvable/acts_as_approvable_generator.rb +68 -0
  61. data/lib/generators/acts_as_approvable/base.rb +30 -0
  62. data/lib/generators/acts_as_approvable/templates/approvals.js +71 -0
  63. data/lib/generators/acts_as_approvable/templates/approvals_controller.rb +91 -0
  64. data/lib/generators/acts_as_approvable/templates/create_approvals.rb +27 -0
  65. data/lib/generators/acts_as_approvable/templates/jquery.form.js +101 -0
  66. data/lib/generators/erb/acts_as_approvable_generator.rb +33 -0
  67. data/lib/generators/erb/templates/_owner_select.html.erb +4 -0
  68. data/lib/generators/erb/templates/_table.html.erb +26 -0
  69. data/lib/generators/erb/templates/index.html.erb +17 -0
  70. data/lib/generators/haml/acts_as_approvable_generator.rb +33 -0
  71. data/lib/generators/haml/templates/_owner_select.html.haml +3 -0
  72. data/lib/generators/haml/templates/_table.html.haml +19 -0
  73. data/lib/generators/haml/templates/index.html.haml +15 -0
  74. data/lib/tasks/acts_as_approvable.rake +4 -0
  75. data/rails/init.rb +1 -0
  76. data/spec/acts_as_approvable/approval_spec.rb +614 -0
  77. data/spec/acts_as_approvable/model/class_methods_spec.rb +219 -0
  78. data/spec/acts_as_approvable/model/create_instance_methods_spec.rb +169 -0
  79. data/spec/acts_as_approvable/model/destroy_instance_methods_spec.rb +71 -0
  80. data/spec/acts_as_approvable/model/instance_methods_spec.rb +328 -0
  81. data/spec/acts_as_approvable/model/update_instance_methods_spec.rb +111 -0
  82. data/spec/acts_as_approvable/model_spec.rb +113 -0
  83. data/spec/acts_as_approvable/ownership/class_methods_spec.rb +134 -0
  84. data/spec/acts_as_approvable/ownership/instance_methods_spec.rb +32 -0
  85. data/spec/acts_as_approvable/ownership_spec.rb +52 -0
  86. data/spec/acts_as_approvable_spec.rb +31 -0
  87. data/spec/spec_helper.rb +51 -0
  88. data/spec/support/database.rb +49 -0
  89. data/spec/support/database.yml +12 -0
  90. data/spec/support/matchers.rb +87 -0
  91. data/spec/support/models.rb +67 -0
  92. data/spec/support/schema.rb +54 -0
  93. metadata +375 -58
  94. data/README.rdoc +0 -38
  95. data/lib/acts_as_approvable/approver.rb +0 -76
  96. data/lib/generators/acts_as_approvable/install_generator.rb +0 -28
  97. data/lib/generators/acts_as_approvable/templates/install.rb +0 -16
@@ -0,0 +1,4 @@
1
+ <%% form_for(approval, :url => assign_approval_path(approval.id), :html => {:class => 'assignment'}) do |f| %>
2
+ <%%= f.select(:owner_id, options_for_select(Approval.options_for_available_owners(true), approval.owner_id)) %>
3
+ <%%= f.submit('Set') %>
4
+ <%% end %>
@@ -0,0 +1,26 @@
1
+ <table>
2
+ <thead>
3
+ <tr>
4
+ <th>Type</th>
5
+ <th>Record</th>
6
+ <% if owner? %> <th>Owner</th>
7
+ <% end %> <th>State</th>
8
+ <th>&nbsp;</th>
9
+ </tr>
10
+ </thead>
11
+ <tbody>
12
+ <%% @approvals.each do |approval| %>
13
+ <tr>
14
+ <td><%%= approval.item_type.classify %></td>
15
+ <td><%%= approval.item.try(:to_s) || approval.item.id %></td>
16
+ <% if owner? %> <td><%%= render :partial => 'owner_select', :locals => {:approval => approval} %></td>
17
+ <% end %> <td><%%= approval.state %></td>
18
+ <td class='actions'>
19
+ <%%= link_to('Approve', approve_approval_path(approval), :class => 'approve') %>
20
+ <%%= link_to('Reject', reject_approval_path(approval), :class => 'reject') %>
21
+ </td>
22
+ </tr>
23
+ <%% end %>
24
+ </tbody>
25
+ </table>
26
+
@@ -0,0 +1,17 @@
1
+ <h1>Approval Queue</h1>
2
+
3
+ <%% form_for(:approval, :url => approvals_path, :html => {:method => :get}) do |f| %>
4
+ <%% unless @conditions[:state].is_a?(Array) # History page shouldn't allow selecting different states %>
5
+ <%%= label_tag(:state) %>
6
+ <%%= select_tag(:state, options_for_select(Approval.options_for_state, @conditions[:state])) %>
7
+ <%% end %>
8
+ <% if owner? %> <%%= label_tag(:owner_id) %>
9
+ <%%= select_tag(:owner_id, options_for_select(Approval.options_for_assigned_owners, @conditions[:owner_id]), :prompt => 'All Users') %>
10
+ <% end %> <%%= label_tag(:item_type) %>
11
+ <%%= select_tag(:item_type, options_for_select(Approval.options_for_type, @conditions[:item_type]), :prompt => 'All Types') %>
12
+ <%%= f.submit('Filter') %>
13
+ <%% end %>
14
+
15
+ <%%= render :partial => @table_partial %>
16
+ <% if scripts? %>
17
+ <%%= javascript_include_tag 'jquery.form.js', 'approvals.js' %><% end %>
@@ -0,0 +1,33 @@
1
+ require 'generators/acts_as_approvable/base'
2
+
3
+ module Haml
4
+ module Generators
5
+ class ActsAsApprovableGenerator < Rails::Generators::Base
6
+ include ActsAsApprovable::Generators::Base
7
+
8
+ source_root File.expand_path('../templates', __FILE__)
9
+
10
+ class_option :owner, :type => :string, :optional => true, :desc => 'Model that can own approvals'
11
+ class_option :scripts, :type => :boolean, :optional => true, :default => false
12
+
13
+ def copy_view_files
14
+ template 'index.html.haml', 'app/views/approvals/index.html.haml'
15
+ template '_table.html.haml', 'app/views/approvals/_table.html.haml'
16
+ template '_owner_select.html.haml', 'app/views/approvals/_owner_select.html.haml' if owner?
17
+ end
18
+
19
+ protected
20
+ def format
21
+ :html
22
+ end
23
+
24
+ def handler
25
+ :haml
26
+ end
27
+
28
+ def filename_with_extensions(name)
29
+ [name, format, handler].compact.join('.')
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ - form_for(approval, :url => assign_approval_path(approval.id), :html => {:class => 'assignment'}) do |f|
2
+ = f.select(:owner_id, options_for_select(Approval.options_for_available_owners(true), approval.owner_id))
3
+ = f.submit('Set')
@@ -0,0 +1,19 @@
1
+ %table
2
+ %thead
3
+ %tr
4
+ %th Type
5
+ %th Record
6
+ <% if owner? %> %th Owner
7
+ <% end %> %th State
8
+ %th
9
+ %tbody
10
+ - @approvals.each do |approval|
11
+ %tr
12
+ %td= approval.item_type.classify
13
+ %td= approval.item.try(:to_s) || approval.item.id
14
+ <% if owner? %> %td= render :partial => 'owner_select', :locals => {:approval => approval}
15
+ <% end %> %td= approval.state
16
+ %td.actions
17
+ = link_to('Approve', approve_approval_path(approval), :class => 'approve')
18
+ = link_to('Reject', reject_approval_path(approval), :class => 'reject')
19
+
@@ -0,0 +1,15 @@
1
+ %h1 Approval Queue
2
+
3
+ - form_for(:approval, :url => approvals_path, :html => {:method => :get}) do |f|
4
+ - unless @conditions[:state].is_a?(Array) # History page shouldn't allow selecting different states
5
+ = label_tag('state', 'State')
6
+ = select_tag('state', options_for_select(Approval.options_for_state, @conditions[:state]))
7
+ <% if owner? %> = label_tag('owner_id', 'Owner')
8
+ = select_tag('owner_id', options_for_select(Approval.options_for_assigned_owners(true), @conditions[:owner_id]))
9
+ <% end %> = label_tag('item_type', 'Type')
10
+ = select_tag('item_type', options_for_select(Approval.options_for_type(true), @conditions[:item_type]))
11
+ %button{:type => 'Submit'} Filter
12
+
13
+ = render :partial => @table_partial
14
+ <% if scripts? %>
15
+ = javascript_include_tag 'jquery.form.js', 'approvals.js'<% end %>
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :acts_as_approvable do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1 @@
1
+ require 'acts-as-approvable'
@@ -0,0 +1,614 @@
1
+ require 'spec_helper'
2
+
3
+ describe Approval do
4
+ before(:each) do
5
+ subject.stub(:save! => true, :save => true)
6
+ end
7
+
8
+ it 'should serialize :object' do
9
+ described_class.serialized_attributes.keys.should include('object')
10
+ end
11
+
12
+ describe '.associations' do
13
+ it { should belong_to(:item) }
14
+ end
15
+
16
+ describe '.validates' do
17
+ it { should validate_presence_of(:item) }
18
+ it { should validate_inclusion_of(:event).in(%w(create update)) }
19
+ it { should validate_numericality_of(:state) }
20
+ it { should ensure_inclusion_of(:state).in_range(0..(described_class::STATES.length - 1)).with_low_message(/greater than/).with_high_message(/less than/)}
21
+ end
22
+
23
+ describe '.enumerate_state' do
24
+ it 'enumerates "pending" to 0' do
25
+ described_class.enumerate_state('pending').should be(0)
26
+ end
27
+
28
+ it 'enumerates "approved" to 1' do
29
+ described_class.enumerate_state('approved').should be(1)
30
+ end
31
+
32
+ it 'enumerates "rejected" to 2' do
33
+ described_class.enumerate_state('rejected').should be(2)
34
+ end
35
+
36
+ it 'enumerates other values to nil' do
37
+ described_class.enumerate_state('not_a_state').should_not be
38
+ end
39
+ end
40
+
41
+ describe '.enumerate_states' do
42
+ it 'enumerates many states at once' do
43
+ described_class.enumerate_states('pending', 'approved', 'rejected').should == [0, 1, 2]
44
+ end
45
+
46
+ it 'ignores states it does not know' do
47
+ described_class.enumerate_states('pending', 'not_a_state', 'rejected').should == [0, 2]
48
+ end
49
+ end
50
+
51
+ describe '.options_for_state' do
52
+ it 'returns an array usable by #options_for_select' do
53
+ described_class.options_for_state.should be_an_options_array
54
+ end
55
+
56
+ it 'includes an "all" option' do
57
+ described_class.options_for_state.should include(['All', -1])
58
+ end
59
+
60
+ it 'includes the pending state' do
61
+ described_class.options_for_state.should include(['Pending', 0])
62
+ end
63
+
64
+ it 'includes the approved state' do
65
+ described_class.options_for_state.should include(['Approved', 1])
66
+ end
67
+
68
+ it 'includes the rejected state' do
69
+ described_class.options_for_state.should include(['Rejected', 2])
70
+ end
71
+ end
72
+
73
+ describe '.options_for_type' do
74
+ before(:each) do
75
+ @default = DefaultApprovable.create
76
+ @creates = CreatesApprovable.create
77
+ @updates = UpdatesApprovable.create
78
+ end
79
+
80
+ it 'returns an array usable by #options_for_select' do
81
+ described_class.options_for_type.should be_an_options_array
82
+ end
83
+
84
+ it 'includes all types with approvals' do
85
+ described_class.options_for_type.should include('DefaultApprovable')
86
+ described_class.options_for_type.should include('CreatesApprovable')
87
+ end
88
+
89
+ it 'does not include types without approvals' do
90
+ described_class.options_for_type.should_not include('UpdatesApprovable')
91
+ end
92
+
93
+ it 'includes a prompt if requested' do
94
+ described_class.options_for_type(true).should include(['All Types', nil])
95
+ end
96
+
97
+ it 'does not includes a prompt by default' do
98
+ described_class.options_for_type.should_not include(['All Types', nil])
99
+ end
100
+ end
101
+
102
+ describe '#state' do
103
+ it 'returns the state as a string' do
104
+ subject.state.should be_a(String)
105
+ end
106
+
107
+ it 'attempts to read the state attribute' do
108
+ subject.should_receive(:read_attribute).with(:state)
109
+ subject.state
110
+ end
111
+ end
112
+
113
+ describe '#state_was' do
114
+ it 'returns the state as a string' do
115
+ subject.state_was.should be_a(String)
116
+ end
117
+
118
+ it 'attempts to read the changed state attribute' do
119
+ subject.should_receive(:changed_attributes).and_return({:state => 1})
120
+ subject.state_was.should == 'approved'
121
+ end
122
+ end
123
+
124
+ describe '#state=' do
125
+ it 'writes the attribute value' do
126
+ subject.should_receive(:write_attribute)
127
+ subject.send(:state=, 'pending')
128
+ end
129
+
130
+ it 'enumerates the given state' do
131
+ subject.should_receive(:write_attribute).with(:state, 0)
132
+ subject.send(:state=, 'pending')
133
+ end
134
+
135
+ it 'skips enumeration for numeric values' do
136
+ subject.should_receive(:write_attribute).with(:state, 10)
137
+ subject.send(:state=, 10)
138
+ end
139
+ end
140
+
141
+ context 'when the state is pending' do
142
+ before(:each) do
143
+ subject.stub(:state => 'pending')
144
+ end
145
+
146
+ it { should be_pending }
147
+ it { should_not be_approved }
148
+ it { should_not be_rejected }
149
+ it { should_not be_locked }
150
+ it { should be_unlocked }
151
+ end
152
+
153
+ context 'when the state is approved' do
154
+ before(:each) do
155
+ subject.stub(:state => 'approved')
156
+ end
157
+
158
+ it { should_not be_pending }
159
+ it { should be_approved }
160
+ it { should_not be_rejected }
161
+ it { should be_locked }
162
+ it { should_not be_unlocked }
163
+ end
164
+
165
+ context 'when the state is rejected' do
166
+ before(:each) do
167
+ subject.stub(:state => 'rejected')
168
+ end
169
+
170
+ it { should_not be_pending }
171
+ it { should_not be_approved }
172
+ it { should be_rejected }
173
+ it { should be_locked }
174
+ it { should_not be_unlocked }
175
+ end
176
+
177
+ context 'when the event is :update' do
178
+ before(:each) do
179
+ subject.stub(:event => 'update')
180
+ end
181
+
182
+ it { should be_update }
183
+ it { should_not be_create }
184
+ it { should_not be_destroy }
185
+
186
+ describe '#reset!' do
187
+ it 'should raise an InvalidTransition error' do
188
+ expect { subject.reset! }.to raise_error(ActsAsApprovable::Error::InvalidTransition)
189
+ end
190
+ end
191
+ end
192
+
193
+ context 'when the event is :create' do
194
+ before(:each) do
195
+ subject.stub(:event => 'create')
196
+ end
197
+
198
+ it { should_not be_update }
199
+ it { should be_create }
200
+ it { should_not be_destroy }
201
+
202
+ describe '#reset!' do
203
+ it 'should not raise an InvalidTransition error' do
204
+ expect { subject.reset! }.not_to raise_error(ActsAsApprovable::Error::InvalidTransition)
205
+ end
206
+
207
+ it 'should save even if no values change' do
208
+ subject.stub(:item => DefaultApprovable.new)
209
+ subject.should_receive(:save!).and_return(true)
210
+ subject.reset!
211
+ end
212
+ end
213
+ end
214
+
215
+ context 'when the event is :destroy' do
216
+ before(:each) do
217
+ subject.stub(:event => 'destroy')
218
+ end
219
+
220
+ it { should_not be_update }
221
+ it { should_not be_create }
222
+ it { should be_destroy }
223
+ end
224
+
225
+ context 'when the approval is unlocked' do
226
+ before(:each) do
227
+ @item = DefaultApprovable.without_approval { |m| m.create }
228
+ subject.stub(:locked? => false, :updated_at => Time.now, :item => @item)
229
+ @item.stub(:updated_at => Time.now)
230
+ end
231
+
232
+ describe '#able_to_save?' do
233
+ it { should be_able_to_save }
234
+
235
+ it 'does not check the what the state was' do
236
+ subject.should_not_receive(:state_was)
237
+ subject.able_to_save?
238
+ end
239
+ end
240
+
241
+ describe '#stale?' do
242
+ it 'checks when the item was changed' do
243
+ @item.should_receive(:has_attribute?).with(:updated_at).and_return(true)
244
+ subject.stale?
245
+ end
246
+ end
247
+
248
+ describe '#fresh?' do
249
+ it 'checks when the item was changed' do
250
+ @item.should_receive(:has_attribute?).with(:updated_at).and_return(true)
251
+ subject.fresh?
252
+ end
253
+ end
254
+
255
+ describe '#reset!' do
256
+ before(:each) do
257
+ subject.stub(:event => 'create')
258
+ end
259
+
260
+ it 'saves the approval record' do
261
+ subject.should_receive(:save!).and_return(true)
262
+ subject.reset!
263
+ end
264
+
265
+ it 'changes the item state' do
266
+ @item.should_receive(:set_approval_state).with('pending')
267
+ subject.reset!
268
+ end
269
+ end
270
+
271
+ context 'when the approval is newer than the last update' do
272
+ before(:each) do
273
+ subject.stub(:updated_at => @item.updated_at + 60)
274
+ end
275
+
276
+ it { should_not be_stale }
277
+ it { should be_fresh }
278
+ end
279
+
280
+ context 'when the approval is older than the last update' do
281
+ before(:each) do
282
+ subject.stub(:updated_at => @item.updated_at - 60)
283
+ end
284
+
285
+ it { should be_stale }
286
+ it { should_not be_fresh }
287
+ end
288
+ end
289
+
290
+ context 'when the approval is locked' do
291
+ before(:each) do
292
+ subject.stub(:locked? => true)
293
+ end
294
+
295
+ it { should_not be_stale }
296
+ it { should be_fresh }
297
+
298
+ describe '#able_to_save?' do
299
+ it 'checks the what the state was' do
300
+ subject.should_receive(:state_was)
301
+ subject.able_to_save?
302
+ end
303
+ end
304
+
305
+ describe '#stale?' do
306
+ it 'does not check when the item was changed' do
307
+ subject.should_not_receive(:item)
308
+ subject.stale?
309
+ end
310
+ end
311
+
312
+ describe '#fresh?' do
313
+ it 'does not check when the item was changed' do
314
+ subject.should_not_receive(:item)
315
+ subject.fresh?
316
+ end
317
+ end
318
+
319
+ describe '#approve!' do
320
+ it 'raises a Locked exception' do
321
+ expect { subject.approve! }.to raise_error(ActsAsApprovable::Error::Locked)
322
+ end
323
+
324
+ it 'leaves the approval in a pending state' do
325
+ begin; subject.approve!; rescue ActsAsApprovable::Error::Locked; end
326
+ subject.should be_pending
327
+ end
328
+ end
329
+
330
+ describe '#reject!' do
331
+ it 'raises a Locked exception' do
332
+ expect { subject.reject! }.to raise_error(ActsAsApprovable::Error::Locked)
333
+ end
334
+
335
+ it 'leaves the approval in a pending state' do
336
+ begin; subject.reject!; rescue ActsAsApprovable::Error::Locked; end
337
+ subject.should be_pending
338
+ end
339
+ end
340
+
341
+ context 'and the state is pending' do
342
+ before(:each) do
343
+ subject.stub(:state_was => 'pending')
344
+ end
345
+
346
+ it { should be_able_to_save }
347
+ end
348
+
349
+ context 'and the state is approved' do
350
+ before(:each) do
351
+ subject.stub(:state_was => 'approved')
352
+ end
353
+
354
+ it { should_not be_able_to_save }
355
+ end
356
+
357
+ context 'and the state is rejected' do
358
+ before(:each) do
359
+ subject.stub(:state_was => 'rejected')
360
+ end
361
+
362
+ it { should_not be_able_to_save }
363
+ end
364
+ end
365
+
366
+ context 'when the approval is stale' do
367
+ before(:each) do
368
+ @item = DefaultApprovable.without_approval { |m| m.create }
369
+ subject.stub(:stale? => true, :item => @item)
370
+ end
371
+
372
+ it { should be_stale }
373
+ it { should_not be_fresh }
374
+
375
+ describe '#approve!' do
376
+ context 'with an :update approval' do
377
+ before(:each) do
378
+ subject.stub(:event => 'update', :object => [])
379
+ end
380
+
381
+ it 'raises a Stale exception' do
382
+ expect { subject.approve! }.to raise_error(ActsAsApprovable::Error::Stale)
383
+ end
384
+
385
+ it 'leaves the approval in a pending state' do
386
+ begin; subject.approve!; rescue ActsAsApprovable::Error::Stale; end
387
+ subject.should be_pending
388
+ end
389
+
390
+ context 'when the stale check is disabled' do
391
+ before(:each) do
392
+ ActsAsApprovable.stub(:stale_check? => false)
393
+ end
394
+
395
+ it 'does not raise a Stale exception' do
396
+ expect { subject.approve!(true) }.to_not raise_error(ActsAsApprovable::Error::Stale)
397
+ end
398
+ end
399
+
400
+ context 'when approval is forced' do
401
+ it 'does not raise a Stale exception' do
402
+ expect { subject.approve!(true) }.to_not raise_error(ActsAsApprovable::Error::Stale)
403
+ end
404
+
405
+ it 'leaves the approval in a pending state' do
406
+ subject.approve!(true)
407
+ subject.should be_approved
408
+ end
409
+ end
410
+ end
411
+
412
+ context 'with a :create approval' do
413
+ before(:each) do
414
+ subject.stub(:event => 'create')
415
+ end
416
+
417
+ it 'does not raise a Stale exception' do
418
+ expect { subject.approve! }.to_not raise_error(ActsAsApprovable::Error::Stale)
419
+ end
420
+
421
+ it 'changes the approval to approved' do
422
+ subject.approve!
423
+ subject.should be_approved
424
+ end
425
+ end
426
+ end
427
+
428
+ describe '#reject!' do
429
+ it 'does not raise a Stale exception' do
430
+ expect { subject.reject! }.to_not raise_error(ActsAsApprovable::Error::Stale)
431
+ end
432
+
433
+ it 'moves the approval to a rejected state' do
434
+ subject.reject!
435
+ subject.should be_rejected
436
+ end
437
+ end
438
+ end
439
+
440
+ context 'when the approval is unlocked and fresh' do
441
+ before(:each) do
442
+ @item = DefaultApprovable.without_approval { |m| m.create }
443
+ subject.stub(:locked? => false, :stale? => false, :item => @item, :object => {})
444
+ end
445
+
446
+ it { should_not be_stale }
447
+ it { should be_fresh }
448
+
449
+ describe '#approve!' do
450
+ it 'does not raise an exception' do
451
+ expect { subject.approve! }.to_not raise_error
452
+ end
453
+
454
+ it 'moves the approval to an approved state' do
455
+ subject.approve!
456
+ subject.should be_approved
457
+ end
458
+
459
+ it 'calls the before and after callbacks' do
460
+ subject.should_receive(:run_item_callback).with(:before_approve).once.and_return(true)
461
+ subject.should_receive(:run_item_callback).with(:after_approve).once
462
+ subject.approve!
463
+ end
464
+
465
+ context 'when the event is :update' do
466
+ before(:each) do
467
+ subject.stub(:event => 'update')
468
+ end
469
+
470
+ it 'sets the item attributes' do
471
+ @item.should_receive(:attributes=)
472
+ subject.approve!
473
+ end
474
+
475
+ it 'does not set the local item state' do
476
+ @item.should_not_receive(:set_approval_state)
477
+ subject.approve!
478
+ end
479
+
480
+ it 'does not destroy the item' do
481
+ @item.should_not_receive(:destroy)
482
+ subject.approve!
483
+ end
484
+ end
485
+
486
+ context 'when the event is :create' do
487
+ before(:each) do
488
+ subject.stub(:event => 'create')
489
+ end
490
+
491
+ it 'does not set the item attributes' do
492
+ @item.should_not_receive(:attributes=)
493
+ subject.approve!
494
+ end
495
+
496
+ it 'sets the local item state' do
497
+ @item.should_receive(:set_approval_state).with('approved')
498
+ subject.approve!
499
+ end
500
+
501
+ it 'does not destroy the item' do
502
+ @item.should_not_receive(:destroy)
503
+ subject.approve!
504
+ end
505
+ end
506
+
507
+ context 'when the event is :destroy' do
508
+ before(:each) do
509
+ subject.stub(:event => 'destroy')
510
+ end
511
+
512
+ it 'does not set the item attributes' do
513
+ @item.should_not_receive(:attributes=)
514
+ subject.approve!
515
+ end
516
+
517
+ it 'does not set the local item state' do
518
+ @item.should_not_receive(:set_approval_state)
519
+ subject.approve!
520
+ end
521
+
522
+ it 'destroys the item' do
523
+ @item.should_receive(:destroy)
524
+ subject.approve!
525
+ end
526
+ end
527
+ end
528
+
529
+ describe '#reject!' do
530
+ it 'does not raise an exception' do
531
+ expect { subject.reject! }.to_not raise_error
532
+ end
533
+
534
+ it 'moves the approval to a rejected state' do
535
+ subject.reject!
536
+ subject.should be_rejected
537
+ end
538
+
539
+ it 'sets the reason if given' do
540
+ subject.reject!('reason')
541
+ subject.reason.should == 'reason'
542
+ end
543
+
544
+ it 'calls the before and after callbacks' do
545
+ subject.should_receive(:run_item_callback).with(:before_reject).once.and_return(true)
546
+ subject.should_receive(:run_item_callback).with(:after_reject).once
547
+ subject.reject!
548
+ end
549
+
550
+ context 'when the event is :update' do
551
+ before(:each) do
552
+ subject.stub(:event => 'update')
553
+ end
554
+
555
+ it 'does not set the item attributes' do
556
+ @item.should_not_receive(:attributes=)
557
+ subject.reject!
558
+ end
559
+
560
+ it 'does not set the local item state' do
561
+ @item.should_not_receive(:set_approval_state)
562
+ subject.reject!
563
+ end
564
+
565
+ it 'does not destroy the item' do
566
+ @item.should_not_receive(:destroy)
567
+ subject.reject!
568
+ end
569
+ end
570
+
571
+ context 'when the event is :create' do
572
+ before(:each) do
573
+ subject.stub(:event => 'create')
574
+ end
575
+
576
+ it 'does not set the item attributes' do
577
+ @item.should_not_receive(:attributes=)
578
+ subject.reject!
579
+ end
580
+
581
+ it 'sets the local item state' do
582
+ @item.should_receive(:set_approval_state).with('rejected')
583
+ subject.reject!
584
+ end
585
+
586
+ it 'does not destroy the item' do
587
+ @item.should_not_receive(:destroy)
588
+ subject.reject!
589
+ end
590
+ end
591
+
592
+ context 'when the event is :destroy' do
593
+ before(:each) do
594
+ subject.stub(:event => 'destroy')
595
+ end
596
+
597
+ it 'does not set the item attributes' do
598
+ @item.should_not_receive(:attributes=)
599
+ subject.reject!
600
+ end
601
+
602
+ it 'does not set the local item state' do
603
+ @item.should_not_receive(:set_approval_state)
604
+ subject.reject!
605
+ end
606
+
607
+ it 'does not destroy the item' do
608
+ @item.should_not_receive(:destroy)
609
+ subject.reject!
610
+ end
611
+ end
612
+ end
613
+ end
614
+ end