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
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
module StateFu
|
|
2
|
+
class TransitionQuery #< Array
|
|
3
|
+
attr_accessor :binding, :options, :result, :args, :block
|
|
4
|
+
|
|
5
|
+
def initialize(binding, options={})
|
|
6
|
+
defaults = { :valid => true, :cyclic => nil }
|
|
7
|
+
@options = defaults.merge(options).symbolize_keys
|
|
8
|
+
@binding = binding
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
include Enumerable
|
|
12
|
+
|
|
13
|
+
def each *a, &b
|
|
14
|
+
result.each *a, &b
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def method_missing(method_name, *args, &block)
|
|
18
|
+
if result.respond_to?(method_name, true)
|
|
19
|
+
result.__send__(method_name, *args, &block)
|
|
20
|
+
else
|
|
21
|
+
super(method_name, *args, &block)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
#
|
|
26
|
+
#
|
|
27
|
+
#
|
|
28
|
+
|
|
29
|
+
def find( event_or_array )
|
|
30
|
+
event, target = parse_destination(event_or_array)
|
|
31
|
+
_args, _block = @args, @block
|
|
32
|
+
returning binding.new_transition(event, target) do |t|
|
|
33
|
+
t.apply!(&_block) if _block
|
|
34
|
+
if _args
|
|
35
|
+
t.args = _args
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def cyclic
|
|
41
|
+
@options.merge! :cyclic => true
|
|
42
|
+
self
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def not_cyclic
|
|
46
|
+
@options.merge! :cyclic => false
|
|
47
|
+
self
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def valid
|
|
51
|
+
@options.merge! :valid => true
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def not_valid
|
|
56
|
+
@options.merge! :valid => false
|
|
57
|
+
self
|
|
58
|
+
end
|
|
59
|
+
alias_method :invalid, :not_valid
|
|
60
|
+
|
|
61
|
+
def to state
|
|
62
|
+
@options.merge! :target => state
|
|
63
|
+
self
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def for event
|
|
67
|
+
@options.merge! :event => event
|
|
68
|
+
self
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def simple
|
|
72
|
+
@options.merge! :simple => true
|
|
73
|
+
self
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#
|
|
77
|
+
#
|
|
78
|
+
#
|
|
79
|
+
|
|
80
|
+
def singular
|
|
81
|
+
result.first if result.length == 1
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def next
|
|
85
|
+
@options[:cyclic] ||= false
|
|
86
|
+
singular
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def next_state
|
|
90
|
+
@options[:cyclic] ||= false
|
|
91
|
+
if result.map(&:target).uniq.length == 1
|
|
92
|
+
result.first.target
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def next_event
|
|
97
|
+
@options[:cyclic] ||= false
|
|
98
|
+
if result.map(&:event).uniq.length == 1
|
|
99
|
+
result.first.event
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
#
|
|
104
|
+
#
|
|
105
|
+
#
|
|
106
|
+
|
|
107
|
+
def events
|
|
108
|
+
map {|t| t.event }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def targets
|
|
112
|
+
map {|t| t.target }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def apply! # (&block
|
|
116
|
+
result.each { |t| t.apply &block if block }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def with(*args, &block)
|
|
120
|
+
@args = args
|
|
121
|
+
@block = block
|
|
122
|
+
self
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def all_destinations
|
|
126
|
+
binding.events.inject([]){ |arr, evt| arr += evt.targets.map{|tgt| [evt,tgt] }; arr}.uniq
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def all_destination_names
|
|
130
|
+
all_destinations.map {|tuple| tuple.map(&:to_sym) }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
#
|
|
136
|
+
# Result
|
|
137
|
+
#
|
|
138
|
+
|
|
139
|
+
module Result
|
|
140
|
+
def states
|
|
141
|
+
map(&:target).uniq.extend StateArray
|
|
142
|
+
end
|
|
143
|
+
alias_method :targets, :states
|
|
144
|
+
alias_method :next_states, :states
|
|
145
|
+
|
|
146
|
+
def events
|
|
147
|
+
map(&:event).uniq.extend EventArray
|
|
148
|
+
end
|
|
149
|
+
end # Result
|
|
150
|
+
|
|
151
|
+
def result
|
|
152
|
+
@result = binding.events.select do |e|
|
|
153
|
+
case options[:cyclic]
|
|
154
|
+
when true
|
|
155
|
+
e.cycle?
|
|
156
|
+
when false
|
|
157
|
+
!e.cycle?
|
|
158
|
+
else
|
|
159
|
+
true
|
|
160
|
+
end
|
|
161
|
+
end.map do |event|
|
|
162
|
+
next if options[:event] and event != options[:event]
|
|
163
|
+
returning [] do |ts|
|
|
164
|
+
|
|
165
|
+
# TODO hmm ... "sequences" ... delete this?
|
|
166
|
+
if options[:sequences]
|
|
167
|
+
if target = event.target_for_origin(current_state)
|
|
168
|
+
ts << binding.transition([event,target], *args) unless options[:cyclic]
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
if event.targets
|
|
173
|
+
next unless event.target if options[:simple]
|
|
174
|
+
event.targets.flatten.each do |target|
|
|
175
|
+
next if options[:target] and target != options[:target]
|
|
176
|
+
t = binding.new_transition( event, target, *args)
|
|
177
|
+
ts << t if (t.valid? or !options[:valid])
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
end
|
|
182
|
+
end.flatten.extend(Result)
|
|
183
|
+
|
|
184
|
+
if @args || @block
|
|
185
|
+
@result.each do |t|
|
|
186
|
+
t.apply!( &@block) if @block
|
|
187
|
+
t.args = @args if @args
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
@result
|
|
192
|
+
end # result
|
|
193
|
+
|
|
194
|
+
# sanitizes / extracts destination for find.
|
|
195
|
+
#
|
|
196
|
+
# takes a single, simple (one target only) event,
|
|
197
|
+
# or an array of [event, target],
|
|
198
|
+
# or one of the above with symbols in place of the objects themselves.
|
|
199
|
+
def parse_destination(event_or_array)
|
|
200
|
+
case event_or_array
|
|
201
|
+
when Event, Symbol
|
|
202
|
+
event = event_or_array
|
|
203
|
+
target = nil
|
|
204
|
+
when Array
|
|
205
|
+
event, target = *event_or_array
|
|
206
|
+
end
|
|
207
|
+
raise ArgumentError.new( [event,target].inspect ) unless
|
|
208
|
+
[Event, Symbol].include?(event.class) &&
|
|
209
|
+
[State, Symbol, NilClass].include?(target.class)
|
|
210
|
+
[event, target]
|
|
211
|
+
end # parse_destination
|
|
212
|
+
|
|
213
|
+
end
|
|
214
|
+
end
|
data/lib/state_fu.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.basename(__FILE__, 'state-fu')))
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
|
|
3
|
+
unless Object.const_defined?('STATE_FU_APP_PATH')
|
|
4
|
+
STATE_FU_APP_PATH = Object.const_defined?('RAILS_ROOT') ? RAILS_ROOT : File.join( File.dirname(__FILE__), '/../..')
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
unless Object.const_defined?('STATE_FU_PLUGIN_PATH')
|
|
8
|
+
STATE_FU_PLUGIN_PATH = Object.const_defined?('RAILS_ROOT') ? File.join( RAILS_ROOT, '/vendor/plugins/state-fu' ) : STATE_FU_APP_PATH
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
namespace :spec do
|
|
12
|
+
def find_last_modified_spec
|
|
13
|
+
require 'find'
|
|
14
|
+
specs = []
|
|
15
|
+
Find.find( File.expand_path(File.join(STATE_FU_APP_PATH,'spec'))) do |f|
|
|
16
|
+
next unless f !~ /\.#/ && f =~ /_spec.rb$/
|
|
17
|
+
specs << f
|
|
18
|
+
end
|
|
19
|
+
spec = specs.sort_by { |spec| File.stat( spec ).mtime }.last
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc "runs the last modified spec; L=n runs only that line"
|
|
23
|
+
Spec::Rake::SpecTask.new(:last) do |t|
|
|
24
|
+
specfile = find_last_modified_spec || return
|
|
25
|
+
t.verbose = true
|
|
26
|
+
t.spec_opts = ["-c","-b","-u"]
|
|
27
|
+
if ENV['L']
|
|
28
|
+
t.spec_opts += ["-l", ENV["L"],"-f", "specdoc"]
|
|
29
|
+
else
|
|
30
|
+
t.spec_opts += ["-f", "profile"]
|
|
31
|
+
end
|
|
32
|
+
t.spec_files = FileList[specfile]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
desc "runs all specs, or those which last failed"
|
|
36
|
+
Spec::Rake::SpecTask.new(:faily) do |t|
|
|
37
|
+
specfile = find_last_modified_spec || return
|
|
38
|
+
faily = 'spec.fail'
|
|
39
|
+
t.verbose = true
|
|
40
|
+
t.spec_opts = ["-f","failing_examples:#{faily}", "-f","n","-c","-b","-u"]
|
|
41
|
+
if File.exists?(faily) && File.read(faily).split("\n")[0] != ""
|
|
42
|
+
t.spec_opts << ["-e",faily]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
|
|
3
|
+
unless Object.const_defined?('STATE_FU_APP_PATH')
|
|
4
|
+
STATE_FU_APP_PATH = Object.const_defined?('RAILS_ROOT') ? RAILS_ROOT : File.join( File.dirname(__FILE__), '/../..')
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
unless Object.const_defined?('STATE_FU_PLUGIN_PATH')
|
|
8
|
+
STATE_FU_PLUGIN_PATH = Object.const_defined?('RAILS_ROOT') ? File.join( RAILS_ROOT, '/vendor/plugins/state-fu' ) : STATE_FU_APP_PATH
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
namespace :state_fu do
|
|
12
|
+
|
|
13
|
+
task :update do
|
|
14
|
+
path = STATE_FU_PLUGIN_PATH
|
|
15
|
+
pwd = FileUtils.pwd
|
|
16
|
+
FileUtils.cd( path )
|
|
17
|
+
system('git pull')
|
|
18
|
+
FileUtils.cd pwd
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def graph_name( klass, machine, doc_path = false )
|
|
22
|
+
parts = ["#{klass}_#{machine}"]
|
|
23
|
+
if doc_path
|
|
24
|
+
folder = parts.unshift( File.join( STATE_FU_APP_PATH, "doc/") )
|
|
25
|
+
FileUtils.mkdir_p( folder )
|
|
26
|
+
parts.push( '.png' )
|
|
27
|
+
end
|
|
28
|
+
parts.join
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def graph( klass, machine )
|
|
32
|
+
name = graph_name( klass, machine )
|
|
33
|
+
graphviz = `which dot`.strip || raise("Graphviz not installed? Can't find dot executable!")
|
|
34
|
+
puts graphviz
|
|
35
|
+
tmp_dot = "/tmp/#{name}.dot"
|
|
36
|
+
klass.machine( machine.to_sym ).graphviz.save_as( tmp_dot )
|
|
37
|
+
tmp_png = tmp_dot + '.png'
|
|
38
|
+
doc_png = graph_name( klass, machine, true )
|
|
39
|
+
puts( "#{graphviz} -Tpng -O #{tmp_dot}" )
|
|
40
|
+
system( "#{graphviz} -Tpng -O #{tmp_dot}" )
|
|
41
|
+
FileUtils.cp tmp_png, doc_png
|
|
42
|
+
doc_png
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
desc "Graph workflows with dot"
|
|
46
|
+
task :graph => :environment do |t|
|
|
47
|
+
state_fu_classes = ObjectSpace.each_object { |o| x << o if o.respond_to? :machines }
|
|
48
|
+
state_fu_classes.each do |klass|
|
|
49
|
+
klass.state_fu_machines.each do |machine_name, machine|
|
|
50
|
+
STDERR.puts "#{klass} -> #{machine_name.inspect}"
|
|
51
|
+
doc_png = graph( klass, machine_name )
|
|
52
|
+
# yield doc_png if block_given?
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
# `open #{doc_png}`
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/vizier.rb
CHANGED
|
@@ -4,43 +4,43 @@ begin
|
|
|
4
4
|
require File.join(File.dirname(__FILE__), '/state_fu/core_ext' )
|
|
5
5
|
rescue LoadError
|
|
6
6
|
require 'activesupport'
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
module Vizier
|
|
15
|
-
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Vizier is a simple library to help generate dot output for graphviz. It is used by StateFu's rake
|
|
10
|
+
# tasks to generate graphs of state machines.
|
|
11
|
+
#
|
|
12
|
+
# Sorry, there's only Heisendocumentation (if I realize anyone's looking for docs, I might write some)
|
|
13
|
+
#
|
|
14
|
+
module Vizier #:nodoc:all
|
|
15
|
+
|
|
16
16
|
module Support
|
|
17
17
|
LEGAL_CHARS = 'a-zA-Z0-9_'
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
def attributes=( attrs )
|
|
20
20
|
@attributes = attrs.symbolize_keys!.extend( Attributes )
|
|
21
21
|
end
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
def attributes
|
|
24
24
|
(@attributes ||= {}).extend( Attributes )
|
|
25
25
|
end
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
def legal?( str )
|
|
28
28
|
str =~ /^[#{LEGAL_CHARS}]+$/ && str == str.split
|
|
29
29
|
end
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
def sanitize(str)
|
|
32
32
|
sanitize( str )
|
|
33
33
|
end
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
def quote( str )
|
|
36
36
|
return str if legal?( str )
|
|
37
37
|
'"' + str.to_s.gsub(/"/,'\"') + '"'
|
|
38
38
|
end
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
def self.included( klass )
|
|
41
41
|
klass.extend( ClassMethods )
|
|
42
42
|
end
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
module Finder
|
|
45
45
|
def []( idx )
|
|
46
46
|
begin
|
|
@@ -56,12 +56,12 @@ module Vizier
|
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
module ClassMethods
|
|
61
61
|
def sanitize( str )
|
|
62
62
|
str.to_s.gsub(/[^#{LEGAL_CHARS}]/,'_').gsub(/__+/,'_')
|
|
63
63
|
end
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
def finder( name )
|
|
66
66
|
class_eval do
|
|
67
67
|
define_method name do
|
|
@@ -71,63 +71,63 @@ module Vizier
|
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
module Attributes
|
|
76
76
|
include Support
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
def to_s
|
|
79
79
|
return '[]' if empty?
|
|
80
80
|
'[ ' + self.map do |k,v|
|
|
81
81
|
"#{quote k} = #{quote v}"
|
|
82
82
|
end.join(" ") + ' ]'
|
|
83
83
|
end
|
|
84
|
-
|
|
84
|
+
|
|
85
85
|
end
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
class Base
|
|
88
88
|
def [](k)
|
|
89
89
|
attributes[k.to_sym]
|
|
90
90
|
end
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
def []=(k,v)
|
|
93
93
|
attributes[k.to_sym] = v
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
class Link < Base
|
|
98
98
|
include Support
|
|
99
99
|
attr_accessor :from
|
|
100
100
|
attr_accessor :to
|
|
101
|
-
|
|
101
|
+
|
|
102
102
|
def initialize( from, to, attrs={} )
|
|
103
103
|
self.attributes = attrs
|
|
104
104
|
@from = extract_name( from )
|
|
105
105
|
@to = extract_name( to )
|
|
106
106
|
end
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
def extract_name( o )
|
|
109
109
|
o.is_a?(String) ? o : o.name
|
|
110
110
|
end
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
def to_str
|
|
113
113
|
"#{quote from} -> #{quote to} #{attributes};"
|
|
114
114
|
end
|
|
115
115
|
end
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
# TODO ..
|
|
118
118
|
module Label
|
|
119
119
|
def []( i )
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
end
|
|
122
122
|
end
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
class Node < Base
|
|
125
125
|
include Support
|
|
126
|
-
|
|
126
|
+
|
|
127
127
|
attr_accessor :object
|
|
128
128
|
attr_accessor :fields
|
|
129
129
|
attr_accessor :name
|
|
130
|
-
|
|
130
|
+
|
|
131
131
|
def initialize( name = nil, attrs={} )
|
|
132
132
|
self.attributes = attrs
|
|
133
133
|
if name.is_a?( String )
|
|
@@ -139,65 +139,65 @@ module Vizier
|
|
|
139
139
|
@label = attrs.delete(:label) || Node.first_response( @object, :name, :identifier, :label ) || name
|
|
140
140
|
end
|
|
141
141
|
end
|
|
142
|
-
|
|
142
|
+
|
|
143
143
|
def self.make_name( obj )
|
|
144
144
|
sanitize [ obj.class, first_response( obj, :name, :identifier, :id, :hash)].join('_')
|
|
145
145
|
end
|
|
146
|
-
|
|
146
|
+
|
|
147
147
|
def self.first_response obj, *method_names
|
|
148
148
|
responder = method_names.flatten.detect { |m| obj.respond_to?(m) }
|
|
149
149
|
obj.send( responder ) unless responder.nil?
|
|
150
150
|
end
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
def name=( str )
|
|
153
153
|
@name = str.to_s.gsub(/[^a-zA-Z0-9_]/,'_').gsub(/__+/,'_')
|
|
154
154
|
end
|
|
155
|
-
|
|
155
|
+
|
|
156
156
|
def to_str
|
|
157
157
|
"#{quote name} #{attributes.to_s};"
|
|
158
158
|
end
|
|
159
|
-
|
|
159
|
+
|
|
160
160
|
def to_s
|
|
161
161
|
quote( name )
|
|
162
162
|
end
|
|
163
163
|
end
|
|
164
|
-
|
|
164
|
+
|
|
165
165
|
class SubGraph < Base
|
|
166
166
|
include Support
|
|
167
|
-
|
|
167
|
+
|
|
168
168
|
finder :nodes
|
|
169
|
-
|
|
169
|
+
|
|
170
170
|
attr_accessor :links
|
|
171
171
|
attr_accessor :name
|
|
172
|
-
|
|
172
|
+
|
|
173
173
|
def initialize( name, attrs={} )
|
|
174
174
|
self.attributes = attrs
|
|
175
175
|
@node = {}
|
|
176
176
|
@edge = {}
|
|
177
|
-
|
|
177
|
+
|
|
178
178
|
@name = name
|
|
179
179
|
@nodes = []
|
|
180
180
|
@links = []
|
|
181
181
|
end
|
|
182
|
-
|
|
182
|
+
|
|
183
183
|
def node(attrs={})
|
|
184
184
|
(@node ||= {}).merge!(attrs).extend(Attributes)
|
|
185
185
|
end
|
|
186
|
-
|
|
186
|
+
|
|
187
187
|
def graph(attrs={})
|
|
188
188
|
self.attributes.merge!(attrs).extend(Attributes)
|
|
189
189
|
end
|
|
190
|
-
|
|
190
|
+
|
|
191
191
|
def edge(attrs={})
|
|
192
192
|
(@edge ||= {}).merge!(attrs).extend(Attributes)
|
|
193
193
|
end
|
|
194
|
-
|
|
194
|
+
|
|
195
195
|
def add_node( n, a={} )
|
|
196
196
|
returning Node.new(n,a) do |n|
|
|
197
197
|
@nodes << n
|
|
198
198
|
end
|
|
199
199
|
end
|
|
200
|
-
|
|
200
|
+
|
|
201
201
|
def add_link(from, to, a={})
|
|
202
202
|
returning Link.new( from, to, a) do |l|
|
|
203
203
|
@links << l
|
|
@@ -205,7 +205,7 @@ module Vizier
|
|
|
205
205
|
end
|
|
206
206
|
alias_method :connect, :add_link
|
|
207
207
|
alias_method :add_edge, :add_link
|
|
208
|
-
|
|
208
|
+
|
|
209
209
|
def build(lines = [], indent = 0)
|
|
210
210
|
lines.map do |line|
|
|
211
211
|
if line.is_a?( Array )
|
|
@@ -215,17 +215,17 @@ module Vizier
|
|
|
215
215
|
end
|
|
216
216
|
end.join("\n")
|
|
217
217
|
end
|
|
218
|
-
|
|
218
|
+
|
|
219
219
|
def write_comment( str, j = 0 )
|
|
220
220
|
l = 40 - (j * 4)
|
|
221
221
|
i = ' ' * (j * 4)
|
|
222
222
|
"\n#{i}/*#{'*'*(l-2)}\n#{i}** #{ str.ljust((l - (6) - (j*4)),' ') }#{i} **\n#{i}#{'*'*(l-1)}/"
|
|
223
223
|
end
|
|
224
|
-
|
|
224
|
+
|
|
225
225
|
def comment(str)
|
|
226
226
|
write_comment(str, 2)
|
|
227
227
|
end
|
|
228
|
-
|
|
228
|
+
|
|
229
229
|
def to_str
|
|
230
230
|
build( ["subgraph #{quote name} {",
|
|
231
231
|
[ # attributes.map {|k,v| "#{quote k} = #{quote v};" },
|
|
@@ -240,16 +240,16 @@ module Vizier
|
|
|
240
240
|
])
|
|
241
241
|
end
|
|
242
242
|
alias_method :generate!, :to_str
|
|
243
|
-
|
|
243
|
+
|
|
244
244
|
end
|
|
245
|
-
|
|
245
|
+
|
|
246
246
|
class Graph < SubGraph
|
|
247
247
|
finder :subgraphs
|
|
248
|
-
|
|
248
|
+
|
|
249
249
|
def comment( str )
|
|
250
250
|
write_comment( str, 1 )
|
|
251
251
|
end
|
|
252
|
-
|
|
252
|
+
|
|
253
253
|
def to_str
|
|
254
254
|
build(["digraph #{quote name} {",
|
|
255
255
|
[
|
|
@@ -267,18 +267,18 @@ module Vizier
|
|
|
267
267
|
"}"])
|
|
268
268
|
end
|
|
269
269
|
alias_method :generate!, :to_str
|
|
270
|
-
|
|
270
|
+
|
|
271
271
|
def publish!( a = {} )
|
|
272
272
|
generate! # -> png
|
|
273
273
|
end
|
|
274
|
-
|
|
274
|
+
|
|
275
275
|
def subgraph(name, a = {})
|
|
276
276
|
returning( SubGraph.new(name, a)) do |g|
|
|
277
277
|
@subgraphs << g
|
|
278
278
|
yield g if block_given?
|
|
279
279
|
end
|
|
280
280
|
end
|
|
281
|
-
|
|
281
|
+
|
|
282
282
|
def cluster(name = nil, a = {}, &block)
|
|
283
283
|
if name && name = "cluster_#{name}"
|
|
284
284
|
subgraph( name, a, &block )
|
|
@@ -286,15 +286,15 @@ module Vizier
|
|
|
286
286
|
clusters
|
|
287
287
|
end
|
|
288
288
|
end
|
|
289
|
-
|
|
289
|
+
|
|
290
290
|
def clusters
|
|
291
291
|
@subgraphs.select {|s| s.name =~ /^cluster_/ }.extend( Finder )
|
|
292
292
|
end
|
|
293
|
-
|
|
293
|
+
|
|
294
294
|
def initialize(name = 'my_graph', attrs = {})
|
|
295
295
|
@subgraphs = []
|
|
296
296
|
super( name, attrs )
|
|
297
297
|
yield self if block_given?
|
|
298
298
|
end
|
|
299
299
|
end
|
|
300
|
-
end
|
|
300
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'spec/runner/formatter/progress_bar_formatter'
|
|
2
|
+
class CustomFormatter < Spec::Runner::Formatter::ProgressBarFormatter
|
|
3
|
+
def add_line(l)
|
|
4
|
+
(@lines||=[]) << l
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def dump_pending
|
|
8
|
+
unless @pending_examples.empty?
|
|
9
|
+
lpad = @pending_examples.map{|e|e[2].length}.max
|
|
10
|
+
@output.puts
|
|
11
|
+
@output.puts "Pending: #{@pending_examples.length}"
|
|
12
|
+
@pending_examples.each do |pending_example|
|
|
13
|
+
@output.puts yellow("#{pending_example[2].strip.ljust(lpad)} # - #{pending_example[1]}")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
@output.flush
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# def example_failed(example, counter, failure)
|
|
20
|
+
# failure.instance_eval do
|
|
21
|
+
# (class<<self;self;end).class_eval { attr_accessor :location }
|
|
22
|
+
# end
|
|
23
|
+
# failure.location = example.location
|
|
24
|
+
# super(example,counter,failure)
|
|
25
|
+
# end
|
|
26
|
+
|
|
27
|
+
def dump_summary(duration, example_count, failure_count, pending_count)
|
|
28
|
+
if @lines
|
|
29
|
+
@output.puts "="*72
|
|
30
|
+
@lines.each do |line|
|
|
31
|
+
@output.puts line
|
|
32
|
+
end
|
|
33
|
+
@output.puts "="*72
|
|
34
|
+
end
|
|
35
|
+
super(duration, example_count, failure_count, pending_count)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def dump_failure(counter, failure)
|
|
39
|
+
@output.puts
|
|
40
|
+
@output.puts "#{counter.to_s})"
|
|
41
|
+
# @output.puts failure.location
|
|
42
|
+
@output.puts colorize_failure("#{failure.header}\n#{failure.exception.message}", failure.inspect)
|
|
43
|
+
@output.puts format_backtrace(failure.exception.backtrace)
|
|
44
|
+
#failure.exception
|
|
45
|
+
line = failure.exception.backtrace.last rescue failure.exception.inspect
|
|
46
|
+
add_line line
|
|
47
|
+
@output.flush
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -37,7 +37,7 @@ describe "extending bindings and transitions with Lathe#helper" do
|
|
|
37
37
|
attr_accessor :ok
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
@machine = Klass.
|
|
40
|
+
@machine = Klass.state_fu_machine do
|
|
41
41
|
helper MySpecHelper::BindingExampleHelper
|
|
42
42
|
helper 'my_spec_helper/other_example_helper'
|
|
43
43
|
|
|
@@ -50,7 +50,7 @@ describe "extending bindings and transitions with Lathe#helper" do
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
@other_machine = Klass.
|
|
53
|
+
@other_machine = Klass.state_fu_machine(:other) do
|
|
54
54
|
helper ::MySpecHelper::OtherExampleHelper
|
|
55
55
|
end
|
|
56
56
|
@obj = Klass.new
|