transitions 0.0.4
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/.document +5 -0
- data/.gitignore +24 -0
- data/LICENSE +20 -0
- data/README.rdoc +27 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/lib/active_record/state_machine.rb +47 -0
- data/lib/state_machine.rb +93 -0
- data/lib/state_machine/event.rb +83 -0
- data/lib/state_machine/machine.rb +97 -0
- data/lib/state_machine/state.rb +67 -0
- data/lib/state_machine/state_transition.rb +60 -0
- data/test/helper.rb +14 -0
- data/test/test_active_record.rb +79 -0
- data/test/test_event.rb +28 -0
- data/test/test_event_arguments.rb +30 -0
- data/test/test_event_being_fired.rb +22 -0
- data/test/test_machine.rb +43 -0
- data/test/test_state.rb +72 -0
- data/test/test_state_transition.rb +45 -0
- data/test/test_state_transition_guard_check.rb +40 -0
- data/transitions.gemspec +81 -0
- metadata +139 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Jakub Kuźma
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
= state_machine
|
2
|
+
|
3
|
+
The gem is based on Rick Olson's code of ActiveModel::StateMachine,
|
4
|
+
axed from ActiveModel in {this
|
5
|
+
commit}[http://github.com/rails/rails/commit/db49c706b62e7ea2ab93f05399dbfddf5087ee0c].
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
Currently there's no gem released on gemcutter (we need a different
|
10
|
+
name). However it's fully usable in bundler, you can just paste the
|
11
|
+
following line into the Gemfile:
|
12
|
+
|
13
|
+
gem "state_machine", :git => "git://github.com/qoobaa/state_machine.git"
|
14
|
+
|
15
|
+
== Note on Patches/Pull Requests
|
16
|
+
|
17
|
+
* Fork the project.
|
18
|
+
* Make your feature addition or bug fix.
|
19
|
+
* Add tests for it. This is important so I don't break it in a
|
20
|
+
future version unintentionally.
|
21
|
+
* Commit, do not mess with rakefile, version, or history.
|
22
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
23
|
+
* Send me a pull request. Bonus points for topic branches.
|
24
|
+
|
25
|
+
== Copyright
|
26
|
+
|
27
|
+
Copyright (c) 2010 Jakub Kuźma. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "rake"
|
5
|
+
|
6
|
+
begin
|
7
|
+
require "jeweler"
|
8
|
+
Jeweler::Tasks.new do |gem|
|
9
|
+
gem.name = "transitions"
|
10
|
+
gem.summary = %Q{State machine extracted from ActiveModel}
|
11
|
+
# gem.description = %Q{TODO: longer description of your gem}
|
12
|
+
gem.email = "qoobaa@gmail.com"
|
13
|
+
gem.homepage = "http://github.com/qoobaa/transitions"
|
14
|
+
gem.authors = ["Jakub Kuźma"]
|
15
|
+
gem.add_development_dependency "test-unit", ">= 2"
|
16
|
+
gem.add_development_dependency "mocha"
|
17
|
+
gem.add_development_dependency "sqlite3-ruby"
|
18
|
+
gem.add_development_dependency "activerecord"
|
19
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
20
|
+
end
|
21
|
+
Jeweler::GemcutterTasks.new
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rake/testtask"
|
27
|
+
Rake::TestTask.new(:test) do |test|
|
28
|
+
test.libs << "lib" << "test"
|
29
|
+
test.pattern = "test/**/test_*.rb"
|
30
|
+
test.verbose = true
|
31
|
+
end
|
32
|
+
|
33
|
+
begin
|
34
|
+
require "rcov/rcovtask"
|
35
|
+
Rcov::RcovTask.new do |test|
|
36
|
+
test.libs << "test"
|
37
|
+
test.pattern = "test/**/test_*.rb"
|
38
|
+
test.verbose = true
|
39
|
+
end
|
40
|
+
rescue LoadError
|
41
|
+
task :rcov do
|
42
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
task :test => :check_dependencies
|
47
|
+
|
48
|
+
task :default => :test
|
49
|
+
|
50
|
+
require "rake/rdoctask"
|
51
|
+
Rake::RDocTask.new do |rdoc|
|
52
|
+
version = File.exist?("VERSION") ? File.read("VERSION") : ""
|
53
|
+
|
54
|
+
rdoc.rdoc_dir = "rdoc"
|
55
|
+
rdoc.title = "state_machine #{version}"
|
56
|
+
rdoc.rdoc_files.include("README*")
|
57
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
58
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.4
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Copyright (c) 2009 Rick Olson
|
2
|
+
|
3
|
+
# Permission is hereby granted, free of charge, to any person
|
4
|
+
# obtaining a copy of this software and associated documentation files
|
5
|
+
# (the "Software"), to deal in the Software without restriction,
|
6
|
+
# including without limitation the rights to use, copy, modify, merge,
|
7
|
+
# publish, distribute, sublicense, and/or sell copies of the Software,
|
8
|
+
# and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
18
|
+
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
19
|
+
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
module ActiveRecord
|
24
|
+
module StateMachine
|
25
|
+
extend ActiveSupport::Concern
|
26
|
+
|
27
|
+
included do
|
28
|
+
include ::StateMachine
|
29
|
+
before_validation :set_initial_state
|
30
|
+
validates_presence_of :state
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def write_state(state_machine, state)
|
36
|
+
update_attributes! :state => state.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
def read_state(state_machine)
|
40
|
+
self.state.to_sym
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_initial_state
|
44
|
+
self.state ||= self.class.state_machine.initial_state.to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# Copyright (c) 2009 Rick Olson
|
2
|
+
|
3
|
+
# Permission is hereby granted, free of charge, to any person
|
4
|
+
# obtaining a copy of this software and associated documentation files
|
5
|
+
# (the "Software"), to deal in the Software without restriction,
|
6
|
+
# including without limitation the rights to use, copy, modify, merge,
|
7
|
+
# publish, distribute, sublicense, and/or sell copies of the Software,
|
8
|
+
# and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
18
|
+
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
19
|
+
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
require "state_machine/event"
|
24
|
+
require "state_machine/machine"
|
25
|
+
require "state_machine/state"
|
26
|
+
require "state_machine/state_transition"
|
27
|
+
|
28
|
+
module StateMachine
|
29
|
+
class InvalidTransition < Exception
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
module ClassMethods
|
34
|
+
def inherited(klass)
|
35
|
+
super
|
36
|
+
klass.state_machines = state_machines
|
37
|
+
end
|
38
|
+
|
39
|
+
def state_machines
|
40
|
+
@state_machines ||= {}
|
41
|
+
end
|
42
|
+
|
43
|
+
def state_machines=(value)
|
44
|
+
@state_machines = value ? value.dup : nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def state_machine(name = nil, options = {}, &block)
|
48
|
+
if name.is_a?(Hash)
|
49
|
+
options = name
|
50
|
+
name = nil
|
51
|
+
end
|
52
|
+
name ||= :default
|
53
|
+
state_machines[name] ||= Machine.new(self, name)
|
54
|
+
block ? state_machines[name].update(options, &block) : state_machines[name]
|
55
|
+
end
|
56
|
+
|
57
|
+
def define_state_query_method(state_name)
|
58
|
+
name = "#{state_name}?"
|
59
|
+
undef_method(name) if method_defined?(name)
|
60
|
+
class_eval "def #{name}; current_state.to_s == %(#{state_name}) end"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.included(base)
|
65
|
+
base.extend(ClassMethods)
|
66
|
+
end
|
67
|
+
|
68
|
+
def current_state(name = nil, new_state = nil, persist = false)
|
69
|
+
sm = self.class.state_machine(name)
|
70
|
+
ivar = sm.current_state_variable
|
71
|
+
if name && new_state
|
72
|
+
if persist && respond_to?(:write_state)
|
73
|
+
write_state(sm, new_state)
|
74
|
+
end
|
75
|
+
|
76
|
+
if respond_to?(:write_state_without_persistence)
|
77
|
+
write_state_without_persistence(sm, new_state)
|
78
|
+
end
|
79
|
+
|
80
|
+
instance_variable_set(ivar, new_state)
|
81
|
+
else
|
82
|
+
instance_variable_set(ivar, nil) unless instance_variable_defined?(ivar)
|
83
|
+
value = instance_variable_get(ivar)
|
84
|
+
return value if value
|
85
|
+
|
86
|
+
if respond_to?(:read_state)
|
87
|
+
value = instance_variable_set(ivar, read_state(sm))
|
88
|
+
end
|
89
|
+
|
90
|
+
value || sm.initial_state
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# Copyright (c) 2009 Rick Olson
|
2
|
+
|
3
|
+
# Permission is hereby granted, free of charge, to any person
|
4
|
+
# obtaining a copy of this software and associated documentation files
|
5
|
+
# (the "Software"), to deal in the Software without restriction,
|
6
|
+
# including without limitation the rights to use, copy, modify, merge,
|
7
|
+
# publish, distribute, sublicense, and/or sell copies of the Software,
|
8
|
+
# and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
18
|
+
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
19
|
+
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
module StateMachine
|
24
|
+
class Event
|
25
|
+
attr_reader :name, :success
|
26
|
+
|
27
|
+
def initialize(machine, name, options = {}, &block)
|
28
|
+
@machine, @name, @transitions = machine, name, []
|
29
|
+
if machine
|
30
|
+
machine.klass.send(:define_method, "#{name}!") do |*args|
|
31
|
+
machine.fire_event(name, self, true, *args)
|
32
|
+
end
|
33
|
+
|
34
|
+
machine.klass.send(:define_method, name.to_s) do |*args|
|
35
|
+
machine.fire_event(name, self, false, *args)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
update(options, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def fire(obj, to_state = nil, *args)
|
42
|
+
transitions = @transitions.select { |t| t.from == obj.current_state(@machine ? @machine.name : nil) }
|
43
|
+
raise InvalidTransition if transitions.size == 0
|
44
|
+
|
45
|
+
next_state = nil
|
46
|
+
transitions.each do |transition|
|
47
|
+
next if to_state && !Array(transition.to).include?(to_state)
|
48
|
+
if transition.perform(obj)
|
49
|
+
next_state = to_state || Array(transition.to).first
|
50
|
+
transition.execute(obj, *args)
|
51
|
+
break
|
52
|
+
end
|
53
|
+
end
|
54
|
+
next_state
|
55
|
+
end
|
56
|
+
|
57
|
+
def transitions_from_state?(state)
|
58
|
+
@transitions.any? { |t| t.from? state }
|
59
|
+
end
|
60
|
+
|
61
|
+
def ==(event)
|
62
|
+
if event.is_a? Symbol
|
63
|
+
name == event
|
64
|
+
else
|
65
|
+
name == event.name
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def update(options = {}, &block)
|
70
|
+
@success = options[:success] if options.key?(:success)
|
71
|
+
instance_eval(&block) if block
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def transitions(trans_opts)
|
78
|
+
Array(trans_opts[:from]).each do |s|
|
79
|
+
@transitions << StateTransition.new(trans_opts.merge({:from => s.to_sym}))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# Copyright (c) 2009 Rick Olson
|
2
|
+
|
3
|
+
# Permission is hereby granted, free of charge, to any person
|
4
|
+
# obtaining a copy of this software and associated documentation files
|
5
|
+
# (the "Software"), to deal in the Software without restriction,
|
6
|
+
# including without limitation the rights to use, copy, modify, merge,
|
7
|
+
# publish, distribute, sublicense, and/or sell copies of the Software,
|
8
|
+
# and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
18
|
+
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
19
|
+
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
module StateMachine
|
24
|
+
class Machine
|
25
|
+
attr_writer :initial_state
|
26
|
+
attr_accessor :states, :events, :state_index
|
27
|
+
attr_reader :klass, :name
|
28
|
+
|
29
|
+
def initialize(klass, name, options = {}, &block)
|
30
|
+
@klass, @name, @states, @state_index, @events = klass, name, [], {}, {}
|
31
|
+
update(options, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def initial_state
|
35
|
+
@initial_state ||= (states.first ? states.first.name : nil)
|
36
|
+
end
|
37
|
+
|
38
|
+
def update(options = {}, &block)
|
39
|
+
@initial_state = options[:initial] if options.key?(:initial)
|
40
|
+
instance_eval(&block) if block
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def fire_event(event, record, persist, *args)
|
45
|
+
state_index[record.current_state(@name)].call_action(:exit, record)
|
46
|
+
if new_state = @events[event].fire(record, nil, *args)
|
47
|
+
state_index[new_state].call_action(:enter, record)
|
48
|
+
|
49
|
+
if record.respond_to?(event_fired_callback)
|
50
|
+
record.send(event_fired_callback, record.current_state, new_state)
|
51
|
+
end
|
52
|
+
|
53
|
+
record.current_state(@name, new_state, persist)
|
54
|
+
record.send(@events[event].success) if @events[event].success
|
55
|
+
true
|
56
|
+
else
|
57
|
+
if record.respond_to?(event_failed_callback)
|
58
|
+
record.send(event_failed_callback, event)
|
59
|
+
end
|
60
|
+
|
61
|
+
false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def states_for_select
|
66
|
+
states.map { |st| [st.display_name, st.name.to_s] }
|
67
|
+
end
|
68
|
+
|
69
|
+
def events_for(state)
|
70
|
+
events = @events.values.select { |event| event.transitions_from_state?(state) }
|
71
|
+
events.map! { |event| event.name }
|
72
|
+
end
|
73
|
+
|
74
|
+
def current_state_variable
|
75
|
+
"@#{@name}_current_state"
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def state(name, options = {})
|
81
|
+
@states << (state_index[name] ||= State.new(name, :machine => self)).update(options)
|
82
|
+
end
|
83
|
+
|
84
|
+
def event(name, options = {}, &block)
|
85
|
+
(@events[name] ||= Event.new(self, name)).update(options, &block)
|
86
|
+
end
|
87
|
+
|
88
|
+
def event_fired_callback
|
89
|
+
@event_fired_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_fired'
|
90
|
+
end
|
91
|
+
|
92
|
+
def event_failed_callback
|
93
|
+
@event_failed_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_failed'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Copyright (c) 2009 Rick Olson
|
2
|
+
|
3
|
+
# Permission is hereby granted, free of charge, to any person
|
4
|
+
# obtaining a copy of this software and associated documentation files
|
5
|
+
# (the "Software"), to deal in the Software without restriction,
|
6
|
+
# including without limitation the rights to use, copy, modify, merge,
|
7
|
+
# publish, distribute, sublicense, and/or sell copies of the Software,
|
8
|
+
# and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
18
|
+
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
19
|
+
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
module StateMachine
|
24
|
+
class State
|
25
|
+
attr_reader :name, :options
|
26
|
+
|
27
|
+
def initialize(name, options = {})
|
28
|
+
@name = name
|
29
|
+
if machine = options.delete(:machine)
|
30
|
+
machine.klass.define_state_query_method(name)
|
31
|
+
end
|
32
|
+
update(options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def ==(state)
|
36
|
+
if state.is_a? Symbol
|
37
|
+
name == state
|
38
|
+
else
|
39
|
+
name == state.name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def call_action(action, record)
|
44
|
+
action = @options[action]
|
45
|
+
case action
|
46
|
+
when Symbol, String
|
47
|
+
record.send(action)
|
48
|
+
when Proc
|
49
|
+
action.call(record)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def display_name
|
54
|
+
@display_name ||= name.to_s.gsub(/_/, ' ').capitalize
|
55
|
+
end
|
56
|
+
|
57
|
+
def for_select
|
58
|
+
[display_name, name.to_s]
|
59
|
+
end
|
60
|
+
|
61
|
+
def update(options = {})
|
62
|
+
@display_name = options.delete(:display) if options.key?(:display)
|
63
|
+
@options = options
|
64
|
+
self
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Copyright (c) 2009 Rick Olson
|
2
|
+
|
3
|
+
# Permission is hereby granted, free of charge, to any person
|
4
|
+
# obtaining a copy of this software and associated documentation files
|
5
|
+
# (the "Software"), to deal in the Software without restriction,
|
6
|
+
# including without limitation the rights to use, copy, modify, merge,
|
7
|
+
# publish, distribute, sublicense, and/or sell copies of the Software,
|
8
|
+
# and to permit persons to whom the Software is furnished to do so,
|
9
|
+
# subject to the following conditions:
|
10
|
+
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
18
|
+
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
19
|
+
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
module StateMachine
|
24
|
+
class StateTransition
|
25
|
+
attr_reader :from, :to, :options
|
26
|
+
|
27
|
+
def initialize(opts)
|
28
|
+
@from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition]
|
29
|
+
@options = opts
|
30
|
+
end
|
31
|
+
|
32
|
+
def perform(obj)
|
33
|
+
case @guard
|
34
|
+
when Symbol, String
|
35
|
+
obj.send(@guard)
|
36
|
+
when Proc
|
37
|
+
@guard.call(obj)
|
38
|
+
else
|
39
|
+
true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def execute(obj, *args)
|
44
|
+
case @on_transition
|
45
|
+
when Symbol, String
|
46
|
+
obj.send(@on_transition, *args)
|
47
|
+
when Proc
|
48
|
+
@on_transition.call(obj, *args)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def ==(obj)
|
53
|
+
@from == obj.from && @to == obj.to
|
54
|
+
end
|
55
|
+
|
56
|
+
def from?(value)
|
57
|
+
@from == value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
gem "test-unit" # enforce gem usage on 1.8.x
|
3
|
+
require "test/unit"
|
4
|
+
require "active_record"
|
5
|
+
require "mocha"
|
6
|
+
|
7
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
8
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
|
+
require "state_machine"
|
10
|
+
require "active_record/state_machine"
|
11
|
+
|
12
|
+
class Test::Unit::TestCase
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class CreateTrafficLights < ActiveRecord::Migration
|
4
|
+
def self.up
|
5
|
+
create_table(:traffic_lights) { |t| t.string :state }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class TrafficLight < ActiveRecord::Base
|
10
|
+
include ActiveRecord::StateMachine
|
11
|
+
|
12
|
+
state_machine do
|
13
|
+
state :off
|
14
|
+
|
15
|
+
state :red
|
16
|
+
state :green
|
17
|
+
state :yellow
|
18
|
+
|
19
|
+
event :red_on do
|
20
|
+
transitions :to => :red, :from => [:yellow]
|
21
|
+
end
|
22
|
+
|
23
|
+
event :green_on do
|
24
|
+
transitions :to => :green, :from => [:red]
|
25
|
+
end
|
26
|
+
|
27
|
+
event :yellow_on do
|
28
|
+
transitions :to => :yellow, :from => [:green]
|
29
|
+
end
|
30
|
+
|
31
|
+
event :reset do
|
32
|
+
transitions :to => :red, :from => [:off]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class TestActiveRecord < Test::Unit::TestCase
|
38
|
+
def setup
|
39
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
40
|
+
ActiveRecord::Migration.verbose = false
|
41
|
+
CreateTrafficLights.migrate(:up)
|
42
|
+
|
43
|
+
@light = TrafficLight.create!
|
44
|
+
end
|
45
|
+
|
46
|
+
test "states initial state" do
|
47
|
+
assert @light.off?
|
48
|
+
assert_equal :off, @light.current_state
|
49
|
+
end
|
50
|
+
|
51
|
+
test "transition to a valid state" do
|
52
|
+
@light.reset
|
53
|
+
assert @light.red?
|
54
|
+
assert_equal :red, @light.current_state
|
55
|
+
|
56
|
+
@light.green_on
|
57
|
+
assert @light.green?
|
58
|
+
assert_equal :green, @light.current_state
|
59
|
+
end
|
60
|
+
|
61
|
+
test "transition does not persist state" do
|
62
|
+
@light.reset
|
63
|
+
assert_equal :red, @light.current_state
|
64
|
+
@light.reload
|
65
|
+
assert_equal "off", @light.state
|
66
|
+
end
|
67
|
+
|
68
|
+
test "transition does persists state" do
|
69
|
+
@light.reset!
|
70
|
+
assert_equal :red, @light.current_state
|
71
|
+
@light.reload
|
72
|
+
assert_equal "red", @light.state
|
73
|
+
end
|
74
|
+
|
75
|
+
test "transition to an invalid state" do
|
76
|
+
assert_raise(StateMachine::InvalidTransition) { @light.yellow_on }
|
77
|
+
assert_equal :off, @light.current_state
|
78
|
+
end
|
79
|
+
end
|
data/test/test_event.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class TestEvent < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@state_name = :close_order
|
6
|
+
@success = :success_callback
|
7
|
+
end
|
8
|
+
|
9
|
+
def new_event
|
10
|
+
@event = StateMachine::Event.new(nil, @state_name, {:success => @success}) do
|
11
|
+
transitions :to => :closed, :from => [:open, :received]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
test "should set the name" do
|
16
|
+
assert_equal @state_name, new_event.name
|
17
|
+
end
|
18
|
+
|
19
|
+
test "should set the success option" do
|
20
|
+
assert_equal @success, new_event.success
|
21
|
+
end
|
22
|
+
|
23
|
+
test "should create StateTransitions" do
|
24
|
+
StateMachine::StateTransition.expects(:new).with(:to => :closed, :from => :open)
|
25
|
+
StateMachine::StateTransition.expects(:new).with(:to => :closed, :from => :received)
|
26
|
+
new_event
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class ArgumentsTestSubject
|
4
|
+
include StateMachine
|
5
|
+
attr_accessor :date
|
6
|
+
|
7
|
+
state_machine do
|
8
|
+
state :initial
|
9
|
+
state :opened
|
10
|
+
|
11
|
+
event :open do
|
12
|
+
transitions :from => :initial, :to => :opened, :on_transition => :update_date
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def update_date(date = Date.today)
|
17
|
+
self.date = date
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class StateMachineMachineTest < Test::Unit::TestCase
|
22
|
+
test "pass arguments to transition method" do
|
23
|
+
subject = ArgumentsTestSubject.new
|
24
|
+
assert_equal :initial, subject.current_state
|
25
|
+
subject.open!(Date.yesterday)
|
26
|
+
assert_equal :opened, subject.current_state
|
27
|
+
assert_equal Date.yesterday, subject.date
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class TestEventBeingFired < Test::Unit::TestCase
|
4
|
+
test "should raise an StateMachine::InvalidTransition error if the transitions are empty" do
|
5
|
+
event = StateMachine::Event.new(nil, :event)
|
6
|
+
|
7
|
+
assert_raise StateMachine::InvalidTransition do
|
8
|
+
event.fire(nil)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
test "should return the state of the first matching transition it finds" do
|
13
|
+
event = StateMachine::Event.new(nil, :event) do
|
14
|
+
transitions :to => :closed, :from => [:open, :received]
|
15
|
+
end
|
16
|
+
|
17
|
+
obj = stub
|
18
|
+
obj.stubs(:current_state).returns(:open)
|
19
|
+
|
20
|
+
assert_equal :closed, event.fire(obj)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class MachineTestSubject
|
4
|
+
include StateMachine
|
5
|
+
|
6
|
+
state_machine do
|
7
|
+
state :open
|
8
|
+
state :closed
|
9
|
+
end
|
10
|
+
|
11
|
+
state_machine :initial => :foo do
|
12
|
+
event :shutdown do
|
13
|
+
transitions :from => :open, :to => :closed
|
14
|
+
end
|
15
|
+
|
16
|
+
event :timeout do
|
17
|
+
transitions :from => :open, :to => :closed
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
state_machine :extra, :initial => :bar do
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class StateMachineMachineTest < Test::Unit::TestCase
|
26
|
+
test "allows reuse of existing machines" do
|
27
|
+
assert_equal 2, MachineTestSubject.state_machines.size
|
28
|
+
end
|
29
|
+
|
30
|
+
test "sets #initial_state from :initial option" do
|
31
|
+
assert_equal :bar, MachineTestSubject.state_machine(:extra).initial_state
|
32
|
+
end
|
33
|
+
|
34
|
+
test "accesses non-default state machine" do
|
35
|
+
assert_kind_of StateMachine::Machine, MachineTestSubject.state_machine(:extra)
|
36
|
+
end
|
37
|
+
|
38
|
+
test "finds events for given state" do
|
39
|
+
events = MachineTestSubject.state_machine.events_for(:open)
|
40
|
+
assert events.include?(:shutdown)
|
41
|
+
assert events.include?(:timeout)
|
42
|
+
end
|
43
|
+
end
|
data/test/test_state.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class StateTestSubject
|
4
|
+
include StateMachine
|
5
|
+
|
6
|
+
state_machine do
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class TestState < Test::Unit::TestCase
|
11
|
+
def setup
|
12
|
+
@state_name = :astate
|
13
|
+
@machine = StateTestSubject.state_machine
|
14
|
+
@options = { :crazy_custom_key => "key", :machine => @machine }
|
15
|
+
end
|
16
|
+
|
17
|
+
def new_state(options={})
|
18
|
+
StateMachine::State.new(@state_name, @options.merge(options))
|
19
|
+
end
|
20
|
+
|
21
|
+
test "sets the name" do
|
22
|
+
assert_equal :astate, new_state.name
|
23
|
+
end
|
24
|
+
|
25
|
+
test "sets the display_name from name" do
|
26
|
+
assert_equal "Astate", new_state.display_name
|
27
|
+
end
|
28
|
+
|
29
|
+
test "sets the display_name from options" do
|
30
|
+
assert_equal "A State", new_state(:display => "A State").display_name
|
31
|
+
end
|
32
|
+
|
33
|
+
test "sets the options and expose them as options" do
|
34
|
+
@options.delete(:machine)
|
35
|
+
assert_equal @options, new_state.options
|
36
|
+
end
|
37
|
+
|
38
|
+
test "equals a symbol of the same name" do
|
39
|
+
assert_equal new_state, :astate
|
40
|
+
end
|
41
|
+
|
42
|
+
test "equals a State of the same name" do
|
43
|
+
assert_equal new_state, new_state
|
44
|
+
end
|
45
|
+
|
46
|
+
test "should send a message to the record for an action if the action is present as a symbol" do
|
47
|
+
state = new_state(:entering => :foo)
|
48
|
+
|
49
|
+
record = stub
|
50
|
+
record.expects(:foo)
|
51
|
+
|
52
|
+
state.call_action(:entering, record)
|
53
|
+
end
|
54
|
+
|
55
|
+
test "should send a message to the record for an action if the action is present as a string" do
|
56
|
+
state = new_state(:entering => "foo")
|
57
|
+
|
58
|
+
record = stub
|
59
|
+
record.expects(:foo)
|
60
|
+
|
61
|
+
state.call_action(:entering, record)
|
62
|
+
end
|
63
|
+
|
64
|
+
test "should call a proc, passing in the record for an action if the action is present" do
|
65
|
+
state = new_state(:entering => Proc.new {|r| r.foobar})
|
66
|
+
|
67
|
+
record = stub
|
68
|
+
record.expects(:foobar)
|
69
|
+
|
70
|
+
state.call_action(:entering, record)
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class TestStateTransition < Test::Unit::TestCase
|
4
|
+
test "should set from, to, and opts attr readers" do
|
5
|
+
opts = {:from => "foo", :to => "bar", :guard => "g"}
|
6
|
+
st = StateMachine::StateTransition.new(opts)
|
7
|
+
|
8
|
+
assert_equal opts[:from], st.from
|
9
|
+
assert_equal opts[:to], st.to
|
10
|
+
assert_equal opts, st.options
|
11
|
+
end
|
12
|
+
|
13
|
+
test "should pass equality check if from and to are the same" do
|
14
|
+
opts = {:from => "foo", :to => "bar", :guard => "g"}
|
15
|
+
st = StateMachine::StateTransition.new(opts)
|
16
|
+
|
17
|
+
obj = stub
|
18
|
+
obj.stubs(:from).returns(opts[:from])
|
19
|
+
obj.stubs(:to).returns(opts[:to])
|
20
|
+
|
21
|
+
assert_equal st, obj
|
22
|
+
end
|
23
|
+
|
24
|
+
test "should fail equality check if from are not the same" do
|
25
|
+
opts = {:from => "foo", :to => "bar", :guard => "g"}
|
26
|
+
st = StateMachine::StateTransition.new(opts)
|
27
|
+
|
28
|
+
obj = stub
|
29
|
+
obj.stubs(:from).returns("blah")
|
30
|
+
obj.stubs(:to).returns(opts[:to])
|
31
|
+
|
32
|
+
assert_not_equal st, obj
|
33
|
+
end
|
34
|
+
|
35
|
+
test "should fail equality check if to are not the same" do
|
36
|
+
opts = {:from => "foo", :to => "bar", :guard => "g"}
|
37
|
+
st = StateMachine::StateTransition.new(opts)
|
38
|
+
|
39
|
+
obj = stub
|
40
|
+
obj.stubs(:from).returns(opts[:from])
|
41
|
+
obj.stubs(:to).returns("blah")
|
42
|
+
|
43
|
+
assert_not_equal st, obj
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class TestStateTransitionGuardCheck < Test::Unit::TestCase
|
4
|
+
test "should return true of there is no guard" do
|
5
|
+
opts = {:from => "foo", :to => "bar"}
|
6
|
+
st = StateMachine::StateTransition.new(opts)
|
7
|
+
|
8
|
+
assert st.perform(nil)
|
9
|
+
end
|
10
|
+
|
11
|
+
test "should call the method on the object if guard is a symbol" do
|
12
|
+
opts = {:from => "foo", :to => "bar", :guard => :test_guard}
|
13
|
+
st = StateMachine::StateTransition.new(opts)
|
14
|
+
|
15
|
+
obj = stub
|
16
|
+
obj.expects(:test_guard)
|
17
|
+
|
18
|
+
st.perform(obj)
|
19
|
+
end
|
20
|
+
|
21
|
+
test "should call the method on the object if guard is a string" do
|
22
|
+
opts = {:from => "foo", :to => "bar", :guard => "test_guard"}
|
23
|
+
st = StateMachine::StateTransition.new(opts)
|
24
|
+
|
25
|
+
obj = stub
|
26
|
+
obj.expects(:test_guard)
|
27
|
+
|
28
|
+
st.perform(obj)
|
29
|
+
end
|
30
|
+
|
31
|
+
test "should call the proc passing the object if the guard is a proc" do
|
32
|
+
opts = {:from => "foo", :to => "bar", :guard => Proc.new {|o| o.test_guard}}
|
33
|
+
st = StateMachine::StateTransition.new(opts)
|
34
|
+
|
35
|
+
obj = stub
|
36
|
+
obj.expects(:test_guard)
|
37
|
+
|
38
|
+
st.perform(obj)
|
39
|
+
end
|
40
|
+
end
|
data/transitions.gemspec
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{transitions}
|
8
|
+
s.version = "0.0.4"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Jakub Kuźma"]
|
12
|
+
s.date = %q{2010-03-14}
|
13
|
+
s.email = %q{qoobaa@gmail.com}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE",
|
16
|
+
"README.rdoc"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".document",
|
20
|
+
".gitignore",
|
21
|
+
"LICENSE",
|
22
|
+
"README.rdoc",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"lib/active_record/state_machine.rb",
|
26
|
+
"lib/state_machine.rb",
|
27
|
+
"lib/state_machine/event.rb",
|
28
|
+
"lib/state_machine/machine.rb",
|
29
|
+
"lib/state_machine/state.rb",
|
30
|
+
"lib/state_machine/state_transition.rb",
|
31
|
+
"test/helper.rb",
|
32
|
+
"test/test_active_record.rb",
|
33
|
+
"test/test_event.rb",
|
34
|
+
"test/test_event_arguments.rb",
|
35
|
+
"test/test_event_being_fired.rb",
|
36
|
+
"test/test_machine.rb",
|
37
|
+
"test/test_state.rb",
|
38
|
+
"test/test_state_transition.rb",
|
39
|
+
"test/test_state_transition_guard_check.rb",
|
40
|
+
"transitions.gemspec"
|
41
|
+
]
|
42
|
+
s.homepage = %q{http://github.com/qoobaa/transitions}
|
43
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubygems_version = %q{1.3.6}
|
46
|
+
s.summary = %q{State machine extracted from ActiveModel}
|
47
|
+
s.test_files = [
|
48
|
+
"test/helper.rb",
|
49
|
+
"test/test_state_transition_guard_check.rb",
|
50
|
+
"test/test_active_record.rb",
|
51
|
+
"test/test_machine.rb",
|
52
|
+
"test/test_state_transition.rb",
|
53
|
+
"test/test_event_arguments.rb",
|
54
|
+
"test/test_state.rb",
|
55
|
+
"test/test_event_being_fired.rb",
|
56
|
+
"test/test_event.rb"
|
57
|
+
]
|
58
|
+
|
59
|
+
if s.respond_to? :specification_version then
|
60
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
61
|
+
s.specification_version = 3
|
62
|
+
|
63
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
64
|
+
s.add_development_dependency(%q<test-unit>, [">= 2"])
|
65
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
66
|
+
s.add_development_dependency(%q<sqlite3-ruby>, [">= 0"])
|
67
|
+
s.add_development_dependency(%q<activerecord>, [">= 0"])
|
68
|
+
else
|
69
|
+
s.add_dependency(%q<test-unit>, [">= 2"])
|
70
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
71
|
+
s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
|
72
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
73
|
+
end
|
74
|
+
else
|
75
|
+
s.add_dependency(%q<test-unit>, [">= 2"])
|
76
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
77
|
+
s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
|
78
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: transitions
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 4
|
9
|
+
version: 0.0.4
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- "Jakub Ku\xC5\xBAma"
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-03-14 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: test-unit
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 2
|
29
|
+
version: "2"
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: mocha
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
type: :development
|
43
|
+
version_requirements: *id002
|
44
|
+
- !ruby/object:Gem::Dependency
|
45
|
+
name: sqlite3-ruby
|
46
|
+
prerelease: false
|
47
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
type: :development
|
55
|
+
version_requirements: *id003
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: activerecord
|
58
|
+
prerelease: false
|
59
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
type: :development
|
67
|
+
version_requirements: *id004
|
68
|
+
description:
|
69
|
+
email: qoobaa@gmail.com
|
70
|
+
executables: []
|
71
|
+
|
72
|
+
extensions: []
|
73
|
+
|
74
|
+
extra_rdoc_files:
|
75
|
+
- LICENSE
|
76
|
+
- README.rdoc
|
77
|
+
files:
|
78
|
+
- .document
|
79
|
+
- .gitignore
|
80
|
+
- LICENSE
|
81
|
+
- README.rdoc
|
82
|
+
- Rakefile
|
83
|
+
- VERSION
|
84
|
+
- lib/active_record/state_machine.rb
|
85
|
+
- lib/state_machine.rb
|
86
|
+
- lib/state_machine/event.rb
|
87
|
+
- lib/state_machine/machine.rb
|
88
|
+
- lib/state_machine/state.rb
|
89
|
+
- lib/state_machine/state_transition.rb
|
90
|
+
- test/helper.rb
|
91
|
+
- test/test_active_record.rb
|
92
|
+
- test/test_event.rb
|
93
|
+
- test/test_event_arguments.rb
|
94
|
+
- test/test_event_being_fired.rb
|
95
|
+
- test/test_machine.rb
|
96
|
+
- test/test_state.rb
|
97
|
+
- test/test_state_transition.rb
|
98
|
+
- test/test_state_transition_guard_check.rb
|
99
|
+
- transitions.gemspec
|
100
|
+
has_rdoc: true
|
101
|
+
homepage: http://github.com/qoobaa/transitions
|
102
|
+
licenses: []
|
103
|
+
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options:
|
106
|
+
- --charset=UTF-8
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
version: "0"
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
version: "0"
|
123
|
+
requirements: []
|
124
|
+
|
125
|
+
rubyforge_project:
|
126
|
+
rubygems_version: 1.3.6
|
127
|
+
signing_key:
|
128
|
+
specification_version: 3
|
129
|
+
summary: State machine extracted from ActiveModel
|
130
|
+
test_files:
|
131
|
+
- test/helper.rb
|
132
|
+
- test/test_state_transition_guard_check.rb
|
133
|
+
- test/test_active_record.rb
|
134
|
+
- test/test_machine.rb
|
135
|
+
- test/test_state_transition.rb
|
136
|
+
- test/test_event_arguments.rb
|
137
|
+
- test/test_state.rb
|
138
|
+
- test/test_event_being_fired.rb
|
139
|
+
- test/test_event.rb
|