davidlee-state-fu 0.3.1 → 0.10.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 +124 -34
- data/Rakefile +36 -30
- data/lib/no_stdout.rb +1 -1
- data/lib/state-fu.rb +9 -8
- data/lib/state_fu/active_support_lite/array/access.rb +12 -5
- data/lib/state_fu/active_support_lite/array/conversions.rb +10 -4
- data/lib/state_fu/active_support_lite/array/extract_options.rb +5 -4
- data/lib/state_fu/active_support_lite/array/grouping.rb +7 -4
- data/lib/state_fu/active_support_lite/array/random_access.rb +4 -3
- data/lib/state_fu/active_support_lite/array/wrapper.rb +4 -3
- data/lib/state_fu/active_support_lite/array.rb +3 -1
- data/lib/state_fu/active_support_lite/blank.rb +18 -9
- data/lib/state_fu/active_support_lite/cattr_reader.rb +4 -1
- data/lib/state_fu/active_support_lite/keys.rb +8 -3
- data/lib/state_fu/active_support_lite/misc.rb +6 -4
- data/lib/state_fu/active_support_lite/module/delegation.rb +130 -0
- data/lib/state_fu/active_support_lite/module.rb +1 -0
- data/lib/state_fu/active_support_lite/object.rb +5 -2
- data/lib/state_fu/active_support_lite/string.rb +6 -1
- data/lib/state_fu/active_support_lite/symbol.rb +2 -1
- data/lib/state_fu/applicable.rb +41 -0
- data/lib/state_fu/{helper.rb → arrays.rb} +45 -121
- data/lib/state_fu/binding.rb +136 -159
- data/lib/state_fu/core_ext.rb +78 -10
- data/lib/state_fu/event.rb +112 -48
- data/lib/state_fu/exceptions.rb +80 -34
- data/lib/state_fu/executioner.rb +149 -0
- data/lib/state_fu/has_options.rb +16 -0
- data/lib/state_fu/hooks.rb +21 -16
- data/lib/state_fu/interface.rb +80 -83
- data/lib/state_fu/lathe.rb +361 -148
- data/lib/state_fu/logger.rb +122 -45
- data/lib/state_fu/machine.rb +60 -32
- data/lib/state_fu/method_factory.rb +180 -72
- data/lib/state_fu/methodical.rb +17 -0
- data/lib/state_fu/persistence/active_record.rb +6 -1
- data/lib/state_fu/persistence/attribute.rb +1 -0
- data/lib/state_fu/persistence/base.rb +8 -6
- data/lib/state_fu/persistence.rb +94 -23
- data/lib/state_fu/sprocket.rb +26 -11
- data/lib/state_fu/state.rb +8 -27
- data/lib/state_fu/transition.rb +207 -98
- data/lib/state_fu/transition_query.rb +214 -0
- data/lib/state_fu.rb +1 -0
- data/lib/tasks/spec_last.rake +46 -0
- data/lib/tasks/state_fu.rake +57 -0
- data/lib/vizier.rb +61 -61
- data/spec/custom_formatter.rb +49 -0
- data/spec/features/binding_and_transition_helper_mixin_spec.rb +2 -2
- data/spec/features/method_missing_only_once_spec.rb +28 -0
- data/spec/features/not_requirements_spec.rb +83 -46
- data/spec/features/plotter_spec.rb +97 -0
- data/spec/features/shared_log_spec.rb +7 -0
- data/spec/features/singleton_machine_spec.rb +39 -0
- data/spec/features/state_and_array_options_accessor_spec.rb +1 -1
- data/spec/features/{transition_boolean_comparison.rb → transition_boolean_comparison_spec.rb} +29 -18
- data/spec/helper.rb +6 -117
- data/spec/integration/active_record_persistence_spec.rb +18 -4
- data/spec/integration/binding_extension_spec.rb +1 -1
- data/spec/integration/class_accessor_spec.rb +49 -59
- data/spec/integration/event_definition_spec.rb +20 -20
- data/spec/integration/example_01_document_spec.rb +13 -8
- data/spec/integration/example_02_string_spec.rb +3 -2
- data/spec/integration/instance_accessor_spec.rb +16 -19
- data/spec/integration/lathe_extension_spec.rb +2 -2
- data/spec/integration/machine_duplication_spec.rb +59 -37
- data/spec/integration/relaxdb_persistence_spec.rb +6 -3
- data/spec/integration/requirement_reflection_spec.rb +66 -57
- data/spec/integration/state_definition_spec.rb +72 -66
- data/spec/integration/transition_spec.rb +169 -173
- data/spec/spec.opts +5 -3
- data/spec/spec_helper.rb +132 -0
- data/spec/state_fu_spec.rb +870 -0
- data/spec/units/binding_spec.rb +33 -22
- data/spec/units/event_spec.rb +3 -22
- data/spec/units/exceptions_spec.rb +7 -0
- data/spec/units/lathe_spec.rb +7 -7
- data/spec/units/machine_spec.rb +67 -75
- data/spec/units/method_factory_spec.rb +55 -48
- data/spec/units/sprocket_spec.rb +5 -7
- data/spec/units/state_spec.rb +33 -24
- metadata +31 -19
- data/lib/state_fu/active_support_lite/inheritable_attributes.rb +0 -1
- data/lib/state_fu/fu_space.rb +0 -51
- data/lib/state_fu/mock_transition.rb +0 -38
- data/spec/BDD/plotter_spec.rb +0 -115
- data/spec/integration/dynamic_requirement_spec.rb +0 -160
- data/spec/integration/ex_machine_for_accounts_spec.rb +0 -79
- data/spec/integration/sanity_spec.rb +0 -31
- data/spec/units/fu_space_spec.rb +0 -95
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class Object
|
|
1
|
+
class Object #:nodoc:all
|
|
2
2
|
# An object is blank if it's false, empty, or a whitespace string.
|
|
3
3
|
# For example, "", " ", +nil+, [], and {} are blank.
|
|
4
4
|
#
|
|
@@ -9,49 +9,58 @@ class Object
|
|
|
9
9
|
# to
|
|
10
10
|
#
|
|
11
11
|
# if !address.blank?
|
|
12
|
+
#:nodoc
|
|
12
13
|
def blank?
|
|
13
14
|
respond_to?(:empty?) ? empty? : !self
|
|
14
15
|
end
|
|
15
|
-
|
|
16
|
+
|
|
16
17
|
# An object is present if it's not blank.
|
|
18
|
+
#:nodoc
|
|
17
19
|
def present?
|
|
18
20
|
!blank?
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
|
|
22
|
-
class NilClass #:nodoc
|
|
24
|
+
class NilClass #:nodoc
|
|
25
|
+
#:nodoc
|
|
23
26
|
def blank?
|
|
24
27
|
true
|
|
25
28
|
end
|
|
26
29
|
end
|
|
27
30
|
|
|
28
|
-
class FalseClass #:nodoc
|
|
31
|
+
class FalseClass #:nodoc
|
|
32
|
+
#:nodoc
|
|
29
33
|
def blank?
|
|
30
34
|
true
|
|
31
35
|
end
|
|
32
36
|
end
|
|
33
37
|
|
|
34
|
-
class TrueClass #:nodoc
|
|
38
|
+
class TrueClass #:nodoc
|
|
39
|
+
#:nodoc
|
|
35
40
|
def blank?
|
|
36
41
|
false
|
|
37
42
|
end
|
|
38
43
|
end
|
|
39
44
|
|
|
40
|
-
class Array #:nodoc
|
|
45
|
+
class Array #:nodoc
|
|
46
|
+
#:nodoc
|
|
41
47
|
alias_method :blank?, :empty?
|
|
42
48
|
end
|
|
43
49
|
|
|
44
|
-
class Hash #:nodoc
|
|
50
|
+
class Hash #:nodoc
|
|
51
|
+
#:nodoc
|
|
45
52
|
alias_method :blank?, :empty?
|
|
46
53
|
end
|
|
47
54
|
|
|
48
|
-
class String #:nodoc
|
|
55
|
+
class String #:nodoc
|
|
56
|
+
#:nodoc
|
|
49
57
|
def blank?
|
|
50
58
|
self !~ /\S/
|
|
51
59
|
end
|
|
52
60
|
end
|
|
53
61
|
|
|
54
|
-
class Numeric #:nodoc
|
|
62
|
+
class Numeric #:nodoc
|
|
63
|
+
#:nodoc
|
|
55
64
|
def blank?
|
|
56
65
|
false
|
|
57
66
|
end
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
# end
|
|
7
7
|
#
|
|
8
8
|
# Person.hair_colors = [:brown, :black, :blonde, :red]
|
|
9
|
-
class Class
|
|
9
|
+
class Class #:nodoc:all
|
|
10
|
+
#:nodoc
|
|
10
11
|
def cattr_reader(*syms)
|
|
11
12
|
syms.flatten.each do |sym|
|
|
12
13
|
next if sym.is_a?(Hash)
|
|
@@ -26,6 +27,7 @@ class Class
|
|
|
26
27
|
end
|
|
27
28
|
end
|
|
28
29
|
|
|
30
|
+
#:nodoc
|
|
29
31
|
def cattr_writer(*syms)
|
|
30
32
|
options = syms.extract_options!
|
|
31
33
|
syms.flatten.each do |sym|
|
|
@@ -47,6 +49,7 @@ class Class
|
|
|
47
49
|
end
|
|
48
50
|
end
|
|
49
51
|
|
|
52
|
+
#:nodoc
|
|
50
53
|
def cattr_accessor(*syms)
|
|
51
54
|
cattr_reader(*syms)
|
|
52
55
|
cattr_writer(*syms)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
module ActiveSupport #:nodoc:
|
|
2
|
-
module CoreExtensions #:nodoc
|
|
3
|
-
module Hash #:nodoc
|
|
1
|
+
module ActiveSupport #:nodoc:all:
|
|
2
|
+
module CoreExtensions #:nodoc
|
|
3
|
+
module Hash #:nodoc
|
|
4
4
|
module Keys
|
|
5
5
|
# Return a new hash with all keys converted to strings.
|
|
6
|
+
#:nodoc
|
|
6
7
|
def stringify_keys
|
|
7
8
|
inject({}) do |options, (key, value)|
|
|
8
9
|
options[key.to_s] = value
|
|
@@ -11,6 +12,7 @@ module ActiveSupport #:nodoc:
|
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
# Destructively convert all keys to strings.
|
|
15
|
+
#:nodoc
|
|
14
16
|
def stringify_keys!
|
|
15
17
|
keys.each do |key|
|
|
16
18
|
self[key.to_s] = delete(key)
|
|
@@ -19,6 +21,7 @@ module ActiveSupport #:nodoc:
|
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
# Return a new hash with all keys converted to symbols.
|
|
24
|
+
#:nodoc
|
|
22
25
|
def symbolize_keys
|
|
23
26
|
inject({}) do |options, (key, value)|
|
|
24
27
|
options[(key.to_sym rescue key) || key] = value
|
|
@@ -27,6 +30,7 @@ module ActiveSupport #:nodoc:
|
|
|
27
30
|
end
|
|
28
31
|
|
|
29
32
|
# Destructively convert all keys to symbols.
|
|
33
|
+
#:nodoc
|
|
30
34
|
def symbolize_keys!
|
|
31
35
|
self.replace(self.symbolize_keys)
|
|
32
36
|
end
|
|
@@ -42,6 +46,7 @@ module ActiveSupport #:nodoc:
|
|
|
42
46
|
# { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
|
|
43
47
|
# { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
|
|
44
48
|
# { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
|
|
49
|
+
#:nodoc
|
|
45
50
|
def assert_valid_keys(*valid_keys)
|
|
46
51
|
unknown_keys = keys - [valid_keys].flatten
|
|
47
52
|
raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class Object
|
|
1
|
+
class Object #:nodoc:all
|
|
2
2
|
# Returns +value+ after yielding +value+ to the block. This simplifies the
|
|
3
3
|
# process of constructing an object, performing work on the object, and then
|
|
4
4
|
# returning the object from a method. It is a Ruby-ized realization of the K
|
|
@@ -25,7 +25,7 @@ class Object
|
|
|
25
25
|
# end
|
|
26
26
|
#
|
|
27
27
|
# foo # => ['bar', 'baz']
|
|
28
|
-
#
|
|
28
|
+
#
|
|
29
29
|
# # returning with a block argument
|
|
30
30
|
# def foo
|
|
31
31
|
# returning [] do |values|
|
|
@@ -33,8 +33,9 @@ class Object
|
|
|
33
33
|
# values << 'baz'
|
|
34
34
|
# end
|
|
35
35
|
# end
|
|
36
|
-
#
|
|
36
|
+
#
|
|
37
37
|
# foo # => ['bar', 'baz']
|
|
38
|
+
#:nodoc
|
|
38
39
|
def returning(value)
|
|
39
40
|
yield(value)
|
|
40
41
|
value
|
|
@@ -50,8 +51,9 @@ class Object
|
|
|
50
51
|
# tap { |x| puts "evens: #{x.inspect}" }.
|
|
51
52
|
# map { |x| x*x }.
|
|
52
53
|
# tap { |x| puts "squares: #{x.inspect}" }
|
|
54
|
+
#:nodoc
|
|
53
55
|
def tap
|
|
54
56
|
yield self
|
|
55
57
|
self
|
|
56
58
|
end unless Object.respond_to?(:tap)
|
|
57
|
-
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
class Module
|
|
2
|
+
# Provides a delegate class method to easily expose contained objects' methods
|
|
3
|
+
# as your own. Pass one or more methods (specified as symbols or strings)
|
|
4
|
+
# and the name of the target object as the final <tt>:to</tt> option (also a symbol
|
|
5
|
+
# or string). At least one method and the <tt>:to</tt> option are required.
|
|
6
|
+
#
|
|
7
|
+
# Delegation is particularly useful with Active Record associations:
|
|
8
|
+
#
|
|
9
|
+
# class Greeter < ActiveRecord::Base
|
|
10
|
+
# def hello() "hello" end
|
|
11
|
+
# def goodbye() "goodbye" end
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# class Foo < ActiveRecord::Base
|
|
15
|
+
# belongs_to :greeter
|
|
16
|
+
# delegate :hello, :to => :greeter
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# Foo.new.hello # => "hello"
|
|
20
|
+
# Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
|
|
21
|
+
#
|
|
22
|
+
# Multiple delegates to the same target are allowed:
|
|
23
|
+
#
|
|
24
|
+
# class Foo < ActiveRecord::Base
|
|
25
|
+
# belongs_to :greeter
|
|
26
|
+
# delegate :hello, :goodbye, :to => :greeter
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# Foo.new.goodbye # => "goodbye"
|
|
30
|
+
#
|
|
31
|
+
# Methods can be delegated to instance variables, class variables, or constants
|
|
32
|
+
# by providing them as a symbols:
|
|
33
|
+
#
|
|
34
|
+
# class Foo
|
|
35
|
+
# CONSTANT_ARRAY = [0,1,2,3]
|
|
36
|
+
# @@class_array = [4,5,6,7]
|
|
37
|
+
#
|
|
38
|
+
# def initialize
|
|
39
|
+
# @instance_array = [8,9,10,11]
|
|
40
|
+
# end
|
|
41
|
+
# delegate :sum, :to => :CONSTANT_ARRAY
|
|
42
|
+
# delegate :min, :to => :@@class_array
|
|
43
|
+
# delegate :max, :to => :@instance_array
|
|
44
|
+
# end
|
|
45
|
+
#
|
|
46
|
+
# Foo.new.sum # => 6
|
|
47
|
+
# Foo.new.min # => 4
|
|
48
|
+
# Foo.new.max # => 11
|
|
49
|
+
#
|
|
50
|
+
# Delegates can optionally be prefixed using the <tt>:prefix</tt> option. If the value
|
|
51
|
+
# is <tt>true</tt>, the delegate methods are prefixed with the name of the object being
|
|
52
|
+
# delegated to.
|
|
53
|
+
#
|
|
54
|
+
# Person = Struct.new(:name, :address)
|
|
55
|
+
#
|
|
56
|
+
# class Invoice < Struct.new(:client)
|
|
57
|
+
# delegate :name, :address, :to => :client, :prefix => true
|
|
58
|
+
# end
|
|
59
|
+
#
|
|
60
|
+
# john_doe = Person.new("John Doe", "Vimmersvej 13")
|
|
61
|
+
# invoice = Invoice.new(john_doe)
|
|
62
|
+
# invoice.client_name # => "John Doe"
|
|
63
|
+
# invoice.client_address # => "Vimmersvej 13"
|
|
64
|
+
#
|
|
65
|
+
# It is also possible to supply a custom prefix.
|
|
66
|
+
#
|
|
67
|
+
# class Invoice < Struct.new(:client)
|
|
68
|
+
# delegate :name, :address, :to => :client, :prefix => :customer
|
|
69
|
+
# end
|
|
70
|
+
#
|
|
71
|
+
# invoice = Invoice.new(john_doe)
|
|
72
|
+
# invoice.customer_name # => "John Doe"
|
|
73
|
+
# invoice.customer_address # => "Vimmersvej 13"
|
|
74
|
+
#
|
|
75
|
+
# If the object to which you delegate can be nil, you may want to use the
|
|
76
|
+
# :allow_nil option. In that case, it returns nil instead of raising a
|
|
77
|
+
# NoMethodError exception:
|
|
78
|
+
#
|
|
79
|
+
# class Foo
|
|
80
|
+
# attr_accessor :bar
|
|
81
|
+
# def initialize(bar = nil)
|
|
82
|
+
# @bar = bar
|
|
83
|
+
# end
|
|
84
|
+
# delegate :zoo, :to => :bar
|
|
85
|
+
# end
|
|
86
|
+
#
|
|
87
|
+
# Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
|
|
88
|
+
#
|
|
89
|
+
# class Foo
|
|
90
|
+
# attr_accessor :bar
|
|
91
|
+
# def initialize(bar = nil)
|
|
92
|
+
# @bar = bar
|
|
93
|
+
# end
|
|
94
|
+
# delegate :zoo, :to => :bar, :allow_nil => true
|
|
95
|
+
# end
|
|
96
|
+
#
|
|
97
|
+
# Foo.new.zoo # returns nil
|
|
98
|
+
#
|
|
99
|
+
def delegate(*methods)
|
|
100
|
+
options = methods.pop
|
|
101
|
+
unless options.is_a?(Hash) && to = options[:to]
|
|
102
|
+
raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/
|
|
106
|
+
raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"
|
|
110
|
+
|
|
111
|
+
file, line = caller.first.split(':', 2)
|
|
112
|
+
line = line.to_i
|
|
113
|
+
|
|
114
|
+
methods.each do |method|
|
|
115
|
+
on_nil =
|
|
116
|
+
if options[:allow_nil]
|
|
117
|
+
'return'
|
|
118
|
+
else
|
|
119
|
+
%(raise "#{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
module_eval(<<-EOS, file, line)
|
|
123
|
+
def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
|
|
124
|
+
#{on_nil} if #{to}.nil?
|
|
125
|
+
#{to}.__send__(#{method.inspect}, *args, &block) # client && client.__send__(:name, *args, &block)
|
|
126
|
+
end # end
|
|
127
|
+
EOS
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require File.join( File.dirname( __FILE__),'module/delegation')
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
class String
|
|
1
|
+
class String #:nodoc:all
|
|
2
|
+
#:nodoc
|
|
2
3
|
def underscore
|
|
3
4
|
self.gsub(/::/, '/').
|
|
4
5
|
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
|
@@ -7,10 +8,12 @@ class String
|
|
|
7
8
|
downcase
|
|
8
9
|
end
|
|
9
10
|
|
|
11
|
+
#:nodoc
|
|
10
12
|
def demodulize
|
|
11
13
|
gsub(/^.*::/, '')
|
|
12
14
|
end
|
|
13
15
|
|
|
16
|
+
#:nodoc
|
|
14
17
|
def constantize
|
|
15
18
|
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
|
|
16
19
|
raise NameError, "#{self} is not a valid constant name!"
|
|
@@ -18,10 +21,12 @@ class String
|
|
|
18
21
|
Object.module_eval("::#{$1}", __FILE__, __LINE__)
|
|
19
22
|
end
|
|
20
23
|
|
|
24
|
+
#:nodoc
|
|
21
25
|
def classify # DOES NOT SINGULARISE
|
|
22
26
|
camelize(self.sub(/.*\./, ''))
|
|
23
27
|
end
|
|
24
28
|
|
|
29
|
+
#:nodoc
|
|
25
30
|
def camelize( first_letter_in_uppercase = true)
|
|
26
31
|
if first_letter_in_uppercase
|
|
27
32
|
gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
unless :to_proc.respond_to?(:to_proc)
|
|
1
|
+
unless :to_proc.respond_to?(:to_proc) #:nodoc:all
|
|
2
2
|
class Symbol
|
|
3
3
|
# Turns the symbol into a simple proc, which is especially useful for enumerations. Examples:
|
|
4
4
|
#
|
|
@@ -7,6 +7,7 @@ unless :to_proc.respond_to?(:to_proc)
|
|
|
7
7
|
#
|
|
8
8
|
# # The same as people.select { |p| p.manager? }.collect { |p| p.salary }
|
|
9
9
|
# people.select(&:manager?).collect(&:salary)
|
|
10
|
+
#:nodoc
|
|
10
11
|
def to_proc
|
|
11
12
|
Proc.new { |*args| args.shift.__send__(self, *args) }
|
|
12
13
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module StateFu
|
|
2
|
+
module Applicable
|
|
3
|
+
module InstanceMethods
|
|
4
|
+
|
|
5
|
+
# if given a hash of options (or a splatted arglist containing
|
|
6
|
+
# one), merge them into @options. If given a block, eval it
|
|
7
|
+
# (yielding self if the block expects it)
|
|
8
|
+
|
|
9
|
+
def apply!( _options=nil, &block )
|
|
10
|
+
if _options.is_a?(Array)
|
|
11
|
+
_options = _options.dup.extract_options!.symbolize_keys
|
|
12
|
+
else
|
|
13
|
+
_options ||= {}
|
|
14
|
+
_options = _options.symbolize_keys!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
@options = @options.nil?? _options : @options.merge(_options)
|
|
18
|
+
returning self do
|
|
19
|
+
if block_given?
|
|
20
|
+
case block.arity.abs
|
|
21
|
+
when 1, -1
|
|
22
|
+
instance_exec self, &block
|
|
23
|
+
when 0
|
|
24
|
+
instance_exec &block
|
|
25
|
+
else
|
|
26
|
+
raise ArgumentError, "Your block wants too many arguments!"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
module ClassMethods
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.included( mod )
|
|
37
|
+
mod.send( :include, InstanceMethods )
|
|
38
|
+
mod.extend( ClassMethods )
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -1,48 +1,12 @@
|
|
|
1
1
|
module StateFu
|
|
2
2
|
|
|
3
|
-
# Utilities and snippets
|
|
4
|
-
module Helper
|
|
5
|
-
|
|
6
|
-
# Instance methods mixed in on inclusion of StateFu::Helper
|
|
7
|
-
module InstanceMethods
|
|
8
|
-
|
|
9
|
-
# if given a hash of options (or a splatted arglist containing
|
|
10
|
-
# one), merge them into @options. If given a block, eval it
|
|
11
|
-
# (yielding self if the block expects it)
|
|
12
|
-
def apply!( options={}, &block )
|
|
13
|
-
options.respond_to?(:keys) || options = options.extract_options!
|
|
14
|
-
@options.merge!( options.symbolize_keys! )
|
|
15
|
-
return self unless block_given?
|
|
16
|
-
case block.arity
|
|
17
|
-
when 1 # lambda{ |state| ... }
|
|
18
|
-
yield self
|
|
19
|
-
when -1, 0 # lambda{ } ( -1 in ruby 1.8.x but 0 in 1.9.x )
|
|
20
|
-
instance_eval &block
|
|
21
|
-
else
|
|
22
|
-
raise ArgumentError, "unexpected block arity: #{block.arity}"
|
|
23
|
-
end
|
|
24
|
-
self
|
|
25
|
-
end
|
|
26
|
-
alias_method :update!, :apply!
|
|
27
|
-
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Class methods mixed in on inclusion of StateFu::Helper
|
|
31
|
-
module ClassMethods
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def self.included( mod )
|
|
35
|
-
mod.send( :include, InstanceMethods )
|
|
36
|
-
mod.extend( ClassMethods )
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
end
|
|
40
|
-
|
|
41
3
|
# Stuff shared between StateArray and EventArray
|
|
42
4
|
module ArrayWithSymbolAccessor
|
|
5
|
+
|
|
43
6
|
# Pass a symbol to the array and get the object with that .name
|
|
44
7
|
# [<Foo @name=:bob>][:bob]
|
|
45
8
|
# => <Foo @name=:bob>
|
|
9
|
+
|
|
46
10
|
def []( idx )
|
|
47
11
|
begin
|
|
48
12
|
super( idx )
|
|
@@ -69,9 +33,41 @@ module StateFu
|
|
|
69
33
|
def only *syms
|
|
70
34
|
select {|el| syms.flatten.compact.map(&:to_sym).include?(el.to_sym) } #.extend ArrayWithSymbolAccessor
|
|
71
35
|
end
|
|
36
|
+
|
|
37
|
+
def all
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def rand
|
|
42
|
+
self.rand
|
|
43
|
+
end
|
|
72
44
|
|
|
73
45
|
end
|
|
74
46
|
|
|
47
|
+
module TransitionArgsArray
|
|
48
|
+
attr_reader :transition
|
|
49
|
+
|
|
50
|
+
def init(t)
|
|
51
|
+
@transition = t
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
delegate :options, :to => :transition
|
|
56
|
+
delegate :binding, :to => :transition
|
|
57
|
+
delegate :machine, :to => :transition
|
|
58
|
+
delegate :origin, :to => :transition
|
|
59
|
+
delegate :target, :to => :transition
|
|
60
|
+
|
|
61
|
+
def []( index )
|
|
62
|
+
begin
|
|
63
|
+
super( index )
|
|
64
|
+
rescue TypeError
|
|
65
|
+
options[index]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
|
|
75
71
|
# Array extender. Used by Machine to keep a list of states.
|
|
76
72
|
module StateArray
|
|
77
73
|
include ArrayWithSymbolAccessor
|
|
@@ -153,6 +149,17 @@ module StateFu
|
|
|
153
149
|
include ModuleRefArray
|
|
154
150
|
end
|
|
155
151
|
|
|
152
|
+
|
|
153
|
+
module MessageArray
|
|
154
|
+
def strings
|
|
155
|
+
select { |m| m.is_a? String }
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def symbols
|
|
159
|
+
select { |m| m.is_a? Symbol }
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
156
163
|
# Extend an Array with this. It's a fairly compact implementation,
|
|
157
164
|
# though it won't be super fast with lots of elements.
|
|
158
165
|
# items. Internally objects are stored as a list of
|
|
@@ -187,87 +194,4 @@ module StateFu
|
|
|
187
194
|
map(&:last)
|
|
188
195
|
end
|
|
189
196
|
end # OrderedHash
|
|
190
|
-
|
|
191
|
-
# satanic incantations we use for evaluating blocks conditionally,
|
|
192
|
-
# massaging their arguments and managing execution context.
|
|
193
|
-
module ContextualEval
|
|
194
|
-
# :nodoc:
|
|
195
|
-
module InstanceMethods
|
|
196
|
-
|
|
197
|
-
# if we use &block syntax it stuffs the arity up, so we have to
|
|
198
|
-
# pass it as a normal argument. Ruby bug!
|
|
199
|
-
def limit_arguments( block, *args )
|
|
200
|
-
case block.arity
|
|
201
|
-
when -1, 0
|
|
202
|
-
nil
|
|
203
|
-
else
|
|
204
|
-
args[0 .. (block.arity - 1) ]
|
|
205
|
-
end
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
def evaluate( *args, &proc )
|
|
209
|
-
if proc.arity > 0
|
|
210
|
-
args = limit_arguments( proc, *args )
|
|
211
|
-
object.instance_exec( *args, &proc )
|
|
212
|
-
else
|
|
213
|
-
instance_eval( &proc )
|
|
214
|
-
end
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
def call_on_object_with_optional_args( name, *args )
|
|
218
|
-
if meth = object.method( name )
|
|
219
|
-
args = limit_arguments( meth, *args )
|
|
220
|
-
if args.nil?
|
|
221
|
-
object.send( name )
|
|
222
|
-
else
|
|
223
|
-
object.send( name, *args )
|
|
224
|
-
end
|
|
225
|
-
else
|
|
226
|
-
raise NoMethodError.new( "undefined method #{name} for #{object.inspect}" )
|
|
227
|
-
end
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def call_on_object_with_self( name )
|
|
231
|
-
call_on_object_with_optional_args( name, self )
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
def evaluate_named_proc_or_method( name, *args )
|
|
235
|
-
if (name.is_a?( Proc ) && proc = name) || proc = machine.named_procs[ name ]
|
|
236
|
-
evaluate( *args, &proc )
|
|
237
|
-
elsif self.respond_to?( name )
|
|
238
|
-
if method(name).arity == 0
|
|
239
|
-
send(name)
|
|
240
|
-
else
|
|
241
|
-
send(name, *args )
|
|
242
|
-
end
|
|
243
|
-
# evaluate( *args, &method(name) )
|
|
244
|
-
elsif object.respond_to?( name )
|
|
245
|
-
call_on_object_with_optional_args( name, *args )
|
|
246
|
-
else # method is not defined
|
|
247
|
-
if name.to_s =~ /^not_(.*)$/
|
|
248
|
-
!evaluate_named_proc_or_method( $1, *args )
|
|
249
|
-
else
|
|
250
|
-
raise NoMethodError.new("#{name} is not defined on #{object} or #{self} or as a named proc in #{machine}")
|
|
251
|
-
end
|
|
252
|
-
end
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
def find_event_target( evt, tgt )
|
|
256
|
-
case tgt
|
|
257
|
-
when StateFu::State
|
|
258
|
-
tgt
|
|
259
|
-
when Symbol
|
|
260
|
-
binding && binding.machine.states[ tgt ] # || raise( tgt.inspect )
|
|
261
|
-
when NilClass
|
|
262
|
-
evt.respond_to?(:target) && evt.target
|
|
263
|
-
else
|
|
264
|
-
raise ArgumentError.new( "#{tgt.class} is not a Symbol, StateFu::State or nil (#{evt})" )
|
|
265
|
-
end
|
|
266
|
-
end
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
def self.included( klass )
|
|
270
|
-
klass.send :include, InstanceMethods
|
|
271
|
-
end
|
|
272
|
-
end
|
|
273
197
|
end
|