statemachine 1.2.0 → 2.0.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/.gitignore +12 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +12 -0
- data/Rakefile +21 -20
- data/doc/website/README +6 -0
- data/doc/website/config.yaml +2 -0
- data/doc/website/index.html +39 -0
- data/doc/website/src/default.css +110 -0
- data/doc/website/src/default.template +41 -0
- data/doc/website/src/documentation.page +45 -0
- data/doc/website/src/example.page +13 -0
- data/doc/website/src/example1.page +59 -0
- data/doc/website/src/example2.page +96 -0
- data/doc/website/src/example3.page +76 -0
- data/doc/website/src/example4.page +65 -0
- data/doc/website/src/images/8l_star.png +0 -0
- data/doc/website/src/images/bg.png +0 -0
- data/doc/website/src/images/bottom_border.png +0 -0
- data/doc/website/src/images/bottom_edge.png +0 -0
- data/doc/website/src/images/examples/vending_machine.png +0 -0
- data/doc/website/src/images/examples/vending_machine2.png +0 -0
- data/doc/website/src/images/examples/vending_machine3.png +0 -0
- data/doc/website/src/images/examples/vending_machine4a.png +0 -0
- data/doc/website/src/images/examples/vending_machine4b.png +0 -0
- data/doc/website/src/images/left_edge.png +0 -0
- data/doc/website/src/images/logo.png +0 -0
- data/doc/website/src/images/right_edge.png +0 -0
- data/doc/website/src/images/side_borders.png +0 -0
- data/doc/website/src/index.page +20 -0
- data/generate_tests/dot_graph/turnstile.rb +29 -0
- data/generate_tests/dot_graph/turnstile2.rb +48 -0
- data/generate_tests/java/turnstile.rb +54 -0
- data/generate_tests/java/turnstile2.rb +72 -0
- data/lib/statemachine/action_invokation.rb +0 -19
- data/lib/statemachine/version.rb +2 -2
- data/rails_plugin/README +183 -0
- data/rails_plugin/Rakefile +11 -0
- data/rails_plugin/app/controllers/admin_controller.rb +43 -0
- data/rails_plugin/app/controllers/application.rb +7 -0
- data/rails_plugin/app/controllers/main_controller.rb +34 -0
- data/rails_plugin/app/helpers/admin_helper.rb +2 -0
- data/rails_plugin/app/helpers/application_helper.rb +3 -0
- data/rails_plugin/app/helpers/main_helper.rb +7 -0
- data/rails_plugin/app/models/product.rb +21 -0
- data/rails_plugin/app/models/vending_machine.rb +36 -0
- data/rails_plugin/app/models/vending_machine_interface.rb +101 -0
- data/rails_plugin/app/models/vending_statemachine.rb +41 -0
- data/rails_plugin/app/views/admin/index.rhtml +45 -0
- data/rails_plugin/app/views/layouts/main.rhtml +12 -0
- data/rails_plugin/app/views/main/_info.rhtml +13 -0
- data/rails_plugin/app/views/main/event.rjs +27 -0
- data/rails_plugin/app/views/main/index.rhtml +38 -0
- data/rails_plugin/config/boot.rb +45 -0
- data/rails_plugin/config/database.yml +21 -0
- data/rails_plugin/config/environment.rb +60 -0
- data/rails_plugin/config/environments/development.rb +21 -0
- data/rails_plugin/config/environments/production.rb +18 -0
- data/rails_plugin/config/environments/test.rb +19 -0
- data/rails_plugin/config/routes.rb +23 -0
- data/rails_plugin/db/migrate/001_create_vending_machines.rb +12 -0
- data/rails_plugin/db/migrate/002_create_products.rb +14 -0
- data/rails_plugin/db/migrate/003_add_position_to_products.rb +9 -0
- data/rails_plugin/db/schema.rb +20 -0
- data/rails_plugin/doc/README_FOR_APP +2 -0
- data/rails_plugin/lib/tasks/base.rake +5 -0
- data/rails_plugin/public/.htaccess +40 -0
- data/rails_plugin/public/404.html +30 -0
- data/rails_plugin/public/500.html +30 -0
- data/rails_plugin/public/dispatch.cgi +10 -0
- data/rails_plugin/public/dispatch.fcgi +24 -0
- data/rails_plugin/public/dispatch.rb +10 -0
- data/rails_plugin/public/favicon.ico +0 -0
- data/rails_plugin/public/images/bricks.jpg +0 -0
- data/rails_plugin/public/images/cash_release_button.png +0 -0
- data/rails_plugin/public/images/change.png +0 -0
- data/rails_plugin/public/images/dime.png +0 -0
- data/rails_plugin/public/images/dollar.png +0 -0
- data/rails_plugin/public/images/money_panel.png +0 -0
- data/rails_plugin/public/images/nickel.png +0 -0
- data/rails_plugin/public/images/quarter.png +0 -0
- data/rails_plugin/public/images/rails.png +0 -0
- data/rails_plugin/public/images/vending_machine_body.png +0 -0
- data/rails_plugin/public/index.html +277 -0
- data/rails_plugin/public/javascripts/application.js +2 -0
- data/rails_plugin/public/javascripts/controls.js +833 -0
- data/rails_plugin/public/javascripts/dragdrop.js +942 -0
- data/rails_plugin/public/javascripts/effects.js +1088 -0
- data/rails_plugin/public/javascripts/prototype.js +2515 -0
- data/rails_plugin/public/robots.txt +1 -0
- data/rails_plugin/public/stylesheets/layout.css +163 -0
- data/rails_plugin/script/about +3 -0
- data/rails_plugin/script/breakpointer +3 -0
- data/rails_plugin/script/console +3 -0
- data/rails_plugin/script/destroy +3 -0
- data/rails_plugin/script/generate +3 -0
- data/rails_plugin/script/performance/benchmarker +3 -0
- data/rails_plugin/script/performance/profiler +3 -0
- data/rails_plugin/script/plugin +3 -0
- data/rails_plugin/script/process/inspector +3 -0
- data/rails_plugin/script/process/reaper +3 -0
- data/rails_plugin/script/process/spawner +3 -0
- data/rails_plugin/script/rails_spec +11 -0
- data/rails_plugin/script/rails_spec_server +43 -0
- data/rails_plugin/script/runner +3 -0
- data/rails_plugin/script/server +3 -0
- data/rails_plugin/spec/controllers/admin_controller_spec.rb +7 -0
- data/rails_plugin/spec/controllers/main_controller_spec.rb +38 -0
- data/rails_plugin/spec/fixtures/products.yml +2 -0
- data/rails_plugin/spec/fixtures/vending_machines.yml +2 -0
- data/rails_plugin/spec/helpers/admin_helper_spec.rb +5 -0
- data/rails_plugin/spec/helpers/main_helper_spec.rb +5 -0
- data/rails_plugin/spec/models/product_spec.rb +16 -0
- data/rails_plugin/spec/models/vending_machine_interface_spec.rb +168 -0
- data/rails_plugin/spec/models/vending_machine_spec.rb +30 -0
- data/rails_plugin/spec/models/vending_statemachine_spec.rb +130 -0
- data/rails_plugin/spec/plugins/context_support_spec.rb +28 -0
- data/rails_plugin/spec/plugins/controller_support_spec.rb +98 -0
- data/rails_plugin/spec/spec.opts +1 -0
- data/rails_plugin/spec/spec_helper.rb +28 -0
- data/rails_plugin/spec/views/main/event_spec.rb +61 -0
- data/rails_plugin/vendor/plugins/statemachine/README +4 -0
- data/rails_plugin/vendor/plugins/statemachine/Rakefile +22 -0
- data/rails_plugin/vendor/plugins/statemachine/init.rb +2 -0
- data/rails_plugin/vendor/plugins/statemachine/install.rb +1 -0
- data/rails_plugin/vendor/plugins/statemachine/lib/context_support.rb +22 -0
- data/rails_plugin/vendor/plugins/statemachine/lib/controller_support.rb +85 -0
- data/rails_plugin/vendor/plugins/statemachine/lib/statemachine_on_rails.rb +11 -0
- data/rails_plugin/vendor/plugins/statemachine/tasks/statemachine_tasks.rake +4 -0
- data/spec/action_invokation_spec.rb +1 -1
- data/spec/builder_spec.rb +1 -1
- data/spec/default_transition_spec.rb +1 -1
- data/spec/generate/dot_graph/dot_graph_stagemachine_spec.rb +1 -1
- data/spec/history_spec.rb +1 -1
- data/spec/sm_action_parameterization_spec.rb +1 -1
- data/spec/sm_entry_exit_actions_spec.rb +1 -1
- data/spec/sm_odds_n_ends_spec.rb +1 -1
- data/spec/sm_simple_spec.rb +1 -1
- data/spec/sm_super_state_spec.rb +1 -1
- data/spec/sm_turnstile_spec.rb +1 -1
- data/spec/transition_spec.rb +1 -1
- metadata +162 -46
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "VendingMachine class with fixtures loaded" do
|
|
4
|
+
fixtures :vending_machines, :products
|
|
5
|
+
|
|
6
|
+
before(:each) do
|
|
7
|
+
@vm = VendingMachine.new(:location => "Cafeteria", :cash => 1000)
|
|
8
|
+
@vm.save!
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "Create with location and cash" do
|
|
12
|
+
loaded = VendingMachine.find(@vm.id)
|
|
13
|
+
loaded.location.should eql("Cafeteria")
|
|
14
|
+
loaded.cash.should equal 1000
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "Add rack" do
|
|
18
|
+
@vm.add_product(20, "Water", 150)
|
|
19
|
+
@vm.save!
|
|
20
|
+
|
|
21
|
+
loaded = VendingMachine.find(@vm.id)
|
|
22
|
+
loaded.products.length.should equal 1
|
|
23
|
+
loaded.products[0].name.should eql("Water")
|
|
24
|
+
loaded.products[0].price.should eql(150)
|
|
25
|
+
loaded.products[0].inventory.should equal 20
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "Vending Statemachine" do
|
|
4
|
+
|
|
5
|
+
before(:each) do
|
|
6
|
+
@sm = VendingStatemachine.statemachine
|
|
7
|
+
@context = mock("context")
|
|
8
|
+
@sm.context = @context
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "start state" do
|
|
12
|
+
@sm.state.should equal :standby
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "dollar" do
|
|
16
|
+
check_money_event(:dollar, :add_dollar)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "quarter" do
|
|
20
|
+
check_money_event(:quarter, :add_quarter)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "dime" do
|
|
24
|
+
check_money_event(:dime, :add_dime)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "nickel" do
|
|
28
|
+
check_money_event(:nickel, :add_nickel)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def check_money_event(event, method)
|
|
32
|
+
@context.should_receive(:clear_dispensers)
|
|
33
|
+
@context.should_receive(method)
|
|
34
|
+
@context.should_receive(:check_max_price)
|
|
35
|
+
|
|
36
|
+
@sm.process_event(event)
|
|
37
|
+
@sm.state.should equal :collecting_money
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "standby selection" do
|
|
41
|
+
@context.should_receive(:clear_dispensers)
|
|
42
|
+
|
|
43
|
+
@sm.selection
|
|
44
|
+
@sm.state.should equal :standby
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "standby collecting_money" do
|
|
48
|
+
@sm.state = :collecting_money
|
|
49
|
+
@context.should_receive(:load_product).with(123)
|
|
50
|
+
@context.should_receive(:check_affordability)
|
|
51
|
+
|
|
52
|
+
@sm.selection(123)
|
|
53
|
+
@sm.state.should equal :validating_purchase
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "reached max_price" do
|
|
57
|
+
@sm.state = :collecting_money
|
|
58
|
+
@context.should_receive(:refuse_money)
|
|
59
|
+
|
|
60
|
+
@sm.reached_max_price
|
|
61
|
+
@sm.state.should equal :max_price_tendered
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "accept purchase" do
|
|
65
|
+
@sm.state = :validating_purchase
|
|
66
|
+
@context.should_receive(:make_sale)
|
|
67
|
+
|
|
68
|
+
@sm.accept_purchase
|
|
69
|
+
@sm.state.should equal :standby
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "refuse purchase" do
|
|
73
|
+
@sm.state = :validating_purchase
|
|
74
|
+
@context.should_receive(:check_max_price)
|
|
75
|
+
|
|
76
|
+
@sm.refuse_purchase
|
|
77
|
+
@sm.state.should equal :collecting_money
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "money in max_price_tendered" do
|
|
81
|
+
@sm.state = :max_price_tendered
|
|
82
|
+
|
|
83
|
+
@sm.dollar
|
|
84
|
+
@sm.state.should equal :max_price_tendered
|
|
85
|
+
@sm.quarter
|
|
86
|
+
@sm.state.should equal :max_price_tendered
|
|
87
|
+
@sm.dime
|
|
88
|
+
@sm.state.should equal :max_price_tendered
|
|
89
|
+
@sm.nickel
|
|
90
|
+
@sm.state.should equal :max_price_tendered
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "selection in max_price_tendered" do
|
|
94
|
+
@sm.state = :max_price_tendered
|
|
95
|
+
@context.should_receive(:load_and_make_sale).with(123)
|
|
96
|
+
@context.should_receive(:accept_money)
|
|
97
|
+
|
|
98
|
+
@sm.selection(123)
|
|
99
|
+
@sm.state.should equal :standby
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "release money from standby" do
|
|
103
|
+
@context.should_receive(:clear_dispensers)
|
|
104
|
+
@sm.release_money
|
|
105
|
+
|
|
106
|
+
@sm.state.should equal :standby
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "release money from collecting money" do
|
|
110
|
+
@sm.state = :collecting_money
|
|
111
|
+
@context.should_receive(:dispense_change)
|
|
112
|
+
|
|
113
|
+
@sm.release_money
|
|
114
|
+
|
|
115
|
+
@sm.state.should equal :standby
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "release money from max_price_tendered" do
|
|
119
|
+
@sm.state = :max_price_tendered
|
|
120
|
+
@context.should_receive(:dispense_change)
|
|
121
|
+
@context.should_receive(:accept_money)
|
|
122
|
+
|
|
123
|
+
@sm.release_money
|
|
124
|
+
|
|
125
|
+
@sm.state.should equal :standby
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "Context Support" do
|
|
4
|
+
|
|
5
|
+
before(:each) do
|
|
6
|
+
@product = Product.new(:name => "Chicken")
|
|
7
|
+
@product.save!
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "Active Recrods Marshalled Compact" do
|
|
11
|
+
io = StringIO.new("")
|
|
12
|
+
Marshal.dump(@product, io)
|
|
13
|
+
io.rewind
|
|
14
|
+
|
|
15
|
+
dump = io.read
|
|
16
|
+
dump.should_not include("Chicken")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "Active Records Loaded properly" do
|
|
20
|
+
io = StringIO.new("")
|
|
21
|
+
Marshal.dump(@product, io)
|
|
22
|
+
io.rewind
|
|
23
|
+
|
|
24
|
+
loaded = Marshal.load(io)
|
|
25
|
+
loaded.name.should eql("Chicken")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
class SampleContext
|
|
4
|
+
include Statemachine::ContextSupport
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class SampleStatemachine
|
|
8
|
+
attr_accessor :context
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class SampleController
|
|
12
|
+
|
|
13
|
+
include Statemachine::ControllerSupport
|
|
14
|
+
supported_by_statemachine SampleContext, lambda { SampleStatemachine.new }
|
|
15
|
+
|
|
16
|
+
attr_accessor :session, :initialized, :before, :after, :params, :can_continue
|
|
17
|
+
|
|
18
|
+
def initialize(statemachine, context)
|
|
19
|
+
@statemachine = statemachine
|
|
20
|
+
@context = context
|
|
21
|
+
@session = {}
|
|
22
|
+
@can_continue = true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def initialize_context(*args)
|
|
26
|
+
@initialized = true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def before_event
|
|
30
|
+
@before = true
|
|
31
|
+
return @can_continue
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def after_event
|
|
35
|
+
@after = true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe "State Machine Support" do
|
|
41
|
+
|
|
42
|
+
before(:each) do
|
|
43
|
+
@statemachine = mock("statemachine")
|
|
44
|
+
@context = mock("context")
|
|
45
|
+
@controller = SampleController.new(@statemachine, @context)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "save state" do
|
|
49
|
+
@controller.send(:save_state)
|
|
50
|
+
|
|
51
|
+
@controller.session[:samplecontroller_state].should equal @context
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "recall state" do
|
|
55
|
+
@controller.session[:samplecontroller_state] = @context
|
|
56
|
+
@context.should_receive(:statemachine)
|
|
57
|
+
|
|
58
|
+
@controller.send(:recall_state)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "new context" do
|
|
62
|
+
@controller = SampleController.new(nil, nil)
|
|
63
|
+
@controller.send(:new_context)
|
|
64
|
+
|
|
65
|
+
@controller.context.should_not equal nil
|
|
66
|
+
@controller.statemachine.should_not equal nil
|
|
67
|
+
@controller.context.statemachine.should equal @controller.statemachine
|
|
68
|
+
@controller.statemachine.context.should equal @controller.context
|
|
69
|
+
@controller.initialized.should equal true
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "event action" do
|
|
73
|
+
@controller.params = {:event => "boo"}
|
|
74
|
+
@controller.session[:samplecontroller_state] = @context
|
|
75
|
+
@context.should_receive(:statemachine).and_return(@statemachine)
|
|
76
|
+
@statemachine.should_receive(:process_event).with("boo", nil)
|
|
77
|
+
|
|
78
|
+
@controller.event
|
|
79
|
+
|
|
80
|
+
@controller.before.should equal true
|
|
81
|
+
@controller.after.should equal true
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "the event is not invoked is before_event return false" do
|
|
85
|
+
@controller.params = {:event => "boo"}
|
|
86
|
+
@controller.session[:samplecontroller_state] = @context
|
|
87
|
+
@context.should_not_receive(:statemachine)
|
|
88
|
+
@statemachine.should_not_receive(:process_event)
|
|
89
|
+
@controller.can_continue = false
|
|
90
|
+
|
|
91
|
+
@controller.event
|
|
92
|
+
|
|
93
|
+
@controller.before.should equal true
|
|
94
|
+
@controller.after.should_not equal true
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--colour
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# This file is copied to ~/spec when you run 'ruby script/generate rspec'
|
|
2
|
+
# from the project root directory.
|
|
3
|
+
ENV["RAILS_ENV"] ||= "test"
|
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
|
|
5
|
+
require 'spec/rails'
|
|
6
|
+
|
|
7
|
+
require 'statemachine_on_rails'
|
|
8
|
+
|
|
9
|
+
# Even if you're using RSpec, RSpec on Rails is reusing some of the
|
|
10
|
+
# Rails-specific extensions for fixtures and stubbed requests, response
|
|
11
|
+
# and other things (via RSpec's inherit mechanism). These extensions are
|
|
12
|
+
# tightly coupled to Test::Unit in Rails, which is why you're seeing it here.
|
|
13
|
+
module Spec
|
|
14
|
+
module Rails
|
|
15
|
+
module Runner
|
|
16
|
+
class EvalContext < Test::Unit::TestCase
|
|
17
|
+
self.use_transactional_fixtures = true
|
|
18
|
+
self.use_instantiated_fixtures = false
|
|
19
|
+
self.fixture_path = RAILS_ROOT + '/spec/fixtures'
|
|
20
|
+
|
|
21
|
+
# You can set up your global fixtures here, or you
|
|
22
|
+
# can do it in individual contexts using "fixtures :table_a, table_b".
|
|
23
|
+
#
|
|
24
|
+
#self.global_fixtures = :table_a, :table_b
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
|
2
|
+
|
|
3
|
+
class Stub
|
|
4
|
+
attr_reader :id
|
|
5
|
+
|
|
6
|
+
def initialize(id)
|
|
7
|
+
@id = id
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe "Event View" do
|
|
12
|
+
|
|
13
|
+
before(:each) do
|
|
14
|
+
@display = mock("display")
|
|
15
|
+
@display.stub!(:message).and_return("$123.45")
|
|
16
|
+
@display.stub!(:affordable_items).and_return([Stub.new(1)])
|
|
17
|
+
@display.stub!(:non_affordable_items).and_return([Stub.new(2)])
|
|
18
|
+
@display.stub!(:sold_out_items).and_return([Stub.new(3)])
|
|
19
|
+
@display.stub!(:dispensed_item).and_return(Product.new(:name => "Milk"))
|
|
20
|
+
@display.stub!(:change).and_return("$0.25")
|
|
21
|
+
|
|
22
|
+
@vending_machine = mock("vending machine")
|
|
23
|
+
@vending_machine.stub!(:location).and_return("Downstairs")
|
|
24
|
+
@vending_machine.stub!(:cash_str).and_return("$10.00")
|
|
25
|
+
@vending_machine.stub!(:products).and_return([])
|
|
26
|
+
|
|
27
|
+
assigns[:context] = @display
|
|
28
|
+
assigns[:vending_machine] = @vending_machine
|
|
29
|
+
|
|
30
|
+
render "/main/event"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "message" do
|
|
34
|
+
response.should have_rjs(:replace_html, "quartz_screen", ")$123.45"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "affordable items" do
|
|
38
|
+
response.body.should include("document.getElementById('product_1').className)= 'affordable'"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "non affordable items" do
|
|
42
|
+
response.body.should include("document.getElementById('product_2').className)= 'non_affordable'"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "sold_out" do
|
|
46
|
+
response.should have_rjs(:replace_html, "product_3_price", ")<span style=\"color: red;\">SOLD OUT</span>"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "dispenser" do
|
|
50
|
+
response.should have_rjs(:replace_html, "dispenser", ")<p>Milk</p>"
|
|
51
|
+
response.should have_rjs(:show, "dispenser")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "change" do
|
|
55
|
+
response.should have_rjs(:replace_html, "change_amount", ")<p>$0.25</p>"
|
|
56
|
+
response.should have_rjs(:show, "change_amount")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'rake'
|
|
2
|
+
require 'rake/testtask'
|
|
3
|
+
require 'rake/rdoctask'
|
|
4
|
+
|
|
5
|
+
desc 'Default: run unit tests.'
|
|
6
|
+
task :default => :test
|
|
7
|
+
|
|
8
|
+
desc 'Test the statemachine_controller plugin.'
|
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
|
10
|
+
t.libs << 'lib'
|
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
|
12
|
+
t.verbose = true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
desc 'Generate documentation for the statemachine_controller plugin.'
|
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
18
|
+
rdoc.title = 'StatemachineController'
|
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
|
20
|
+
rdoc.rdoc_files.include('README')
|
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
22
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Install hook code here
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Statemachine
|
|
2
|
+
|
|
3
|
+
module ContextSupport
|
|
4
|
+
|
|
5
|
+
attr_accessor :statemachine, :context
|
|
6
|
+
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module ActiveRecordMarshalling
|
|
10
|
+
def marshal_dump
|
|
11
|
+
return self.id
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def marshal_load(id)
|
|
15
|
+
@attributes = {}
|
|
16
|
+
@new_record = false
|
|
17
|
+
self.id = id
|
|
18
|
+
self.reload
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require 'statemachine'
|
|
2
|
+
|
|
3
|
+
module Statemachine
|
|
4
|
+
module ControllerSupport
|
|
5
|
+
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.extend SupportMacro
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module SupportMacro
|
|
11
|
+
|
|
12
|
+
def supported_by_statemachine(a_context_class, a_statemachine_creation)
|
|
13
|
+
self.extend ClassMethods
|
|
14
|
+
self.send(:include, InstanceMethods)
|
|
15
|
+
|
|
16
|
+
self.context_class = a_context_class
|
|
17
|
+
self.statemachine_creation = a_statemachine_creation
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module ClassMethods
|
|
23
|
+
|
|
24
|
+
attr_accessor :context_class, :statemachine_creation
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module InstanceMethods
|
|
29
|
+
|
|
30
|
+
attr_reader :statemachine, :context
|
|
31
|
+
|
|
32
|
+
def event
|
|
33
|
+
can_continue = before_event
|
|
34
|
+
if can_continue
|
|
35
|
+
recall_state
|
|
36
|
+
puts "@context_event1: #{@context}"
|
|
37
|
+
event = params[:event]
|
|
38
|
+
arg = params[:arg]
|
|
39
|
+
@statemachine.process_event(event, arg)
|
|
40
|
+
after_event
|
|
41
|
+
save_state
|
|
42
|
+
puts "@context_event2: #{@context}"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
protected
|
|
47
|
+
|
|
48
|
+
def before_event
|
|
49
|
+
return true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def after_event
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def new_context(*args)
|
|
56
|
+
@context = self.class.context_class.new
|
|
57
|
+
puts "@context_new_context: #{@context}"
|
|
58
|
+
@statemachine = self.class.statemachine_creation.call(*args)
|
|
59
|
+
@statemachine.context = @context
|
|
60
|
+
@context.statemachine = @statemachine
|
|
61
|
+
initialize_context(*args)
|
|
62
|
+
save_state
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def save_state
|
|
66
|
+
session[state_session_key] = @context
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def initialize_context(*args)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def prepare_for_render
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def recall_state
|
|
76
|
+
@context = session[state_session_key]
|
|
77
|
+
@statemachine = @context.statemachine
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def state_session_key
|
|
81
|
+
return "#{self.class.name.downcase}_state".to_sym
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|