hifsm 0.3.1 → 0.4.0

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: 313a9e688988c6dac1ee778973a602db61af4ca2
4
- data.tar.gz: cf7dc5e882dbf4f8dfaf34f211aa04d136474b01
3
+ metadata.gz: 4d654a06d28adc762a19f31cacf281568503e4de
4
+ data.tar.gz: 2d8e83ee31acc329efcce082d33d43d65574e71c
5
5
  SHA512:
6
- metadata.gz: b585b14728b541d4d2863d71e7ac55a59699f71d33ed48ef8b9a366b8f2f2440b8426fbad9287564748e6acb0fb05308f476b66aa72cd61d70877d9691db30d9
7
- data.tar.gz: 53df6c486ca83c6745fca49e9cec581348f5b6919d3efcfd76aa6316682d4d3c6dafb31342777d3530838852a9c0665cebfa3b3bdcfd3f0bc319b8e221854f1a
6
+ metadata.gz: 5ea67c4a15aaee373a2b672184781e975ba67b2ef2681ce461a66c9c062d7bce9509d5bc01e1d8ce82848391d0a63c1df21e9530bc177251d6fb798f2a5560f5
7
+ data.tar.gz: 7eb5c56002cc5a98c74a55255e465fa280e91b9ba038b3d99355c23884f960040938b97931d65ea0406c63f84e73afe0e978ed37d490b2f6e3d485a7369bc92a
data/README.md CHANGED
@@ -36,13 +36,17 @@ __This is in early development, so be careful.__
36
36
 
37
37
  ## Usage
38
38
 
39
+ Start with the [basic example](https://github.com/stiff/hifsm/blob/master/test/test_basic_fsm.rb) and then try [something](https://github.com/stiff/hifsm/blob/master/test/test_hierarchical.rb) [more](https://github.com/stiff/hifsm/blob/master/test/test_many_states.rb) [interesting](https://github.com/stiff/hifsm/blob/master/test/test_dynamic_initial_state.rb).
40
+
39
41
  Here is how to use it to model a monster in a Quake-like game. It covers most Hifsm features:
40
42
 
41
43
  ```ruby
42
44
  require 'hifsm'
43
45
 
44
46
  class Monster
45
- include Hifsm.fsm_module {
47
+ include Hifsm
48
+
49
+ hifsm do
46
50
  state :idle, :initial => true
47
51
  state :attacking do
48
52
  state :acquiring_target, :initial => true do
@@ -92,7 +96,7 @@ class Monster
92
96
  self.target = nil
93
97
  end
94
98
  end
95
- }
99
+ end
96
100
 
97
101
  attr_accessor :target, :low_hp, :debug
98
102
 
@@ -103,11 +107,13 @@ class Monster
103
107
  @low_hp = false
104
108
  end
105
109
 
106
- def act!
110
+ def act_with_tick!
107
111
  debug && puts("Acting @#{state}")
108
- state_machine.act! @tick
112
+ act_without_tick! @tick
109
113
  @tick = @tick + 1
110
114
  end
115
+ alias_method :act_without_tick!, :act!
116
+ alias_method :act!, :act_with_tick!
111
117
 
112
118
  def hit(target)
113
119
  debug && puts("~~> #{target}")
@@ -168,11 +174,9 @@ ogre.act! # Acting @runaway
168
174
 
169
175
  ```
170
176
 
171
- Note the use of `{..}` construct instead of `do..end` in `include`. `do..end` is treated as block for include itself, instead of `fsm_module`.
172
-
173
177
  ## Guards
174
178
 
175
- Events are tried in order they were defined, if guard callback returns `false` then event is skipped.
179
+ Events are tried in order they were defined, if guard callback returns `false` then event is skipped as if it was not defined at all. See [example of this](https://github.com/stiff/hifsm/blob/master/test/test_event_guard.rb).
176
180
 
177
181
  ## Callbacks
178
182
 
@@ -188,7 +192,7 @@ On event:
188
192
 
189
193
  If any of `before...` callbacks returns `false` then no further processing is done, no exceptions raised, machine state is not changed.
190
194
 
191
- On `act!` just calls action block if it was given.
195
+ On `act!` state's actions called from top state to nested. If [several FSMs defined]](https://github.com/stiff/hifsm/blob/master/test/test_two_machines.rb), object's `act!` invokes them all in order as they were defined and returns value from last action.
192
196
 
193
197
  ## ActiveRecord integration
194
198
 
@@ -9,7 +9,8 @@ module Hifsm
9
9
 
10
10
  module ClassMethods
11
11
  def hifsm(column, &block)
12
- include Hifsm.fsm_module(column, &block)
12
+ super
13
+
13
14
  before_save "hifsm_write_#{column}_attribute"
14
15
 
15
16
  send("#{column}_machine_definition").all_states.each do |st|
@@ -40,5 +41,6 @@ module Hifsm
40
41
  end
41
42
 
42
43
  ActiveRecord::Base.class_eval do
44
+ include Hifsm
43
45
  include Hifsm::Adapters::ActiveRecordAdapter
44
46
  end
@@ -4,10 +4,10 @@ class Callbacks
4
4
  def invoke(target, cb, *args)
5
5
  if cb.nil?
6
6
  elsif cb.is_a? Symbol
7
- if target.method(cb).arity > 0
8
- target.send(cb, *args)
9
- else
7
+ if target.method(cb).arity.zero?
10
8
  target.send(cb)
9
+ else
10
+ target.send(cb, *args)
11
11
  end
12
12
  else
13
13
  target.instance_exec(*args, &cb)
data/lib/hifsm/fsm.rb CHANGED
@@ -82,16 +82,9 @@ module Hifsm
82
82
  machine_var = "@#{name}_machine"
83
83
  machine_name = "#{name}_machine"
84
84
 
85
- module_class_methods = Module.new do
86
- define_method("#{machine_name}_definition") { fsm }
87
- end
88
-
89
85
  Module.new do
90
- const_set('ClassMethods', module_class_methods)
91
- def self.included(base)
92
- base.class_exec do
93
- extend const_get('ClassMethods')
94
- end
86
+ define_singleton_method :included do |base|
87
+ base.send(:define_singleton_method, "#{machine_name}_definition") { fsm }
95
88
  end
96
89
 
97
90
  # <state>_machine returns machine instance
data/lib/hifsm/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Hifsm
2
- VERSION = "0.3.1"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/hifsm.rb CHANGED
@@ -18,9 +18,21 @@ module Hifsm
18
18
  end
19
19
  end
20
20
 
21
- class <<self
22
- def fsm_module(name = :state, &block)
23
- FSM::new(name, &block).to_module
21
+ def self.included(base)
22
+ base.send(:extend, ClassMethods) unless base.respond_to?(:hifsm)
23
+ end
24
+
25
+ module ClassMethods
26
+ def hifsm(name = :state, &block)
27
+ include FSM::new(name, &block).to_module
28
+
29
+ # act!
30
+ define_method("act_with_#{name}_machine!") do |*args|
31
+ send("act_without_#{name}_machine!", *args) if respond_to?("act_without_#{name}_machine!")
32
+ send("#{name}_machine").act!(*args)
33
+ end
34
+ alias_method "act_without_#{name}_machine!", :act! if method_defined?(:act!)
35
+ alias_method :act!, "act_with_#{name}_machine!"
24
36
  end
25
37
  end
26
38
  end
data/test/monster.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require 'hifsm'
2
2
 
3
3
  class Monster
4
- include Hifsm.fsm_module {
4
+ include Hifsm
5
+
6
+ hifsm do
5
7
  state :idle, :initial => true
6
8
  state :attacking do
7
9
  state :acquiring_target, :initial => true do
@@ -51,7 +53,7 @@ class Monster
51
53
  self.target = nil
52
54
  end
53
55
  end
54
- }
56
+ end
55
57
 
56
58
  attr_accessor :target, :low_hp, :debug
57
59
 
@@ -62,11 +64,13 @@ class Monster
62
64
  @low_hp = false
63
65
  end
64
66
 
65
- def act!
67
+ def act_with_tick!
66
68
  debug && puts("Acting @#{state}")
67
- state_machine.act! @tick
69
+ act_without_tick! @tick
68
70
  @tick = @tick + 1
69
71
  end
72
+ alias_method :act_without_tick!, :act!
73
+ alias_method :act!, :act_with_tick!
70
74
 
71
75
  def hit(target)
72
76
  debug && puts("~~> #{target}")
@@ -2,7 +2,8 @@ require 'setup_tests'
2
2
 
3
3
  class TestDynamicInitialState < Minitest::Test
4
4
  class Value < Struct.new(:value)
5
- include Hifsm.fsm_module(:group) {
5
+ include Hifsm
6
+ hifsm :group do
6
7
  state :few do
7
8
  state :very
8
9
  state :almost
@@ -13,7 +14,7 @@ class TestDynamicInitialState < Minitest::Test
13
14
  end
14
15
  state :throng
15
16
  state :swarm
16
- }
17
+ end
17
18
 
18
19
  def initial_group
19
20
  case value
@@ -2,7 +2,9 @@ require 'setup_tests'
2
2
 
3
3
  class TestEventGuard < Minitest::Test
4
4
  class Wall < Struct.new(:stones)
5
- include Hifsm.fsm_module {
5
+ include Hifsm
6
+
7
+ hifsm do
6
8
  state :constructed, :initial => true
7
9
  state :broken
8
10
 
@@ -10,9 +12,10 @@ class TestEventGuard < Minitest::Test
10
12
  event :break, :from => :constructed, :to => :broken, :guard => proc { stones < 5 }
11
13
 
12
14
  # event parameters are passed to guards only if arity > 0
13
- event :shoot, :from => :constructed, :to => :broken, :guard => :breakable?
14
- }
15
- def breakable?(hits)
15
+ event :break, :from => :constructed, :to => :broken, :guard => :breakable?
16
+ end
17
+
18
+ def breakable?(hits = 1)
16
19
  hits * 5 > stones
17
20
  end
18
21
  end
@@ -32,7 +35,7 @@ class TestEventGuard < Minitest::Test
32
35
 
33
36
  def test_can_break_thick_wall_if_hit_3_times
34
37
  wall = Wall.new(10)
35
- wall.shoot 3
38
+ wall.break 3
36
39
  assert_equal 'broken', wall.state
37
40
  end
38
41
 
@@ -2,7 +2,8 @@ require 'setup_tests'
2
2
 
3
3
  class TestIflessFactorial < Minitest::Test
4
4
  class Value < Struct.new(:value)
5
- include Hifsm.fsm_module {
5
+ include Hifsm
6
+ hifsm do
6
7
  state :idle, :initial => true
7
8
  state :computing
8
9
 
@@ -16,7 +17,7 @@ class TestIflessFactorial < Minitest::Test
16
17
  self.value *= x
17
18
  end
18
19
  end
19
- }
20
+ end
20
21
  end
21
22
 
22
23
  def factorial(n)
@@ -1,27 +1,34 @@
1
1
  require 'setup_tests'
2
2
 
3
3
  class TestTwoMachines < Minitest::Test
4
- class ColorPrinter
5
- include Hifsm.fsm_module(:working_state) {
4
+ class ColorPrinter < Struct.new(:log)
5
+ include Hifsm
6
+
7
+ hifsm :color do
8
+ state :red, :initial => true
9
+ state :green
10
+ state :blue do
11
+ action do
12
+ self.log = [log, 'blue'].compact.join(' ; ')
13
+ end
14
+ end
15
+
16
+ event :cycle_color!, :from => :red, :to => :green
17
+ event :cycle_color!, :from => :green, :to => :blue
18
+ event :cycle_color!, :from => :blue, :to => :red
19
+ end
20
+
21
+ hifsm :working_state do
6
22
  state :off, :initial => true
7
23
  state :on do
8
24
  action do
9
- color_machine.to_s
25
+ 'printing ' + color_machine.to_s
10
26
  end
11
27
  end
12
28
 
13
29
  event :toggle, :from => :off, :to => :on
14
30
  event :toggle, :from => :on, :to => :off
15
- }
16
- include Hifsm.fsm_module(:color) {
17
- state :red, :initial => true
18
- state :green
19
- state :blue
20
-
21
- event :cycle_color!, :from => :red, :to => :green
22
- event :cycle_color!, :from => :green, :to => :blue
23
- event :cycle_color!, :from => :blue, :to => :red
24
- }
31
+ end
25
32
  end
26
33
 
27
34
  def setup
@@ -38,4 +45,24 @@ class TestTwoMachines < Minitest::Test
38
45
  assert_equal 'red', @color_printer.color
39
46
  end
40
47
 
48
+ def test_color_changes_independently
49
+ @color_printer.cycle_color!
50
+ assert_equal 'off', @color_printer.working_state
51
+ assert_equal 'green', @color_printer.color
52
+ end
53
+
54
+ def test_working_state_changes_independently
55
+ @color_printer.cycle_color!
56
+ assert_equal 'off', @color_printer.working_state
57
+ assert_equal 'green', @color_printer.color
58
+ end
59
+
60
+ def test_both_machines_act
61
+ @color_printer.cycle_color!
62
+ @color_printer.toggle
63
+ @color_printer.cycle_color!
64
+ assert_equal 'printing blue', @color_printer.act!
65
+ assert_equal 'blue', @color_printer.log
66
+ end
67
+
41
68
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hifsm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Meremyanin