switcher 0.0.1.beta → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ .rvmrc
data/Gemfile CHANGED
@@ -2,3 +2,10 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in switcher.gemspec
4
4
  gemspec
5
+
6
+ gem "guard-rspec"
7
+
8
+ group "activerecord" do
9
+ gem "activerecord", "~> 3.0.0"
10
+ gem "sqlite3"
11
+ end
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard 'rspec', :version => 2, :all_after_pass => true, :all_on_start => true,
2
+ :cli => "--colour --format documentation --profile" do
3
+ watch(%r{^lib/(.+)\.rb$})
4
+ watch(%r{^spec/.+_spec\.rb$})
5
+ end
data/README.md CHANGED
@@ -1,3 +1,17 @@
1
- TODO - write README
1
+ Not for production use
2
2
 
3
- See specs
3
+ Docs: See specs
4
+
5
+ ## TODO:
6
+
7
+ * 1.8 compat
8
+ * Write README
9
+ * Refactoring
10
+ * Events without state
11
+ * Ability to define validations, associations etc. in state (ability to call static methods from object)
12
+
13
+ ## Adapters TODO:
14
+
15
+ * ActiveModel
16
+ * Virtus
17
+ * Mongoid
@@ -0,0 +1,78 @@
1
+ module Switcher
2
+ module ActiveRecord
3
+
4
+ module Initializer
5
+ end
6
+
7
+ module ClassMethods
8
+ def switcher_pre_initialize(inst, spec)
9
+ inst.class_eval do
10
+ spec_name = spec.name
11
+
12
+ define_method(:"#{spec_name}_spec") { spec }
13
+ define_method(:"#{spec_name}_prev") { self.instance_variable_get(:"@#{spec_name}_statement").state_prev }
14
+
15
+ define_method(:"#{spec_name}") { self.instance_variable_get(:"@#{spec_name}_statement").state_current }
16
+
17
+ events = []
18
+
19
+ spec.states.each_pair do |state_name, state|
20
+ define_method(:"#{spec_name}_#{state_name}?") do
21
+ state_name == self.send(:"#{spec_name}")
22
+ end
23
+
24
+ events << state.event_names
25
+ end
26
+
27
+ events.flatten.each do |event_name|
28
+ define_method(:"can_#{event_name}?") do
29
+ self.class.class_variable_get(:@@__specs__).map { |spc|
30
+ spc.states[self.send(:"#{spc.name}")].event_names.include?(event_name)
31
+ }.include?(true)
32
+ end
33
+
34
+ define_method(:"#{event_name}!") do |*args|
35
+ switch_from(nil, event_name, *args)
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+
43
+ def self.included(base)
44
+ base.extend Switcher::ClassMethods
45
+ base.extend Switcher::ActiveRecord::ClassMethods
46
+
47
+ base.class_eval do
48
+ after_initialize do
49
+ self.class.class_variable_get(:@@__specs__).each do |spec|
50
+ spec_name = spec.name
51
+ state_current = read_attribute(spec_name)
52
+ state_prev = (has_attribute?("#{spec_name}_prev") ? read_attribute("#{spec_name}_prev") : nil)
53
+
54
+ self.instance_variable_set(:"@#{spec_name}_statement", Statement.new(self, spec, state_current, state_prev))
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ def switch(event, *args)
61
+ switch_from(self.class, event, *args)
62
+ end
63
+
64
+ def switch_from(orig, event, *args)
65
+ self.class.class_variable_get(:@@__specs__).each do |spc|
66
+ spc_name = spc.name
67
+
68
+ self.instance_variable_get(:"@#{spc_name}_statement").publish(event, orig, args)
69
+
70
+ write_attribute(spc_name, self.send(:"#{spc_name}"))
71
+ if has_attribute?("#{spc_name}_prev")
72
+ write_attribute("#{spc_name}_prev", self.send(:"#{spc_name}_prev"))
73
+ end
74
+ end
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,65 @@
1
+ module Switcher
2
+ module Object
3
+ module Initializer
4
+ def new(*args, &block)
5
+ instance = allocate
6
+ instance.instance_eval { initialize(*args, &block) }
7
+ instance.class.class_variable_get(:@@__specs__).each do |spc| # TODO - move to something like Switcher.initial_statement(instance)
8
+ instance.instance_variable_set(:"@#{spc.name}_statement", Statement.new(instance, spc))
9
+ end
10
+ instance
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ def switcher_pre_initialize(inst, spec)
16
+ inst.class_eval do
17
+ spec_name = spec.name
18
+
19
+ define_method(:"#{spec_name}_spec") { spec }
20
+ define_method(:"#{spec_name}_prev") { self.instance_variable_get(:"@#{spec_name}_statement").state_prev }
21
+
22
+ define_method(:"#{spec_name}") { self.instance_variable_get(:"@#{spec_name}_statement").state_current }
23
+
24
+ events = []
25
+
26
+ spec.states.each_pair do |state_name, state|
27
+ define_method(:"#{spec_name}_#{state_name}?") do
28
+ state_name == self.send(:"#{spec_name}")
29
+ end
30
+
31
+ events << state.event_names
32
+ end
33
+
34
+ events.flatten.each do |event_name|
35
+ define_method(:"can_#{event_name}?") do
36
+ self.class.class_variable_get(:@@__specs__).map { |spc|
37
+ spc.states[self.send(:"#{spc.name}")].event_names.include?(event_name)
38
+ }.include?(true)
39
+ end
40
+
41
+ define_method(:"#{event_name}!") do |*args|
42
+ switch(event_name, *args)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def self.included(base)
50
+ base.extend Switcher::ClassMethods
51
+ base.extend Switcher::Object::Initializer
52
+ base.extend Switcher::Object::ClassMethods
53
+ end
54
+
55
+ def switch(event, *args)
56
+ switch_from(self.class, event, *args)
57
+ end
58
+
59
+ def switch_from(orig, event, *args)
60
+ self.class.class_variable_get(:@@__specs__).each do |spc|
61
+ self.instance_variable_get(:"@#{spc.name}_statement").publish(event, orig, args)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,15 +1,18 @@
1
1
  module Switcher
2
2
  class Facade
3
- def initialize(data, target_state=nil)
3
+ def initialize(target, data)
4
4
  @args = data
5
5
  @stopped = false
6
- @target_state = target_state
6
+ @bubble_cancelled = false
7
+ @target_state = nil
8
+ @target = target
7
9
  end
8
10
 
9
- attr_reader :args, :stopped, :target_state
11
+ attr_reader :args, :stopped, :target, :target_state, :bubble_cancelled
10
12
 
11
13
  def stop
12
14
  @stopped = true
15
+ @bubble_cancelled = true
13
16
  end
14
17
 
15
18
  alias :restrict :stop
@@ -17,5 +20,9 @@ module Switcher
17
20
  def switch_to(state)
18
21
  @target_state = state.to_sym
19
22
  end
23
+
24
+ def cancel_bubble
25
+ @bubble_cancelled = true
26
+ end
20
27
  end
21
28
  end
data/lib/switcher/spec.rb CHANGED
@@ -7,10 +7,10 @@ module Switcher
7
7
  @name = name.to_sym
8
8
  @states = {}
9
9
  @states_list = []
10
- @state_prev = nil
10
+ @targets = []
11
11
  end
12
12
 
13
- attr_reader :name, :states, :state_prev
13
+ attr_reader :name, :states, :states_list, :targets
14
14
 
15
15
  def state(name, &block)
16
16
  listener = Listener.new(name)
@@ -20,26 +20,9 @@ module Switcher
20
20
  @states[name.to_sym] = listener
21
21
  end
22
22
 
23
- def current_state
24
- @current_state || @states_list.first
23
+ def notify(*list)
24
+ @targets = list
25
25
  end
26
26
 
27
- def publish(current_state, event, instance, args)
28
- facade = Facade.new(args)
29
- ["before_#{event}", event.to_s].each do |ev|
30
- @states[current_state.to_sym].trigger(ev, facade, instance, args)
31
- end
32
- set_state(facade)
33
- @states[current_state.to_sym].trigger("after_#{event}", facade, instance, args)
34
- end
35
-
36
- private
37
-
38
- def set_state(facade)
39
- unless facade.stopped && facade.target_state.nil?
40
- @state_prev = @current_state
41
- @current_state = facade.target_state.to_sym
42
- end
43
- end
44
27
  end
45
28
  end
@@ -0,0 +1,64 @@
1
+ module Switcher
2
+ class Statement
3
+ def initialize(instance, spec, state_current=nil, state_prev=nil)
4
+ @instance = instance
5
+
6
+ @spec = spec
7
+
8
+ @state_prev = state_prev
9
+ @state_current = state_current
10
+ end
11
+
12
+ attr_reader :name, :states
13
+
14
+ def state_current
15
+ (@state_current || @spec.states_list.first).to_sym
16
+ end
17
+
18
+ def state_prev
19
+ @state_prev ? @state_prev.to_sym : nil
20
+ end
21
+
22
+ def publish(event, original, args)
23
+ states = @spec.states
24
+ state = state_current
25
+
26
+ return unless states.has_key?(state)
27
+
28
+ facade = Facade.new(original, args)
29
+
30
+ ["before_#{event}", event.to_s].each do |ev|
31
+ states[state].trigger(ev, facade, @instance, args)
32
+ end
33
+
34
+ set_state(facade)
35
+
36
+ states[state].trigger("after_#{event}", facade, @instance, args)
37
+
38
+ return if facade.bubble_cancelled
39
+
40
+ if @spec.targets.length > 0
41
+ @spec.targets.each do |target|
42
+ target_instance = @instance.send(target.to_sym)
43
+ if target_instance.respond_to?(:each)
44
+ target_instance.each do |ti|
45
+ ti.switch_from(original, event, args) if ti.respond_to?(:switch_from)
46
+ end
47
+ else
48
+ target_instance.switch_from(original, event, args) if target_instance.respond_to?(:switch_from)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def set_state(facade)
57
+ unless facade.stopped || facade.target_state.nil?
58
+ @state_prev = state_current
59
+ @state_current = facade.target_state.to_sym
60
+ end
61
+ end
62
+
63
+ end
64
+ end
@@ -1,3 +1,3 @@
1
1
  module Switcher
2
- VERSION = "0.0.1.beta"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/switcher.rb CHANGED
@@ -1,7 +1,20 @@
1
1
  require "switcher/version"
2
+ require 'switcher/spec'
3
+ require 'switcher/statement'
4
+ require 'switcher/adapters/object'
5
+ require 'switcher/adapters/active_record'
2
6
 
3
7
  module Switcher
4
- # Your code goes here...
5
- end
8
+ module ClassMethods
9
+ def switcher(name, options={}, &block)
10
+ self.class_variable_defined?(:@@__specs__) or self.class_variable_set(:@@__specs__, [])
6
11
 
7
- require 'switcher/machine'
12
+ spec = Spec.new(name, options)
13
+ spec.instance_eval(&block)
14
+
15
+ self.class_variable_get(:@@__specs__) << spec
16
+
17
+ switcher_pre_initialize(self, spec)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ class Airbag
2
+ include Switcher::Object
3
+
4
+ def initialize
5
+ @crashed_from = nil
6
+ end
7
+
8
+ attr_reader :crashed_from
9
+
10
+ switcher :state do
11
+ state :idle do
12
+ on :crash, switch_to: :deployed do |ev|
13
+ @crashed_from = ev.target
14
+ end
15
+ end
16
+
17
+ state :deployed
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ require 'active_record'
2
+
3
+ class Box < ActiveRecord::Base
4
+ include Switcher::ActiveRecord
5
+
6
+ switcher :state do
7
+ state :requested do
8
+ on :produce, switch_to: :produced
9
+ end
10
+
11
+ state :produced do
12
+ on :break, switch_to: :broken
13
+ end
14
+
15
+ state :broken
16
+ end
17
+ end
@@ -0,0 +1,37 @@
1
+ class Car
2
+ include Switcher::Object
3
+
4
+ attr_accessor :airbag, :airbags
5
+
6
+ switcher :state do
7
+ notify :airbag, :airbags
8
+
9
+ state :new do
10
+ on :buy, switch_to: :buyed
11
+ end
12
+
13
+ state :buyed do
14
+ on :start, switch_to: :used
15
+ end
16
+
17
+ state :used do
18
+ on :crash, switch_to: :damaged do |ev, fast=true|
19
+ ev.cancel_bubble unless fast
20
+ end
21
+ end
22
+
23
+ state :damaged do
24
+ on :repair, switch_to: :used
25
+ end
26
+ end
27
+
28
+ switcher :movement do
29
+ state :stands do
30
+ on :start, switch_to: :moves
31
+ end
32
+
33
+ state :moves do
34
+ on :stop, switch_to: :stands
35
+ end
36
+ end
37
+ end
@@ -1,5 +1,5 @@
1
1
  class Human
2
- include Switcher::Machine
2
+ include Switcher::Object
3
3
 
4
4
  def initialize
5
5
  @easy_to_fight = false
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+ require 'active_record'
3
+ require 'fixtures/box'
4
+ require 'fileutils'
5
+
6
+ describe Switcher::ActiveRecord do
7
+ before :all do
8
+ FileUtils.rm("test.sqlite3") if File.exists?("test.sqlite3")
9
+
10
+ ActiveRecord::Migration.verbose = false
11
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "test.sqlite3")
12
+
13
+ ActiveRecord::Schema.define do
14
+ create_table :boxes do |t|
15
+ t.string :type
16
+ t.string :state
17
+ t.string :state_prev
18
+ end
19
+ end
20
+ end
21
+
22
+ after :all do
23
+ FileUtils.rm("test.sqlite3")
24
+ end
25
+
26
+ let(:box) do
27
+ Box.new
28
+ end
29
+
30
+ it "should have initial state" do
31
+ box.state.should eq(:requested)
32
+ end
33
+
34
+ it "should switch states" do
35
+ box.produce!
36
+
37
+ box.state.should eq(:produced)
38
+ end
39
+
40
+ it "should save states" do
41
+ box.produce!
42
+ box.save
43
+
44
+ box.reload
45
+
46
+ box.state.should eq(:produced)
47
+ end
48
+
49
+ it "should save previous states if possible" do
50
+ box.produce!
51
+ box.save
52
+
53
+ box.reload
54
+
55
+ box.state_prev.should eq(:requested)
56
+ end
57
+
58
+ it "should save last defined state" do
59
+ box.produce!
60
+ box.break!
61
+ box.save
62
+
63
+ box.reload
64
+
65
+ box.state.should eq(:broken)
66
+ end
67
+
68
+ it "can detect possibility to switch" do
69
+ box.can_produce?.should be_true
70
+ box.can_break?.should be_false
71
+ end
72
+
73
+ it "should have state predicate" do
74
+ box.state_requested?.should be_true
75
+ box.state_produced?.should be_false
76
+ end
77
+ end
@@ -1,54 +1,158 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  require 'fixtures/human'
4
+ require 'fixtures/car'
5
+ require 'fixtures/airbag'
4
6
 
5
7
  describe Switcher do
6
8
 
7
9
  describe "Basic functionality" do
8
- people = Human.new
10
+ person = Human.new
11
+
12
+ it "should have initial state" do
13
+ person.state.should eq(:newborn)
14
+ end
9
15
 
10
16
  it "should switch to state with option" do
11
- people.go_to_school!
12
- people.state.should eq(:scholar)
17
+ person.go_to_school!
18
+
19
+ person.state.should eq(:scholar)
13
20
  end
14
21
 
15
22
  it "should switch to state by internal condition" do
16
- people.finish_school!(2)
17
- people.state.should eq(:worker)
23
+ person.finish_school!(2)
24
+
25
+ person.state.should eq(:worker)
18
26
  end
19
27
 
20
28
  it "should have before and after callbacks" do
21
- people.full_age!(100)
22
- people.fight!(true)
29
+ person.full_age!(100)
30
+ person.fight!(true)
23
31
 
24
- people.state.should eq(:civil)
25
- people.easy_to_fight.should be_true
26
- people.honor.should eq(100)
32
+ person.state.should eq(:civil)
33
+ person.easy_to_fight.should be_true
34
+ person.honor.should eq(100)
27
35
  end
28
36
 
29
37
  it "can prevent switch" do
30
- people.find_job!(false)
31
- people.state.should eq(:civil)
38
+ person.find_job!(false)
39
+
40
+ person.state.should eq(:civil)
32
41
  end
33
42
 
34
43
  it "and can not" do
35
- people.find_job!(true)
36
- people.state.should eq(:manager)
44
+ person.switch("find_job", true)
45
+
46
+ person.state.should eq(:manager)
37
47
  end
38
48
 
39
49
  it "can detect possibility to switch" do
40
- people.can_fight?.should be_false
41
- people.can_die?.should be_true
50
+ person.can_fight?.should be_false
51
+ person.can_die?.should be_true
42
52
  end
43
53
 
44
54
  it "should remember previous state" do
45
- people.state_prev.should eq(:civil)
55
+ person.state_prev.should eq(:civil)
46
56
  end
47
57
 
48
58
  it "should have state predicate" do
49
- people.manager?.should be_true
50
- people.civil?.should be_false
59
+ person.state_manager?.should be_true
60
+ person.state_civil?.should be_false
61
+ end
62
+ end
63
+
64
+ describe "Multiple states" do
65
+ car = Car.new
66
+
67
+ it "should have different states" do
68
+ car.state.should eq(:new)
69
+ car.movement.should eq(:stands)
70
+ end
71
+
72
+ it "should switch states" do
73
+ car.buy!
74
+
75
+ car.state.should eq(:buyed)
76
+ car.movement.should eq(:stands)
77
+ end
78
+
79
+ it "should have cross-stated events" do
80
+ car.start!
81
+
82
+ car.state.should eq(:used)
83
+ car.movement.should eq(:moves)
84
+ end
85
+
86
+ it "should remember previous states" do
87
+ car.state_prev.should eq(:buyed)
88
+ car.movement_prev.should eq(:stands)
89
+ end
90
+
91
+ it "should have state predicates" do
92
+ car.state_used?.should be_true
93
+ car.state_new?.should be_false
94
+
95
+ car.movement_moves?.should be_true
96
+ car.movement_stands?.should be_false
51
97
  end
52
98
  end
53
99
 
100
+ describe "Multiple instances" do
101
+ car_one = Car.new
102
+ car_two = Car.new
103
+
104
+ it "should work independent" do
105
+ car_one.buy!
106
+
107
+ car_two.state.should eq(:new)
108
+ end
109
+ end
110
+
111
+ describe "Event bubbling" do
112
+ it "should bubble event" do
113
+ car = Car.new
114
+ car.airbag = Airbag.new
115
+
116
+ car.buy!
117
+ car.start!
118
+ car.crash!
119
+
120
+ car.airbag.state.should eq(:deployed)
121
+ end
122
+
123
+ it "should bubble event to collections" do
124
+ car = Car.new
125
+ car.airbags = [Airbag.new, Airbag.new]
126
+
127
+ car.buy!
128
+ car.start!
129
+ car.crash!
130
+
131
+ car.airbags.each do |ab|
132
+ ab.state.should eq(:deployed)
133
+ end
134
+ end
135
+
136
+ it "should define original target" do
137
+ car = Car.new
138
+ car.airbag = Airbag.new
139
+
140
+ car.buy!
141
+ car.start!
142
+ car.crash!
143
+
144
+ car.airbag.crashed_from.to_s.should eq("Car")
145
+ end
146
+
147
+ it "should cancel bubble" do
148
+ car = Car.new
149
+ car.airbag = Airbag.new
150
+
151
+ car.buy!
152
+ car.start!
153
+ car.crash!(false)
154
+
155
+ car.airbag.state.should eq(:idle)
156
+ end
157
+ end
54
158
  end
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: switcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.beta
5
- prerelease: 6
4
+ version: 0.1.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Andrey Savchenko
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-28 00:00:00.000000000Z
12
+ date: 2012-03-04 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &70269386935900 !ruby/object:Gem::Requirement
16
+ requirement: &70251275731280 !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: *70269386935900
24
+ version_requirements: *70251275731280
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70269386935320 !ruby/object:Gem::Requirement
27
+ requirement: &70251275730860 !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: *70269386935320
35
+ version_requirements: *70251275730860
36
36
  description: Switcher is simple, event-driven state machine
37
37
  email:
38
38
  - andrey@aejis.eu
@@ -42,16 +42,23 @@ extra_rdoc_files: []
42
42
  files:
43
43
  - .gitignore
44
44
  - Gemfile
45
+ - Guardfile
45
46
  - README.md
46
47
  - Rakefile
47
48
  - lib/switcher.rb
49
+ - lib/switcher/adapters/active_record.rb
50
+ - lib/switcher/adapters/object.rb
48
51
  - lib/switcher/facade.rb
49
52
  - lib/switcher/listener.rb
50
- - lib/switcher/machine.rb
51
53
  - lib/switcher/spec.rb
54
+ - lib/switcher/statement.rb
52
55
  - lib/switcher/version.rb
56
+ - spec/fixtures/airbag.rb
57
+ - spec/fixtures/box.rb
58
+ - spec/fixtures/car.rb
53
59
  - spec/fixtures/human.rb
54
60
  - spec/spec_helper.rb
61
+ - spec/switcher_active_record_spec.rb
55
62
  - spec/switcher_spec.rb
56
63
  - switcher.gemspec
57
64
  homepage: https://github.com/Ptico/switcher
@@ -69,9 +76,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
69
76
  required_rubygems_version: !ruby/object:Gem::Requirement
70
77
  none: false
71
78
  requirements:
72
- - - ! '>'
79
+ - - ! '>='
73
80
  - !ruby/object:Gem::Version
74
- version: 1.3.1
81
+ version: '0'
75
82
  requirements: []
76
83
  rubyforge_project: switcher
77
84
  rubygems_version: 1.8.6
@@ -79,6 +86,10 @@ signing_key:
79
86
  specification_version: 3
80
87
  summary: Switcher is simple, event-driven state machine
81
88
  test_files:
89
+ - spec/fixtures/airbag.rb
90
+ - spec/fixtures/box.rb
91
+ - spec/fixtures/car.rb
82
92
  - spec/fixtures/human.rb
83
93
  - spec/spec_helper.rb
94
+ - spec/switcher_active_record_spec.rb
84
95
  - spec/switcher_spec.rb
@@ -1,43 +0,0 @@
1
- require 'switcher/spec'
2
-
3
- module Switcher
4
- module Machine
5
-
6
- module ClassMethods
7
- def switcher(name, options={}, &block)
8
- self.class_variable_defined?(:@@__specs__) or self.class_variable_set(:@@__specs__, [])
9
-
10
- spec = Spec.new(name, options)
11
- spec.instance_eval(&block)
12
-
13
- self.class_variable_get(:@@__specs__) << spec # dup and destroy?
14
-
15
- self.class_eval do
16
- define_method(:"#{spec.name}_spec") { spec }
17
- define_method(:"#{spec.name}_prev") { spec.state_prev }
18
-
19
- define_method(:"#{spec.name}") { spec.current_state }
20
-
21
- events = []
22
-
23
- spec.states.each_pair do |state_name, state|
24
- define_method(:"#{state_name}?") { state_name == self.send(:"#{spec.name}") }
25
-
26
- events << state.event_names
27
- end
28
-
29
- events.flatten.each do |event_name|
30
- define_method(:"can_#{event_name}?") { spec.states[self.send(:"#{spec.name}")].event_names.include?(event_name) }
31
- define_method(:"#{event_name}!") do |*args|
32
- spec.publish(self.send(:"#{spec.name}"), event_name, self, args)
33
- end
34
- end
35
- end
36
- end
37
- end
38
-
39
- def self.included(base)
40
- base.extend ClassMethods
41
- end
42
- end
43
- end