strict_machine 0.2.0 → 0.2.1

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
  SHA1:
3
- metadata.gz: cd8176055fcd833b0c5e292f89d251ef32067687
4
- data.tar.gz: 8c5526e2c67353ce0bb71d52713584219a901d1c
3
+ metadata.gz: af5b761971dfb358c71f10ac26f27fe6e8bd5d2d
4
+ data.tar.gz: c09dd654724097dc1db5c1ca9d4106460a3c8303
5
5
  SHA512:
6
- metadata.gz: e364a995db6ffbde2984893fdc5f2e0b7d2dccf67b85514904aba17a5e66b86db90d958eb1e8e475e85b63589f70ecdcd7cb7cba6f800fb930073c0e7b043257
7
- data.tar.gz: 0c2f0375d4df894541648ca6ddd7e2ebc7a14ca90edf6ed867c91a63b8effe15d914aece8e9496d6f9c964b10541bd9fb3853e145d717655272516eabfcb29ff
6
+ metadata.gz: fb18988d67119ceab8121d57c61c12ab65dd7ba1b55a1ad009d24343488b33a1557185c3f4938cc66671a2425ec9cf3157ef11b7c014cb1093683e7bbd949aad
7
+ data.tar.gz: 28c3aa5e21a28d1247c4fee56e9b3d0864be384dd831251d9f685cb556bca515c69db7fa81826afa8560d6aa67d56d8f64d9f48bcee5958504a223262b6bc59a
data/README.md CHANGED
@@ -20,17 +20,134 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- Still working on this one...
23
+ You can use this gem in two ways - either by embedding a state machine onto
24
+ your class, or by mounting a separate class.
25
+
26
+ ### Embedded
27
+
28
+ ```ruby
29
+ class A < StrictMachine::Base
30
+ strict_machine do
31
+ state :initial do
32
+ on hop: :middle
33
+ end
34
+ state :middle do
35
+ on hop: :final
36
+ end
37
+ state :final
38
+ on_transition do |from, to, trigger_event, duration|
39
+ log from, to, trigger_event, duration
40
+ end
41
+ end
42
+
43
+ def log(from, to, trigger_event, duration)
44
+ # ...
45
+ end
46
+ end
47
+ ```
48
+
49
+ ### Mounting
50
+
51
+ (using same class A as previous example, minus the `log` method)
52
+
53
+ ```ruby
54
+ class B
55
+ include StrictMachine::MountStateMachine
56
+
57
+ mount_state_machine A
58
+
59
+ def log(from, to, trigger_event, duration)
60
+ # ...
61
+ end
62
+ end
63
+ ```
64
+
65
+ **NOTE**: when mounting, the methods referenced should always be on the class doing the mounting, not on the state machine one!
66
+
67
+ ### Object extensions
68
+
69
+ Whether embedding or mounting, an object instance will have the following
70
+ methods added to it:
71
+
72
+ - `#state` returns the current state's name
73
+ - `#trigger(*transitions)` triggers the given transition(s) by name
74
+ - `#state_attr` the name of the state attribute being used
75
+ - `#states` list of `State` objects in the machine's definition
76
+
77
+ and an object's class will have:
78
+
79
+ - `#definition` the DSL evaluation context object
80
+ - `#strict_machine_attr` the name of the attribute to store state in
81
+ - `#strict_machine_class` either the class containing the state machine, when
82
+ mounting, or `self` when embedding
83
+ - `#strict_machine` the DSL hook
84
+
85
+ ### Internals
86
+
87
+ The gem works by storing, at the object's class level, the state machine's
88
+ definition and the name of the state storage attribute. Every instance of the class embedding or mounting a state machine will have its own state (stored in said attribute (by default `state`), and upon transitions, the current state and the transition requested will be passed to `#change_state` in
89
+ `InstanceMethods` and a new state will be reached (and `true` returned), or
90
+ an exception will be raised. Existing exceptions:
91
+
92
+ - `StateNotFoundError` tried to transition to a non-existing state
93
+ - `TransitionNotFoundError` the transition requested does not exist
94
+ - `GuardedTransitionError` the guard condition for the transition was not met
95
+
96
+ You can bypass the guard condition on a transition by adding a bang `!` to the
97
+ transition's name, i.e.,
98
+
99
+ ```ruby
100
+ obj.trigger('transition!') # bypass guards
101
+ ```
102
+
103
+ ## The DSL
104
+
105
+ Here's a complete example:
106
+
107
+ ```ruby
108
+ strict_machine do
109
+ state :new do
110
+ on submit: :awaiting_review
111
+ end
112
+
113
+ state :awaiting_review do
114
+ on review: :under_review
115
+ end
116
+
117
+ state :under_review do
118
+ on_entry { |previous, trigger| some_method(previous, trigger) }
119
+ on accept: :accepted, if: :cool_article?
120
+ on reject: :rejected, if: :bad_article?
121
+ end
122
+
123
+ state :accepted
124
+ state :rejected
125
+
126
+ on_transition do |from, to, trigger_event, duration|
127
+ log from, to, trigger_event, duration
128
+ end
129
+ end
130
+ ```
131
+
132
+ Guards (the `:if`s on `on` calls) must return `true` in order to pass.
133
+
134
+ When mounting, you can specify the state attribute's name with:
135
+
136
+ `mount_state_machine SomeClass, state: "meh"`
137
+
138
+ And when embedding:
139
+
140
+ `strict_machine("meh") do ....`
24
141
 
25
142
  ## Development
26
143
 
27
144
  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.
28
145
 
29
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
146
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `strict_machine.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
30
147
 
31
148
  ## Contributing
32
149
 
33
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/strict_machine.
150
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sardaukar/strict_machine.
34
151
 
35
152
 
36
153
  ## License
@@ -31,5 +31,11 @@ module StrictMachine
31
31
  def add_on_entry(proc)
32
32
  @on_entry << proc
33
33
  end
34
+
35
+ def run_on_entries(instance, current_state_name, trigger)
36
+ @on_entry.each do |proc|
37
+ instance.instance_exec(current_state_name, trigger, &proc)
38
+ end
39
+ end
34
40
  end
35
41
  end
@@ -21,6 +21,14 @@ module StrictMachine
21
21
  @states.first.name
22
22
  end
23
23
 
24
+ def run_transitions(instance, current_state, new_state, trigger, duration)
25
+ @transitions.each do |proc|
26
+ instance.instance_exec(
27
+ current_state, new_state, trigger, duration, &proc
28
+ )
29
+ end
30
+ end
31
+
24
32
  ### DSL
25
33
 
26
34
  def state(name, &block)
@@ -1,8 +1,10 @@
1
+ require "benchmark"
2
+
1
3
  module StrictMachine
2
4
  module MountStateMachine
3
5
  module InstanceMethods
4
6
  def trigger(*transitions)
5
- transitions.map {|t| change_state(t, state) }
7
+ transitions.map { |t| change_state(t, state) }
6
8
 
7
9
  true
8
10
  end
@@ -14,7 +16,7 @@ module StrictMachine
14
16
  end
15
17
 
16
18
  def state_attr
17
- self.class.strict_machine_attr.to_s.gsub("@",'')
19
+ self.class.strict_machine_attr.to_s.delete("@")
18
20
  end
19
21
 
20
22
  def states
@@ -44,31 +46,29 @@ module StrictMachine
44
46
  end
45
47
 
46
48
  def change_state(trigger, current_state_name)
47
- dt = Time.now
48
- current_state = definition.get_state_by_name(current_state_name)
49
- is_bang, transition = current_state.get_transition(trigger)
49
+ new_state = nil
50
50
 
51
- if transition.guarded? && !is_bang
52
- raise GuardedTransitionError unless self.public_send(
53
- transition.guard
54
- )
55
- end
51
+ duration = Benchmark.measure do
52
+ is_bang, transition = current_state_obj.get_transition(trigger)
56
53
 
57
- new_state = definition.get_state_by_name(transition.to)
58
- new_state.on_entry.each do |proc|
59
- self.instance_exec(current_state_name, trigger.to_sym, &proc)
60
- end
54
+ if transition.guarded? && !is_bang
55
+ raise GuardedTransitionError unless public_send(transition.guard)
56
+ end
61
57
 
62
- duration = Time.now - dt
58
+ new_state = definition.get_state_by_name(transition.to)
59
+ new_state.run_on_entries(self, current_state_name, trigger.to_sym)
60
+ end.real
63
61
 
64
- definition.transitions.each do |proc|
65
- self.instance_exec(
66
- current_state_name, new_state.name, trigger.to_sym, duration, &proc
67
- )
68
- end
62
+ definition.run_transitions(
63
+ self, current_state_name, new_state.name, trigger.to_sym, duration
64
+ )
69
65
 
70
66
  write_state(new_state.name)
71
67
  end
68
+
69
+ def current_state_obj
70
+ definition.get_state_by_name(current_state_attr_value)
71
+ end
72
72
  end
73
73
  end
74
74
  end
@@ -1,13 +1,12 @@
1
1
  require_relative "ext/object"
2
2
 
3
3
  module StrictMachine
4
- VERSION = "0.2.0".freeze
4
+ VERSION = "0.2.1".freeze
5
5
 
6
6
  class << self; attr_accessor :list; end
7
7
  end
8
8
 
9
9
  class StateNotFoundError < StandardError; end
10
- class InvalidTransitionError < StandardError; end
11
10
  class TransitionNotFoundError < StandardError; end
12
11
  class GuardedTransitionError < StandardError; end
13
12
 
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.email = ["sardaukar.siet@gmail.com"]
12
12
 
13
13
  spec.summary = "State machine functionality for Ruby classes"
14
- spec.homepage = "https://not-yet.com"
14
+ spec.homepage = "https://github.com/sardaukar/strict_machine"
15
15
  spec.license = "MIT"
16
16
 
17
17
  spec.files = `git ls-files -z`
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strict_machine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bruno Antunes
@@ -101,7 +101,7 @@ files:
101
101
  - spec/mountable_machine_spec.rb
102
102
  - spec/spec_helper.rb
103
103
  - strict_machine.gemspec
104
- homepage: https://not-yet.com
104
+ homepage: https://github.com/sardaukar/strict_machine
105
105
  licenses:
106
106
  - MIT
107
107
  metadata: {}