rails-workflow 1.4.5.4 → 1.4.6.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +23 -0
  4. data/Gemfile +2 -1
  5. data/Rakefile +4 -4
  6. data/bin/console +3 -3
  7. data/lib/active_support/overloads.rb +13 -6
  8. data/lib/workflow.rb +12 -279
  9. data/lib/workflow/adapters/active_record.rb +57 -50
  10. data/lib/workflow/adapters/active_record_validations.rb +25 -19
  11. data/lib/workflow/adapters/adapter.rb +23 -0
  12. data/lib/workflow/adapters/remodel.rb +8 -9
  13. data/lib/workflow/callbacks.rb +60 -45
  14. data/lib/workflow/callbacks/callback.rb +23 -37
  15. data/lib/workflow/callbacks/method_callback.rb +12 -0
  16. data/lib/workflow/callbacks/proc_callback.rb +23 -0
  17. data/lib/workflow/callbacks/string_callback.rb +12 -0
  18. data/lib/workflow/callbacks/transition_callback.rb +88 -78
  19. data/lib/workflow/callbacks/transition_callbacks/method_caller.rb +53 -0
  20. data/lib/workflow/callbacks/transition_callbacks/proc_caller.rb +60 -0
  21. data/lib/workflow/configuration.rb +1 -0
  22. data/lib/workflow/definition.rb +73 -0
  23. data/lib/workflow/errors.rb +37 -6
  24. data/lib/workflow/event.rb +30 -15
  25. data/lib/workflow/helper_method_configurator.rb +100 -0
  26. data/lib/workflow/specification.rb +45 -22
  27. data/lib/workflow/state.rb +45 -36
  28. data/lib/workflow/transition_context.rb +5 -4
  29. data/lib/workflow/transitions.rb +94 -0
  30. data/lib/workflow/version.rb +2 -1
  31. data/rails-workflow.gemspec +18 -18
  32. data/tags.markdown +31 -0
  33. metadata +13 -5
  34. data/lib/workflow/callbacks/transition_callbacks/method_wrapper.rb +0 -102
  35. data/lib/workflow/callbacks/transition_callbacks/proc_wrapper.rb +0 -48
  36. data/lib/workflow/draw.rb +0 -79
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c00b8e437381e8b05f1f9b870667c24c06b9ec64
4
- data.tar.gz: 429a9bbdff4aab6ff053d2f395d2be1bbafbba2c
3
+ metadata.gz: 5aea9261c6bf6fa5ab1c793179ef8440a67be0d6
4
+ data.tar.gz: aa69e4c243b24250f6880cfcfcf3efafc33b9a15
5
5
  SHA512:
6
- metadata.gz: 732795994aa948a71b19bbe1fe1c5da5ea6814de9a146615d0eb5ae1fac2cd67c174619b9d1e6c64a943b94c32e6ccbab151c668bbcb8d56c532f02d4e7d46f1
7
- data.tar.gz: a44fb7792d8f027fb953d9b789a12bbad7d837002f3a14b9acfda52375e3f274391fb352f3c29ad6f43477123b7f715726a91a38a2f794c7ce50aec8a698eef5
6
+ metadata.gz: ad8375bdbe64ae0ddadf8d9e272e22d198dfc87108327fba67d7c2852955c334334efafe24195b4f0d6ffe2d18ce726205fff343142a8c2cbe46c0e44721988e
7
+ data.tar.gz: ccb120fe43028613ab269556076cbee0a7113da2831d0c32308cd68a680080e99fc21e68b50142f8843bb57813fe67d82facc0bfa1d387e6eabac5ad2c40287c
data/.gitignore CHANGED
@@ -22,3 +22,5 @@ tmp
22
22
  .byebug_history
23
23
 
24
24
  lib/active_support/callbacks.rb
25
+
26
+ errors.html
data/.rubocop.yml ADDED
@@ -0,0 +1,23 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+ Exclude:
4
+ - 'vendor/**/*'
5
+ - 'config/initializers/*'
6
+ - 'tmp/**/*'
7
+ - 'db/**/*'
8
+ - 'spec/spec_helper.rb'
9
+ - 'lib/tasks/**/*'
10
+ - 'spec/rails_helper.rb'
11
+ - 'spec/support/*'
12
+ - 'spec/active_support_callbacks_spec.rb'
13
+ - 'rails-workflow.gemspec'
14
+ - 'bin/*'
15
+
16
+ Style/Documentation:
17
+ Enabled: false
18
+
19
+ Metrics/LineLength:
20
+ Max: 100
21
+
22
+ Metrics/MethodLength:
23
+ Max: 14
data/Gemfile CHANGED
@@ -1,3 +1,4 @@
1
- source "http://rubygems.org"
1
+ # frozen_string_literal: true
2
+ source 'http://rubygems.org'
2
3
  gem 'byebug'
3
4
  gemspec
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rubygems'
2
3
  require 'rake/testtask'
3
4
  require 'rdoc/task'
@@ -5,7 +6,7 @@ require 'rdoc/task'
5
6
  require 'bundler'
6
7
  Bundler.setup
7
8
 
8
- task :default => [:test]
9
+ task default: [:test]
9
10
 
10
11
  require 'rake'
11
12
  Rake::TestTask.new do |t|
@@ -24,7 +25,6 @@ Rake::TestTask.new do |t|
24
25
  end
25
26
 
26
27
  Rake::RDocTask.new do |rdoc|
27
- rdoc.rdoc_files.include("lib/**/*.rb")
28
- rdoc.options << "-S"
28
+ rdoc.rdoc_files.include('lib/**/*.rb')
29
+ rdoc.options << '-S'
29
30
  end
30
-
data/bin/console CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "workflow"
3
+ require 'bundler/setup'
4
+ require 'workflow'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +10,5 @@ require "workflow"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
13
+ require 'irb'
14
14
  IRB.start
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'active_support/callbacks'
2
3
 
3
4
  module ActiveSupport
@@ -6,16 +7,17 @@ module ActiveSupport
6
7
  # to a lambda for the present purposes.
7
8
  module CallbackOverloads
8
9
  private
10
+
9
11
  def make_lambda(filter)
10
- if filter.kind_of? Workflow::Callbacks::TransitionCallback
11
- super(filter.wrapper)
12
+ if filter.is_a? Workflow::Callbacks::TransitionCallback
13
+ filter
12
14
  else
13
15
  super
14
16
  end
15
17
  end
16
18
 
17
19
  def compute_identifier(filter)
18
- if filter.kind_of? Workflow::Callbacks::TransitionCallback
20
+ if filter.is_a? Workflow::Callbacks::TransitionCallback
19
21
  super(filter.raw_proc)
20
22
  else
21
23
  super
@@ -24,9 +26,14 @@ module ActiveSupport
24
26
  end
25
27
  end
26
28
 
27
- # Overload {ActiveSupport::Callbacks::Callback} with methods from {ActiveSupport::CallbackOverloads}.
29
+ # Overload {ActiveSupport::Callbacks::Callback} with methods from
30
+ # {ActiveSupport::CallbackOverloads}.
28
31
  # {Workflow::Callbacks::TransitionCallback}, which is duck-type equivalent
29
32
  # to a lambda for the present purposes.
30
- class ::ActiveSupport::Callbacks::Callback
31
- prepend ActiveSupport::CallbackOverloads
33
+ module ActiveSupport
34
+ module Callbacks
35
+ class Callback
36
+ prepend ActiveSupport::CallbackOverloads
37
+ end
38
+ end
32
39
  end
data/lib/workflow.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rubygems'
2
3
  require 'active_support/concern'
3
4
  require 'active_support/callbacks'
@@ -5,21 +6,30 @@ require 'workflow/version'
5
6
  require 'workflow/configuration'
6
7
  require 'workflow/specification'
7
8
  require 'workflow/callbacks'
9
+ require 'workflow/helper_method_configurator'
8
10
  require 'workflow/adapters/active_record'
9
11
  require 'workflow/adapters/remodel'
12
+ require 'workflow/transitions'
13
+ require 'workflow/definition'
14
+ require 'workflow/adapters/adapter'
10
15
  require 'workflow/adapters/active_record_validations'
11
16
  require 'workflow/transition_context'
12
17
  require 'active_support/overloads'
13
18
 
14
-
15
19
  # See also README.markdown for documentation
16
20
  module Workflow
17
21
  # @!parse include Callbacks
22
+ # @!parse include Transitions
23
+ # @!parse include Definition
18
24
  # @!parse extend Callbacks::ClassMethods
19
25
 
20
26
  extend ActiveSupport::Concern
21
27
  include Callbacks
22
28
  include Errors
29
+ include Transitions
30
+ include Definition
31
+
32
+ include Adapters::Adapter
23
33
 
24
34
  # The application-wide Workflow configuration object
25
35
  CONFIGURATION = Configuration.new
@@ -30,283 +40,6 @@ module Workflow
30
40
  # @return [nil]
31
41
  def self.config(&block)
32
42
  block.call(CONFIGURATION) if block_given?
33
- return CONFIGURATION
34
- end
35
-
36
- included do
37
-
38
- # Look for a hook; otherwise detect based on ancestor class.
39
- if respond_to?(:workflow_adapter)
40
- include self.workflow_adapter
41
- else
42
- if Object.const_defined?(:ActiveRecord) && self < ActiveRecord::Base
43
- include Adapter::ActiveRecord
44
- include Adapter::ActiveRecordValidations
45
- end
46
- if Object.const_defined?(:Remodel) && klass < Adapter::Remodel::Entity
47
- include Adapter::Remodel::InstanceMethods
48
- end
49
- end
50
- end
51
-
52
- # Returns a state object representing the current workflow state.
53
- #
54
- # @return [State] Current workflow state
55
- def current_state
56
- loaded_state = load_workflow_state
57
- res = workflow_spec.states.find{|t| t.name==loaded_state.to_sym} if loaded_state
58
- res || workflow_spec.initial_state
59
- end
60
-
61
- # Deprecated. Check for false return value from {#transition!}
62
- # @return [Boolean] true if the last transition was halted by one of the transition callbacks.
63
- def halted?
64
- @halted
65
- end
66
-
67
- # Returns the reason given to a call to {#halt} or {#halt!}, if any.
68
- # @return [String] The reason the transition was aborted.
69
- attr_reader :halted_because
70
-
71
- # @api private
72
- # @return [Workflow::TransitionContext] During transition, or nil if no transition is underway.
73
- # During a state transition, contains transition-specific information:
74
- # * The name of the {Workflow::State} being exited,
75
- # * The name of the {Workflow::State} being entered,
76
- # * The name of the {Workflow::Event} that was fired,
77
- # * And whatever arguments were passed to the {Workflow#transition!} method.
78
- attr_reader :transition_context
79
-
80
- # Initiates state transition via the named event
81
- #
82
- # @param [Symbol] name name of event to initiate
83
- # @param [Array] args State transition arguments.
84
- # @return [Symbol] The name of the new state, or `false` if the transition failed.
85
- # TODO: connect args to documentation on how arguments are accessed during state transitions.
86
- def transition!(name, *args, **attributes)
87
- name = name.to_sym
88
- event = current_state.find_event(name)
89
- raise NoTransitionAllowed.new(
90
- "There is no event #{name} defined for the #{current_state.name} state") \
91
- if event.nil?
92
-
93
- @halted_because = nil
94
- @halted = false
95
-
96
- target = event.evaluate(self)
97
- unless target
98
- raise NoMatchingTransitionError.new("No matching transition found on #{name} for target #{target}. Consider adding a catchall transition.")
99
- end
100
-
101
- from = current_state
102
- return_value = false
103
- begin
104
- @transition_context = TransitionContext.new \
105
- from: from.name,
106
- to: target.name,
107
- event: name,
108
- event_args: args,
109
- attributes: attributes,
110
- named_arguments: workflow_spec.named_arguments
111
-
112
- run_all_callbacks do
113
- callback_value = run_action_callback name, *args
114
- persist_value = persist_workflow_state(target.name)
115
- return_value = callback_value || persist_value
116
- end
117
- ensure
118
- @transition_context = nil
119
- end
120
- return_value
121
- end
122
-
123
- # Stop the current transition and set the reason for the abort.
124
- #
125
- # @param [String] reason Optional reason for halting transition.
126
- # @return [nil]
127
- def halt(reason = nil)
128
- @halted_because = reason
129
- @halted = true
130
- throw :abort
131
- end
132
-
133
- # Sets halt reason and raises [TransitionHaltedError] error.
134
- #
135
- # @param [String] reason Optional reason for halting
136
- # @return [nil]
137
- def halt!(reason = nil)
138
- @halted_because = reason
139
- @halted = true
140
- raise TransitionHaltedError.new(reason)
141
- end
142
-
143
- # The specification for this object.
144
- # Could be set on a singleton for the object, on the object's class,
145
- # Or else on a superclass of the object.
146
- # @return [Specification] The Specification that applies to this object.
147
- def workflow_spec
148
- # check the singleton class first
149
- class << self
150
- return workflow_spec if workflow_spec
151
- end
152
-
153
- c = self.class
154
- # using a simple loop instead of class_inheritable_accessor to avoid
155
- # dependency on Rails' ActiveSupport
156
- until c.workflow_spec || !(c.include? Workflow)
157
- c = c.superclass
158
- end
159
- c.workflow_spec
160
- end
161
-
162
- private
163
-
164
- def has_callback?(action)
165
- # 1. public callback method or
166
- # 2. protected method somewhere in the class hierarchy or
167
- # 3. private in the immediate class (parent classes ignored)
168
- action = action.to_sym
169
- self.respond_to?(action) or
170
- self.class.protected_method_defined?(action) or
171
- self.private_methods(false).map(&:to_sym).include?(action)
172
- end
173
-
174
- def run_action_callback(action_name, *args)
175
- action = action_name.to_sym
176
- if has_callback?(action)
177
- meth = method(action)
178
- check_method_arity! meth, *args
179
- meth.call *args
180
- end
181
- end
182
-
183
- def check_method_arity!(method, *args)
184
- arity = method.arity
185
-
186
- unless (arity >= 0 && args.length == arity) || (arity < 0 && (args.length + 1) >= arity.abs)
187
- raise CallbackArityError.new("Method #{method.name} has arity #{arity} but was called with #{args.length} arguments.")
188
- end
189
- end
190
-
191
- # load_workflow_state and persist_workflow_state
192
- # can be overriden to handle the persistence of the workflow state.
193
- #
194
- # Default (non ActiveRecord) implementation stores the current state
195
- # in a variable.
196
- #
197
- # Default ActiveRecord implementation uses a 'workflow_state' database column.
198
- def load_workflow_state
199
- @workflow_state if instance_variable_defined? :@workflow_state
200
- end
201
-
202
- def persist_workflow_state(new_value)
203
- @workflow_state = new_value
204
- end
205
-
206
- module ClassMethods
207
- attr_reader :workflow_spec
208
-
209
- # Instructs Workflow which column to use to persist workflow state.
210
- #
211
- # @param [Symbol] column_name If provided, will set a new workflow column name.
212
- # @return [Symbol] The current (or new) name for the workflow column on this class.
213
- def workflow_column(column_name=nil)
214
- if column_name
215
- @workflow_state_column_name = column_name.to_sym
216
- end
217
- if !instance_variable_defined?('@workflow_state_column_name') && superclass.respond_to?(:workflow_column)
218
- @workflow_state_column_name = superclass.workflow_column
219
- end
220
- @workflow_state_column_name ||= :workflow_state
221
- end
222
-
223
-
224
- ##
225
- # Define workflow for the class.
226
- #
227
- # @yield [] Specification of workflow. Example below and in README.markdown
228
- # @return [nil]
229
- #
230
- # Workflow definition takes place inside the yielded block.
231
- # @see Specification::state
232
- # @see Specification::event
233
- #
234
- # ~~~ruby
235
- #
236
- # class Article
237
- # include Workflow
238
- # workflow do
239
- # state :new do
240
- # event :submit, :transitions_to => :awaiting_review
241
- # end
242
- # state :awaiting_review do
243
- # event :review, :transitions_to => :being_reviewed
244
- # end
245
- # state :being_reviewed do
246
- # event :accept, :transitions_to => :accepted
247
- # event :reject, :transitions_to => :rejected
248
- # end
249
- # state :accepted
250
- # state :rejected
251
- # end
252
- # end
253
- #
254
- #~~~
255
- #
256
- def workflow(&specification)
257
- assign_workflow Specification.new(Hash.new, &specification)
258
- end
259
-
260
- private
261
-
262
- # Creates the convinience methods like `my_transition!`
263
- def assign_workflow(specification_object)
264
- # Merging two workflow specifications can **not** be done automically, so
265
- # just make the latest specification win. Same for inheritance -
266
- # definition in the subclass wins.
267
- if self.superclass.respond_to?(:workflow_spec, true) && self.superclass.workflow_spec
268
- undefine_methods_defined_by_workflow_spec superclass.workflow_spec
269
- end
270
-
271
- @workflow_spec = specification_object
272
- @workflow_spec.states.each do |state|
273
- state_name = state.name
274
- module_eval do
275
- define_method "#{state_name}?" do
276
- state_name == current_state.name
277
- end
278
- end
279
-
280
- state.events.each do |event|
281
- event_name = event.name
282
- module_eval do
283
- define_method "#{event_name}!".to_sym do |*args|
284
- transition!(event_name, *args)
285
- end
286
-
287
- define_method "can_#{event_name}?" do
288
- return !!current_state.find_event(event_name)&.evaluate(self)
289
- end
290
- end
291
- end
292
- end
293
- end
294
-
295
- def undefine_methods_defined_by_workflow_spec(inherited_workflow_spec)
296
- inherited_workflow_spec.states.each do |state|
297
- state_name = state.name
298
- module_eval do
299
- undef_method "#{state_name}?"
300
- end
301
-
302
- state.events.each do |event|
303
- event_name = event.name
304
- module_eval do
305
- undef_method "#{event_name}!".to_sym
306
- undef_method "can_#{event_name}?"
307
- end
308
- end
309
- end
310
- end
43
+ CONFIGURATION
311
44
  end
312
45
  end