switcher 0.1.0 → 0.2.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.
- data/.travis.yml +9 -0
- data/Gemfile +2 -1
- data/README.md +105 -3
- data/Rakefile +6 -0
- data/lib/switcher/adapters/active_record.rb +7 -0
- data/lib/switcher/adapters/object.rb +7 -0
- data/lib/switcher/listener.rb +3 -0
- data/lib/switcher/statement.rb +6 -0
- data/lib/switcher/version.rb +1 -1
- data/spec/fixtures/human.rb +5 -3
- data/spec/switcher_spec.rb +7 -0
- metadata +7 -6
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,11 +1,113 @@
|
|
1
|
-
|
1
|
+
[](http://travis-ci.org/Ptico/switcher)
|
2
2
|
|
3
|
-
|
3
|
+
Switcher is lightweight event-driven state machine.
|
4
|
+
|
5
|
+
## Basic usage:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
class Package
|
9
|
+
include Switcher::Object
|
10
|
+
|
11
|
+
def initialize(weight)
|
12
|
+
@sent_weight = weight.to_f
|
13
|
+
@tracking_number = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
switcher :delivery do
|
17
|
+
state :requested do
|
18
|
+
on :send, switch_to: :sent do |ev, num|
|
19
|
+
@tracking_number = num
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
state :sent do
|
24
|
+
before :receive, call: :check_number
|
25
|
+
|
26
|
+
on :receive do |ev, number, weight|
|
27
|
+
if weight < (@weight - 0.3)
|
28
|
+
ev.switch_to :stolen
|
29
|
+
else
|
30
|
+
ev.switch_to :received
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
after :receive do |ev|
|
35
|
+
unpack if delivery_received?
|
36
|
+
end
|
37
|
+
|
38
|
+
on :miss, switch_to: :missed
|
39
|
+
end
|
40
|
+
|
41
|
+
state :received
|
42
|
+
state :stolen
|
43
|
+
state :missed
|
44
|
+
end
|
45
|
+
|
46
|
+
def check_number(ev, number, weight)
|
47
|
+
ev.stop if number.to_s != @tracking_number
|
48
|
+
end
|
49
|
+
|
50
|
+
def unpack
|
51
|
+
puts "TADA!"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
package = Package.new(4.2)
|
56
|
+
|
57
|
+
package.delivery # => :requested
|
58
|
+
package.can_send? # => true
|
59
|
+
package.send! "AB123456CD"
|
60
|
+
package.delivery # => :sent
|
61
|
+
package.receive!(3.5, "CD654321AB")
|
62
|
+
package.delivery # => :sent
|
63
|
+
package.receive!(4.1, "AB123456CD")
|
64
|
+
# > TADA!
|
65
|
+
|
66
|
+
package.delivery # => :received
|
67
|
+
package.delivery_prev # => :sent
|
68
|
+
package.delivery_received? # => true
|
69
|
+
package.can_miss? # => false
|
70
|
+
```
|
71
|
+
|
72
|
+
## Usage with Active Record:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
class User < ActiveRecord::Base
|
76
|
+
include Switcher::ActiveRecord
|
77
|
+
|
78
|
+
switcher :membership do
|
79
|
+
state :guest do
|
80
|
+
on :approve, switch_to: :member
|
81
|
+
end
|
82
|
+
state :member do
|
83
|
+
on :ban, switch_to: :banned do |ev, reason|
|
84
|
+
ev.stop unless reason
|
85
|
+
ban_reason = reason
|
86
|
+
end
|
87
|
+
end
|
88
|
+
state :banned do
|
89
|
+
on :unban, switch_to: :member
|
90
|
+
after :unban do
|
91
|
+
ban_reason = nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
user = User.find(5)
|
98
|
+
user.ban! "Stupid bastard"
|
99
|
+
user.membership # => :banned
|
100
|
+
user.save
|
101
|
+
```
|
102
|
+
|
103
|
+
## More examples:
|
104
|
+
|
105
|
+
* [Wiki](https://github.com/Ptico/switcher/wiki)
|
106
|
+
* [Specs](https://github.com/Ptico/switcher/tree/master/spec)
|
4
107
|
|
5
108
|
## TODO:
|
6
109
|
|
7
110
|
* 1.8 compat
|
8
|
-
* Write README
|
9
111
|
* Refactoring
|
10
112
|
* Events without state
|
11
113
|
* Ability to define validations, associations etc. in state (ability to call static methods from object)
|
data/Rakefile
CHANGED
@@ -13,6 +13,9 @@ module Switcher
|
|
13
13
|
define_method(:"#{spec_name}_prev") { self.instance_variable_get(:"@#{spec_name}_statement").state_prev }
|
14
14
|
|
15
15
|
define_method(:"#{spec_name}") { self.instance_variable_get(:"@#{spec_name}_statement").state_current }
|
16
|
+
define_method(:"#{spec_name}=") { nil } # FIXME - raise exception
|
17
|
+
|
18
|
+
define_method(:"#{spec_name}_force") { |state| self.instance_variable_get(:"@#{spec_name}_statement").force_state(state.to_sym) }
|
16
19
|
|
17
20
|
events = []
|
18
21
|
|
@@ -62,6 +65,8 @@ module Switcher
|
|
62
65
|
end
|
63
66
|
|
64
67
|
def switch_from(orig, event, *args)
|
68
|
+
return false unless (self.respond_to?(:"can_#{event}?") and self.send(:"can_#{event}?"))
|
69
|
+
|
65
70
|
self.class.class_variable_get(:@@__specs__).each do |spc|
|
66
71
|
spc_name = spc.name
|
67
72
|
|
@@ -72,6 +77,8 @@ module Switcher
|
|
72
77
|
write_attribute("#{spc_name}_prev", self.send(:"#{spc_name}_prev"))
|
73
78
|
end
|
74
79
|
end
|
80
|
+
|
81
|
+
true
|
75
82
|
end
|
76
83
|
|
77
84
|
end
|
@@ -20,6 +20,9 @@ module Switcher
|
|
20
20
|
define_method(:"#{spec_name}_prev") { self.instance_variable_get(:"@#{spec_name}_statement").state_prev }
|
21
21
|
|
22
22
|
define_method(:"#{spec_name}") { self.instance_variable_get(:"@#{spec_name}_statement").state_current }
|
23
|
+
define_method(:"#{spec_name}=") { nil }
|
24
|
+
|
25
|
+
define_method(:"#{spec_name}_force") { |state| self.instance_variable_get(:"@#{spec_name}_statement").force_state(state.to_sym) }
|
23
26
|
|
24
27
|
events = []
|
25
28
|
|
@@ -57,9 +60,13 @@ module Switcher
|
|
57
60
|
end
|
58
61
|
|
59
62
|
def switch_from(orig, event, *args)
|
63
|
+
return false unless (self.respond_to?(:"can_#{event}?") and self.send(:"can_#{event}?"))
|
64
|
+
|
60
65
|
self.class.class_variable_get(:@@__specs__).each do |spc|
|
61
66
|
self.instance_variable_get(:"@#{spc.name}_statement").publish(event, orig, args)
|
62
67
|
end
|
68
|
+
|
69
|
+
true
|
63
70
|
end
|
64
71
|
end
|
65
72
|
end
|
data/lib/switcher/listener.rb
CHANGED
@@ -28,6 +28,9 @@ module Switcher
|
|
28
28
|
|
29
29
|
def trigger(event, facade, instance, args)
|
30
30
|
if ev = @events[event]
|
31
|
+
if ev[:options][:call]
|
32
|
+
instance.instance_exec(facade, *args) { |facad, *arguments| self.send(ev[:options][:call], facad, *arguments) }
|
33
|
+
end
|
31
34
|
instance.instance_exec(facade, *args, &ev[:callback]) if ev[:callback].respond_to?(:call)
|
32
35
|
if !facade.stopped && ev[:options][:allow_switch] && switch_to = ev[:options][:switch_to]
|
33
36
|
facade.switch_to(switch_to)
|
data/lib/switcher/statement.rb
CHANGED
@@ -51,6 +51,12 @@ module Switcher
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
def force_state(state)
|
55
|
+
return unless @spec.states_list.include?(state.to_sym) # FIXME - raise exception
|
56
|
+
@state_prev = state_current
|
57
|
+
@state_current = state.to_sym
|
58
|
+
end
|
59
|
+
|
54
60
|
private
|
55
61
|
|
56
62
|
def set_state(facade)
|
data/lib/switcher/version.rb
CHANGED
data/spec/fixtures/human.rb
CHANGED
@@ -68,9 +68,7 @@ class Human
|
|
68
68
|
end
|
69
69
|
|
70
70
|
state :civil do
|
71
|
-
on :find_job, switch_to: :manager
|
72
|
-
ev.restrict unless cv
|
73
|
-
end
|
71
|
+
on :find_job, call: :employment, switch_to: :manager
|
74
72
|
end
|
75
73
|
|
76
74
|
state :manager do
|
@@ -89,4 +87,8 @@ class Human
|
|
89
87
|
def hard_to?(learn=false)
|
90
88
|
!!learn
|
91
89
|
end
|
90
|
+
|
91
|
+
def employment(ev, cv)
|
92
|
+
ev.restrict unless cv
|
93
|
+
end
|
92
94
|
end
|
data/spec/switcher_spec.rb
CHANGED
@@ -59,6 +59,13 @@ describe Switcher do
|
|
59
59
|
person.state_manager?.should be_true
|
60
60
|
person.state_civil?.should be_false
|
61
61
|
end
|
62
|
+
|
63
|
+
it "should force state if needed" do
|
64
|
+
crashed_car = Car.new
|
65
|
+
|
66
|
+
crashed_car.state_force(:damaged)
|
67
|
+
crashed_car.state.should eq(:damaged)
|
68
|
+
end
|
62
69
|
end
|
63
70
|
|
64
71
|
describe "Multiple states" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: switcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03-
|
12
|
+
date: 2012-03-05 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: &
|
16
|
+
requirement: &70322138874120 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70322138874120
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70322138873700 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70322138873700
|
36
36
|
description: Switcher is simple, event-driven state machine
|
37
37
|
email:
|
38
38
|
- andrey@aejis.eu
|
@@ -41,6 +41,7 @@ extensions: []
|
|
41
41
|
extra_rdoc_files: []
|
42
42
|
files:
|
43
43
|
- .gitignore
|
44
|
+
- .travis.yml
|
44
45
|
- Gemfile
|
45
46
|
- Guardfile
|
46
47
|
- README.md
|