mikejones-acts_as_state_machine 0.0.1

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/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