mikejones-acts_as_state_machine 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,13 @@
1
+ * trunk *
2
+ break with true value [Kaspar Schiess]
3
+
4
+ * 2.1 *
5
+ After actions [Saimon Moore]
6
+
7
+ * 2.0 * (2006-01-20 15:26:28 -0500)
8
+ Enter / Exit actions
9
+ Transition guards
10
+ Guards and actions can be a symbol pointing to a method or a Proc
11
+
12
+ * 1.0 * (2006-01-15 12:16:55 -0500)
13
+ Initial Release
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006 Scott Barron
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 ADDED
@@ -0,0 +1,33 @@
1
+ = Acts As State Machine
2
+
3
+ This act gives an Active Record model the ability to act as a finite state
4
+ machine (FSM).
5
+
6
+ Acquire via subversion at:
7
+
8
+ http://elitists.textdriven.com/svn/plugins/acts_as_state_machine/trunk
9
+
10
+ If prompted, use the user/pass anonymous/anonymous.
11
+
12
+ == Example
13
+
14
+ class Order < ActiveRecord::Base
15
+ acts_as_state_machine :initial => :opened
16
+
17
+ state :opened
18
+ state :closed, :enter => Proc.new {|o| Mailer.send_notice(o)}
19
+ state :returned
20
+
21
+ event :close do
22
+ transitions :to => :closed, :from => :opened
23
+ end
24
+
25
+ event :return do
26
+ transitions :to => :returned, :from => :closed
27
+ end
28
+ end
29
+
30
+ o = Order.create
31
+ o.close! # notice is sent by mailer
32
+ o.return!
33
+
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => [:clean_db, :test]
7
+
8
+ desc 'Remove the stale db file'
9
+ task :clean_db do
10
+ `rm -f #{File.dirname(__FILE__)}/test/state_machine.sqlite.db`
11
+ end
12
+
13
+ desc 'Test the acts as state machine plugin.'
14
+ Rake::TestTask.new(:test) do |t|
15
+ t.libs << 'lib'
16
+ t.pattern = 'test/**/*_test.rb'
17
+ t.verbose = true
18
+ end
19
+
20
+ desc 'Generate documentation for the acts as state machine plugin.'
21
+ Rake::RDocTask.new(:rdoc) do |rdoc|
22
+ rdoc.rdoc_dir = 'rdoc'
23
+ rdoc.title = 'Acts As State Machine'
24
+ rdoc.options << '--line-numbers --inline-source'
25
+ rdoc.rdoc_files.include('README')
26
+ rdoc.rdoc_files.include('TODO')
27
+ rdoc.rdoc_files.include('lib/**/*.rb')
28
+ end
data/TODO ADDED
@@ -0,0 +1,11 @@
1
+ * Currently invalid events are ignored, create an option so that they can be
2
+ ignored or raise an exception.
3
+
4
+ * Query for a list of possible next states.
5
+
6
+ * Make listing states optional since they can be inferred from the events.
7
+ Only required to list a state if you want to define a transition block for it.
8
+
9
+ * Real transition actions
10
+
11
+ * Default states
@@ -0,0 +1,270 @@
1
+ module ScottBarron #:nodoc:
2
+ module Acts #:nodoc:
3
+ module StateMachine #:nodoc:
4
+ class InvalidState < Exception #:nodoc:
5
+ end
6
+ class NoInitialState < Exception #:nodoc:
7
+ end
8
+
9
+ def self.included(base) #:nodoc:
10
+ base.extend ActMacro
11
+ end
12
+
13
+ module SupportingClasses
14
+ class State
15
+ attr_reader :name
16
+
17
+ def initialize(name, opts)
18
+ @name, @opts = name, opts
19
+ end
20
+
21
+ def entering(record)
22
+ enteract = @opts[:enter]
23
+ record.send(:run_transition_action, enteract) if enteract
24
+ end
25
+
26
+ def entered(record)
27
+ afteractions = @opts[:after]
28
+ return unless afteractions
29
+ Array(afteractions).each do |afteract|
30
+ record.send(:run_transition_action, afteract)
31
+ end
32
+ end
33
+
34
+ def exited(record)
35
+ exitact = @opts[:exit]
36
+ record.send(:run_transition_action, exitact) if exitact
37
+ end
38
+ end
39
+
40
+ class StateTransition
41
+ attr_reader :from, :to, :opts
42
+
43
+ def initialize(opts)
44
+ @from, @to, @guard = opts[:from], opts[:to], opts[:guard]
45
+ @opts = opts
46
+ end
47
+
48
+ def guard(obj)
49
+ @guard ? obj.send(:run_transition_action, @guard) : true
50
+ end
51
+
52
+ def perform(record)
53
+ return false unless guard(record)
54
+ loopback = record.current_state == to
55
+ states = record.class.read_inheritable_attribute(:states)
56
+ next_state = states[to]
57
+ old_state = states[record.current_state]
58
+
59
+ next_state.entering(record) unless loopback
60
+
61
+ record.update_attribute(record.class.state_column, to.to_s)
62
+
63
+ next_state.entered(record) unless loopback
64
+ old_state.exited(record) unless loopback
65
+ true
66
+ end
67
+
68
+ def ==(obj)
69
+ @from == obj.from && @to == obj.to
70
+ end
71
+ end
72
+
73
+ class Event
74
+ attr_reader :name
75
+ attr_reader :transitions
76
+ attr_reader :opts
77
+
78
+ def initialize(name, opts, transition_table, &block)
79
+ @name = name.to_sym
80
+ @transitions = transition_table[@name] = []
81
+ instance_eval(&block) if block
82
+ @opts = opts
83
+ @opts.freeze
84
+ @transitions.freeze
85
+ freeze
86
+ end
87
+
88
+ def next_states(record)
89
+ @transitions.select { |t| t.from == record.current_state }
90
+ end
91
+
92
+ def fire(record)
93
+ next_states(record).each do |transition|
94
+ break true if transition.perform(record)
95
+ end
96
+ end
97
+
98
+ def transitions(trans_opts)
99
+ Array(trans_opts[:from]).each do |s|
100
+ @transitions << SupportingClasses::StateTransition.new(trans_opts.merge({:from => s.to_sym}))
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ module ActMacro
107
+ # Configuration options are
108
+ #
109
+ # * +column+ - specifies the column name to use for keeping the state (default: state)
110
+ # * +initial+ - specifies an initial state for newly created objects (required)
111
+ def acts_as_state_machine(opts)
112
+ self.extend(ClassMethods)
113
+ raise NoInitialState unless opts[:initial]
114
+
115
+ write_inheritable_attribute :states, {}
116
+ write_inheritable_attribute :initial_state, opts[:initial]
117
+ write_inheritable_attribute :transition_table, {}
118
+ write_inheritable_attribute :event_table, {}
119
+ write_inheritable_attribute :state_column, opts[:column] || 'state'
120
+
121
+ class_inheritable_reader :initial_state
122
+ class_inheritable_reader :state_column
123
+ class_inheritable_reader :transition_table
124
+ class_inheritable_reader :event_table
125
+
126
+ self.send(:include, ScottBarron::Acts::StateMachine::InstanceMethods)
127
+
128
+ before_create :set_initial_state
129
+ after_create :run_initial_state_actions
130
+ end
131
+ end
132
+
133
+ module InstanceMethods
134
+ def set_initial_state #:nodoc:
135
+ write_attribute self.class.state_column, self.class.initial_state.to_s
136
+ end
137
+
138
+ def run_initial_state_actions
139
+ initial = self.class.read_inheritable_attribute(:states)[self.class.initial_state.to_sym]
140
+ initial.entering(self)
141
+ initial.entered(self)
142
+ end
143
+
144
+ # Returns the current state the object is in, as a Ruby symbol.
145
+ def current_state
146
+ self.send(self.class.state_column).to_sym
147
+ end
148
+
149
+ # Returns what the next state for a given event would be, as a Ruby symbol.
150
+ def next_state_for_event(event)
151
+ ns = next_states_for_event(event)
152
+ ns.empty? ? nil : ns.first.to
153
+ end
154
+
155
+ def next_states_for_event(event)
156
+ self.class.read_inheritable_attribute(:transition_table)[event.to_sym].select do |s|
157
+ s.from == current_state
158
+ end
159
+ end
160
+
161
+ def run_transition_action(action)
162
+ Symbol === action ? self.method(action).call : action.call(self)
163
+ end
164
+ private :run_transition_action
165
+ end
166
+
167
+ module ClassMethods
168
+ # Returns an array of all known states.
169
+ def states
170
+ read_inheritable_attribute(:states).keys
171
+ end
172
+
173
+ # Define an event. This takes a block which describes all valid transitions
174
+ # for this event.
175
+ #
176
+ # Example:
177
+ #
178
+ # class Order < ActiveRecord::Base
179
+ # acts_as_state_machine :initial => :open
180
+ #
181
+ # state :open
182
+ # state :closed
183
+ #
184
+ # event :close_order do
185
+ # transitions :to => :closed, :from => :open
186
+ # end
187
+ # end
188
+ #
189
+ # +transitions+ takes a hash where <tt>:to</tt> is the state to transition
190
+ # to and <tt>:from</tt> is a state (or Array of states) from which this
191
+ # event can be fired.
192
+ #
193
+ # This creates an instance method used for firing the event. The method
194
+ # created is the name of the event followed by an exclamation point (!).
195
+ # Example: <tt>order.close_order!</tt>.
196
+ def event(event, opts={}, &block)
197
+ tt = read_inheritable_attribute(:transition_table)
198
+
199
+ et = read_inheritable_attribute(:event_table)
200
+ e = et[event.to_sym] = SupportingClasses::Event.new(event, opts, tt, &block)
201
+ define_method("#{event.to_s}!") { e.fire(self) }
202
+ end
203
+
204
+ # Define a state of the system. +state+ can take an optional Proc object
205
+ # which will be executed every time the system transitions into that
206
+ # state. The proc will be passed the current object.
207
+ #
208
+ # Example:
209
+ #
210
+ # class Order < ActiveRecord::Base
211
+ # acts_as_state_machine :initial => :open
212
+ #
213
+ # state :open
214
+ # state :closed, Proc.new { |o| Mailer.send_notice(o) }
215
+ # end
216
+ def state(name, opts={})
217
+ state = SupportingClasses::State.new(name.to_sym, opts)
218
+ read_inheritable_attribute(:states)[name.to_sym] = state
219
+
220
+ define_method("#{state.name}?") { current_state == state.name }
221
+ end
222
+
223
+ # Wraps ActiveRecord::Base.find to conveniently find all records in
224
+ # a given state. Options:
225
+ #
226
+ # * +number+ - This is just :first or :all from ActiveRecord +find+
227
+ # * +state+ - The state to find
228
+ # * +args+ - The rest of the args are passed down to ActiveRecord +find+
229
+ def find_in_state(number, state, *args)
230
+ with_state_scope state do
231
+ find(number, *args)
232
+ end
233
+ end
234
+
235
+ # Wraps ActiveRecord::Base.count to conveniently count all records in
236
+ # a given state. Options:
237
+ #
238
+ # * +state+ - The state to find
239
+ # * +args+ - The rest of the args are passed down to ActiveRecord +find+
240
+ def count_in_state(state, *args)
241
+ with_state_scope state do
242
+ count(*args)
243
+ end
244
+ end
245
+
246
+ # Wraps ActiveRecord::Base.calculate to conveniently calculate all records in
247
+ # a given state. Options:
248
+ #
249
+ # * +state+ - The state to find
250
+ # * +args+ - The rest of the args are passed down to ActiveRecord +calculate+
251
+ def calculate_in_state(state, *args)
252
+ with_state_scope state do
253
+ calculate(*args)
254
+ end
255
+ end
256
+
257
+ protected
258
+ def with_state_scope(state)
259
+ raise InvalidState unless states.include?(state)
260
+
261
+ with_scope :find => {:conditions => ["#{table_name}.#{state_column} = ?", state.to_s]} do
262
+ yield if block_given?
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ ActiveRecord::Base.class_eval { ScottBarron::Acts::StateMachine }
@@ -0,0 +1,224 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ include ScottBarron::Acts::StateMachine
4
+
5
+ class ActsAsStateMachineTest < Test::Unit::TestCase
6
+ fixtures :conversations
7
+
8
+ def test_no_initial_value_raises_exception
9
+ assert_raise(NoInitialState) {
10
+ Person.acts_as_state_machine({})
11
+ }
12
+ end
13
+
14
+ def test_initial_state_value
15
+ assert_equal :needs_attention, Conversation.initial_state
16
+ end
17
+
18
+ def test_column_was_set
19
+ assert_equal 'state_machine', Conversation.state_column
20
+ end
21
+
22
+ def test_initial_state
23
+ c = Conversation.create
24
+ assert_equal :needs_attention, c.current_state
25
+ assert c.needs_attention?
26
+ end
27
+
28
+ def test_states_were_set
29
+ [:needs_attention, :read, :closed, :awaiting_response, :junk].each do |s|
30
+ assert Conversation.states.include?(s)
31
+ end
32
+ end
33
+
34
+ def test_event_methods_created
35
+ c = Conversation.create
36
+ %w(new_message! view! reply! close! junk! unjunk!).each do |event|
37
+ assert c.respond_to?(event)
38
+ end
39
+ end
40
+
41
+ def test_query_methods_created
42
+ c = Conversation.create
43
+ %w(needs_attention? read? closed? awaiting_response? junk?).each do |event|
44
+ assert c.respond_to?(event)
45
+ end
46
+ end
47
+
48
+ def test_transition_table
49
+ tt = Conversation.transition_table
50
+
51
+ assert tt[:new_message].include?(SupportingClasses::StateTransition.new(:from => :read, :to => :needs_attention))
52
+ assert tt[:new_message].include?(SupportingClasses::StateTransition.new(:from => :closed, :to => :needs_attention))
53
+ assert tt[:new_message].include?(SupportingClasses::StateTransition.new(:from => :awaiting_response, :to => :needs_attention))
54
+ end
55
+
56
+ def test_next_state_for_event
57
+ c = Conversation.create
58
+ assert_equal :read, c.next_state_for_event(:view)
59
+ end
60
+
61
+ def test_change_state
62
+ c = Conversation.create
63
+ c.view!
64
+ assert c.read?
65
+ end
66
+
67
+ def test_can_go_from_read_to_closed_because_guard_passes
68
+ c = Conversation.create
69
+ c.can_close = true
70
+ c.view!
71
+ c.reply!
72
+ c.close!
73
+ assert_equal :closed, c.current_state
74
+ end
75
+
76
+ def test_cannot_go_from_read_to_closed_because_of_guard
77
+ c = Conversation.create
78
+ c.can_close = false
79
+ c.view!
80
+ c.reply!
81
+ c.close!
82
+ assert_equal :read, c.current_state
83
+ end
84
+
85
+ def test_ignore_invalid_events
86
+ c = Conversation.create
87
+ c.view!
88
+ c.junk!
89
+
90
+ # This is the invalid event
91
+ c.new_message!
92
+ assert_equal :junk, c.current_state
93
+ end
94
+
95
+ def test_entry_action_executed
96
+ c = Conversation.create
97
+ c.read_enter = false
98
+ c.view!
99
+ assert c.read_enter
100
+ end
101
+
102
+ def test_after_actions_executed
103
+ c = Conversation.create
104
+
105
+ c.read_after_first = false
106
+ c.read_after_second = false
107
+ c.closed_after = false
108
+
109
+ c.view!
110
+ assert c.read_after_first
111
+ assert c.read_after_second
112
+
113
+ c.can_close = true
114
+ c.close!
115
+
116
+ assert c.closed_after
117
+ assert_equal :closed, c.current_state
118
+ end
119
+
120
+ def test_after_actions_not_run_on_loopback_transition
121
+ c = Conversation.create
122
+
123
+ c.view!
124
+ c.read_after_first = false
125
+ c.read_after_second = false
126
+ c.view!
127
+
128
+ assert !c.read_after_first
129
+ assert !c.read_after_second
130
+
131
+ c.can_close = true
132
+
133
+ c.close!
134
+ c.closed_after = false
135
+ c.close!
136
+
137
+ assert !c.closed_after
138
+ end
139
+
140
+ def test_exit_action_executed
141
+ c = Conversation.create
142
+ c.read_exit = false
143
+ c.view!
144
+ c.junk!
145
+ assert c.read_exit
146
+ end
147
+
148
+ def test_entry_and_exit_not_run_on_loopback_transition
149
+ c = Conversation.create
150
+ c.view!
151
+ c.read_enter = false
152
+ c.read_exit = false
153
+ c.view!
154
+ assert !c.read_enter
155
+ assert !c.read_exit
156
+ end
157
+
158
+ def test_entry_and_after_actions_called_for_initial_state
159
+ c = Conversation.create
160
+ assert c.needs_attention_enter
161
+ assert c.needs_attention_after
162
+ end
163
+
164
+ def test_run_transition_action_is_private
165
+ c = Conversation.create
166
+ assert_raise(NoMethodError) { c.run_transition_action :foo }
167
+ end
168
+
169
+ def test_find_all_in_state
170
+ cs = Conversation.find_in_state(:all, :read)
171
+
172
+ assert_equal 2, cs.size
173
+ end
174
+
175
+ def test_find_first_in_state
176
+ c = Conversation.find_in_state(:first, :read)
177
+
178
+ assert_equal conversations(:first).id, c.id
179
+ end
180
+
181
+ def test_find_all_in_state_with_conditions
182
+ cs = Conversation.find_in_state(:all, :read, :conditions => ['subject = ?', conversations(:second).subject])
183
+
184
+ assert_equal 1, cs.size
185
+ assert_equal conversations(:second).id, cs.first.id
186
+ end
187
+
188
+ def test_find_first_in_state_with_conditions
189
+ c = Conversation.find_in_state(:first, :read, :conditions => ['subject = ?', conversations(:second).subject])
190
+ assert_equal conversations(:second).id, c.id
191
+ end
192
+
193
+ def test_count_in_state
194
+ cnt0 = Conversation.count(['state_machine = ?', 'read'])
195
+ cnt = Conversation.count_in_state(:read)
196
+
197
+ assert_equal cnt0, cnt
198
+ end
199
+
200
+ def test_count_in_state_with_conditions
201
+ cnt0 = Conversation.count(['state_machine = ? AND subject = ?', 'read', 'Foo'])
202
+ cnt = Conversation.count_in_state(:read, ['subject = ?', 'Foo'])
203
+
204
+ assert_equal cnt0, cnt
205
+ end
206
+
207
+ def test_find_in_invalid_state_raises_exception
208
+ assert_raise(InvalidState) {
209
+ Conversation.find_in_state(:all, :dead)
210
+ }
211
+ end
212
+
213
+ def test_count_in_invalid_state_raises_exception
214
+ assert_raise(InvalidState) {
215
+ Conversation.count_in_state(:dead)
216
+ }
217
+ end
218
+
219
+ def test_can_access_events_via_event_table
220
+ event = Conversation.event_table[:junk]
221
+ assert_equal :junk, event.name
222
+ assert_equal "finished", event.opts[:note]
223
+ end
224
+ end
data/test/database.yml ADDED
@@ -0,0 +1,18 @@
1
+ sqlite:
2
+ :adapter: sqlite
3
+ :dbfile: state_machine.sqlite.db
4
+ sqlite3:
5
+ :adapter: sqlite3
6
+ :dbfile: state_machine.sqlite3.db
7
+ postgresql:
8
+ :adapter: postgresql
9
+ :username: postgres
10
+ :password: postgres
11
+ :database: state_machine_test
12
+ :min_messages: ERROR
13
+ mysql:
14
+ :adapter: mysql
15
+ :host: localhost
16
+ :username: rails
17
+ :password:
18
+ :database: state_machine_test
@@ -0,0 +1,67 @@
1
+ class Conversation < ActiveRecord::Base
2
+ attr_writer :can_close
3
+ attr_accessor :read_enter, :read_exit, :read_after_first, :read_after_second,
4
+ :closed_after, :needs_attention_enter, :needs_attention_after
5
+
6
+ acts_as_state_machine :initial => :needs_attention, :column => 'state_machine'
7
+
8
+ state :needs_attention, :enter => Proc.new { |o| o.needs_attention_enter = true },
9
+ :after => Proc.new { |o| o.needs_attention_after = true }
10
+
11
+ state :read, :enter => :read_enter_action,
12
+ :exit => Proc.new { |o| o.read_exit = true },
13
+ :after => [:read_after_first_action, :read_after_second_action]
14
+
15
+ state :closed, :after => :closed_after_action
16
+ state :awaiting_response
17
+ state :junk
18
+
19
+ event :new_message do
20
+ transitions :to => :needs_attention, :from => [:read, :closed, :awaiting_response]
21
+ end
22
+
23
+ event :view do
24
+ transitions :to => :read, :from => [:needs_attention, :read]
25
+ end
26
+
27
+ event :reply do
28
+ transitions :to => :awaiting_response, :from => [:read, :closed]
29
+ end
30
+
31
+ event :close do
32
+ transitions :to => :closed, :from => [:read, :awaiting_response], :guard => Proc.new {|o| o.can_close?}
33
+ transitions :to => :read, :from => [:read, :awaiting_response], :guard => :always_true
34
+ end
35
+
36
+ event :junk, :note => "finished" do
37
+ transitions :to => :junk, :from => [:read, :closed, :awaiting_response]
38
+ end
39
+
40
+ event :unjunk do
41
+ transitions :to => :closed, :from => :junk
42
+ end
43
+
44
+ def can_close?
45
+ @can_close
46
+ end
47
+
48
+ def read_enter_action
49
+ self.read_enter = true
50
+ end
51
+
52
+ def always_true
53
+ true
54
+ end
55
+
56
+ def read_after_first_action
57
+ self.read_after_first = true
58
+ end
59
+
60
+ def read_after_second_action
61
+ self.read_after_second = true
62
+ end
63
+
64
+ def closed_after_action
65
+ self.closed_after = true
66
+ end
67
+ end
@@ -0,0 +1,11 @@
1
+ first:
2
+ id: 1
3
+ state_machine: read
4
+ subject: This is a test
5
+ closed: false
6
+
7
+ second:
8
+ id: 2
9
+ state_machine: read
10
+ subject: Foo
11
+ closed: false
@@ -0,0 +1,2 @@
1
+ class Person < ActiveRecord::Base
2
+ end
data/test/schema.rb ADDED
@@ -0,0 +1,11 @@
1
+ ActiveRecord::Schema.define(:version => 1) do
2
+ create_table :conversations, :force => true do |t|
3
+ t.column :state_machine, :string
4
+ t.column :subject, :string
5
+ t.column :closed, :boolean
6
+ end
7
+
8
+ create_table :people, :force => true do |t|
9
+ t.column :name, :string
10
+ end
11
+ end
@@ -0,0 +1,38 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+ RAILS_ROOT = File.dirname(__FILE__)
3
+
4
+ require 'rubygems'
5
+ require 'test/unit'
6
+ require 'active_record'
7
+ require 'active_record/fixtures'
8
+ require 'active_support/binding_of_caller'
9
+ require 'active_support/breakpoint'
10
+ require "#{File.dirname(__FILE__)}/../init"
11
+
12
+
13
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
14
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
15
+ ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite'])
16
+
17
+ load(File.dirname(__FILE__) + "/schema.rb") if File.exist?(File.dirname(__FILE__) + "/schema.rb")
18
+
19
+ Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
20
+ $LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path)
21
+
22
+ class Test::Unit::TestCase #:nodoc:
23
+ def create_fixtures(*table_names)
24
+ if block_given?
25
+ Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
26
+ else
27
+ Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
28
+ end
29
+ end
30
+
31
+ # Turn off transactional fixtures if you're working with MyISAM tables in MySQL
32
+ self.use_transactional_fixtures = true
33
+
34
+ # Instantiated fixtures are slow, but give you @david where you otherwise would need people(:david)
35
+ self.use_instantiated_fixtures = false
36
+
37
+ # Add more helper methods to be used by all tests here...
38
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mikejones-acts_as_state_machine
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Not me
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-07-15 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: This act gives an Active Record model the ability to act as a finite state machine (FSM).
17
+ email: michael.daniel.jones@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - CHANGELOG
26
+ - lib/acts_as_state_machine.rb
27
+ - MIT-LICENSE
28
+ - Rakefile
29
+ - README
30
+ - TODO
31
+ - test/acts_as_state_machine_test.rb
32
+ - test/database.yml
33
+ - test/fixtures
34
+ - test/fixtures/conversation.rb
35
+ - test/fixtures/conversations.yml
36
+ - test/fixtures/person.rb
37
+ - test/schema.rb
38
+ - test/test_helper.rb
39
+ has_rdoc: false
40
+ homepage: http://elitists.textdriven.com/svn/plugins/acts_as_state_machine/
41
+ post_install_message:
42
+ rdoc_options: []
43
+
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.2.0
62
+ signing_key:
63
+ specification_version: 2
64
+ summary: Gem of acts as state machine
65
+ test_files:
66
+ - test/acts_as_state_machine_test.rb
67
+ - test/database.yml
68
+ - test/fixtures
69
+ - test/fixtures/conversation.rb
70
+ - test/fixtures/conversations.yml
71
+ - test/fixtures/person.rb
72
+ - test/schema.rb
73
+ - test/test_helper.rb