davidlee-state-fu 0.11.1 → 0.12.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/README.textile +144 -145
- data/lib/binding.rb +40 -28
- data/lib/event.rb +1 -1
- data/lib/executioner.rb +8 -26
- data/lib/interface.rb +12 -14
- data/lib/lathe.rb +19 -2
- data/lib/machine.rb +25 -17
- data/lib/method_factory.rb +21 -20
- data/lib/persistence.rb +9 -9
- data/lib/state-fu.rb +1 -3
- data/lib/support/core_ext.rb +0 -2
- data/lib/support/plotter.rb +0 -1
- data/lib/tasks/spec_last.rake +37 -28
- data/lib/transition.rb +4 -0
- data/lib/transition_query.rb +2 -2
- data/spec/features/machine_alias_spec.rb +46 -0
- data/spec/features/singleton_machine_spec.rb +17 -4
- data/spec/features/when_methods_are_defined_spec.rb +114 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/state_fu_spec.rb +88 -0
- data/spec/units/binding_spec.rb +7 -9
- data/spec/units/machine_spec.rb +5 -8
- metadata +6 -2
data/lib/lathe.rb
CHANGED
|
@@ -58,6 +58,17 @@ module StateFu
|
|
|
58
58
|
machine.lathe
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
+
alias_method :context, :state_or_event
|
|
62
|
+
|
|
63
|
+
def context_state
|
|
64
|
+
state_or_event if state_or_event.is_a?(State)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def context_event
|
|
68
|
+
state_or_event if state_or_event.is_a?(Event)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
61
72
|
#
|
|
62
73
|
# methods for extending the DSL
|
|
63
74
|
#
|
|
@@ -68,11 +79,17 @@ module StateFu
|
|
|
68
79
|
end
|
|
69
80
|
|
|
70
81
|
# helpers are mixed into all binding / transition contexts
|
|
71
|
-
def tool( *modules )
|
|
82
|
+
def tool( *modules, &block )
|
|
72
83
|
machine.tool *modules
|
|
84
|
+
if block_given?
|
|
85
|
+
tool = Module.new
|
|
86
|
+
tool.module_eval &block
|
|
87
|
+
machine.tools << tool
|
|
88
|
+
end
|
|
73
89
|
# inject them into self for immediate use
|
|
74
90
|
modules.flatten.extend( ToolArray ).inject_into( self )
|
|
75
91
|
end
|
|
92
|
+
alias_method :extend_dsl, :tool
|
|
76
93
|
|
|
77
94
|
#
|
|
78
95
|
# event definition methods
|
|
@@ -514,7 +531,7 @@ module StateFu
|
|
|
514
531
|
end
|
|
515
532
|
args.map do |name|
|
|
516
533
|
self.send type, name, options.dup, &block
|
|
517
|
-
end.extend
|
|
534
|
+
end.extend(mod)
|
|
518
535
|
end
|
|
519
536
|
|
|
520
537
|
end
|
data/lib/machine.rb
CHANGED
|
@@ -4,7 +4,7 @@ module StateFu
|
|
|
4
4
|
def self.BINDINGS
|
|
5
5
|
@@_bindings ||= {}
|
|
6
6
|
end
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
include Applicable
|
|
9
9
|
include HasOptions
|
|
10
10
|
|
|
@@ -17,10 +17,9 @@ module StateFu
|
|
|
17
17
|
def self.for_class(klass, name, options={}, &block)
|
|
18
18
|
options.symbolize_keys!
|
|
19
19
|
name = name.to_sym
|
|
20
|
-
|
|
21
20
|
unless machine = klass.state_fu_machines[ name ]
|
|
22
21
|
machine = new(options)
|
|
23
|
-
machine.bind! klass, name, options
|
|
22
|
+
machine.bind! klass, name, options
|
|
24
23
|
end
|
|
25
24
|
if block_given?
|
|
26
25
|
machine.apply! &block
|
|
@@ -30,26 +29,36 @@ module StateFu
|
|
|
30
29
|
|
|
31
30
|
# make it so that a class which has included StateFu has a binding to
|
|
32
31
|
# this machine
|
|
33
|
-
def self.bind!(
|
|
32
|
+
def self.bind!(machine, owner, name, options={})
|
|
34
33
|
name = name.to_sym
|
|
34
|
+
options[:define_methods] = (name == DEFAULT) unless options.symbolize_keys!.has_key?(:define_methods)
|
|
35
|
+
options[:field_name] ||= Persistence.default_field_name(name)
|
|
36
|
+
options[:singleton] = true unless owner.is_a?(Class)
|
|
35
37
|
# define an accessor method with the given name
|
|
36
|
-
if
|
|
37
|
-
owner
|
|
38
|
-
owner
|
|
38
|
+
if options[:singleton]
|
|
39
|
+
_binding = StateFu::Binding.new machine, owner, name, options
|
|
40
|
+
MethodFactory.define_singleton_method(owner, name) { _binding }
|
|
41
|
+
if alias_name = options[:alias] || options[:as]
|
|
42
|
+
MethodFactory.define_singleton_method(owner, alias_name) { _binding }
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
owner.state_fu_machines[name] = machine
|
|
46
|
+
owner.state_fu_options[name] = options
|
|
39
47
|
# method_missing to catch NoMethodError for event methods, etc
|
|
40
|
-
StateFu::MethodFactory.define_once_only_method_missing
|
|
41
|
-
unless owner.respond_to?
|
|
48
|
+
StateFu::MethodFactory.define_once_only_method_missing owner
|
|
49
|
+
unless owner.respond_to? name
|
|
42
50
|
owner.class_eval do
|
|
43
51
|
define_method name do
|
|
44
|
-
state_fu
|
|
52
|
+
state_fu name
|
|
53
|
+
end
|
|
54
|
+
# allow aliases to be set up, e.g. machine(:as => :status)
|
|
55
|
+
if alias_name = options[:alias] || options[:as]
|
|
56
|
+
alias_method alias_name, name
|
|
45
57
|
end
|
|
46
58
|
end
|
|
47
59
|
end
|
|
48
60
|
# prepare the persistence field
|
|
49
|
-
StateFu::Persistence.prepare_field owner, field_name
|
|
50
|
-
else
|
|
51
|
-
_binding = StateFu::Binding.new machine, owner, name, :field_name => field_name, :singleton => true
|
|
52
|
-
MethodFactory.define_singleton_method(owner, name) { _binding }
|
|
61
|
+
StateFu::Persistence.prepare_field owner, options[:field_name]
|
|
53
62
|
end
|
|
54
63
|
end
|
|
55
64
|
|
|
@@ -116,9 +125,8 @@ module StateFu
|
|
|
116
125
|
|
|
117
126
|
# make it so a class which has included StateFu has a binding to
|
|
118
127
|
# this machine
|
|
119
|
-
def bind!(
|
|
120
|
-
|
|
121
|
-
self.class.bind!(self, owner, name, field_name)
|
|
128
|
+
def bind!(owner, name=DEFAULT, options={})
|
|
129
|
+
self.class.bind!(self, owner, name, options)
|
|
122
130
|
end
|
|
123
131
|
|
|
124
132
|
def empty?
|
data/lib/method_factory.rb
CHANGED
|
@@ -7,14 +7,15 @@ module StateFu
|
|
|
7
7
|
|
|
8
8
|
class MethodFactory
|
|
9
9
|
attr_accessor :method_definitions
|
|
10
|
-
attr_reader :binding
|
|
11
|
-
|
|
10
|
+
attr_reader :binding, :machine
|
|
11
|
+
|
|
12
12
|
# An instance of MethodFactory is created to define methods on a specific StateFu::Binding, and
|
|
13
13
|
# on the object it is bound to.
|
|
14
14
|
|
|
15
|
-
def initialize(
|
|
16
|
-
@binding = _binding
|
|
17
|
-
|
|
15
|
+
def initialize(_binding)
|
|
16
|
+
@binding = _binding
|
|
17
|
+
@machine = binding.machine
|
|
18
|
+
simple_events, complex_events = machine.events.partition &:simple?
|
|
18
19
|
@method_definitions = {}
|
|
19
20
|
|
|
20
21
|
# simple event methods
|
|
@@ -95,7 +96,7 @@ module StateFu
|
|
|
95
96
|
end unless event.targets.nil?
|
|
96
97
|
end
|
|
97
98
|
|
|
98
|
-
|
|
99
|
+
machine.states.each do |state|
|
|
99
100
|
method_definitions["#{state.name}?"] = lambda do
|
|
100
101
|
_binding.current_state == state
|
|
101
102
|
end
|
|
@@ -114,9 +115,9 @@ module StateFu
|
|
|
114
115
|
# Note this happens when a machine is first bound to the class,
|
|
115
116
|
# not when StateFu is included.
|
|
116
117
|
|
|
117
|
-
def self.prepare_class(
|
|
118
|
+
def self.prepare_class(klass)
|
|
118
119
|
raise caller.inspect
|
|
119
|
-
self.define_once_only_method_missing(
|
|
120
|
+
self.define_once_only_method_missing(klass)
|
|
120
121
|
end # prepare_class
|
|
121
122
|
|
|
122
123
|
# When triggered, method_missing will first call state_fu!,
|
|
@@ -137,7 +138,7 @@ module StateFu
|
|
|
137
138
|
# or thoroughly understand what's happening in
|
|
138
139
|
# MethodFactory#define_once_only_method_missing.
|
|
139
140
|
|
|
140
|
-
def self.define_once_only_method_missing(
|
|
141
|
+
def self.define_once_only_method_missing(klass)
|
|
141
142
|
raise ArgumentError.new(klass.to_s) unless klass.is_a?(Class)
|
|
142
143
|
|
|
143
144
|
klass.class_eval do
|
|
@@ -160,14 +161,14 @@ module StateFu
|
|
|
160
161
|
end
|
|
161
162
|
|
|
162
163
|
# call the newly defined method, or the original method_missing
|
|
163
|
-
if respond_to?
|
|
164
|
+
if respond_to? method_name, true
|
|
164
165
|
# it was defined by calling state_fu!, which instantiated bindings
|
|
165
166
|
# for its state machines, which defined singleton methods for its
|
|
166
167
|
# states & events when it was constructed.
|
|
167
|
-
__send__
|
|
168
|
+
__send__ method_name, *args, &block
|
|
168
169
|
else
|
|
169
170
|
# call the original method_missing (method_missing_before_state_fu)
|
|
170
|
-
method_missing
|
|
171
|
+
method_missing method_name, *args, &block
|
|
171
172
|
end
|
|
172
173
|
end # method_missing
|
|
173
174
|
end # class_eval
|
|
@@ -177,8 +178,8 @@ module StateFu
|
|
|
177
178
|
# object. Any existing methods will not be tampered with, but a
|
|
178
179
|
# warning will be issued in the logs if any methods cannot be defined.
|
|
179
180
|
def install!
|
|
180
|
-
define_event_methods_on
|
|
181
|
-
define_event_methods_on
|
|
181
|
+
define_event_methods_on @binding
|
|
182
|
+
define_event_methods_on @binding.object if @binding.options[:define_methods]
|
|
182
183
|
end
|
|
183
184
|
|
|
184
185
|
#
|
|
@@ -202,13 +203,13 @@ module StateFu
|
|
|
202
203
|
# itself. The remaining arguments are passed into the transition,
|
|
203
204
|
# as with simple event methods.
|
|
204
205
|
#
|
|
205
|
-
def define_event_methods_on(
|
|
206
|
+
def define_event_methods_on(obj)
|
|
206
207
|
method_definitions.each do |method_name, method_body|
|
|
207
|
-
define_singleton_method
|
|
208
|
+
define_singleton_method obj, method_name, &method_body
|
|
208
209
|
end
|
|
209
210
|
end # define_event_methods_on
|
|
210
211
|
|
|
211
|
-
def define_singleton_method(
|
|
212
|
+
def define_singleton_method(object, method_name, &block)
|
|
212
213
|
MethodFactory.define_singleton_method object, method_name, &block
|
|
213
214
|
end
|
|
214
215
|
|
|
@@ -221,8 +222,8 @@ module StateFu
|
|
|
221
222
|
#
|
|
222
223
|
# existing methods will never be overwritten.
|
|
223
224
|
|
|
224
|
-
def self.define_singleton_method(
|
|
225
|
-
if object.respond_to?
|
|
225
|
+
def self.define_singleton_method(object, method_name, options={}, &block)
|
|
226
|
+
if object.respond_to? method_name, true
|
|
226
227
|
msg = !options[:force]
|
|
227
228
|
Logger.info "Existing method #{method(method_name) rescue [method_name].inspect} "\
|
|
228
229
|
"for #{object.class} #{object} "\
|
|
@@ -231,7 +232,7 @@ module StateFu
|
|
|
231
232
|
else
|
|
232
233
|
metaclass = class << object; self; end
|
|
233
234
|
metaclass.class_eval do
|
|
234
|
-
define_method
|
|
235
|
+
define_method method_name, &block
|
|
235
236
|
end
|
|
236
237
|
end
|
|
237
238
|
end
|
data/lib/persistence.rb
CHANGED
|
@@ -39,7 +39,7 @@ module StateFu
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
# returns the appropriate persister class for the given class & field name.
|
|
42
|
-
def self.class_for(
|
|
42
|
+
def self.class_for(klass, field_name)
|
|
43
43
|
raise ArgumentError if [klass, field_name].any?(&:nil?)
|
|
44
44
|
@@class_for[klass] ||= {}
|
|
45
45
|
@@class_for[klass][field_name] ||=
|
|
@@ -52,7 +52,7 @@ module StateFu
|
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
def self.for_class(
|
|
55
|
+
def self.for_class(klass, binding, field_name)
|
|
56
56
|
persister_class = class_for klass, field_name
|
|
57
57
|
prepare_field( klass, field_name, persister_class)
|
|
58
58
|
returning persister_class.new( binding, field_name ) do |persister|
|
|
@@ -60,7 +60,7 @@ module StateFu
|
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
def self.for_instance(
|
|
63
|
+
def self.for_instance(binding, field_name)
|
|
64
64
|
metaclass = class << binding.object; self; end
|
|
65
65
|
for_class( metaclass, binding, field_name )
|
|
66
66
|
end
|
|
@@ -69,7 +69,7 @@ module StateFu
|
|
|
69
69
|
# also ensures the persister class method :prepare_field has been called
|
|
70
70
|
# once for the given class & field name so the field can be set up; eg an
|
|
71
71
|
# attr_accessor or a before_save hook defined
|
|
72
|
-
def self.for(
|
|
72
|
+
def self.for(binding)
|
|
73
73
|
field_name = binding.field_name.to_sym
|
|
74
74
|
if binding.singleton?
|
|
75
75
|
for_instance( binding, field_name )
|
|
@@ -95,21 +95,21 @@ module StateFu
|
|
|
95
95
|
# checks to see if the field_name for persistence is a
|
|
96
96
|
# RelaxDB attribute.
|
|
97
97
|
# Safe to use (skipped) if RelaxDB is not included.
|
|
98
|
-
def self.relaxdb_document_property?(
|
|
98
|
+
def self.relaxdb_document_property?(klass, field_name)
|
|
99
99
|
Object.const_defined?('RelaxDB') &&
|
|
100
100
|
klass.ancestors.include?( ::RelaxDB::Document ) &&
|
|
101
|
-
klass.properties.map(&:to_s).include?(
|
|
101
|
+
klass.properties.map(&:to_s).include?(field_name.to_s)
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
# checks to see if the field_name for persistence is an
|
|
105
105
|
# ActiveRecord column.
|
|
106
106
|
# Safe to use (skipped) if ActiveRecord is not included.
|
|
107
|
-
def self.active_record_column?(
|
|
107
|
+
def self.active_record_column?(klass, field_name)
|
|
108
108
|
Object.const_defined?("ActiveRecord") &&
|
|
109
109
|
::ActiveRecord.const_defined?("Base") &&
|
|
110
|
-
klass.ancestors.include?(
|
|
110
|
+
klass.ancestors.include?(::ActiveRecord::Base) &&
|
|
111
111
|
klass.table_exists? &&
|
|
112
|
-
klass.columns.map(&:name).include?(
|
|
112
|
+
klass.columns.map(&:name).include?(field_name.to_s)
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
end
|
data/lib/state-fu.rb
CHANGED
|
@@ -16,15 +16,13 @@
|
|
|
16
16
|
#
|
|
17
17
|
# It is also delightfully elegant and easy to use for simple things.
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
require 'rubygems'
|
|
21
|
-
|
|
22
19
|
[ 'support/core_ext',
|
|
23
20
|
'support/logger',
|
|
24
21
|
'support/applicable',
|
|
25
22
|
'support/arrays',
|
|
26
23
|
'support/methodical',
|
|
27
24
|
'support/has_options',
|
|
25
|
+
'support/vizier',
|
|
28
26
|
'support/plotter',
|
|
29
27
|
'support/exceptions',
|
|
30
28
|
'executioner',
|
data/lib/support/core_ext.rb
CHANGED
data/lib/support/plotter.rb
CHANGED
data/lib/tasks/spec_last.rake
CHANGED
|
@@ -8,39 +8,48 @@ unless Object.const_defined?('STATE_FU_PLUGIN_PATH')
|
|
|
8
8
|
STATE_FU_PLUGIN_PATH = Object.const_defined?('RAILS_ROOT') ? File.join( RAILS_ROOT, '/vendor/plugins/state-fu' ) : STATE_FU_APP_PATH
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
begin
|
|
12
|
+
require 'rake'
|
|
13
|
+
require 'spec'
|
|
14
|
+
require 'spec/rake/spectask'
|
|
15
|
+
|
|
16
|
+
namespace :spec do
|
|
17
|
+
def find_last_modified_spec
|
|
18
|
+
require 'find'
|
|
19
|
+
specs = []
|
|
20
|
+
Find.find( File.expand_path(File.join(STATE_FU_APP_PATH,'spec'))) do |f|
|
|
21
|
+
next unless f !~ /\.#/ && f =~ /_spec.rb$/
|
|
22
|
+
specs << f
|
|
23
|
+
end
|
|
24
|
+
spec = specs.sort_by { |spec| File.stat( spec ).mtime }.last
|
|
18
25
|
end
|
|
19
|
-
spec = specs.sort_by { |spec| File.stat( spec ).mtime }.last
|
|
20
|
-
end
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
desc "runs the last modified spec; L=n runs only that line"
|
|
28
|
+
Spec::Rake::SpecTask.new(:last) do |t|
|
|
29
|
+
specfile = find_last_modified_spec || return
|
|
30
|
+
t.verbose = true
|
|
31
|
+
t.spec_opts = ["-c","-b","-u"]
|
|
32
|
+
if ENV['L']
|
|
33
|
+
t.spec_opts += ["-l", ENV["L"],"-f", "specdoc"]
|
|
34
|
+
else
|
|
35
|
+
t.spec_opts += ["-f", "profile"]
|
|
36
|
+
end
|
|
37
|
+
t.spec_files = FileList[specfile]
|
|
31
38
|
end
|
|
32
|
-
t.spec_files = FileList[specfile]
|
|
33
|
-
end
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
desc "runs all specs, or those which last failed"
|
|
41
|
+
Spec::Rake::SpecTask.new(:faily) do |t|
|
|
42
|
+
specfile = find_last_modified_spec || return
|
|
43
|
+
faily = 'spec.fail'
|
|
44
|
+
t.verbose = true
|
|
45
|
+
t.spec_opts = ["-f","failing_examples:#{faily}", "-f","n","-c","-b","-u"]
|
|
46
|
+
if File.exists?(faily) && File.read(faily).split("\n")[0] != ""
|
|
47
|
+
t.spec_opts << ["-e",faily]
|
|
48
|
+
end
|
|
43
49
|
end
|
|
44
50
|
end
|
|
45
51
|
|
|
52
|
+
rescue LoadError
|
|
53
|
+
# fail quietly if rspec is not installed
|
|
46
54
|
end
|
|
55
|
+
|
data/lib/transition.rb
CHANGED
|
@@ -256,6 +256,10 @@ module StateFu
|
|
|
256
256
|
alias_method :original_state, :origin
|
|
257
257
|
alias_method :initial_state, :origin
|
|
258
258
|
alias_method :from, :origin
|
|
259
|
+
|
|
260
|
+
def cycle?
|
|
261
|
+
origin == target
|
|
262
|
+
end
|
|
259
263
|
|
|
260
264
|
# an accepted transition == true
|
|
261
265
|
# an unaccepted transition == false
|
data/lib/transition_query.rb
CHANGED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../helper")
|
|
2
|
+
|
|
3
|
+
describe "defining an alias for the default machine" do
|
|
4
|
+
describe "with machine(:as => :status)" do
|
|
5
|
+
before do
|
|
6
|
+
make_pristine_class('Klass')
|
|
7
|
+
Klass.machine :as => :status do
|
|
8
|
+
state :active
|
|
9
|
+
end
|
|
10
|
+
@obj = Klass.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should not prevent normal access through #state_fu" do
|
|
14
|
+
@obj.state_fu.machine.should == Klass.machine
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should not affect the internal name of the machine" do
|
|
18
|
+
@obj.status.method_name.should == :default
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should let you access the machine through #status" do
|
|
22
|
+
@obj.status.should be_kind_of(StateFu::Binding)
|
|
23
|
+
@obj.status.machine.should == Klass.machine
|
|
24
|
+
@obj.status.current_state.should == :active
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "defining an alias for a default singleton machine" do
|
|
31
|
+
describe "with #bind! :default, :as => :alias" do
|
|
32
|
+
before do
|
|
33
|
+
make_pristine_class('Klass')
|
|
34
|
+
@machine = StateFu::Machine.new do
|
|
35
|
+
state :exemplary
|
|
36
|
+
end
|
|
37
|
+
@obj = Klass.new
|
|
38
|
+
@machine.bind!( @obj, :default, :as => :example)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should work too" do
|
|
42
|
+
@obj.example.machine.should == @machine
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
@@ -5,7 +5,7 @@ describe "singleton machines" do
|
|
|
5
5
|
make_pristine_class('Klass')
|
|
6
6
|
@m = StateFu::Machine.new do
|
|
7
7
|
state :a do
|
|
8
|
-
event :
|
|
8
|
+
event :bify, :transitions_to => :b
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
@m.events.length.should == 1
|
|
@@ -20,9 +20,22 @@ describe "singleton machines" do
|
|
|
20
20
|
@obj.my_binding.object.should == @obj
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
it "should have
|
|
24
|
-
%w/
|
|
23
|
+
it "should have methods defined on the binding by default" do
|
|
24
|
+
%w/bify can_bify? bify!/.each do |method_name|
|
|
25
25
|
@obj.my_binding.should respond_to(method_name)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "should not have methods defined on the instance by default" do
|
|
30
|
+
%w/bify can_bify? bify!/.each do |method_name|
|
|
31
|
+
@obj.should_not respond_to(method_name)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
it "should have methods defined on the instance if bound with :define_methods => true" do
|
|
37
|
+
@m.bind!( @obj, :my_binding, :define_methods => true)
|
|
38
|
+
%w/bify can_bify? bify!/.each do |method_name|
|
|
26
39
|
@obj.should respond_to(method_name)
|
|
27
40
|
end
|
|
28
41
|
end
|
|
@@ -30,7 +43,7 @@ describe "singleton machines" do
|
|
|
30
43
|
it "should transition" do
|
|
31
44
|
@b = @obj.my_binding
|
|
32
45
|
@b.current_state.should == :a
|
|
33
|
-
t = @obj.
|
|
46
|
+
t = @obj.my_binding.bify!
|
|
34
47
|
t.should be_kind_of(StateFu::Transition)
|
|
35
48
|
t.should be_accepted
|
|
36
49
|
@b.current_state.should == :b
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../helper")
|
|
2
|
+
|
|
3
|
+
describe "instance methods defined for a class's machine," do
|
|
4
|
+
before do
|
|
5
|
+
make_pristine_class('Klass')
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
describe "for the default machine" do
|
|
9
|
+
it "should define methods for states and events" do
|
|
10
|
+
Klass.machine do
|
|
11
|
+
event :go, :from => :a, :to => :b
|
|
12
|
+
end
|
|
13
|
+
instance = Klass.new
|
|
14
|
+
instance.state_fu!
|
|
15
|
+
instance.should respond_to(:a?)
|
|
16
|
+
instance.should respond_to(:can_go?)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should not define methods given :define_methods => false" do
|
|
20
|
+
Klass.machine :define_methods => false do
|
|
21
|
+
event :go, :from => :a, :to => :b
|
|
22
|
+
end
|
|
23
|
+
instance = Klass.new
|
|
24
|
+
instance.state_fu!
|
|
25
|
+
instance.should_not respond_to(:a?)
|
|
26
|
+
instance.should_not respond_to(:can_go?)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "for other machines" do
|
|
31
|
+
it "should not define methods" do
|
|
32
|
+
Klass.machine :other do
|
|
33
|
+
event :go, :from => :a, :to => :b
|
|
34
|
+
end
|
|
35
|
+
instance = Klass.new
|
|
36
|
+
instance.state_fu!
|
|
37
|
+
instance.should_not respond_to(:a?)
|
|
38
|
+
instance.should_not respond_to(:can_go?)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should define methods given :define_methods => true" do
|
|
42
|
+
Klass.machine :define_methods => true do
|
|
43
|
+
event :go, :from => :a, :to => :b
|
|
44
|
+
end
|
|
45
|
+
instance = Klass.new
|
|
46
|
+
instance.state_fu!
|
|
47
|
+
instance.should respond_to(:a?)
|
|
48
|
+
instance.should respond_to(:can_go?)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
describe "instance methods when you #bind! a machine" do
|
|
55
|
+
before do
|
|
56
|
+
make_pristine_class('Klass')
|
|
57
|
+
@machine = StateFu::Machine.new do
|
|
58
|
+
state :hot
|
|
59
|
+
state :cold
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe "as the default machine," do
|
|
64
|
+
describe "the default behaviour" do
|
|
65
|
+
before do
|
|
66
|
+
@machine.bind! Klass, :default, :define_methods => true
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "defines methods" do
|
|
70
|
+
instance = Klass.new
|
|
71
|
+
instance.state_fu!
|
|
72
|
+
instance.should respond_to(:hot?)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
describe "when it is bound with :define_methods => false" do
|
|
77
|
+
before do
|
|
78
|
+
@machine.bind! Klass, :default, :define_methods => false
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "should not define methods" do
|
|
82
|
+
instance = Klass.new
|
|
83
|
+
instance.state_fu!
|
|
84
|
+
instance.should_not respond_to(:hot?)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe "as another machine," do
|
|
90
|
+
describe "the default behaviour" do
|
|
91
|
+
before do
|
|
92
|
+
@machine.bind! Klass, :temperature
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should not define methods" do
|
|
96
|
+
instance = Klass.new
|
|
97
|
+
instance.state_fu!
|
|
98
|
+
instance.should_not respond_to(:hot?)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
describe "when it is bound with :define_methods => true" do
|
|
103
|
+
before do
|
|
104
|
+
@machine.bind! Klass, :temperature, :define_methods => true
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "should define methods" do
|
|
108
|
+
instance = Klass.new
|
|
109
|
+
instance.state_fu!
|
|
110
|
+
instance.should respond_to(:hot?)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -4,7 +4,7 @@ thisdir = File.expand_path(File.dirname(__FILE__))
|
|
|
4
4
|
# ensure we require state-fu from lib, not gems
|
|
5
5
|
$LOAD_PATH.unshift( "#{thisdir}/../lib" )
|
|
6
6
|
require 'state-fu'
|
|
7
|
-
require 'no_stdout'
|
|
7
|
+
require 'support/no_stdout'
|
|
8
8
|
require 'fileutils'
|
|
9
9
|
require 'rubygems'
|
|
10
10
|
require 'spec'
|