statesman 4.1.2 → 4.1.3

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