rails-workflow 1.4.5.4 → 1.4.6.4

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.
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