nxt_state_machine 0.1.3 → 0.1.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e9bf5b5563e13832c4dde4f772eca02272eec81d24cbc7f65b5d04d65b2a7c72
4
- data.tar.gz: 5cf98caf5c0deceb3b49cfb9290523f0feb5fac9959d8be9775734b1b3b05cb1
3
+ metadata.gz: 89b1ad3a07fcc60e4b3541b770f1e1f287713e973d3a038f3496be0a145006c5
4
+ data.tar.gz: f213f914ffe2ff39a0d90d8c61f574857b312c9065e1241e9e09dce16cf6cc3a
5
5
  SHA512:
6
- metadata.gz: b49ac744754bd5ec04521f1dce4e506c45269a5f68215a85d39f4b7304e0bb42e0b7d48bdabf2ccbbf9c3e088760b4ed820d2377e4332d2797489361b0bb4571
7
- data.tar.gz: b712cc23c728cabc29020fbafe36b5e5cd468fe671ee698c1ba1a5906b43f1036abcfe82579965f3f1316e907a2027e2510f6d27a8196cf2ecc2ed43b4cd9cc9
6
+ metadata.gz: 48b17f521db0a19ccc500fff321b459f4e962875792e047923185a45691c20b81bfec5bbab03b77cf6eaeb10be2b8191ec73bfbc4d4b05c9e518389b1d9cc497
7
+ data.tar.gz: 286382b6dd9512abc0a0b1c1f177196edab5aced50bc8888d0febf4a58a86697ac152b8a7617c238f3268cbbada15f9767248b333264cf1608421d6a9b9c2b93
@@ -16,11 +16,17 @@ jobs:
16
16
  steps:
17
17
  - checkout
18
18
 
19
+ - run:
20
+ name: Install apt dependencies
21
+ command: |
22
+ sudo apt update -q \
23
+ && sudo apt upgrade -q \
24
+ && sudo apt install -qq graphviz
19
25
  # Download and cache dependencies
20
26
  - restore_cache:
21
27
  keys:
22
28
  - v1-dependencies-{{ checksum "Gemfile.lock" }}
23
-
29
+
24
30
  - run: gem install bundler --version $BUNDLER_VERSION
25
31
 
26
32
  - run:
@@ -53,4 +59,4 @@ jobs:
53
59
  path: /tmp/test-results
54
60
  - store_artifacts:
55
61
  path: /tmp/test-results
56
- destination: test-results
62
+ destination: test-results
@@ -0,0 +1,7 @@
1
+ # v0.1.8 2020-0-05
2
+
3
+ ### Added
4
+
5
+ - [internal] Allow to draw state machine graph with rake task
6
+
7
+ [Compare v0.1.7...v0.1.8](https://github.com/nxt-insurance/nxt_state_machine/compare/v0.1.7...v0.1.8)
@@ -1,37 +1,38 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nxt_state_machine (0.1.3)
4
+ nxt_state_machine (0.1.8)
5
5
  activesupport
6
6
  nxt_registry (~> 0.1.3)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activemodel (6.0.2.1)
12
- activesupport (= 6.0.2.1)
13
- activerecord (6.0.2.1)
14
- activemodel (= 6.0.2.1)
15
- activesupport (= 6.0.2.1)
16
- activesupport (6.0.2.1)
11
+ activemodel (6.0.3.1)
12
+ activesupport (= 6.0.3.1)
13
+ activerecord (6.0.3.1)
14
+ activemodel (= 6.0.3.1)
15
+ activesupport (= 6.0.3.1)
16
+ activesupport (6.0.3.1)
17
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
18
  i18n (>= 0.7, < 2)
19
19
  minitest (~> 5.1)
20
20
  tzinfo (~> 1.1)
21
- zeitwerk (~> 2.2)
21
+ zeitwerk (~> 2.2, >= 2.2.2)
22
22
  coderay (1.1.2)
23
- concurrent-ruby (1.1.5)
23
+ concurrent-ruby (1.1.6)
24
24
  diff-lcs (1.3)
25
25
  i18n (1.8.2)
26
26
  concurrent-ruby (~> 1.0)
27
- method_source (0.9.2)
28
- minitest (5.14.0)
29
- nxt_registry (0.1.4)
27
+ method_source (1.0.0)
28
+ minitest (5.14.1)
29
+ nxt_registry (0.1.5)
30
30
  activesupport
31
- pry (0.12.2)
32
- coderay (~> 1.1.0)
33
- method_source (~> 0.9.0)
34
- rake (10.5.0)
31
+ pry (0.13.1)
32
+ coderay (~> 1.1)
33
+ method_source (~> 1.0)
34
+ rake (12.3.3)
35
+ rexml (3.2.4)
35
36
  rspec (3.9.0)
36
37
  rspec-core (~> 3.9.0)
37
38
  rspec-expectations (~> 3.9.0)
@@ -47,11 +48,13 @@ GEM
47
48
  rspec-support (3.9.0)
48
49
  rspec_junit_formatter (0.4.1)
49
50
  rspec-core (>= 2, < 4, != 2.12.0)
51
+ ruby-graphviz (1.2.5)
52
+ rexml
50
53
  sqlite3 (1.4.2)
51
54
  thread_safe (0.3.6)
52
- tzinfo (1.2.6)
55
+ tzinfo (1.2.7)
53
56
  thread_safe (~> 0.1)
54
- zeitwerk (2.2.2)
57
+ zeitwerk (2.3.0)
55
58
 
56
59
  PLATFORMS
57
60
  ruby
@@ -61,10 +64,11 @@ DEPENDENCIES
61
64
  bundler (~> 2.0)
62
65
  nxt_state_machine!
63
66
  pry
64
- rake (~> 10.0)
67
+ rake (~> 12.0)
65
68
  rspec (~> 3.0)
66
69
  rspec_junit_formatter
70
+ ruby-graphviz
67
71
  sqlite3
68
72
 
69
73
  BUNDLED WITH
70
- 2.0.2
74
+ 2.1.4
data/README.md CHANGED
@@ -83,6 +83,10 @@ class ArticleWorkflow
83
83
  puts 'around transition exit'
84
84
  end
85
85
 
86
+ on_success from: any_state, to: :approved do |transition|
87
+ # This is the last callback in the chain - It runs outside of the active record transaction
88
+ end
89
+
86
90
  on_error CustomError from: any_state, to: :approved do |error, transition|
87
91
  end
88
92
  end
@@ -277,10 +281,10 @@ Transitions can be halted in callbacks and during the transition itself simply b
277
281
 
278
282
  ### Callbacks
279
283
 
280
- You can register `before_transition`, `around_transition` and `after_transition` callbacks. By defining the
281
- :from and :to states you decide on which transitions the callback actually runs. Around callbacks need to call the
282
- proc object that they get passed in. Registering callbacks inside an event block or on the state_machine top level
283
- behaves exactly the same way and is only a matter of structure. The only thing that defines when callbacks run is
284
+ You can register `before_transition`, `around_transition`, `after_transition` and `on_success` callbacks.
285
+ By defining the :from and :to states you decide on which transitions the callback actually runs. Around callbacks need
286
+ to call the proc object that they get passed in. Registering callbacks inside an event block or on the state_machine top
287
+ level behaves exactly the same way and is only a matter of structure. The only thing that defines when callbacks run is
284
288
  the :from and :to parameters with which they are registered.
285
289
 
286
290
 
@@ -298,6 +302,11 @@ event :approve do
298
302
  block.call
299
303
  puts 'around transition exit'
300
304
  end
305
+
306
+ # Use this to trigger another event after the transaction around the transition completed
307
+ on_success from: any_state, to: :approved do |transition|
308
+ # This is the last callback in the chain - It runs outside of the active record transaction
309
+ end
301
310
  end
302
311
  ```
303
312
 
@@ -327,7 +336,42 @@ state_machine do
327
336
  end
328
337
  ```
329
338
 
330
- ### Multiple state machines in the same class
339
+ ### ActiveRecord transaction, rollback and locks - breaking the flow by defusing errors
340
+
341
+ You want to break out of your transition (which is wrapped inside a lock)?
342
+ You can raise an error, have everything rolled back and then have your error handler take over.
343
+ **NOTE:** Unless you reload your model all assignments you did, previous to the error, should still be available in your
344
+ error handler. You can also defuse errors. This means they will not cause a rollback of the transaction during the
345
+ transition and you can actually persist changes to your model before the defused error is raised and handled.
346
+
347
+ ```ruby
348
+ state_machine do
349
+ # ...
350
+ #
351
+ defuse CustomError, from: any_state, to: all_states
352
+
353
+ event :approve do
354
+ # You can also defuse on event level
355
+ # defuse CustomError, from: %i[written submitted deleted], to: :approved
356
+
357
+ transition from: %i[written submitted deleted], to: :approved do |headline:|
358
+ # This will be save to the database even if defused CustomError is raised after
359
+ article.update!(headline: headline)
360
+ raise CustomError, 'This does not rollback the headline update above'
361
+ end
362
+ end
363
+
364
+ on_error! CustomError from: any_state, to: :approved do |error, transition|
365
+ # You can still handle the defused Error if you want to
366
+ # You should probably reload your model here to not accidentally save changes that
367
+ # were made to the model during the transition before a non defused error was raised
368
+ article.reload
369
+ # The error callback does not run inside the transaction. No more strings attached here.
370
+ # You can now persist changes to your model again.
371
+ article.update!(error: error.message)
372
+ end
373
+ end
374
+ ```
331
375
 
332
376
  In theory you can also have multiple state_machines in the same class. To do so you have to give each
333
377
  state_machine a name. Events need to be unique globally in order to determine which state_machine will be called.
@@ -349,17 +393,6 @@ class Article < ApplicationRecord
349
393
  end
350
394
  ```
351
395
 
352
-
353
- ## TODO
354
- - Test implementations for Hash, AttrAccessor
355
- - Thread safety spec!
356
- - Spec locks?
357
- - Explain locking in readme!
358
- - Should we clone machines for each context?
359
- - What about inheritance? => What would be the expected behaviour? (dup vs. no dup)
360
- => Might also make sense to walk the ancestors chain and collect configure blocks
361
-
362
-
363
396
  ## Development
364
397
 
365
398
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/Rakefile CHANGED
@@ -3,4 +3,4 @@ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
@@ -16,6 +16,7 @@ require "nxt_state_machine/callable"
16
16
  require "nxt_state_machine/state_registry"
17
17
  require "nxt_state_machine/callback_registry"
18
18
  require "nxt_state_machine/error_callback_registry"
19
+ require "nxt_state_machine/defuse_registry"
19
20
  require "nxt_state_machine/event_registry"
20
21
  require "nxt_state_machine/state"
21
22
  require "nxt_state_machine/event"
@@ -30,6 +31,7 @@ require "nxt_state_machine/state_machine"
30
31
  require "nxt_state_machine/integrations/active_record"
31
32
  require "nxt_state_machine/integrations/attr_accessor"
32
33
  require "nxt_state_machine/integrations/hash"
34
+ require "nxt_state_machine/graph"
33
35
 
34
36
  module NxtStateMachine
35
37
  module ClassMethods
@@ -0,0 +1,26 @@
1
+ module NxtStateMachine
2
+ class DefuseRegistry
3
+ include ::NxtRegistry
4
+
5
+ def register(from, to, kind)
6
+ Array(from).each do |from_state|
7
+ Array(to).each do |to_state|
8
+ defusing_errors = errors.from(from_state).to(to_state)
9
+ Array(kind).each_with_object(defusing_errors) { |error, acc| acc << error }
10
+ end
11
+ end
12
+ end
13
+
14
+ def resolve(transition)
15
+ errors.from(transition.from.enum).to(transition.to.enum)
16
+ end
17
+
18
+ private
19
+
20
+ def errors
21
+ @errors ||= registry :from do
22
+ nested :to, default: -> { [] }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -18,11 +18,13 @@ module NxtStateMachine
18
18
  delegate :before_transition,
19
19
  :after_transition,
20
20
  :around_transition,
21
+ :on_success,
21
22
  :on_error,
22
23
  :on_error!,
23
24
  :any_state,
24
25
  :all_states,
25
26
  :all_states_except,
27
+ :defuse,
26
28
  to: :state_machine
27
29
 
28
30
  def transitions(from:, to:, &block)
@@ -39,6 +41,10 @@ module NxtStateMachine
39
41
  event_transitions.resolve!(state).present?
40
42
  end
41
43
 
44
+ def to_s
45
+ "#{self.class.name}[:#{name}]"
46
+ end
47
+
42
48
  private
43
49
 
44
50
  def configure(&block)
@@ -0,0 +1,77 @@
1
+ module NxtStateMachine
2
+ class Graph
3
+ def initialize(state_machines, **options)
4
+ @state_machines = state_machines
5
+ @options = default_options.merge(**options)
6
+ end
7
+
8
+ def draw
9
+ require 'ruby-graphviz'
10
+
11
+ state_machines.each do |_, state_machine|
12
+ add_nodes(state_machine)
13
+ add_edges(state_machine)
14
+ end
15
+
16
+ filename = File.join(options[:path], "#{options[:name]}.#{options[:format]}")
17
+
18
+ graph.output options[:format] => filename
19
+
20
+ puts '----------------------------------------------'
21
+ puts 'Please run the following to open the generated file:'
22
+ puts "open '#{filename}'"
23
+ puts '----------------------------------------------'
24
+
25
+ graph
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :options, :state_machines
31
+
32
+ def graph
33
+ @graph ||= ::GraphViz.new(
34
+ 'G',
35
+ rankdir: options[:orientation] == 'landscape' ? 'LR' : 'TB',
36
+ ratio: options[:ratio]
37
+ )
38
+ end
39
+
40
+ def add_nodes(state_machine)
41
+ binding.pry
42
+ state_machine.states.values.each do |state|
43
+ add_node(state)
44
+ end
45
+ end
46
+
47
+ def add_node(state)
48
+ node_options = {
49
+ label: state.to_s,
50
+ width: '1',
51
+ height: '1',
52
+ shape: 'ellipse'
53
+ }
54
+
55
+ graph.add_nodes(state.to_s, node_options)
56
+ end
57
+
58
+ def add_edges(state_machine)
59
+ state_machine.events.values.each do |event|
60
+ event.event_transitions.values.each do |transition|
61
+ graph.add_edges(transition.from.to_s, transition.to.to_s, label: event.name)
62
+ end
63
+ end
64
+ end
65
+
66
+ def default_options
67
+ {
68
+ name: 'state_machine',
69
+ path: '.',
70
+ orientation: 'landscape',
71
+ ratio: 'fill',
72
+ format: 'png',
73
+ font: 'Helvetica'
74
+ }
75
+ end
76
+ end
77
+ end
@@ -21,34 +21,21 @@ module NxtStateMachine
21
21
  end
22
22
 
23
23
  machine.set_state_with do |target, transition|
24
- target.with_lock do
25
- transition.run_before_callbacks
26
- result = set_state(target, transition, state_attr, :save)
27
- transition.run_after_callbacks
28
-
29
- result
30
- end
31
- rescue StandardError => error
32
- target.assign_attributes(state_attr => transition.from.to_s)
33
-
34
- if error.is_a?(NxtStateMachine::Errors::TransitionHalted)
35
- false
36
- else
37
- raise
38
- end
24
+ set_state(machine, target, transition, state_attr, :save)
39
25
  end
40
26
 
41
27
  machine.set_state_with! do |target, transition|
42
- target.with_lock do
43
- transition.run_before_callbacks
44
- result = set_state(target, transition, state_attr, :save!)
45
- transition.run_after_callbacks
28
+ set_state(machine, target, transition, state_attr, :save!)
29
+ end
46
30
 
47
- result
31
+ machine.define_singleton_method :add_state_methods_to_model do |model_class|
32
+ model_class.class_eval do
33
+ machine.states.keys.each do |state_name|
34
+ define_method "#{state_name}?" do
35
+ send(machine.options.fetch(:state_attr)) == state_name
36
+ end
37
+ end
48
38
  end
49
- rescue StandardError
50
- target.assign_attributes(state_attr => transition.from.to_s)
51
- raise
52
39
  end
53
40
 
54
41
  machine
@@ -58,11 +45,40 @@ module NxtStateMachine
58
45
  module InstanceMethods
59
46
  private
60
47
 
61
- def set_state(target, transition, state_attr, method)
48
+ def set_state(machine, target, transition, state_attr, save_with_method)
49
+ result = nil
50
+ defused_error = nil
51
+
52
+ target.with_lock do
53
+ transition.run_before_callbacks
54
+ result = execute_transition(target, transition, state_attr, save_with_method)
55
+ transition.run_after_callbacks
56
+
57
+ result
58
+ rescue StandardError => error
59
+ if machine.defuse_registry.resolve(transition).find { |error_class| error.is_a?(error_class) }
60
+ defused_error = error
61
+ else
62
+ raise error
63
+ end
64
+ end
65
+
66
+ raise defused_error if defused_error
67
+
68
+ transition.run_success_callbacks || result
69
+ rescue StandardError => error
70
+ target.assign_attributes(state_attr => transition.from.to_s)
71
+
72
+ raise unless save_with_method == :save && error.is_a?(NxtStateMachine::Errors::TransitionHalted)
73
+
74
+ false
75
+ end
76
+
77
+ def execute_transition(target, transition, state_attr, save_with_method)
62
78
  transition.execute do |block|
63
79
  result = block ? block.call : nil
64
80
  target.assign_attributes(state_attr => transition.to.to_s)
65
- set_state_result = target.send(method) || halt_transition
81
+ set_state_result = target.send(save_with_method) || halt_transition
66
82
  block ? result : set_state_result
67
83
  end
68
84
  end
@@ -22,7 +22,7 @@ module NxtStateMachine
22
22
  result = set_state(target, transition, state_attr)
23
23
  transition.run_after_callbacks
24
24
 
25
- result
25
+ transition.run_success_callbacks || result
26
26
  rescue StandardError => error
27
27
  target.send("#{state_attr}=", transition.from.enum)
28
28
 
@@ -38,7 +38,7 @@ module NxtStateMachine
38
38
  result = set_state(target, transition, state_attr)
39
39
  transition.run_after_callbacks
40
40
 
41
- result
41
+ transition.run_success_callbacks || result
42
42
  rescue StandardError
43
43
  target.send("#{state_attr}=", transition.from.enum)
44
44
  raise
@@ -21,7 +21,7 @@ module NxtStateMachine
21
21
  transition.run_before_callbacks
22
22
  result = set_state(current_target, transition, state_attr)
23
23
  transition.run_after_callbacks
24
- result
24
+ transition.run_success_callbacks || result
25
25
  rescue StandardError => error
26
26
  current_target[state_attr] = transition.from.enum
27
27
 
@@ -37,7 +37,7 @@ module NxtStateMachine
37
37
  result = set_state(current_target, transition, state_attr)
38
38
  transition.run_after_callbacks
39
39
 
40
- result
40
+ transition.run_success_callbacks || result
41
41
  rescue StandardError
42
42
  current_target[state_attr] = transition.from.enum
43
43
  raise
@@ -10,11 +10,12 @@ module NxtStateMachine
10
10
  @events = event_registry
11
11
  @callbacks = CallbackRegistry.new
12
12
  @error_callback_registry = ErrorCallbackRegistry.new
13
+ @defuse_registry = DefuseRegistry.new
13
14
 
14
15
  @initial_state = nil
15
16
  end
16
17
 
17
- attr_reader :class_context, :transitions, :events, :options, :callbacks, :name, :error_callback_registry
18
+ attr_reader :class_context, :events, :options, :callbacks, :name, :error_callback_registry, :defuse_registry
18
19
  attr_accessor :initial_state
19
20
 
20
21
  def get_state_with(method = nil, &block)
@@ -89,6 +90,7 @@ module NxtStateMachine
89
90
  class_context.define_method event_name do |*args, **opts|
90
91
  event.state_machine.can_transition!(name, event.state_machine.current_state_name(self))
91
92
  transition = event.event_transitions.resolve(event.state_machine.current_state_name(self))
93
+ # Transition is build every time and thus should be thread safe!
92
94
  transition.build_transition(event_name, self, set_state_method, *args, **opts)
93
95
  end
94
96
  end
@@ -116,6 +118,14 @@ module NxtStateMachine
116
118
  callbacks.register(from, to, :after, run, block)
117
119
  end
118
120
 
121
+ def on_success(from:, to:, run: nil, &block)
122
+ callbacks.register(from, to, :success, run, block)
123
+ end
124
+
125
+ def defuse(errors = [], from:, to:)
126
+ defuse_registry.register(from, to, errors)
127
+ end
128
+
119
129
  def on_error(error = StandardError, from:, to:, run: nil, &block)
120
130
  error_callback_registry.register(from, to, error, run, block)
121
131
  end
@@ -142,16 +152,23 @@ module NxtStateMachine
142
152
  run_callbacks(transition, :after, context)
143
153
  end
144
154
 
155
+ def run_success_callbacks(transition, context)
156
+ run_callbacks(transition, :success, context)
157
+ end
158
+
145
159
  def find_error_callback(error, transition)
146
160
  error_callback_registry.resolve(error, transition)
147
161
  end
148
162
 
149
163
  def run_callbacks(transition, kind, context)
150
164
  current_callbacks = callbacks.resolve(transition, kind)
165
+ return unless current_callbacks.any?
151
166
 
152
167
  current_callbacks.each do |callback|
153
168
  Callable.new(callback).bind(context).call(transition)
154
169
  end
170
+
171
+ true
155
172
  end
156
173
 
157
174
  def current_state_name(context)
@@ -11,20 +11,18 @@ module NxtStateMachine
11
11
  @set_state_method = set_state_method
12
12
  @context = context
13
13
  @block = block
14
+ @result = nil
14
15
  end
15
16
 
16
- attr_reader :name, :from, :to, :block, :event
17
+ attr_reader :name, :from, :to, :block, :event, :result
17
18
 
18
- def prepare
19
+ # This triggers the set state method
20
+ def trigger
19
21
  Callable.new(
20
22
  state_machine.send(set_state_method)
21
23
  ).bind(
22
24
  context
23
25
  ).call(state_machine.target(context), self)
24
- end
25
-
26
- def execute(&block)
27
- Transition::Proxy.new(event, state_machine,self, context).call(&block)
28
26
  rescue StandardError => error
29
27
  callback = state_machine.find_error_callback(error, self)
30
28
  raise unless callback
@@ -32,6 +30,11 @@ module NxtStateMachine
32
30
  Callable.new(callback).bind(context).call(error, self)
33
31
  end
34
32
 
33
+ # This must be used in set_state method to actually execute the transition within the around callback chain
34
+ def execute(&block)
35
+ self.result = Transition::Proxy.new(event, state_machine,self, context).call(&block)
36
+ end
37
+
35
38
  alias_method :with_around_callbacks, :execute
36
39
 
37
40
  def run_before_callbacks
@@ -42,9 +45,13 @@ module NxtStateMachine
42
45
  state_machine.run_after_callbacks(self, context)
43
46
  end
44
47
 
48
+ def run_success_callbacks
49
+ state_machine.run_success_callbacks(self, context)
50
+ end
51
+
45
52
  private
46
53
 
47
54
  attr_reader :state_machine, :set_state_method, :context
48
- attr_writer :block
55
+ attr_writer :block, :result
49
56
  end
50
57
  end
@@ -39,7 +39,7 @@ module NxtStateMachine
39
39
  end)
40
40
  end
41
41
 
42
- transition.prepare
42
+ transition.trigger
43
43
  end
44
44
 
45
45
  private
@@ -1,3 +1,3 @@
1
1
  module NxtStateMachine
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.8"
3
3
  end
@@ -0,0 +1,8 @@
1
+ class Railtie < Rails::Railtie
2
+ railtie_name :nxt_state_machine
3
+
4
+ rake_tasks do
5
+ path = File.expand_path(__dir__)
6
+ Dir.glob("#{path}/tasks/**/*.rake").each { |f| load f }
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ namespace :graph do
2
+ desc 'draw the graph of a state machine'
3
+ task :draw, [:state_machine_class] => [:environment] do |_, args|
4
+ state_machine_class = Object.const_get(args.fetch(:state_machine_class))
5
+ state_machine = state_machine_class.state_machine
6
+ NxtStateMachine::Graph.new(state_machine).draw
7
+ end
8
+ end
@@ -38,9 +38,10 @@ Gem::Specification.new do |spec|
38
38
  spec.add_dependency "nxt_registry", "~> 0.1.3"
39
39
 
40
40
  spec.add_development_dependency "bundler", "~> 2.0"
41
- spec.add_development_dependency "rake", "~> 10.0"
41
+ spec.add_development_dependency "rake", "~> 12.0"
42
42
  spec.add_development_dependency "rspec", "~> 3.0"
43
43
  spec.add_development_dependency "pry"
44
44
  spec.add_development_dependency "activerecord"
45
45
  spec.add_development_dependency "sqlite3"
46
+ spec.add_development_dependency "ruby-graphviz"
46
47
  end
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nxt_state_machine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Robecke
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2020-01-26 00:00:00.000000000 Z
14
+ date: 2020-06-08 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activesupport
@@ -61,14 +61,14 @@ dependencies:
61
61
  requirements:
62
62
  - - "~>"
63
63
  - !ruby/object:Gem::Version
64
- version: '10.0'
64
+ version: '12.0'
65
65
  type: :development
66
66
  prerelease: false
67
67
  version_requirements: !ruby/object:Gem::Requirement
68
68
  requirements:
69
69
  - - "~>"
70
70
  - !ruby/object:Gem::Version
71
- version: '10.0'
71
+ version: '12.0'
72
72
  - !ruby/object:Gem::Dependency
73
73
  name: rspec
74
74
  requirement: !ruby/object:Gem::Requirement
@@ -125,6 +125,20 @@ dependencies:
125
125
  - - ">="
126
126
  - !ruby/object:Gem::Version
127
127
  version: '0'
128
+ - !ruby/object:Gem::Dependency
129
+ name: ruby-graphviz
130
+ requirement: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ type: :development
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
128
142
  description: A state machine library that can be used with ActiveRecord or in plain
129
143
  ruby and should be easy to customize for other integrations
130
144
  email:
@@ -139,6 +153,7 @@ files:
139
153
  - ".rspec"
140
154
  - ".ruby-version"
141
155
  - ".travis.yml"
156
+ - CHANGELOG.md
142
157
  - Gemfile
143
158
  - Gemfile.lock
144
159
  - LICENSE.txt
@@ -149,6 +164,7 @@ files:
149
164
  - lib/nxt_state_machine.rb
150
165
  - lib/nxt_state_machine/callable.rb
151
166
  - lib/nxt_state_machine/callback_registry.rb
167
+ - lib/nxt_state_machine/defuse_registry.rb
152
168
  - lib/nxt_state_machine/error_callback_registry.rb
153
169
  - lib/nxt_state_machine/errors/error.rb
154
170
  - lib/nxt_state_machine/errors/event_already_registered.rb
@@ -165,6 +181,7 @@ files:
165
181
  - lib/nxt_state_machine/event.rb
166
182
  - lib/nxt_state_machine/event/names.rb
167
183
  - lib/nxt_state_machine/event_registry.rb
184
+ - lib/nxt_state_machine/graph.rb
168
185
  - lib/nxt_state_machine/integrations/active_record.rb
169
186
  - lib/nxt_state_machine/integrations/attr_accessor.rb
170
187
  - lib/nxt_state_machine/integrations/hash.rb
@@ -178,7 +195,10 @@ files:
178
195
  - lib/nxt_state_machine/transition/proxy.rb
179
196
  - lib/nxt_state_machine/transition/store.rb
180
197
  - lib/nxt_state_machine/version.rb
198
+ - lib/railtie.rb
199
+ - lib/tasks/draw_graph.rake
181
200
  - nxt_state_machine.gemspec
201
+ - state_machine.png
182
202
  homepage: https://github.com/nxt-insurance/nxt_state_machine
183
203
  licenses:
184
204
  - MIT