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
data/README.textile
CHANGED
|
@@ -4,25 +4,35 @@ h2. What is it?
|
|
|
4
4
|
|
|
5
5
|
State-Fu is:
|
|
6
6
|
|
|
7
|
-
*
|
|
7
|
+
* a generalized, extensible framework for state-oriented,
|
|
8
|
+
event-driven programming in Ruby.
|
|
8
9
|
|
|
9
|
-
* a rich DSL for describing workflows, rules engines and
|
|
10
|
+
* a rich DSL for describing workflows, rules engines, behaviours and
|
|
11
|
+
processes
|
|
10
12
|
|
|
11
|
-
*
|
|
13
|
+
* useful both as a Rails plugin, and in standalone ruby programs
|
|
14
|
+
without any additional dependencies.
|
|
12
15
|
|
|
13
16
|
It lets you describe:
|
|
14
17
|
|
|
15
18
|
* series of discrete states
|
|
16
19
|
|
|
17
|
-
* events which
|
|
20
|
+
* events which allow transitions to occur between states
|
|
18
21
|
|
|
19
|
-
* rules about when these
|
|
22
|
+
* rules (requirements) about when these transitions can occur
|
|
20
23
|
|
|
21
24
|
* behaviours which occur when they do
|
|
22
25
|
|
|
26
|
+
Which adds up to a surprisingly useful way to skin a lot of
|
|
27
|
+
problems
|
|
28
|
+
|
|
23
29
|
Other libraries exist for ruby which do some or all of these
|
|
24
30
|
things. "What's different about State-Fu?", you may ask.
|
|
25
31
|
|
|
32
|
+
h2. Why StateFu is not your grandmother's state machine
|
|
33
|
+
|
|
34
|
+
h3. Flippant answer:
|
|
35
|
+
|
|
26
36
|
Those libraries you've played with are toys. They're made of
|
|
27
37
|
plastic. State-Fu is forged from a reassuringly dense but
|
|
28
38
|
unidentifiable metal which comes only from the rarest of meteorites,
|
|
@@ -61,6 +71,13 @@ It is also delightfully elegant and easy to use for simple things:
|
|
|
61
71
|
event :delete, :from => :ALL, :to => :deleted do
|
|
62
72
|
execute :destroy
|
|
63
73
|
end
|
|
74
|
+
|
|
75
|
+
# save all states once transition is complete.
|
|
76
|
+
# this wants to be last, as it iterates over each state which is
|
|
77
|
+
# already defined.
|
|
78
|
+
states do
|
|
79
|
+
accepted { object.save! }
|
|
80
|
+
end
|
|
64
81
|
end
|
|
65
82
|
end
|
|
66
83
|
|
|
@@ -80,18 +97,17 @@ It is also delightfully elegant and easy to use for simple things:
|
|
|
80
97
|
|
|
81
98
|
</code></pre>
|
|
82
99
|
|
|
83
|
-
|
|
100
|
+
h3. Feature Comparison and Detailed Answer
|
|
84
101
|
|
|
85
|
-
|
|
102
|
+
A few of the features which set State-Fu apart for more ambitious work are:
|
|
86
103
|
|
|
87
|
-
*
|
|
88
|
-
|
|
104
|
+
* a lovely, simple and flexible API gives you plenty of choices about
|
|
105
|
+
how to describe your problem domain.
|
|
89
106
|
|
|
90
|
-
*
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
them across multiple classes
|
|
107
|
+
* define any number of workflows on the same object / model;
|
|
108
|
+
workflows (Machines) can be entirely separate, or interact with
|
|
109
|
+
each other. Re-use machines across multiple classes, serialize them
|
|
110
|
+
to a database, or build them on the fly.
|
|
95
111
|
|
|
96
112
|
* events can transition from / to any number of states
|
|
97
113
|
|
|
@@ -101,8 +117,8 @@ A few of the features which set State-Fu apart for more ambitious work are:
|
|
|
101
117
|
state machine itself
|
|
102
118
|
|
|
103
119
|
* requirements determine at runtime whether a particular state
|
|
104
|
-
transition can occur, and if not, can tell a user
|
|
105
|
-
to satisfy the requirements.
|
|
120
|
+
transition can occur, and if not, can tell a user (or developer)
|
|
121
|
+
what they must do to satisfy the requirements.
|
|
106
122
|
|
|
107
123
|
* requirement failure messages can be generated at runtime, making
|
|
108
124
|
use of whatever application and state-machine context they need
|
|
@@ -111,23 +127,51 @@ A few of the features which set State-Fu apart for more ambitious work are:
|
|
|
111
127
|
determine why, and where from
|
|
112
128
|
|
|
113
129
|
* in every event hook, requirement filter, and other method calls,
|
|
114
|
-
you have complete and
|
|
115
|
-
|
|
116
|
-
without breathing through a straw!
|
|
130
|
+
you have complete and consistent access to your classes, its
|
|
131
|
+
StateFu::Machines, and any Transition context. Use real ruby code
|
|
132
|
+
anywhere, without breathing through a straw!
|
|
133
|
+
|
|
134
|
+
* extend State-Fu (with Lathe#helper) Binding and Transition
|
|
135
|
+
instances for your Machine to define your a DSL customized for your
|
|
136
|
+
problem domain, and to keep your class definitions clean. Helper
|
|
137
|
+
methods have easy access to all the context associated with your
|
|
138
|
+
object instance, its StateFu::Machines and any Transition in
|
|
139
|
+
progress.
|
|
117
140
|
|
|
118
|
-
* extend State-Fu with
|
|
119
|
-
|
|
120
|
-
or for an individual event transition
|
|
141
|
+
* extend a State-Fu Lathe (with Lathe#tool) to keep your Machine
|
|
142
|
+
definitions DRY
|
|
121
143
|
|
|
122
144
|
* store arbitrary meta-data on any component of State-Fu - a simple
|
|
123
|
-
but extremely powerful tool for integration
|
|
145
|
+
but extremely powerful tool for integration with almost anything.
|
|
124
146
|
|
|
125
147
|
* designed for transparency, introspection and ease of debugging,
|
|
126
148
|
which means a dynamic, powerful system you can actually use without
|
|
127
|
-
headaches
|
|
149
|
+
headaches.
|
|
150
|
+
|
|
151
|
+
* flexible and helpful logging out of the box - will use the Rails
|
|
152
|
+
logger if you're in a Rails project, or standalone logging to
|
|
153
|
+
STDOUT or a file. Configurable loglevel and message prefixes help
|
|
154
|
+
StateFu be a good citizen in a shared application log.
|
|
128
155
|
|
|
129
156
|
* magically generate diagrams of state machines / workflows with graphviz
|
|
130
157
|
|
|
158
|
+
* "magically" use an ActiveRecord field for state persistence, or just an
|
|
159
|
+
attribute - or use both, on the same class, for different
|
|
160
|
+
workflows. If an ActiveRecord field exists for a machine's
|
|
161
|
+
field_name (by default, the machine's name suffixed with '_field';
|
|
162
|
+
the default machine name is 'state_fu', so if you don't explicitly
|
|
163
|
+
name a machine it will look for 'state_fu_field' in your
|
|
164
|
+
ActiveRecord columns (if ActiveRecord is included) and use
|
|
165
|
+
that. Otherwise, an attr_accessor will be used (and created, if
|
|
166
|
+
necessary).
|
|
167
|
+
|
|
168
|
+
* customising the persistence mechanism (eg to use a Rails session,
|
|
169
|
+
or a text file, or your choice of ORM) is usually as easy as
|
|
170
|
+
defining a getter and setter method for the persistence field, and
|
|
171
|
+
a rule about when to use it. If you want to use StateFu with a
|
|
172
|
+
persistence mechanism which is not yet supported, I'd like to hear
|
|
173
|
+
about it.
|
|
174
|
+
|
|
131
175
|
* fast, lightweight and useful enough to use in any ruby
|
|
132
176
|
project - works with Rails but does not require it.
|
|
133
177
|
|
|
@@ -146,6 +190,32 @@ I'd also like to tip my hat at John Barnette, who's own
|
|
|
146
190
|
(coincidentally named) Stateful set a very high standard with an
|
|
147
191
|
exceptionally elegant API.
|
|
148
192
|
|
|
193
|
+
h3. StateFu is not a complete BPM (Business Process Management) platform
|
|
194
|
+
|
|
195
|
+
It's worth noting that StateFu is at it's core a state machine, which
|
|
196
|
+
strives to be powerful enough to be able to drive many kinds of
|
|
197
|
+
application behaviour.
|
|
198
|
+
|
|
199
|
+
It is not, however, a "proper" workflow engine on par with Ruote. In
|
|
200
|
+
StateFu the basic units with which "workflows" are built are states
|
|
201
|
+
and events; Ruote takes a higher level view, dealing with processes
|
|
202
|
+
and participants. As a result, it's capable of directly implementing
|
|
203
|
+
these design patterns:
|
|
204
|
+
|
|
205
|
+
http://openwferu.rubyforge.org/patterns.html
|
|
206
|
+
|
|
207
|
+
Whereas StateFu cannot, for example, readily model forking / merging
|
|
208
|
+
of processes (nor does it handles scheduling, process management, etc.
|
|
209
|
+
|
|
210
|
+
The author of Ruote, the Ruby Workflow Engine, outlines the difference
|
|
211
|
+
pretty clearly here:
|
|
212
|
+
|
|
213
|
+
http://jmettraux.wordpress.com/2009/07/03/state-machine-workflow-engine/
|
|
214
|
+
|
|
215
|
+
If your application can be described with StateFu, you'll likely find
|
|
216
|
+
it simpler to get running and work with; if not, you may find Ruote,
|
|
217
|
+
or a combination of the two, suits your needs perfectly.
|
|
218
|
+
|
|
149
219
|
h2. Getting started
|
|
150
220
|
|
|
151
221
|
You can either clone the repository in the usual fashion (eg to
|
|
@@ -183,22 +253,42 @@ To install the dependencies for running specs:
|
|
|
183
253
|
|
|
184
254
|
Now you can simply <code>include StateFu</code> in any class you wish to make stateful.
|
|
185
255
|
|
|
186
|
-
The spec
|
|
256
|
+
The spec/ and features/ folders are currently one of the best source
|
|
257
|
+
of documentation. The documentation is gradually evolving to catch up
|
|
258
|
+
with the features, but if you have any questions I'm happy to help you
|
|
259
|
+
get started.
|
|
187
260
|
|
|
188
|
-
If you have questions, feature request or ideas, please join the
|
|
261
|
+
If you have questions, feature request or ideas, please join the
|
|
262
|
+
"google group":http://groups.google.com/group/state-fu or send me a
|
|
263
|
+
message on GitHub.
|
|
189
264
|
|
|
190
|
-
A note about ActiveSupport
|
|
265
|
+
h3. A note about ActiveSupport
|
|
191
266
|
|
|
192
267
|
StateFu will use ActiveSupport if it is already loaded. If not, it
|
|
193
|
-
will load its own (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
268
|
+
will load its own (heavily trimmed) 'lite' version.
|
|
269
|
+
|
|
270
|
+
In most projects this will behave transparently, but it does mean that
|
|
271
|
+
if you require StateFu *before* other libraries which
|
|
272
|
+
require ActiveSupport (e.g. ActiveRecord), you may have to
|
|
273
|
+
explicitly <code>require 'activesupport'</code> before loading the
|
|
274
|
+
dependent libraries.
|
|
275
|
+
|
|
276
|
+
So if you plan to use ActiveSupport in a stand-alone project with
|
|
277
|
+
StateFu, you should require it before StateFu.
|
|
278
|
+
|
|
279
|
+
h3. Addditional Resources
|
|
198
280
|
|
|
199
281
|
Also see the "issue tracker":http://github.com/davidlee/state-fu/issues
|
|
200
282
|
|
|
201
283
|
And the "build monitor":http://runcoderun.com/davidlee/state-fu/
|
|
202
284
|
|
|
203
|
-
And the "RDoc":http://rdoc.info/projects/davidlee/state-fu
|
|
204
|
-
|
|
285
|
+
And the "RDoc":http://rdoc.info/projects/davidlee/state-fu
|
|
286
|
+
|
|
287
|
+
h3. Thanks
|
|
288
|
+
|
|
289
|
+
* dsturnbull, for helping w/ a patch to stop an error being raised
|
|
290
|
+
when an activerecord model has no database table
|
|
291
|
+
|
|
292
|
+
* lachie, benkimball for pointing out README bugs / typos
|
|
293
|
+
|
|
294
|
+
* Ryan Allen for his original Workflow library
|
data/Rakefile
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
#!/usr/bin/
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
2
|
require "spec/rake/spectask"
|
|
3
3
|
#require 'cucumber/rake/task'
|
|
4
4
|
require "date"
|
|
5
5
|
require "fileutils"
|
|
6
6
|
require "rubygems"
|
|
7
7
|
|
|
8
|
+
load File.join( File.dirname(__FILE__),"/lib/tasks/state_fu.rake" )
|
|
9
|
+
|
|
8
10
|
module Rakefile
|
|
9
11
|
def self.windows?
|
|
10
12
|
/djgpp|(cyg|ms|bcc)win|mingw/ =~ RUBY_PLATFORM
|
|
11
13
|
end
|
|
12
14
|
end
|
|
13
15
|
|
|
16
|
+
load 'lib/tasks/spec_last.rake'
|
|
17
|
+
load 'lib/tasks/state_fu.rake'
|
|
18
|
+
|
|
14
19
|
# to build the gem:
|
|
15
20
|
#
|
|
16
21
|
# gem install jeweller
|
|
@@ -38,30 +43,37 @@ rescue LoadError
|
|
|
38
43
|
end
|
|
39
44
|
|
|
40
45
|
namespace :spec do
|
|
41
|
-
|
|
46
|
+
|
|
47
|
+
desc 'run the nice new specs'
|
|
48
|
+
Spec::Rake::SpecTask.new(:state_fu) do |t|
|
|
49
|
+
t.spec_files = FileList["spec/state_fu_spec.rb"]
|
|
50
|
+
t.spec_opts = ["--options", "spec/spec.opts"]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
desc "Run all (old) specs"
|
|
42
55
|
Spec::Rake::SpecTask.new(:all) do |t|
|
|
43
56
|
t.spec_files = FileList["spec/**/*_spec.rb"]
|
|
44
57
|
t.spec_opts = ["--options", "spec/spec.opts"]
|
|
45
58
|
end
|
|
46
59
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
t.spec_files = FileList["spec/units/*_spec.rb"]
|
|
50
|
-
t.spec_opts = ["--options", "spec/spec.opts"]
|
|
60
|
+
task :skip_slow do
|
|
61
|
+
ENV['SKIP_SLOW_SPECS'] = 'true'
|
|
51
62
|
end
|
|
52
|
-
task :unit => :units
|
|
53
63
|
|
|
54
|
-
desc "Run
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
desc "Run all specs, except especially slow ones"
|
|
65
|
+
task :quick => [:skip_slow, :all]
|
|
66
|
+
|
|
67
|
+
desc "Run all specs with profiling & backtrace"
|
|
68
|
+
Spec::Rake::SpecTask.new(:prof) do |t|
|
|
69
|
+
t.spec_files = FileList["spec/**/*_spec.rb"]
|
|
70
|
+
t.spec_opts = ['-c','-b','-u','-f','profile','-R','-L','mtime']
|
|
58
71
|
end
|
|
59
|
-
task :system => :integration
|
|
60
72
|
|
|
61
73
|
desc "Print Specdoc for all specs (eaxcluding plugin specs)"
|
|
62
74
|
Spec::Rake::SpecTask.new(:doc) do |t|
|
|
63
75
|
t.spec_files = FileList["spec/**/*_spec.rb"]
|
|
64
|
-
t.spec_opts = ["--format", "nested","--
|
|
76
|
+
t.spec_opts = ["--format", "nested","--color"]
|
|
65
77
|
end
|
|
66
78
|
|
|
67
79
|
desc "Run autotest"
|
|
@@ -69,26 +81,12 @@ namespace :spec do
|
|
|
69
81
|
exec 'autospec'
|
|
70
82
|
end
|
|
71
83
|
|
|
72
|
-
|
|
73
|
-
require 'find'
|
|
74
|
-
specs = []
|
|
75
|
-
Find.find( File.expand_path(File.join(File.dirname(__FILE__),'spec'))) do |f|
|
|
76
|
-
next unless f !~ /\.#/ && f =~ /_spec.rb$/
|
|
77
|
-
specs << f
|
|
78
|
-
end
|
|
79
|
-
spec = specs.sort_by { |spec| File.stat( spec ).mtime }.last
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
desc "runs the last modified spec, without mucking about"
|
|
83
|
-
Spec::Rake::SpecTask.new(:last) do |t|
|
|
84
|
-
t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
|
|
85
|
-
t.spec_files = FileList[find_last_modified_spec]
|
|
86
|
-
end
|
|
84
|
+
task :default => :state_fu
|
|
87
85
|
end
|
|
88
86
|
|
|
89
87
|
desc 'Runs irb in this project\'s context'
|
|
90
88
|
task :irb do |t|
|
|
91
|
-
exec 'irb -I lib -r state-fu'
|
|
89
|
+
exec 'irb -I lib -I spec -r state-fu -r spec_helper'
|
|
92
90
|
end
|
|
93
91
|
|
|
94
92
|
desc 'Runs rdoc on the project lib directory'
|
|
@@ -96,6 +94,13 @@ task :doc do |t|
|
|
|
96
94
|
exec 'rdoc lib/'
|
|
97
95
|
end
|
|
98
96
|
|
|
97
|
+
desc 'Delete logfiles'
|
|
98
|
+
namespace :log do
|
|
99
|
+
task :clear do |t|
|
|
100
|
+
Dir['log/*.log'].each { |log| File.rm(log) }
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
99
104
|
begin
|
|
100
105
|
require 'cucumber/rake/task'
|
|
101
106
|
|
|
@@ -105,4 +110,5 @@ begin
|
|
|
105
110
|
rescue LoadError => e
|
|
106
111
|
end
|
|
107
112
|
|
|
108
|
-
task :
|
|
113
|
+
task :all => 'spec:all'
|
|
114
|
+
task :default => :all
|
data/lib/no_stdout.rb
CHANGED
|
@@ -3,7 +3,7 @@ require 'stringio'
|
|
|
3
3
|
# a module for suppressing or capturing STDOUT or STDERR.
|
|
4
4
|
# useful when shelling out to "noisy" applications or to suppress
|
|
5
5
|
# output during tests.
|
|
6
|
-
module NoStdout
|
|
6
|
+
module NoStdout #:nodoc:all
|
|
7
7
|
module InstanceMethods
|
|
8
8
|
|
|
9
9
|
# Suppresses or redirects STDOUT inside the given block.
|
data/lib/state-fu.rb
CHANGED
|
@@ -59,9 +59,12 @@ require 'rubygems'
|
|
|
59
59
|
|
|
60
60
|
[ 'core_ext',
|
|
61
61
|
'logger',
|
|
62
|
-
'
|
|
62
|
+
'applicable',
|
|
63
|
+
'arrays',
|
|
64
|
+
'methodical',
|
|
65
|
+
'has_options',
|
|
66
|
+
'executioner',
|
|
63
67
|
'exceptions',
|
|
64
|
-
'fu_space',
|
|
65
68
|
'machine',
|
|
66
69
|
'lathe',
|
|
67
70
|
'method_factory',
|
|
@@ -77,21 +80,19 @@ require 'rubygems'
|
|
|
77
80
|
'hooks',
|
|
78
81
|
'interface',
|
|
79
82
|
'transition',
|
|
80
|
-
'
|
|
83
|
+
'transition_query',
|
|
81
84
|
'plotter' ].each do |lib|
|
|
82
85
|
require File.expand_path( File.join( File.dirname(__FILE__), 'state_fu', lib ))
|
|
83
86
|
end
|
|
84
87
|
|
|
85
88
|
module StateFu
|
|
86
|
-
|
|
89
|
+
DEFAULT = :default
|
|
90
|
+
DEFAULT_FIELD = :state_fu_field
|
|
87
91
|
|
|
88
92
|
def self.included( klass )
|
|
89
93
|
klass.extend( Interface::ClassMethods )
|
|
90
94
|
klass.send( :include, Interface::InstanceMethods )
|
|
95
|
+
klass.extend( Interface::Aliases )
|
|
91
96
|
end
|
|
92
97
|
end
|
|
93
98
|
|
|
94
|
-
if __FILE__ == $0
|
|
95
|
-
# drop into irb?
|
|
96
|
-
end
|
|
97
|
-
|
|
@@ -1,49 +1,56 @@
|
|
|
1
|
-
module ActiveSupport #:nodoc:
|
|
2
|
-
module CoreExtensions #:nodoc
|
|
3
|
-
module Array #:nodoc
|
|
1
|
+
module ActiveSupport #:nodoc:all
|
|
2
|
+
module CoreExtensions #:nodoc
|
|
3
|
+
module Array #:nodoc
|
|
4
4
|
# Makes it easier to access parts of an array.
|
|
5
|
-
module Access
|
|
5
|
+
module Access #:nodoc:all
|
|
6
6
|
# Returns the tail of the array from +position+.
|
|
7
7
|
#
|
|
8
8
|
# %w( a b c d ).from(0) # => %w( a b c d )
|
|
9
9
|
# %w( a b c d ).from(2) # => %w( c d )
|
|
10
10
|
# %w( a b c d ).from(10) # => nil
|
|
11
11
|
# %w().from(0) # => nil
|
|
12
|
+
#:nodoc
|
|
12
13
|
def from(position)
|
|
13
14
|
self[position..-1]
|
|
14
15
|
end
|
|
15
|
-
|
|
16
|
+
|
|
16
17
|
# Returns the beginning of the array up to +position+.
|
|
17
18
|
#
|
|
18
19
|
# %w( a b c d ).to(0) # => %w( a )
|
|
19
20
|
# %w( a b c d ).to(2) # => %w( a b c )
|
|
20
21
|
# %w( a b c d ).to(10) # => %w( a b c d )
|
|
21
22
|
# %w().to(0) # => %w()
|
|
23
|
+
#:nodoc
|
|
22
24
|
def to(position)
|
|
23
25
|
self[0..position]
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
# Equal to <tt>self[1]</tt>.
|
|
29
|
+
#:nodoc
|
|
27
30
|
def second
|
|
28
31
|
self[1]
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
# Equal to <tt>self[2]</tt>.
|
|
35
|
+
#:nodoc
|
|
32
36
|
def third
|
|
33
37
|
self[2]
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
# Equal to <tt>self[3]</tt>.
|
|
41
|
+
#:nodoc
|
|
37
42
|
def fourth
|
|
38
43
|
self[3]
|
|
39
44
|
end
|
|
40
45
|
|
|
41
46
|
# Equal to <tt>self[4]</tt>.
|
|
47
|
+
#:nodoc
|
|
42
48
|
def fifth
|
|
43
49
|
self[4]
|
|
44
50
|
end
|
|
45
51
|
|
|
46
52
|
# Equal to <tt>self[41]</tt>. Also known as accessing "the reddit".
|
|
53
|
+
#:nodoc
|
|
47
54
|
def forty_two
|
|
48
55
|
self[41]
|
|
49
56
|
end
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
module ActiveSupport #:nodoc
|
|
2
|
-
module CoreExtensions #:nodoc
|
|
3
|
-
module Array #:nodoc
|
|
1
|
+
module ActiveSupport #:nodoc
|
|
2
|
+
module CoreExtensions #:nodoc
|
|
3
|
+
module Array #:nodoc
|
|
4
4
|
module Conversions
|
|
5
5
|
# Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options:
|
|
6
6
|
# * <tt>:words_connector</tt> - The sign or word used to join the elements in arrays with two or more elements (default: ", ")
|
|
7
7
|
# * <tt>:two_words_connector</tt> - The sign or word used to join the elements in arrays with two elements (default: " and ")
|
|
8
8
|
# * <tt>:last_word_connector</tt> - The sign or word used to join the last element in arrays with three or more elements (default: ", and ")
|
|
9
|
+
#:nodoc
|
|
9
10
|
def to_sentence(options = {})
|
|
10
11
|
default_words_connector = I18n.translate(:'support.array.words_connector', :locale => options[:locale])
|
|
11
12
|
default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale])
|
|
@@ -42,6 +43,7 @@ module ActiveSupport #:nodoc:
|
|
|
42
43
|
|
|
43
44
|
# Calls <tt>to_param</tt> on all its elements and joins the result with
|
|
44
45
|
# slashes. This is used by <tt>url_for</tt> in Action Pack.
|
|
46
|
+
#:nodoc
|
|
45
47
|
def to_param
|
|
46
48
|
collect { |e| e.to_param }.join '/'
|
|
47
49
|
end
|
|
@@ -50,12 +52,14 @@ module ActiveSupport #:nodoc:
|
|
|
50
52
|
# using the given +key+ as the param name.
|
|
51
53
|
#
|
|
52
54
|
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
|
|
55
|
+
#:nodoc
|
|
53
56
|
def to_query(key)
|
|
54
57
|
prefix = "#{key}[]"
|
|
55
58
|
collect { |value| value.to_query(prefix) }.join '&'
|
|
56
59
|
end
|
|
57
60
|
|
|
58
|
-
|
|
61
|
+
#:nodoc
|
|
62
|
+
def self.included(base) #:nodoc
|
|
59
63
|
base.class_eval do
|
|
60
64
|
alias_method :to_default_s, :to_s
|
|
61
65
|
alias_method :to_s, :to_formatted_s
|
|
@@ -71,6 +75,7 @@ module ActiveSupport #:nodoc:
|
|
|
71
75
|
# output:
|
|
72
76
|
#
|
|
73
77
|
# Blog.find(:all).to_formatted_s(:db) # => "First Post,Second Post,Third Post"
|
|
78
|
+
#:nodoc
|
|
74
79
|
def to_formatted_s(format = :default)
|
|
75
80
|
case format
|
|
76
81
|
when :db
|
|
@@ -159,6 +164,7 @@ module ActiveSupport #:nodoc:
|
|
|
159
164
|
# </message>
|
|
160
165
|
# </messages>
|
|
161
166
|
#
|
|
167
|
+
#:nodoc
|
|
162
168
|
def to_xml(options = {})
|
|
163
169
|
raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml }
|
|
164
170
|
require 'builder' unless defined?(Builder)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
module ActiveSupport #:nodoc
|
|
2
|
-
module CoreExtensions #:nodoc
|
|
3
|
-
module Array #:nodoc
|
|
4
|
-
module ExtractOptions
|
|
1
|
+
module ActiveSupport #:nodoc
|
|
2
|
+
module CoreExtensions #:nodoc
|
|
3
|
+
module Array #:nodoc
|
|
4
|
+
module ExtractOptions #:nodoc:all
|
|
5
5
|
# Extracts options from a set of arguments. Removes and returns the last
|
|
6
6
|
# element in the array if it's a hash, otherwise returns a blank hash.
|
|
7
7
|
#
|
|
@@ -11,6 +11,7 @@ module ActiveSupport #:nodoc:
|
|
|
11
11
|
#
|
|
12
12
|
# options(1, 2) # => {}
|
|
13
13
|
# options(1, 2, :a => :b) # => {:a=>:b}
|
|
14
|
+
#:nodoc
|
|
14
15
|
def extract_options!
|
|
15
16
|
last.is_a?(::Hash) ? pop : {}
|
|
16
17
|
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
require 'enumerator'
|
|
2
2
|
|
|
3
|
-
module ActiveSupport #:nodoc
|
|
4
|
-
module CoreExtensions #:nodoc
|
|
5
|
-
module Array #:nodoc
|
|
6
|
-
module Grouping
|
|
3
|
+
module ActiveSupport #:nodoc
|
|
4
|
+
module CoreExtensions #:nodoc
|
|
5
|
+
module Array #:nodoc
|
|
6
|
+
module Grouping #:nodoc:all
|
|
7
7
|
# Splits or iterates over the array in groups of size +number+,
|
|
8
8
|
# padding any remaining slots with +fill_with+ unless it is +false+.
|
|
9
9
|
#
|
|
@@ -19,6 +19,7 @@ module ActiveSupport #:nodoc:
|
|
|
19
19
|
# %w(1 2 3).in_groups_of(2, false) {|group| p group}
|
|
20
20
|
# ["1", "2"]
|
|
21
21
|
# ["3"]
|
|
22
|
+
#:nodoc
|
|
22
23
|
def in_groups_of(number, fill_with = nil)
|
|
23
24
|
if fill_with == false
|
|
24
25
|
collection = self
|
|
@@ -56,6 +57,7 @@ module ActiveSupport #:nodoc:
|
|
|
56
57
|
# ["1", "2", "3"]
|
|
57
58
|
# ["4", "5"]
|
|
58
59
|
# ["6", "7"]
|
|
60
|
+
#:nodoc
|
|
59
61
|
def in_groups(number, fill_with = nil)
|
|
60
62
|
# size / number gives minor group size;
|
|
61
63
|
# size % number gives how many objects need extra accomodation;
|
|
@@ -87,6 +89,7 @@ module ActiveSupport #:nodoc:
|
|
|
87
89
|
#
|
|
88
90
|
# [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
|
|
89
91
|
# (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
|
|
92
|
+
#:nodoc
|
|
90
93
|
def split(value = nil)
|
|
91
94
|
using_block = block_given?
|
|
92
95
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
module ActiveSupport #:nodoc
|
|
2
|
-
module CoreExtensions #:nodoc
|
|
3
|
-
module Array #:nodoc
|
|
1
|
+
module ActiveSupport #:nodoc
|
|
2
|
+
module CoreExtensions #:nodoc
|
|
3
|
+
module Array #:nodoc
|
|
4
4
|
module RandomAccess
|
|
5
5
|
# Returns a random element from the array.
|
|
6
|
+
#:nodoc
|
|
6
7
|
def rand
|
|
7
8
|
self[Kernel.rand(length)]
|
|
8
9
|
end
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
module ActiveSupport #:nodoc
|
|
2
|
-
module CoreExtensions #:nodoc
|
|
3
|
-
module Array #:nodoc
|
|
1
|
+
module ActiveSupport #:nodoc
|
|
2
|
+
module CoreExtensions #:nodoc
|
|
3
|
+
module Array #:nodoc
|
|
4
4
|
module Wrapper
|
|
5
5
|
# Wraps the object in an Array unless it's an Array. Converts the
|
|
6
6
|
# object to an Array using #to_ary if it implements that.
|
|
7
|
+
#:nodoc
|
|
7
8
|
def wrap(object)
|
|
8
9
|
case object
|
|
9
10
|
when nil
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
require File.join( File.dirname( __FILE__),'array/extract_options')
|
|
2
|
+
require File.join( File.dirname( __FILE__),'array/random_access')
|
|
2
3
|
require File.join( File.dirname( __FILE__),'array/grouping')
|
|
3
4
|
|
|
4
|
-
class Array #:nodoc:
|
|
5
|
+
class Array #:nodoc:all:
|
|
5
6
|
include ActiveSupport::CoreExtensions::Array::ExtractOptions
|
|
7
|
+
include ActiveSupport::CoreExtensions::Array::RandomAccess
|
|
6
8
|
include ActiveSupport::CoreExtensions::Array::Grouping
|
|
7
9
|
end
|