statesman 4.1.2 → 4.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a4d4aa46f593b25a40fdb2ce22c437d06edf7ec4
4
- data.tar.gz: 9b734682ee2d770977863fff48db0e0606fea028
3
+ metadata.gz: ad6e72cd43e5b54c25a618524a51aa2029d50a60
4
+ data.tar.gz: dd974b992be7e1a764f1b0b58613fe1dbf2951f9
5
5
  SHA512:
6
- metadata.gz: 890add9a875cfa4bda6a926059b57bdc03c64c8aa3d525d0ee7ff3530a0d2c4c6b0230acfebd2079e89e4eafb655b368acc7d64492650fce6464bcb5958e29ae
7
- data.tar.gz: 289d75e87a6a8ba34493fde16d24b3747840b3c865bae67b0bf9fbb991de86e2f0006e9cf48679c89a6bcbbe66deaec19bfaf1e4023b60918ce4088c9ab43e2c
6
+ metadata.gz: ac91aa0a9d34410eda08fab8eded5fba9efc280617dad18eb93c6ed91c8ca46f8a34c3e13f5c7d5ca3797053551e4a514b9bcb1d6433cf7fb9ca2d29bf3da423
7
+ data.tar.gz: 6223d5ca89126a28905943f0b9e91bef5d5c1c2090e3effb6e78b3849a2c7c01c24069f1171307ae0fb4d3c2757dede80f6a04239f21c0bba7f50af1e6c80fe3
@@ -37,7 +37,6 @@ jobs:
37
37
  environment:
38
38
  - RAILS_VERSION=4.2.9
39
39
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
40
- - EXCLUDE_MONGOID=true
41
40
  - DATABASE_DEPENDENCY_PORT=3306
42
41
  - image: circleci/mysql:5.7.18
43
42
  environment:
@@ -52,7 +51,6 @@ jobs:
52
51
  environment:
53
52
  - RAILS_VERSION=4.2.9
54
53
  - DATABASE_URL=postgres://postgres@localhost/statesman_test
55
- - EXCLUDE_MONGOID=true
56
54
  - DATABASE_DEPENDENCY_PORT=5432
57
55
  - image: circleci/postgres:9.6
58
56
  environment:
@@ -65,7 +63,6 @@ jobs:
65
63
  environment:
66
64
  - RAILS_VERSION=5.0.5
67
65
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
68
- - EXCLUDE_MONGOID=true
69
66
  - DATABASE_DEPENDENCY_PORT=3306
70
67
  - image: circleci/mysql:5.7.18
71
68
  environment:
@@ -80,7 +77,6 @@ jobs:
80
77
  environment:
81
78
  - RAILS_VERSION=5.0.5
82
79
  - DATABASE_URL=postgres://postgres@localhost/statesman_test
83
- - EXCLUDE_MONGOID=true
84
80
  - DATABASE_DEPENDENCY_PORT=5432
85
81
  - image: circleci/postgres:9.6
86
82
  environment:
@@ -93,7 +89,6 @@ jobs:
93
89
  environment:
94
90
  - RAILS_VERSION=5.1.3
95
91
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
96
- - EXCLUDE_MONGOID=true
97
92
  - DATABASE_DEPENDENCY_PORT=3306
98
93
  - image: circleci/mysql:5.7.18
99
94
  environment:
@@ -108,7 +103,6 @@ jobs:
108
103
  environment:
109
104
  - RAILS_VERSION=5.1.3
110
105
  - DATABASE_URL=postgres://postgres@localhost/statesman_test
111
- - EXCLUDE_MONGOID=true
112
106
  - DATABASE_DEPENDENCY_PORT=5432
113
107
  - image: circleci/postgres:9.6
114
108
  environment:
@@ -121,7 +115,6 @@ jobs:
121
115
  environment:
122
116
  - RAILS_VERSION=6.0.0
123
117
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
124
- - EXCLUDE_MONGOID=true
125
118
  - DATABASE_DEPENDENCY_PORT=3306
126
119
  - image: circleci/mysql:5.7.18
127
120
  environment:
@@ -136,7 +129,6 @@ jobs:
136
129
  environment:
137
130
  - RAILS_VERSION=6.0.0
138
131
  - DATABASE_URL=postgres://postgres@localhost/statesman_test
139
- - EXCLUDE_MONGOID=true
140
132
  - DATABASE_DEPENDENCY_PORT=5432
141
133
  - image: circleci/postgres:9.6
142
134
  environment:
@@ -149,7 +141,6 @@ jobs:
149
141
  environment:
150
142
  - RAILS_VERSION=master
151
143
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
152
- - EXCLUDE_MONGOID=true
153
144
  - DATABASE_DEPENDENCY_PORT=3306
154
145
  - image: circleci/mysql:5.7.18
155
146
  environment:
@@ -177,7 +168,6 @@ jobs:
177
168
  environment:
178
169
  - RAILS_VERSION=5.2.3
179
170
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
180
- - EXCLUDE_MONGOID=true
181
171
  - DATABASE_DEPENDENCY_PORT=3306
182
172
  - image: circleci/mysql:5.7.18
183
173
  environment:
@@ -192,7 +182,6 @@ jobs:
192
182
  environment:
193
183
  - RAILS_VERSION=5.2.3
194
184
  - DATABASE_URL=postgres://postgres@localhost/statesman_test
195
- - EXCLUDE_MONGOID=true
196
185
  - DATABASE_DEPENDENCY_PORT=5432
197
186
  - image: circleci/postgres:9.6
198
187
  environment:
@@ -205,7 +194,6 @@ jobs:
205
194
  environment:
206
195
  - RAILS_VERSION=4.2.9
207
196
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
208
- - EXCLUDE_MONGOID=true
209
197
  - DATABASE_DEPENDENCY_PORT=3306
210
198
  - image: circleci/mysql:5.7.18
211
199
  environment:
@@ -220,7 +208,6 @@ jobs:
220
208
  environment:
221
209
  - RAILS_VERSION=4.2.9
222
210
  - DATABASE_URL=postgres://postgres@localhost/statesman_test
223
- - EXCLUDE_MONGOID=true
224
211
  - DATABASE_DEPENDENCY_PORT=5432
225
212
  - image: circleci/postgres:9.6
226
213
  environment:
@@ -233,7 +220,6 @@ jobs:
233
220
  environment:
234
221
  - RAILS_VERSION=5.0.5
235
222
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
236
- - EXCLUDE_MONGOID=true
237
223
  - DATABASE_DEPENDENCY_PORT=3306
238
224
  - image: circleci/mysql:5.7.18
239
225
  environment:
@@ -248,7 +234,6 @@ jobs:
248
234
  environment:
249
235
  - RAILS_VERSION=5.0.5
250
236
  - DATABASE_URL=postgres://postgres@localhost/statesman_test
251
- - EXCLUDE_MONGOID=true
252
237
  - DATABASE_DEPENDENCY_PORT=5432
253
238
  - image: circleci/postgres:9.6
254
239
  environment:
@@ -261,7 +246,6 @@ jobs:
261
246
  environment:
262
247
  - RAILS_VERSION=5.1.3
263
248
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
264
- - EXCLUDE_MONGOID=true
265
249
  - DATABASE_DEPENDENCY_PORT=3306
266
250
  - image: circleci/mysql:5.7.18
267
251
  environment:
@@ -276,7 +260,6 @@ jobs:
276
260
  environment:
277
261
  - RAILS_VERSION=5.1.3
278
262
  - DATABASE_URL=postgres://postgres@localhost/statesman_test
279
- - EXCLUDE_MONGOID=true
280
263
  - DATABASE_DEPENDENCY_PORT=5432
281
264
  - image: circleci/postgres:9.6
282
265
  environment:
@@ -289,7 +272,6 @@ jobs:
289
272
  environment:
290
273
  - RAILS_VERSION=4.2.9
291
274
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
292
- - EXCLUDE_MONGOID=true
293
275
  - DATABASE_DEPENDENCY_PORT=3306
294
276
  - image: circleci/mysql:5.7.18
295
277
  environment:
@@ -304,7 +286,6 @@ jobs:
304
286
  environment:
305
287
  - RAILS_VERSION=4.2.9
306
288
  - DATABASE_URL=postgres://postgres@localhost/statesman_test
307
- - EXCLUDE_MONGOID=true
308
289
  - DATABASE_DEPENDENCY_PORT=5432
309
290
  - image: circleci/postgres:9.6
310
291
  environment:
@@ -317,7 +298,6 @@ jobs:
317
298
  environment:
318
299
  - RAILS_VERSION=5.0.5
319
300
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
320
- - EXCLUDE_MONGOID=true
321
301
  - DATABASE_DEPENDENCY_PORT=3306
322
302
  - image: circleci/mysql:5.7.18
323
303
  environment:
@@ -332,7 +312,6 @@ jobs:
332
312
  environment:
333
313
  - RAILS_VERSION=5.0.5
334
314
  - DATABASE_URL=postgres://postgres@localhost/statesman_test
335
- - EXCLUDE_MONGOID=true
336
315
  - DATABASE_DEPENDENCY_PORT=5432
337
316
  - image: circleci/postgres:9.6
338
317
  environment:
@@ -345,7 +324,6 @@ jobs:
345
324
  environment:
346
325
  - RAILS_VERSION=5.1.3
347
326
  - DATABASE_URL=mysql2://root@127.0.0.1/statesman_test
348
- - EXCLUDE_MONGOID=true
349
327
  - DATABASE_DEPENDENCY_PORT=3306
350
328
  - image: circleci/mysql:5.7.18
351
329
  environment:
@@ -360,7 +338,6 @@ jobs:
360
338
  environment:
361
339
  - RAILS_VERSION=5.1.3
362
340
  - DATABASE_URL=postgres://postgres@localhost/statesman_test
363
- - EXCLUDE_MONGOID=true
364
341
  - DATABASE_DEPENDENCY_PORT=5432
365
342
  - image: circleci/postgres:9.6
366
343
  environment:
@@ -85,7 +85,6 @@ RSpec/NestedGroups:
85
85
  RSpec/ScatteredSetup:
86
86
  Exclude:
87
87
  - 'spec/statesman/adapters/active_record_spec.rb'
88
- - 'spec/statesman/adapters/mongoid_spec.rb'
89
88
  - 'spec/statesman/adapters/shared_examples.rb'
90
89
  - 'spec/statesman/machine_spec.rb'
91
90
 
@@ -96,5 +95,4 @@ RSpec/VerifiedDoubles:
96
95
  - 'spec/generators/statesman/active_record_transition_generator_spec.rb'
97
96
  - 'spec/generators/statesman/migration_generator_spec.rb'
98
97
  - 'spec/statesman/adapters/active_record_spec.rb'
99
- - 'spec/statesman/adapters/mongoid_spec.rb'
100
98
  - 'spec/statesman/adapters/shared_examples.rb'
@@ -1,3 +1,8 @@
1
+ ## v4.1.3, 6th November 2019
2
+
3
+ - Add accessible from / to state attributes on the `TransitionFailedError` to avoid parsing strings [@ahjmorton](https://github.com/gocardless/statesman/pull/367)
4
+ - Add `after_transition_failure` mechanism [@credric-cordenier](https://github.com/gocardless/statesman/pull/366)
5
+
1
6
  ## v4.1.2, 17th August 2019
2
7
 
3
8
  - Add support for Rails 6 [@greysteil](https://github.com/gocardless/statesman/pull/360)
data/Gemfile CHANGED
@@ -11,8 +11,6 @@ end
11
11
  # rubocop:enable Bundler/DuplicatedGem
12
12
 
13
13
  group :development do
14
- gem "mongoid", ">= 3.1" unless ENV["EXCLUDE_MONGOID"]
15
-
16
14
  # test/unit is no longer bundled with Ruby 2.2, but required by Rails
17
15
  gem "test-unit", "~> 3.0" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.2.0")
18
16
  end
data/README.md CHANGED
@@ -76,22 +76,16 @@ Then, link it to your model:
76
76
 
77
77
  ```ruby
78
78
  class Order < ActiveRecord::Base
79
- include Statesman::Adapters::ActiveRecordQueries
80
-
81
79
  has_many :order_transitions, autosave: false
82
80
 
81
+ include Statesman::Adapters::ActiveRecordQueries[
82
+ transition_class: OrderTransition,
83
+ initial_state: :pending
84
+ ]
85
+
83
86
  def state_machine
84
87
  @state_machine ||= OrderStateMachine.new(self, transition_class: OrderTransition)
85
88
  end
86
-
87
- def self.transition_class
88
- OrderTransition
89
- end
90
-
91
- def self.initial_state
92
- :pending
93
- end
94
- private_class_method :initial_state
95
89
  end
96
90
  ```
97
91
 
@@ -164,8 +158,9 @@ class Order < ActiveRecord::Base
164
158
  end
165
159
 
166
160
  # Optionally delegate some methods
167
- delegate :can_transition_to?, :transition_to!, :transition_to, :current_state,
168
- to: :state_machine
161
+
162
+ delegate :can_transition_to?, :current_state, :history, :last_transition,
163
+ :transition_to!, :transition_to, :in_state?, to: :state_machine
169
164
  end
170
165
  ```
171
166
  #### Using PostgreSQL JSON column
@@ -199,13 +194,11 @@ or 5. To do that
199
194
  ```ruby
200
195
  Statesman.configure do
201
196
  storage_adapter(Statesman::Adapters::ActiveRecord)
202
- # ...or
203
- storage_adapter(Statesman::Adapters::Mongoid)
204
197
  end
205
198
  ```
206
199
  Statesman defaults to storing transitions in memory. If you're using rails, you
207
200
  can instead configure it to persist transitions to the database by using the
208
- ActiveRecord or Mongoid adapter.
201
+ ActiveRecord adapter.
209
202
 
210
203
  Statesman will fallback to memory unless you specify a transition_class when instantiating your state machine. This allows you to only persist transitions on certain state machines in your app.
211
204
 
@@ -234,7 +227,8 @@ end
234
227
  ```
235
228
  Define a guard. `to` and `from` parameters are optional, a nil parameter means
236
229
  guard all transitions. The passed block should evaluate to a boolean and must
237
- be idempotent as it could be called many times.
230
+ be idempotent as it could be called many times. The guard will pass when it
231
+ evaluates to a truthy value and fail when it evaluates to a falsey value (`nil` or `false`).
238
232
 
239
233
  #### `Machine.before_transition`
240
234
  ```ruby
@@ -261,6 +255,35 @@ after the transition.
261
255
  If you specify `after_commit: true`, the callback will be executed once the
262
256
  transition has been committed to the database.
263
257
 
258
+ #### `Machine.after_transition_failure`
259
+ ```ruby
260
+ Machine.after_transition_failure(from: :some_state, to: :another_state) do |object|
261
+ Logger.info("transition failed for #{object.id}")
262
+ end
263
+ ```
264
+ Define a callback to run if `Statesman::TransitionFailedError` is raised
265
+ during the execution of transition callbacks. `to` and `from`
266
+ parameters are optional, a nil parameter means run after all transitions.
267
+ The model object is passed as an argument to the callback.
268
+ This is executed outside of the transaction wrapping other callbacks.
269
+ If using `transition!` the exception is re-raised after these callbacks are
270
+ executed.
271
+
272
+ #### `Machine.after_guard_failure`
273
+ ```ruby
274
+ Machine.after_guard_failure(from: :some_state, to: :another_state) do |object|
275
+ Logger.info("guard failed for #{object.id}")
276
+ end
277
+ ```
278
+ Define a callback to run if `Statesman::GuardFailedError` is raised
279
+ during the execution of guard callbacks. `to` and `from`
280
+ parameters are optional, a nil parameter means run after all transitions.
281
+ The model object is passed as an argument to the callback.
282
+ This is executed outside of the transaction wrapping other callbacks.
283
+ If using `transition!` the exception is re-raised after these callbacks are
284
+ executed.
285
+
286
+
264
287
  #### `Machine.new`
265
288
  ```ruby
266
289
  my_machine = Machine.new(my_model, transition_class: MyTransitionModel)
@@ -328,43 +351,34 @@ callback code throws an exception, it will not be caught.)
328
351
 
329
352
  A mixin is provided for the ActiveRecord adapter which adds scopes to easily
330
353
  find all models currently in (or not in) a given state. Include it into your
331
- model and define `transition_class` and `initial_state` class methods:
354
+ model and passing in `transition_class` and `initial_state` as options.
355
+
356
+ In 4.1.1 and below, these two options had to be defined as methods on the model,
357
+ but 4.2.0 and above allow this style of configuration as well. The old method
358
+ pollutes the model with extra class methods, and is deprecated, to be removed
359
+ in 5.0.0.
332
360
 
333
361
  ```ruby
334
362
  class Order < ActiveRecord::Base
335
- include Statesman::Adapters::ActiveRecordQueries
336
-
337
- def self.transition_class
338
- OrderTransition
339
- end
340
- private_class_method :transition_class
341
-
342
- def self.initial_state
343
- OrderStateMachine.initial_state
344
- end
345
- private_class_method :initial_state
363
+ has_many :order_transitions, autosave: false
364
+ include Statesman::Adapters::ActiveRecordQueries[
365
+ transition_class: OrderTransition,
366
+ initial_state: OrderStateMachine.initial_state
367
+ ]
346
368
  end
347
369
  ```
348
370
 
349
371
  If the transition class-name differs from the association name, you will also
350
- need to define a corresponding `transition_name` class method:
372
+ need to pass `transition_name` as an option:
351
373
 
352
374
  ```ruby
353
375
  class Order < ActiveRecord::Base
354
376
  has_many :transitions, class_name: "OrderTransition", autosave: false
355
-
356
- def self.transition_name
357
- :transitions
358
- end
359
-
360
- def self.transition_class
361
- OrderTransition
362
- end
363
-
364
- def self.initial_state
365
- OrderStateMachine.initial_state
366
- end
367
- private_class_method :initial_state
377
+ include Statesman::Adapters::ActiveRecordQueries[
378
+ transition_class: OrderTransition,
379
+ initial_state: OrderStateMachine.initial_state,
380
+ transition_name: :transitions
381
+ ]
368
382
  end
369
383
  ```
370
384
 
@@ -375,7 +389,7 @@ Returns all models currently in any of the supplied states.
375
389
  Returns all models not currently in any of the supplied states.
376
390
 
377
391
 
378
- ### `Model.most_recent_transition_join`
392
+ #### `Model.most_recent_transition_join`
379
393
  This joins the model to its most recent transition whatever that may be.
380
394
  We expose this method to ease use of ActiveRecord's `or` e.g
381
395
 
@@ -425,6 +439,22 @@ class OrderStateMachine
425
439
  end
426
440
  ```
427
441
 
442
+ #### Deleting records.
443
+
444
+ If you need to delete the Parent model regularly you will need to change
445
+ either the association deletion behaviour or add a `DELETE CASCADE` condition
446
+ to foreign key in your database.
447
+
448
+ E.g
449
+ ```
450
+ has_many :order_transitions, autosave: false, dependent: :destroy
451
+ ```
452
+ or when migrating the transition model
453
+ ```
454
+ add_foreign_key :order_transitions, :orders, on_delete: :cascade
455
+ ```
456
+
457
+
428
458
  ## Testing Statesman Implementations
429
459
 
430
460
  This answer was abstracted from [this issue](https://github.com/gocardless/statesman/issues/77).
data/Rakefile CHANGED
@@ -4,10 +4,6 @@ require "rspec/core/rake_task"
4
4
  RSpec::Core::RakeTask.new(:spec) do |task|
5
5
  task.rspec_opts = []
6
6
 
7
- if ENV["EXCLUDE_MONGOID"]
8
- task.rspec_opts += ["--tag ~mongo", "--exclude-pattern **/*mongo*"]
9
- end
10
-
11
7
  if ENV["CIRCLECI"]
12
8
  task.rspec_opts += ["--format RspecJunitFormatter",
13
9
  "--out /tmp/test-results/rspec.xml",
@@ -12,9 +12,6 @@ module Statesman
12
12
  "statesman/adapters/active_record_transition"
13
13
  autoload :ActiveRecordQueries,
14
14
  "statesman/adapters/active_record_queries"
15
- autoload :Mongoid, "statesman/adapters/mongoid"
16
- autoload :MongoidTransition,
17
- "statesman/adapters/mongoid_transition"
18
15
  end
19
16
  require "statesman/railtie" if defined?(::Rails::Railtie)
20
17
 
@@ -1,51 +1,122 @@
1
1
  module Statesman
2
2
  module Adapters
3
3
  module ActiveRecordQueries
4
+ def self.check_missing_methods!(base)
5
+ missing_methods = %i[transition_class initial_state].
6
+ reject { |_method| base.respond_to?(:method) }
7
+ return if missing_methods.none?
8
+
9
+ raise NotImplementedError,
10
+ "#{missing_methods.join(', ')} method(s) should be defined on " \
11
+ "the model. Alternatively, use the new form of `extend " \
12
+ "Statesman::Adapters::ActiveRecordQueries[" \
13
+ "transition_class: MyTransition, " \
14
+ "initial_state: :some_state]`"
15
+ end
16
+
4
17
  def self.included(base)
5
- base.extend(ClassMethods)
18
+ check_missing_methods!(base)
19
+
20
+ base.include(
21
+ ClassMethods.new(
22
+ transition_class: base.transition_class,
23
+ initial_state: base.initial_state,
24
+ most_recent_transition_alias: base.try(:most_recent_transition_alias),
25
+ transition_name: base.try(:transition_name),
26
+ ),
27
+ )
6
28
  end
7
29
 
8
- module ClassMethods
9
- def in_state(*states)
10
- states = states.flatten.map(&:to_s)
30
+ def self.[](**args)
31
+ ClassMethods.new(**args)
32
+ end
33
+
34
+ class ClassMethods < Module
35
+ def initialize(**args)
36
+ @args = args
37
+ end
38
+
39
+ def included(base)
40
+ ensure_inheritance(base)
41
+
42
+ query_builder = QueryBuilder.new(base, **@args)
43
+
44
+ base.define_singleton_method(:most_recent_transition_join) do
45
+ query_builder.most_recent_transition_join
46
+ end
47
+
48
+ define_in_state(base, query_builder)
49
+ define_not_in_state(base, query_builder)
50
+ end
11
51
 
12
- joins(most_recent_transition_join).
13
- where(states_where(most_recent_transition_alias, states), states)
52
+ private
53
+
54
+ def ensure_inheritance(base)
55
+ klass = self
56
+ existing_inherited = base.method(:inherited)
57
+ base.define_singleton_method(:inherited) do |subclass|
58
+ existing_inherited.call(subclass)
59
+ subclass.send(:include, klass)
60
+ end
14
61
  end
15
62
 
16
- def not_in_state(*states)
17
- states = states.flatten.map(&:to_s)
63
+ def define_in_state(base, query_builder)
64
+ base.define_singleton_method(:in_state) do |*states|
65
+ states = states.flatten.map(&:to_s)
18
66
 
19
- joins(most_recent_transition_join).
20
- where("NOT (#{states_where(most_recent_transition_alias, states)})",
21
- states)
67
+ joins(most_recent_transition_join).
68
+ where(query_builder.states_where(states), states)
69
+ end
70
+ end
71
+
72
+ def define_not_in_state(base, query_builder)
73
+ base.define_singleton_method(:not_in_state) do |*states|
74
+ states = states.flatten.map(&:to_s)
75
+
76
+ joins(most_recent_transition_join).
77
+ where("NOT (#{query_builder.states_where(states)})", states)
78
+ end
79
+ end
80
+ end
81
+
82
+ class QueryBuilder
83
+ def initialize(model, transition_class:, initial_state:,
84
+ most_recent_transition_alias: nil,
85
+ transition_name: nil)
86
+ @model = model
87
+ @transition_class = transition_class
88
+ @initial_state = initial_state
89
+ @most_recent_transition_alias = most_recent_transition_alias
90
+ @transition_name = transition_name
91
+ end
92
+
93
+ def states_where(states)
94
+ if initial_state.to_s.in?(states.map(&:to_s))
95
+ "#{most_recent_transition_alias}.to_state IN (?) OR " \
96
+ "#{most_recent_transition_alias}.to_state IS NULL"
97
+ else
98
+ "#{most_recent_transition_alias}.to_state IN (?) AND " \
99
+ "#{most_recent_transition_alias}.to_state IS NOT NULL"
100
+ end
22
101
  end
23
102
 
24
103
  def most_recent_transition_join
25
104
  "LEFT OUTER JOIN #{model_table} AS #{most_recent_transition_alias}
26
- ON #{table_name}.id =
105
+ ON #{model.table_name}.id =
27
106
  #{most_recent_transition_alias}.#{model_foreign_key}
28
107
  AND #{most_recent_transition_alias}.most_recent = #{db_true}"
29
108
  end
30
109
 
31
110
  private
32
111
 
33
- def transition_class
34
- raise NotImplementedError, "A transition_class method should be " \
35
- "defined on the model"
36
- end
37
-
38
- def initial_state
39
- raise NotImplementedError, "An initial_state method should be " \
40
- "defined on the model"
41
- end
112
+ attr_reader :model, :transition_class, :initial_state
42
113
 
43
114
  def transition_name
44
- transition_class.table_name.to_sym
115
+ @transition_name || transition_class.table_name.to_sym
45
116
  end
46
117
 
47
118
  def transition_reflection
48
- reflect_on_all_associations(:has_many).each do |value|
119
+ model.reflect_on_all_associations(:has_many).each do |value|
49
120
  return value if value.klass == transition_class
50
121
  end
51
122
 
@@ -62,18 +133,9 @@ module Statesman
62
133
  transition_reflection.table_name
63
134
  end
64
135
 
65
- def states_where(temporary_table_name, states)
66
- if initial_state.to_s.in?(states.map(&:to_s))
67
- "#{temporary_table_name}.to_state IN (?) OR " \
68
- "#{temporary_table_name}.to_state IS NULL"
69
- else
70
- "#{temporary_table_name}.to_state IN (?) AND " \
71
- "#{temporary_table_name}.to_state IS NOT NULL"
72
- end
73
- end
74
-
75
136
  def most_recent_transition_alias
76
- "most_recent_#{transition_name.to_s.singularize}"
137
+ @most_recent_transition_alias ||
138
+ "most_recent_#{transition_name.to_s.singularize}"
77
139
  end
78
140
 
79
141
  def db_true