state-fu 0.11.1 → 0.12.1
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/lib/binding.rb +11 -7
- data/lib/interface.rb +12 -14
- data/lib/machine.rb +25 -17
- data/lib/method_factory.rb +21 -20
- data/lib/persistence.rb +9 -9
- data/lib/support/logger.rb +18 -27
- 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/state_fu_spec.rb +22 -5
- data/spec/units/binding_spec.rb +6 -8
- data/spec/units/machine_spec.rb +5 -8
- metadata +6 -2
data/lib/binding.rb
CHANGED
@@ -3,13 +3,12 @@ module StateFu
|
|
3
3
|
|
4
4
|
attr_reader :object, :machine, :method_name, :field_name, :persister, :transitions, :options, :target
|
5
5
|
|
6
|
-
|
7
6
|
# the constructor should not be called manually; a binding is
|
8
7
|
# returned when an instance of a class with a StateFu::Machine
|
9
8
|
# calls:
|
10
9
|
#
|
11
10
|
# instance.#state_fu (for the default machine which is called :state_fu),
|
12
|
-
# instance.#state_fu(
|
11
|
+
# instance.#state_fu(:<machine_name>) ,or
|
13
12
|
# instance.#<machine_name>
|
14
13
|
#
|
15
14
|
def initialize( machine, object, method_name, options={} )
|
@@ -18,13 +17,18 @@ module StateFu
|
|
18
17
|
@method_name = method_name
|
19
18
|
@transitions = []
|
20
19
|
@options = options.symbolize_keys!
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
if options[:singleton]
|
21
|
+
@target = object
|
22
|
+
else
|
23
|
+
@target = object.class
|
24
|
+
@options = @target.state_fu_options[@method_name].merge(options)
|
25
|
+
end
|
26
|
+
@field_name = @options.delete(:field_name) || raise("No field_name supplied")
|
27
|
+
@persister = Persistence.for self
|
24
28
|
|
25
29
|
# define event methods on this binding and its @object
|
26
|
-
MethodFactory.new(
|
27
|
-
@machine.helpers.inject_into
|
30
|
+
MethodFactory.new(self).install!
|
31
|
+
@machine.helpers.inject_into self
|
28
32
|
end
|
29
33
|
|
30
34
|
alias_method :o, :object
|
data/lib/interface.rb
CHANGED
@@ -2,21 +2,19 @@ module StateFu
|
|
2
2
|
module Interface
|
3
3
|
module SoftAlias
|
4
4
|
|
5
|
-
|
6
|
-
# so we can be liberal with them.
|
7
|
-
def soft_alias(x)
|
8
|
-
aliases = [ x.to_a[0] ].flatten
|
9
|
-
original = aliases.shift
|
5
|
+
def soft_alias(hash)
|
10
6
|
existing_method_names = (self.instance_methods | self.protected_instance_methods | self.private_instance_methods).map(&:to_sym)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
hash.each do |original, aliases|
|
8
|
+
aliases.
|
9
|
+
reject { |a| existing_method_names.include?(a.to_sym) }.
|
10
|
+
each { |a| alias_method a, original}
|
11
|
+
end
|
12
|
+
end
|
16
13
|
end
|
17
14
|
|
18
15
|
module Aliases
|
19
|
-
|
16
|
+
# define aliases that won't clobber existing methods -
|
17
|
+
# so we can be liberal with them.
|
20
18
|
def self.extended(base)
|
21
19
|
base.extend SoftAlias
|
22
20
|
base.class_eval do
|
@@ -70,8 +68,8 @@ module StateFu
|
|
70
68
|
end
|
71
69
|
alias_method :machine, :state_fu_machine
|
72
70
|
|
73
|
-
def
|
74
|
-
@
|
71
|
+
def state_fu_options
|
72
|
+
@_state_fu_options ||= {}
|
75
73
|
end
|
76
74
|
|
77
75
|
def state_fu_machines
|
@@ -97,7 +95,7 @@ module StateFu
|
|
97
95
|
# can access a StateFu::Machine, the object's current state, the
|
98
96
|
# methods which trigger event transitions, etc.
|
99
97
|
|
100
|
-
def state_fu_binding(
|
98
|
+
def state_fu_binding(name = DEFAULT)
|
101
99
|
name = name.to_sym
|
102
100
|
if machine = self.class.state_fu_machines[name]
|
103
101
|
state_fu_bindings[name] ||= StateFu::Binding.new( machine, self, name )
|
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/support/logger.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
require 'logger'
|
3
2
|
module StateFu
|
4
3
|
#
|
@@ -32,9 +31,9 @@ module StateFu
|
|
32
31
|
@@shared = false
|
33
32
|
@@log_level = nil
|
34
33
|
|
35
|
-
def self.new( logger
|
36
|
-
|
37
|
-
|
34
|
+
def self.new( logger=nil, options={} )
|
35
|
+
@@suppress = false
|
36
|
+
set_logger(logger, options)
|
38
37
|
self
|
39
38
|
end
|
40
39
|
|
@@ -55,19 +54,19 @@ module StateFu
|
|
55
54
|
end
|
56
55
|
|
57
56
|
def self.shared?
|
58
|
-
|
57
|
+
!!@@shared
|
59
58
|
end
|
60
59
|
|
61
60
|
def self.prefix
|
62
61
|
shared? ? @@prefix : nil
|
63
62
|
end
|
64
63
|
|
65
|
-
def self.logger=
|
66
|
-
set_logger
|
64
|
+
def self.logger= new_logger
|
65
|
+
set_logger new_logger
|
67
66
|
end
|
68
67
|
|
69
68
|
def self.instance
|
70
|
-
@@logger ||=
|
69
|
+
@@logger ||= default_logger
|
71
70
|
end
|
72
71
|
|
73
72
|
def self.suppress!
|
@@ -108,7 +107,7 @@ module StateFu
|
|
108
107
|
when Logger.activesupport_logger_available? && ActiveSupport::BufferedLogger
|
109
108
|
@@logger = logger
|
110
109
|
else
|
111
|
-
|
110
|
+
default_logger
|
112
111
|
end
|
113
112
|
self.shared = !!options.symbolize_keys![:shared]
|
114
113
|
if shared?
|
@@ -123,27 +122,19 @@ module StateFu
|
|
123
122
|
|
124
123
|
private
|
125
124
|
|
126
|
-
def self.get_logger( logr = $stdout )
|
127
|
-
if Object.const_defined?( "RAILS_DEFAULT_LOGGER" )
|
128
|
-
set_logger RAILS_DEFAULT_LOGGER, :shared => true
|
129
|
-
else
|
130
|
-
if Object.const_defined?( 'ActiveSupport' ) && ActiveSupport.const_defined?('BufferedLogger')
|
131
|
-
set_logger( ActiveSupport::BufferedLogger.new( logr ))
|
132
|
-
else
|
133
|
-
set_logger ::Logger.new( logr )
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
125
|
def self.activesupport_logger_available?
|
139
126
|
Object.const_defined?( 'ActiveSupport' ) && ActiveSupport.const_defined?('BufferedLogger')
|
140
127
|
end
|
141
128
|
|
142
|
-
def self.default_logger
|
143
|
-
if
|
144
|
-
RAILS_DEFAULT_LOGGER
|
145
|
-
|
146
|
-
|
129
|
+
def self.default_logger(target=$stdout)
|
130
|
+
if activesupport_logger_available?
|
131
|
+
if Object.const_defined?("RAILS_DEFAULT_LOGGER")
|
132
|
+
RAILS_DEFAULT_LOGGER
|
133
|
+
else
|
134
|
+
ActiveSupport::BufferedLogger.new(target)
|
135
|
+
end
|
136
|
+
else
|
137
|
+
::Logger.new(target)
|
147
138
|
end
|
148
139
|
end
|
149
140
|
|
@@ -156,7 +147,7 @@ module StateFu
|
|
156
147
|
when nil
|
157
148
|
level
|
158
149
|
else
|
159
|
-
raise ArgumentError
|
150
|
+
raise ArgumentError.new(input.inspect)
|
160
151
|
end
|
161
152
|
end
|
162
153
|
|
@@ -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/state_fu_spec.rb
CHANGED
@@ -885,7 +885,23 @@ describe "Chameleon" do
|
|
885
885
|
event :go_inside, :from => {:outside => :inside}
|
886
886
|
end
|
887
887
|
|
888
|
-
|
888
|
+
# With :define_methods => true, we can create methods for the :skin
|
889
|
+
# machine directly on our Chameleon, so we can type e.g.
|
890
|
+
#
|
891
|
+
# @chameleon.comoflage! :bark
|
892
|
+
# instead of:
|
893
|
+
# @chameleon.skin.camoflage! :bark
|
894
|
+
#
|
895
|
+
# This is the usual behaviour for the default machine, but not for any
|
896
|
+
# machine given an explicit name. Otherwise, it would cause confusion
|
897
|
+
# when (like the PokerMachine example) multiple machines would compete
|
898
|
+
# for the same methods.
|
899
|
+
#
|
900
|
+
# Hint for the masochistic: state / event methods will never overwrite a
|
901
|
+
# pre-existing method, so in the event of overlap, the first machine
|
902
|
+
# defined will take precedence.
|
903
|
+
|
904
|
+
machine :skin, :define_methods => true do
|
889
905
|
initial_state :green
|
890
906
|
|
891
907
|
states :plaid, :paisley, :tartan, :location => :indoors
|
@@ -897,9 +913,9 @@ describe "Chameleon" do
|
|
897
913
|
else
|
898
914
|
case transition.target[:location]
|
899
915
|
when :indoors
|
900
|
-
inside?
|
916
|
+
location.inside?
|
901
917
|
when :outdoors
|
902
|
-
outside?
|
918
|
+
location.outside?
|
903
919
|
else
|
904
920
|
true
|
905
921
|
end
|
@@ -923,13 +939,13 @@ describe "Chameleon" do
|
|
923
939
|
@chameleon.current_state(:location).should == :outside
|
924
940
|
@chameleon.current_state(:skin).should == :green
|
925
941
|
|
926
|
-
@chameleon.outside?.should == true
|
942
|
+
@chameleon.location.outside?.should == true
|
927
943
|
|
928
944
|
@chameleon.skin.valid_transitions.targets.names.should == [:bark, :pebbles, :foliage]
|
929
945
|
@chameleon.camoflage!(:bark)
|
930
946
|
@chameleon.skin.should == :bark
|
931
947
|
|
932
|
-
@chameleon.go_inside!
|
948
|
+
@chameleon.location.go_inside!
|
933
949
|
@chameleon.skin.valid_transitions.targets.names.should == [:green, :plaid, :paisley, :tartan]
|
934
950
|
|
935
951
|
@chameleon.camoflage!(:tartan)
|
@@ -943,6 +959,7 @@ describe "Chameleon" do
|
|
943
959
|
end
|
944
960
|
|
945
961
|
end
|
962
|
+
|
946
963
|
end
|
947
964
|
|
948
965
|
|
data/spec/units/binding_spec.rb
CHANGED
@@ -14,12 +14,6 @@ describe StateFu::Binding do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
17
|
#
|
24
18
|
#
|
25
19
|
#
|
@@ -33,8 +27,10 @@ describe StateFu::Binding do
|
|
33
27
|
|
34
28
|
describe "constructor" do
|
35
29
|
before do
|
36
|
-
mock(Klass).
|
37
|
-
{
|
30
|
+
mock(Klass).state_fu_options.at_most(2) do
|
31
|
+
{
|
32
|
+
:example => {:field_name => :example_field}
|
33
|
+
}
|
38
34
|
end
|
39
35
|
end
|
40
36
|
|
@@ -63,10 +59,12 @@ describe StateFu::Binding do
|
|
63
59
|
end
|
64
60
|
|
65
61
|
describe "when StateFu::Persistence.active_record_column? is true" do
|
62
|
+
|
66
63
|
before do
|
67
64
|
mock( StateFu::Persistence ).active_record_column?(Klass, :example_field).times(1) { true }
|
68
65
|
mock( Klass ).before_create( :state_fu!) { }
|
69
66
|
end
|
67
|
+
|
70
68
|
it "should get an ActiveRecord persister" do
|
71
69
|
mock( StateFu::Persistence::ActiveRecord ).new( anything, :example_field ) { @p }
|
72
70
|
b = StateFu::Binding.new( Klass.state_fu_machine, @obj, :example )
|
data/spec/units/machine_spec.rb
CHANGED
@@ -20,22 +20,19 @@ describe StateFu::Machine do
|
|
20
20
|
before do
|
21
21
|
reset!
|
22
22
|
make_pristine_class 'Klass'
|
23
|
-
# mock( Klass ).machines() { {} }
|
24
23
|
end
|
25
24
|
|
26
25
|
it "should create a new machine and bind! it" do
|
27
26
|
@machine = Object.new
|
28
|
-
mock(
|
29
|
-
mock(
|
30
|
-
StateFu::Machine.for_class
|
27
|
+
mock(@machine).bind!(Klass, :moose, {})
|
28
|
+
mock(StateFu::Machine).new({}) { @machine }
|
29
|
+
StateFu::Machine.for_class Klass, :moose
|
31
30
|
end
|
32
31
|
|
33
32
|
it "should apply the block (via lathe) if one is given" do
|
34
|
-
@m = StateFu::Machine.for_class
|
33
|
+
@m = StateFu::Machine.for_class Klass, :snoo do
|
35
34
|
state :porpoise
|
36
35
|
end
|
37
|
-
# mock( Klass ).machines() { {} }
|
38
|
-
# @m.states.map(&:name).should == [:porpoise]
|
39
36
|
end
|
40
37
|
end
|
41
38
|
|
@@ -85,7 +82,7 @@ describe StateFu::Machine do
|
|
85
82
|
name = :StinkJuice
|
86
83
|
field_name = 'stink_juice_field'
|
87
84
|
@m.bind!( Klass, name )
|
88
|
-
Klass.
|
85
|
+
Klass.state_fu_options[name][:field_name].should == 'stink_juice_field'
|
89
86
|
end
|
90
87
|
end
|
91
88
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: state-fu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Lee
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-09-26 00:00:00 +10:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -73,6 +73,7 @@ files:
|
|
73
73
|
- lib/transition_query.rb
|
74
74
|
- spec/custom_formatter.rb
|
75
75
|
- spec/features/binding_and_transition_helper_mixin_spec.rb
|
76
|
+
- spec/features/machine_alias_spec.rb
|
76
77
|
- spec/features/method_missing_only_once_spec.rb
|
77
78
|
- spec/features/not_requirements_spec.rb
|
78
79
|
- spec/features/plotter_spec.rb
|
@@ -80,6 +81,7 @@ files:
|
|
80
81
|
- spec/features/singleton_machine_spec.rb
|
81
82
|
- spec/features/state_and_array_options_accessor_spec.rb
|
82
83
|
- spec/features/transition_boolean_comparison_spec.rb
|
84
|
+
- spec/features/when_methods_are_defined_spec.rb
|
83
85
|
- spec/helper.rb
|
84
86
|
- spec/integration/active_record_persistence_spec.rb
|
85
87
|
- spec/integration/binding_extension_spec.rb
|
@@ -138,6 +140,7 @@ summary: A rich library for state-oriented programming with state machines / wor
|
|
138
140
|
test_files:
|
139
141
|
- spec/custom_formatter.rb
|
140
142
|
- spec/features/binding_and_transition_helper_mixin_spec.rb
|
143
|
+
- spec/features/machine_alias_spec.rb
|
141
144
|
- spec/features/method_missing_only_once_spec.rb
|
142
145
|
- spec/features/not_requirements_spec.rb
|
143
146
|
- spec/features/plotter_spec.rb
|
@@ -145,6 +148,7 @@ test_files:
|
|
145
148
|
- spec/features/singleton_machine_spec.rb
|
146
149
|
- spec/features/state_and_array_options_accessor_spec.rb
|
147
150
|
- spec/features/transition_boolean_comparison_spec.rb
|
151
|
+
- spec/features/when_methods_are_defined_spec.rb
|
148
152
|
- spec/helper.rb
|
149
153
|
- spec/integration/active_record_persistence_spec.rb
|
150
154
|
- spec/integration/binding_extension_spec.rb
|