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.
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Appraisals +22 -0
- data/CHANGELOG +76 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +84 -0
- data/MIT-LICENSE +2 -2
- data/README.md +146 -0
- data/Rakefile +90 -7
- data/TODO.md +30 -0
- data/VERSION +1 -0
- data/acts_as_approvable.gemspec +40 -0
- data/features/create_approval.feature +36 -0
- data/features/destroy_approval.feature +19 -0
- data/features/reset_approval.feature +13 -0
- data/features/step_definitions/cucumber_steps.rb +132 -0
- data/features/support/env.rb +14 -0
- data/features/support/large.txt +29943 -0
- data/features/support/second_large.txt +31798 -0
- data/features/update_approval.feature +48 -0
- data/gemfiles/Gemfile.ci +14 -0
- data/gemfiles/Gemfile.ci.lock +98 -0
- data/gemfiles/mysql2.gemfile +7 -0
- data/gemfiles/mysql2.gemfile.lock +86 -0
- data/gemfiles/rails2.gemfile +8 -0
- data/gemfiles/rails2.gemfile.lock +86 -0
- data/gemfiles/rails30.gemfile +9 -0
- data/gemfiles/rails30.gemfile.lock +124 -0
- data/gemfiles/rails31.gemfile +9 -0
- data/gemfiles/rails31.gemfile.lock +135 -0
- data/gemfiles/sqlite.gemfile +7 -0
- data/generators/acts_as_approvable/USAGE +3 -0
- data/generators/acts_as_approvable/acts_as_approvable_generator.rb +81 -0
- data/generators/acts_as_approvable/templates/approvals.js +71 -0
- data/generators/acts_as_approvable/templates/approvals_controller.rb +91 -0
- data/generators/acts_as_approvable/templates/create_approvals.rb +27 -0
- data/generators/acts_as_approvable/templates/initializer.rb +3 -0
- data/generators/acts_as_approvable/templates/jquery.form.js +101 -0
- data/generators/acts_as_approvable/templates/views/erb/_owner_select.html.erb +4 -0
- data/generators/acts_as_approvable/templates/views/erb/_table.html.erb +26 -0
- data/generators/acts_as_approvable/templates/views/erb/index.html.erb +17 -0
- data/generators/acts_as_approvable/templates/views/haml/_owner_select.html.haml +3 -0
- data/generators/acts_as_approvable/templates/views/haml/_table.html.haml +19 -0
- data/generators/acts_as_approvable/templates/views/haml/index.html.haml +15 -0
- data/init.rb +1 -0
- data/lib/acts_as_approvable.rb +96 -2
- data/lib/acts_as_approvable/approval.rb +205 -11
- data/lib/acts_as_approvable/error.rb +34 -0
- data/lib/acts_as_approvable/model.rb +60 -0
- data/lib/acts_as_approvable/model/class_methods.rb +63 -0
- data/lib/acts_as_approvable/model/create_instance_methods.rb +88 -0
- data/lib/acts_as_approvable/model/destroy_instance_methods.rb +38 -0
- data/lib/acts_as_approvable/model/instance_methods.rb +107 -0
- data/lib/acts_as_approvable/model/update_instance_methods.rb +61 -0
- data/lib/acts_as_approvable/ownership.rb +141 -0
- data/lib/acts_as_approvable/railtie.rb +7 -0
- data/lib/acts_as_approvable/version.rb +1 -8
- data/lib/generators/acts_as_approvable/USAGE +1 -0
- data/lib/generators/acts_as_approvable/acts_as_approvable_generator.rb +68 -0
- data/lib/generators/acts_as_approvable/base.rb +30 -0
- data/lib/generators/acts_as_approvable/templates/approvals.js +71 -0
- data/lib/generators/acts_as_approvable/templates/approvals_controller.rb +91 -0
- data/lib/generators/acts_as_approvable/templates/create_approvals.rb +27 -0
- data/lib/generators/acts_as_approvable/templates/jquery.form.js +101 -0
- data/lib/generators/erb/acts_as_approvable_generator.rb +33 -0
- data/lib/generators/erb/templates/_owner_select.html.erb +4 -0
- data/lib/generators/erb/templates/_table.html.erb +26 -0
- data/lib/generators/erb/templates/index.html.erb +17 -0
- data/lib/generators/haml/acts_as_approvable_generator.rb +33 -0
- data/lib/generators/haml/templates/_owner_select.html.haml +3 -0
- data/lib/generators/haml/templates/_table.html.haml +19 -0
- data/lib/generators/haml/templates/index.html.haml +15 -0
- data/lib/tasks/acts_as_approvable.rake +4 -0
- data/rails/init.rb +1 -0
- data/spec/acts_as_approvable/approval_spec.rb +614 -0
- data/spec/acts_as_approvable/model/class_methods_spec.rb +219 -0
- data/spec/acts_as_approvable/model/create_instance_methods_spec.rb +169 -0
- data/spec/acts_as_approvable/model/destroy_instance_methods_spec.rb +71 -0
- data/spec/acts_as_approvable/model/instance_methods_spec.rb +328 -0
- data/spec/acts_as_approvable/model/update_instance_methods_spec.rb +111 -0
- data/spec/acts_as_approvable/model_spec.rb +113 -0
- data/spec/acts_as_approvable/ownership/class_methods_spec.rb +134 -0
- data/spec/acts_as_approvable/ownership/instance_methods_spec.rb +32 -0
- data/spec/acts_as_approvable/ownership_spec.rb +52 -0
- data/spec/acts_as_approvable_spec.rb +31 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/support/database.rb +49 -0
- data/spec/support/database.yml +12 -0
- data/spec/support/matchers.rb +87 -0
- data/spec/support/models.rb +67 -0
- data/spec/support/schema.rb +54 -0
- metadata +375 -58
- data/README.rdoc +0 -38
- data/lib/acts_as_approvable/approver.rb +0 -76
- data/lib/generators/acts_as_approvable/install_generator.rb +0 -28
- data/lib/generators/acts_as_approvable/templates/install.rb +0 -16
@@ -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> </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,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 %>
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/rails/init'
|
data/lib/acts_as_approvable.rb
CHANGED
@@ -1,4 +1,98 @@
|
|
1
1
|
require 'active_record'
|
2
|
-
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
4
|
+
|
5
|
+
require 'acts_as_approvable/model'
|
6
|
+
require 'acts_as_approvable/approval'
|
7
|
+
require 'acts_as_approvable/error'
|
8
|
+
require 'acts_as_approvable/ownership'
|
3
9
|
require 'acts_as_approvable/version'
|
4
|
-
|
10
|
+
|
11
|
+
if defined?(Rails) && Rails.version =~ /^3\./
|
12
|
+
require 'acts_as_approvable/railtie'
|
13
|
+
elsif defined?(ActiveRecord)
|
14
|
+
ActiveRecord::Base.send :extend, ActsAsApprovable::Model
|
15
|
+
end
|
16
|
+
|
17
|
+
$LOAD_PATH.shift
|
18
|
+
|
19
|
+
module ActsAsApprovable
|
20
|
+
##
|
21
|
+
# Enable the approval queue at a global level.
|
22
|
+
def self.enable
|
23
|
+
@enabled = true
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Disable the approval queue at a global level.
|
28
|
+
def self.disable
|
29
|
+
@enabled = false
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Returns true if the approval queue is enabled globally.
|
34
|
+
def self.enabled?
|
35
|
+
@enabled = true if @enabled.nil?
|
36
|
+
@enabled
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Set the referenced Owner class to be used by generic finders.
|
41
|
+
#
|
42
|
+
# @see Ownership
|
43
|
+
def self.owner_class=(klass)
|
44
|
+
@owner_class = klass
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Get the referenced Owner class to be used by generic finders.
|
49
|
+
#
|
50
|
+
# @see Ownership
|
51
|
+
def self.owner_class
|
52
|
+
@owner_class
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Set the class used for overriding Ownership retrieval
|
57
|
+
#
|
58
|
+
# @see Ownership
|
59
|
+
def self.owner_source=(source)
|
60
|
+
@owner_source = source
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Get the class used for overriding Ownership retrieval
|
65
|
+
#
|
66
|
+
# @see Ownership
|
67
|
+
def self.owner_source
|
68
|
+
@owner_source
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Set the engine used for rendering view files.
|
73
|
+
def self.view_language=(lang)
|
74
|
+
@lang = lang
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Get the engine used for rendering view files. Defaults to 'erb'
|
79
|
+
def self.view_language
|
80
|
+
if Rails.version =~ /^3\./
|
81
|
+
Rails.configuration.generators.rails[:template_engine].try(:to_s) || 'erb'
|
82
|
+
else
|
83
|
+
@lang || 'erb'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Enable or disable the stale record check when approving updates.
|
89
|
+
def self.stale_check=(bool)
|
90
|
+
@stale_check = !!bool
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Get the state of the stale check.
|
95
|
+
def self.stale_check?
|
96
|
+
@stale_check || true
|
97
|
+
end
|
98
|
+
end
|
@@ -1,12 +1,206 @@
|
|
1
1
|
class Approval < ActiveRecord::Base
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
2
|
+
##
|
3
|
+
# Enumeration of available states.
|
4
|
+
STATES = %w(pending approved rejected)
|
5
|
+
|
6
|
+
belongs_to :item, :polymorphic => true
|
7
|
+
|
8
|
+
validates_presence_of :item
|
9
|
+
validates_inclusion_of :event, :in => %w(create update destroy)
|
10
|
+
validates_numericality_of :state, :greater_than_or_equal_to => 0, :less_than => STATES.length
|
11
|
+
|
12
|
+
serialize :object
|
13
|
+
serialize :original
|
14
|
+
|
15
|
+
before_save :able_to_save?
|
16
|
+
|
17
|
+
##
|
18
|
+
# Find the enumerated value for a given state.
|
19
|
+
#
|
20
|
+
# @return [Integer]
|
21
|
+
def self.enumerate_state(state)
|
22
|
+
enumerate_states(state).first
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Find the enumerated values for a list of states.
|
27
|
+
#
|
28
|
+
# @return [Array]
|
29
|
+
def self.enumerate_states(*states)
|
30
|
+
states.map { |name| STATES.index(name) }.compact
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Build an array of states usable by Rails' `#options_for_select`.
|
35
|
+
def self.options_for_state
|
36
|
+
options = [['All', -1]]
|
37
|
+
STATES.each_index { |x| options << [STATES[x].capitalize, x] }
|
38
|
+
options
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Build an array of types usable by Rails' `#options_for_select`.
|
43
|
+
def self.options_for_type(with_prompt = false)
|
44
|
+
types = all(:select => 'DISTINCT(item_type)').map { |row| row.item_type }
|
45
|
+
types.unshift(['All Types', nil]) if with_prompt
|
46
|
+
types
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Get the current state of the approval. Converts from integer via {STATES} constant.
|
51
|
+
def state
|
52
|
+
STATES[(read_attribute(:state) || 0)]
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Get the previous state of the approval. Converts from integer via {STATES} constant.
|
57
|
+
def state_was
|
58
|
+
STATES[(changed_attributes[:state] || 0)]
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Set the state of the approval. Converts from string to integer via {STATES} constant.
|
63
|
+
def state=(state)
|
64
|
+
state = self.class.enumerate_state(state) if state.is_a?(String)
|
65
|
+
write_attribute(:state, state)
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Returns true if the approval is still pending.
|
70
|
+
def pending?
|
71
|
+
state == 'pending'
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Returns true if the approval has been approved.
|
76
|
+
def approved?
|
77
|
+
state == 'approved'
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Returns true if the approval has been rejected.
|
82
|
+
def rejected?
|
83
|
+
state == 'rejected'
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# Returns true if the approval has been approved or rejected.
|
88
|
+
def locked?
|
89
|
+
approved? or rejected?
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Returns true if the approval has not been approved or rejected.
|
94
|
+
def unlocked?
|
95
|
+
not locked?
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Returns true if the approval able to be saved. This requires an unlocked
|
100
|
+
# approval, or an approval just leaving the 'pending' state.
|
101
|
+
def able_to_save?
|
102
|
+
unlocked? or state_was == 'pending'
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Returns true if the affected item has been updated since this approval was
|
107
|
+
# last updated.
|
108
|
+
def stale?
|
109
|
+
unlocked? and item.has_attribute?(:updated_at) and updated_at < item.updated_at
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Returns true if the affected item has not been updated since this approval
|
114
|
+
# was created.
|
115
|
+
def fresh?
|
116
|
+
not stale?
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Returns true if the approval is stale and the stale check is enabled.
|
121
|
+
def stale_approval?
|
122
|
+
ActsAsApprovable.stale_check? and update? and stale?
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Returns true if this is an `:update` approval event.
|
127
|
+
def update?
|
128
|
+
event == 'update'
|
129
|
+
end
|
130
|
+
|
131
|
+
##
|
132
|
+
# Returns true if this is a `:create` approval event.
|
133
|
+
def create?
|
134
|
+
event == 'create'
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
138
|
+
# Returns true if this is a `:destroy` approval event.
|
139
|
+
def destroy?
|
140
|
+
event == 'destroy'
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# Attempt to approve the record change.
|
145
|
+
#
|
146
|
+
# @param [Boolean] force if the approval record is stale force the acceptance.
|
147
|
+
# @raise [ActsAsApprovable::Error::Locked] raised if the record is {#locked? locked}.
|
148
|
+
# @raise [ActsAsApprovable::Error::Stale] raised if the record is {#stale? stale} and `force` is false.
|
149
|
+
def approve!(force = false)
|
150
|
+
raise ActsAsApprovable::Error::Locked if locked?
|
151
|
+
raise ActsAsApprovable::Error::Stale if !force and stale_approval?
|
152
|
+
return unless run_item_callback(:before_approve)
|
153
|
+
|
154
|
+
if update?
|
155
|
+
data = {}
|
156
|
+
object.each do |attr, value|
|
157
|
+
data[attr] = value if item.attribute_names.include?(attr)
|
158
|
+
end
|
159
|
+
|
160
|
+
item.attributes = data
|
161
|
+
elsif create?
|
162
|
+
item.set_approval_state('approved')
|
163
|
+
elsif destroy?
|
164
|
+
item.destroy_without_approval
|
165
|
+
end
|
166
|
+
|
167
|
+
item.save_without_approval! unless destroy?
|
168
|
+
update_attributes!(:state => 'approved')
|
169
|
+
run_item_callback(:after_approve)
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
# Attempt to reject the record change.
|
174
|
+
#
|
175
|
+
# @param [String] reason a reason for rejecting the change.
|
176
|
+
# @raise [ActsAsApprovable::Error::Locked] raised if the record is {#locked? locked}.
|
177
|
+
def reject!(reason = nil)
|
178
|
+
raise ActsAsApprovable::Error::Locked if locked?
|
179
|
+
return unless run_item_callback(:before_reject)
|
180
|
+
|
181
|
+
item.set_approval_state('rejected') if create?
|
182
|
+
|
183
|
+
item.save_without_approval!
|
184
|
+
update_attributes!(:state => 'rejected', :reason => reason)
|
185
|
+
run_item_callback(:after_reject)
|
186
|
+
end
|
187
|
+
|
188
|
+
##
|
189
|
+
# Force the approval back into a 'pending' state. Only valid for :create events.
|
190
|
+
#
|
191
|
+
# @raise [ActsAsApprovable::Error::InvalidTransition] raised if the event is not {#create? :create}.
|
192
|
+
def reset!
|
193
|
+
raise ActsAsApprovable::Error::InvalidTransition.new(state, 'pending', self) unless create?
|
194
|
+
|
195
|
+
item.set_approval_state('pending')
|
196
|
+
item.save_without_approval!
|
197
|
+
|
198
|
+
state_will_change! # Force an update to the record
|
199
|
+
update_attributes!(:state => 'rejected')
|
200
|
+
end
|
201
|
+
|
202
|
+
private
|
203
|
+
def run_item_callback(callback)
|
204
|
+
item.send(callback, self) != false
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ActsAsApprovable
|
2
|
+
class Error < RuntimeError
|
3
|
+
##
|
4
|
+
# Raised when a locked approval is accepted or rejected.
|
5
|
+
class Locked < ActsAsApprovable::Error
|
6
|
+
def initialize(*args)
|
7
|
+
super('this approval is locked')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# Raised when a stale approval is accepted.
|
13
|
+
class Stale < ActsAsApprovable::Error
|
14
|
+
def initialize(*args)
|
15
|
+
super('this approval is stale and should not be approved')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Raised when a record is assigned as owner that is not found in
|
21
|
+
# {ActsAsApprovable::Ownership::ClassMethods#available_owners}.
|
22
|
+
class InvalidOwner < ActsAsApprovable::Error
|
23
|
+
def initialize(*args)
|
24
|
+
super('this record cannot be assigned as an owner')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class InvalidTransition < ActsAsApprovable::Error
|
29
|
+
def initialize(from, to, approval)
|
30
|
+
super("you may not transition from #{from} to #{to} in a #{approval.event} approval")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|