state_machines-activerecord 0.103.0 → 0.200.0
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
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f022e63c2bfd6af186733c95cbfc19cb7f25dfff128c791e5199b1a3f4e6d760
|
|
4
|
+
data.tar.gz: '094b3a2befdd974fa1828e9237f75f0a5a38319b7e38b56b8a3dda376a1e797d'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cfb9f8bca11c37eeaff38ff0a237c2cc241986bba88ee4a8859199ea05ee7d06b4449da73574e0125f678de3082f3ba4e472f491f0efe9100fc2b585cac5e818
|
|
7
|
+
data.tar.gz: 8fb449a5ead40d4cf43e92208d307bb34f557e9aae068cda123c968ded28aecfa7a97c416426b04f99afd2550a50b54f852bc6e04c5ab6dc4fef62199d5e615d
|
data/README.md
CHANGED
|
@@ -164,6 +164,62 @@ machine.state_machine_methods
|
|
|
164
164
|
# => ["status_pending?", "status_processing?", "status_completed?", "status_cancelled?", ...]
|
|
165
165
|
```
|
|
166
166
|
|
|
167
|
+
## Integer-backed state attributes
|
|
168
|
+
|
|
169
|
+
Integer columns whose states don't declare explicit values are converted
|
|
170
|
+
transparently: application code reads state names while the database stores
|
|
171
|
+
integers (mapped by definition order).
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
class Order < ApplicationRecord
|
|
175
|
+
state_machine :status, initial: :pending do
|
|
176
|
+
state :pending
|
|
177
|
+
state :approved
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
order = Order.create!
|
|
182
|
+
order.status = :approved
|
|
183
|
+
order.status # => "approved"
|
|
184
|
+
# The database stores 1.
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Machines where every state declares an explicit integer value keep the classic
|
|
188
|
+
raw-integer behavior automatically: reads return the integer and `status_name`
|
|
189
|
+
returns the state name:
|
|
190
|
+
|
|
191
|
+
```ruby
|
|
192
|
+
class LegacyOrder < ApplicationRecord
|
|
193
|
+
self.table_name = "orders"
|
|
194
|
+
|
|
195
|
+
state_machine :status, initial: :pending do
|
|
196
|
+
state :pending, value: 0
|
|
197
|
+
state :approved, value: 1
|
|
198
|
+
|
|
199
|
+
event :approve do
|
|
200
|
+
transition pending: :approved
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
order = LegacyOrder.create!
|
|
206
|
+
order.approve!
|
|
207
|
+
order.status # => 1
|
|
208
|
+
order.status_name # => :approved
|
|
209
|
+
# The database stores 1.
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Applications that want no type conversion at all can disable it during boot,
|
|
213
|
+
before defining affected state machines:
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
# config/initializers/state_machines.rb
|
|
217
|
+
|
|
218
|
+
StateMachines::Integrations::ActiveRecord.auto_convert_integer_state_attributes = false
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
With this setting disabled, integer-backed state attributes are left entirely
|
|
222
|
+
to ActiveRecord's normal integer handling.
|
|
167
223
|
|
|
168
224
|
### Requirements for Enum Integration
|
|
169
225
|
|
|
@@ -9,52 +9,113 @@ module StateMachines
|
|
|
9
9
|
# States without explicit integer values are mapped by their index position
|
|
10
10
|
# in the states collection (0, 1, 2, …). States with an explicit integer
|
|
11
11
|
# value (e.g. state :pending, value: 2) use that value directly.
|
|
12
|
+
#
|
|
13
|
+
# When *every* named state has an explicit integer value, the column already
|
|
14
|
+
# stores the canonical state values and no name<->integer conversion is
|
|
15
|
+
# needed. In that case the type delegates to the column's original integer
|
|
16
|
+
# type, preserving the classic raw-integer behavior
|
|
17
|
+
# (e.g. record.status # => 1, record.status_name # => :approved).
|
|
12
18
|
class Integer < ::ActiveRecord::Type::Value
|
|
13
|
-
|
|
19
|
+
# The column's original attribute type, exposed so re-registration
|
|
20
|
+
# (e.g. for STI subclasses) can reuse it instead of wrapping this type.
|
|
21
|
+
#
|
|
22
|
+
# @return [ActiveModel::Type::Value]
|
|
23
|
+
attr_reader :raw_type
|
|
24
|
+
|
|
25
|
+
# @param states [StateMachines::StateCollection] live collection of the
|
|
26
|
+
# machine's states; held by reference because states are defined after
|
|
27
|
+
# the type is registered
|
|
28
|
+
# @param raw_type [ActiveModel::Type::Value, nil] the column's original
|
|
29
|
+
# attribute type, used verbatim in passthrough mode so adapter-specific
|
|
30
|
+
# integer behavior (limits, range checks) is preserved
|
|
31
|
+
def initialize(states, raw_type: nil)
|
|
14
32
|
@states = states
|
|
33
|
+
@raw_type = raw_type || ::ActiveModel::Type::Integer.new
|
|
15
34
|
super()
|
|
16
35
|
end
|
|
17
36
|
|
|
18
|
-
# integer from
|
|
37
|
+
# Converts an integer from the database to a state name string.
|
|
38
|
+
#
|
|
39
|
+
# @param value [Integer, String, nil] raw database value
|
|
40
|
+
# @return [String, Integer, nil] state name, or the original type's value
|
|
41
|
+
# in passthrough mode, or the raw value when no state matches
|
|
19
42
|
def deserialize(value)
|
|
43
|
+
states = named_states
|
|
44
|
+
return @raw_type.deserialize(value) if passthrough?(states)
|
|
20
45
|
return nil if value.nil?
|
|
46
|
+
|
|
21
47
|
int_val = value.to_i
|
|
22
|
-
state =
|
|
48
|
+
state = states.detect { |s| state_integer(s, states) == int_val }
|
|
23
49
|
state ? state.name.to_s : value
|
|
24
50
|
end
|
|
25
51
|
|
|
26
|
-
#
|
|
52
|
+
# Converts an assigned value (symbol, string, or integer) to the in-memory
|
|
53
|
+
# state name string.
|
|
54
|
+
#
|
|
55
|
+
# @param value [Symbol, String, Integer, nil] assigned value
|
|
56
|
+
# @return [String, Integer, nil] state name, or the original type's cast
|
|
57
|
+
# in passthrough mode
|
|
27
58
|
def cast(value)
|
|
59
|
+
states = named_states
|
|
60
|
+
return @raw_type.cast(value) if passthrough?(states)
|
|
28
61
|
return nil if value.nil?
|
|
29
|
-
|
|
30
|
-
state
|
|
62
|
+
|
|
63
|
+
state = states.detect { |s| s.name.to_s == value.to_s }
|
|
64
|
+
state ||= states.detect { |s| state_integer(s, states) == value.to_i } if value.respond_to?(:to_i)
|
|
31
65
|
state ? state.name.to_s : value.to_s
|
|
32
66
|
end
|
|
33
67
|
|
|
34
|
-
# state name string
|
|
68
|
+
# Converts a state name string to its integer for the database write.
|
|
69
|
+
#
|
|
70
|
+
# @param value [String, Symbol, Integer, nil] in-memory value
|
|
71
|
+
# @return [Integer, nil] integer to store
|
|
35
72
|
def serialize(value)
|
|
73
|
+
states = named_states
|
|
74
|
+
return @raw_type.serialize(value) if passthrough?(states)
|
|
36
75
|
return nil if value.nil?
|
|
37
|
-
|
|
38
|
-
state
|
|
76
|
+
|
|
77
|
+
state = states.detect { |s| s.name.to_s == value.to_s }
|
|
78
|
+
state ? state_integer(state, states) : value
|
|
39
79
|
end
|
|
40
80
|
|
|
81
|
+
# @return [Symbol] the ActiveModel type identifier
|
|
41
82
|
def type
|
|
42
83
|
:integer
|
|
43
84
|
end
|
|
44
85
|
|
|
45
86
|
private
|
|
46
87
|
|
|
47
|
-
# All non-nil states in definition order
|
|
88
|
+
# All non-nil states in definition order, not memoized because states are
|
|
48
89
|
# added to the collection after the type is instantiated.
|
|
90
|
+
#
|
|
91
|
+
# @return [Array<StateMachines::State>]
|
|
49
92
|
def named_states
|
|
50
93
|
@states.reject { |s| s.name.nil? }
|
|
51
94
|
end
|
|
52
95
|
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
|
|
96
|
+
# Whether the machine was defined for raw integer storage, in which case
|
|
97
|
+
# conversion would change documented behavior. True when every named state
|
|
98
|
+
# declares an explicit integer value. Evaluated lazily on every call
|
|
99
|
+
# because states (and their values) are defined after the type is
|
|
100
|
+
# registered. Uses value(false) so dynamic (Proc) state values are never
|
|
101
|
+
# evaluated for this metadata decision.
|
|
102
|
+
#
|
|
103
|
+
# @param states [Array<StateMachines::State>] pre-computed named states
|
|
104
|
+
# @return [Boolean]
|
|
105
|
+
def passthrough?(states)
|
|
106
|
+
states.any? && states.all? { |s| s.value(false).is_a?(::Integer) }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# The integer to use for storage: the explicit state value if set
|
|
110
|
+
# (e.g. state :pending, value: 2), otherwise the index position among
|
|
111
|
+
# named states.
|
|
112
|
+
#
|
|
113
|
+
# @param state [StateMachines::State]
|
|
114
|
+
# @param states [Array<StateMachines::State>] pre-computed named states
|
|
115
|
+
# @return [Integer]
|
|
116
|
+
def state_integer(state, states)
|
|
117
|
+
value = state.value(false)
|
|
118
|
+
value.is_a?(::Integer) ? value : states.index(state)
|
|
58
119
|
end
|
|
59
120
|
end
|
|
60
121
|
end
|
|
@@ -316,6 +316,12 @@ module StateMachines
|
|
|
316
316
|
# * (8) *after_transition*
|
|
317
317
|
# * (-) end transaction (if enabled)
|
|
318
318
|
# * (9) after_commit
|
|
319
|
+
# * (10) *after_transition* callbacks defined with <tt>:after_commit => true</tt>
|
|
320
|
+
#
|
|
321
|
+
# An +after_transition+ callback defined with the <tt>:after_commit</tt>
|
|
322
|
+
# option is deferred until the surrounding database transaction has been
|
|
323
|
+
# committed (and discarded if it rolls back). See the documentation for
|
|
324
|
+
# +after_transition+ in this integration for more details.
|
|
319
325
|
#
|
|
320
326
|
# == Internationalization
|
|
321
327
|
#
|
|
@@ -372,6 +378,7 @@ module StateMachines
|
|
|
372
378
|
|
|
373
379
|
# The default options to use for state machines using this integration
|
|
374
380
|
@defaults = { action: :save, use_transactions: true }
|
|
381
|
+
@auto_convert_integer_state_attributes = true
|
|
375
382
|
|
|
376
383
|
# Machine-specific methods for enum integration
|
|
377
384
|
module MachineMethods
|
|
@@ -382,7 +389,24 @@ module StateMachines
|
|
|
382
389
|
def after_initialize
|
|
383
390
|
super
|
|
384
391
|
initialize_enum_integration
|
|
385
|
-
|
|
392
|
+
|
|
393
|
+
register_integer_type if register_integer_type?
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# Hook called when a machine is assigned to a class, including when an
|
|
397
|
+
# inherited machine is cloned for an STI subclass. The cloned machine
|
|
398
|
+
# carries the parent's @integer_type_registered flag while the subclass
|
|
399
|
+
# still inherits the parent's attribute type, which references the
|
|
400
|
+
# parent machine's state collection. Re-register so the subclass type
|
|
401
|
+
# sees this machine's (cloned) states, picking up subclass-added states
|
|
402
|
+
# as they are defined.
|
|
403
|
+
#
|
|
404
|
+
# @param klass [Class] the new owner class
|
|
405
|
+
# @return [Class] the assigned owner class
|
|
406
|
+
def owner_class=(klass)
|
|
407
|
+
super.tap do
|
|
408
|
+
register_integer_type if integer_type_registered?
|
|
409
|
+
end
|
|
386
410
|
end
|
|
387
411
|
|
|
388
412
|
# Check if enum integration should be enabled for this machine
|
|
@@ -418,9 +442,39 @@ module StateMachines
|
|
|
418
442
|
# Generate methods after each state addition if enum integration is enabled
|
|
419
443
|
generate_state_machine_methods if enum_integrated?
|
|
420
444
|
|
|
445
|
+
# States defined after initialization (e.g. adding an explicit value
|
|
446
|
+
# to the initial state) can change how the column default maps to
|
|
447
|
+
# states, so re-evaluate the conflicting-default warning
|
|
448
|
+
recheck_conflicting_attribute_default if integer_type_registered?
|
|
449
|
+
|
|
421
450
|
result
|
|
422
451
|
end
|
|
423
452
|
|
|
453
|
+
# Warns at most once when the column default conflicts with the
|
|
454
|
+
# machine's initial state. The conflict is re-evaluated as states are
|
|
455
|
+
# defined, so remember when the warning has been issued.
|
|
456
|
+
def check_conflicting_attribute_default
|
|
457
|
+
return if @attribute_default_conflict_warned
|
|
458
|
+
|
|
459
|
+
initial_state = states.detect(&:initial)
|
|
460
|
+
conflict = !owner_class_attribute_default.nil? && (
|
|
461
|
+
dynamic_initial_state? || !owner_class_attribute_default_matches?(initial_state)
|
|
462
|
+
)
|
|
463
|
+
return unless conflict
|
|
464
|
+
|
|
465
|
+
@attribute_default_conflict_warned = true
|
|
466
|
+
super
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# Returns true when this machine should use the custom integer attribute type
|
|
470
|
+
# to convert between Ruby state names and integer database values. This only
|
|
471
|
+
# applies to non-enum integer columns when automatic conversion is enabled.
|
|
472
|
+
def register_integer_type?
|
|
473
|
+
StateMachines::Integrations::ActiveRecord.auto_convert_integer_state_attributes &&
|
|
474
|
+
integer_column? &&
|
|
475
|
+
!enum_integrated?
|
|
476
|
+
end
|
|
477
|
+
|
|
424
478
|
# Check if this machine has enum integration enabled
|
|
425
479
|
def enum_integrated?
|
|
426
480
|
enum_integration && enum_integration[:enabled]
|
|
@@ -451,33 +505,132 @@ module StateMachines
|
|
|
451
505
|
!!@integer_type_registered
|
|
452
506
|
end
|
|
453
507
|
|
|
508
|
+
# Creates a callback that will be invoked *after* a transition is
|
|
509
|
+
# performed, so long as the given requirements match the transition.
|
|
510
|
+
#
|
|
511
|
+
# In addition to the configuration options supported by the core
|
|
512
|
+
# +after_transition+ (see StateMachines::Machine#after_transition), the
|
|
513
|
+
# ActiveRecord integration supports:
|
|
514
|
+
# * <tt>:after_commit</tt> - Defer execution of the callback until the
|
|
515
|
+
# database transaction wrapping the transition has been committed.
|
|
516
|
+
# When no transaction is open at that point, the callback runs
|
|
517
|
+
# immediately. When the transaction (or an outer transaction wrapping
|
|
518
|
+
# it) is rolled back, the callback is discarded.
|
|
519
|
+
#
|
|
520
|
+
# This is the safe place to enqueue background jobs that reference the
|
|
521
|
+
# record (e.g. via GlobalID), since a regular +after_transition+ runs
|
|
522
|
+
# inside the transaction, before the record's changes are visible to
|
|
523
|
+
# other connections:
|
|
524
|
+
#
|
|
525
|
+
# class Vehicle < ApplicationRecord
|
|
526
|
+
# state_machine do
|
|
527
|
+
# after_transition on: :ignite, after_commit: true do |vehicle|
|
|
528
|
+
# EngineWarmupJob.perform_later(vehicle)
|
|
529
|
+
# end
|
|
530
|
+
#
|
|
531
|
+
# ...
|
|
532
|
+
# end
|
|
533
|
+
# end
|
|
534
|
+
#
|
|
535
|
+
# Note that a deferred callback cannot halt the callback chain or
|
|
536
|
+
# affect the result of the transition: by the time it runs, the
|
|
537
|
+
# transition has already been committed. For the same reason, an
|
|
538
|
+
# exception raised by a deferred callback is not propagated (doing so
|
|
539
|
+
# would revert the record's in-memory state even though the database
|
|
540
|
+
# was already updated); it is reported to +ActiveSupport.error_reporter+
|
|
541
|
+
# (+Rails.error+) instead. Conditions (<tt>:if</tt>/<tt>:unless</tt>)
|
|
542
|
+
# and state requirements are evaluated when the transition is
|
|
543
|
+
# performed, not at commit time.
|
|
544
|
+
#
|
|
545
|
+
# Like ActiveRecord's own +after_commit+, a surrounding
|
|
546
|
+
# <tt>transaction(joinable: false)</tt> wrapper is transparent: the
|
|
547
|
+
# callback fires at the inner commit. This is what makes it fire
|
|
548
|
+
# under transactional test fixtures.
|
|
549
|
+
def after_transition(*args, **options, &block)
|
|
550
|
+
# The flag may hide in a legacy trailing positional options hash
|
|
551
|
+
positional_options = args.last.is_a?(Hash) ? args.pop.dup : {}
|
|
552
|
+
options = positional_options.merge(options)
|
|
553
|
+
|
|
554
|
+
# Only a boolean is the flag — a non-boolean value is the implicit
|
|
555
|
+
# state-requirement form for a state named :after_commit
|
|
556
|
+
flag = options[:after_commit]
|
|
557
|
+
return super unless flag == true || flag == false
|
|
558
|
+
|
|
559
|
+
options.delete(:after_commit)
|
|
560
|
+
return super unless flag
|
|
561
|
+
|
|
562
|
+
# Method handling goes to a real Callback (reusing core's binding,
|
|
563
|
+
# arity and :do semantics); branch matching stays on the wrapper so
|
|
564
|
+
# conditions are evaluated at transition time
|
|
565
|
+
parsed = parse_callback_arguments(args, options)
|
|
566
|
+
method_options = parsed.slice(:do, :bind_to_object)
|
|
567
|
+
method_options[:terminator] = callback_terminator
|
|
568
|
+
branch_options = parsed.except(:do, :bind_to_object, :terminator)
|
|
569
|
+
|
|
570
|
+
deferred = Callback.new(:after, method_options, &block)
|
|
571
|
+
|
|
572
|
+
super(**branch_options, bind_to_object: false) do |object, transition|
|
|
573
|
+
object.class.current_transaction.after_commit do
|
|
574
|
+
# The transition's catch(:halt) is gone at commit time
|
|
575
|
+
catch(:halt) { deferred.call(object, {}, transition) }
|
|
576
|
+
rescue StandardError => e
|
|
577
|
+
# Raising would roll back in-memory state already committed; report instead
|
|
578
|
+
ActiveSupport.error_reporter.report(e, handled: false, source: 'state_machines-activerecord')
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
end
|
|
582
|
+
|
|
454
583
|
# Machine internals (state matching, validations) call read() to get the
|
|
455
|
-
# current state value and compare it against state.value.
|
|
456
|
-
#
|
|
457
|
-
#
|
|
458
|
-
#
|
|
584
|
+
# current state value and compare it against state.value. The custom
|
|
585
|
+
# integer type already returns the canonical value when state values are
|
|
586
|
+
# uniform: raw integers when every named state has an explicit integer
|
|
587
|
+
# value (passthrough), name strings when none do (state.value is the
|
|
588
|
+
# name). Only machines mixing explicit and auto-indexed values need an
|
|
589
|
+
# override, because the type returns name strings while the explicit
|
|
590
|
+
# states match on integers; map the stored value back to the matched
|
|
591
|
+
# state's canonical state.value.
|
|
459
592
|
#
|
|
460
|
-
#
|
|
461
|
-
#
|
|
593
|
+
# @param object [ActiveRecord::Base] record being read
|
|
594
|
+
# @param attr_sym [Symbol] attribute kind (:state, :event, ...)
|
|
595
|
+
# @param ivar [Boolean] whether to read from an instance variable
|
|
596
|
+
# @return [Object] a value machine internals can match on state.value
|
|
462
597
|
def read(object, attr_sym, ivar = false)
|
|
463
598
|
return super unless integer_type_registered? && attr_sym == :state
|
|
464
|
-
return super unless
|
|
599
|
+
return super unless mixed_integer_state_values?
|
|
465
600
|
|
|
466
601
|
raw = object.read_attribute_before_type_cast(attribute.to_s)
|
|
467
602
|
if raw.is_a?(::String) || raw.is_a?(::Symbol)
|
|
468
603
|
matched = states.detect { |s| s.name && s.name.to_s == raw.to_s }
|
|
469
|
-
return matched
|
|
604
|
+
return matched.value if matched
|
|
470
605
|
end
|
|
471
|
-
|
|
606
|
+
|
|
607
|
+
name = owner_class.type_for_attribute(attribute.to_s).deserialize(raw)
|
|
608
|
+
matched = states.detect { |s| s.name && s.name.to_s == name.to_s }
|
|
609
|
+
matched ? matched.value : raw
|
|
472
610
|
end
|
|
473
611
|
|
|
474
612
|
private
|
|
475
613
|
|
|
476
|
-
#
|
|
477
|
-
#
|
|
478
|
-
#
|
|
479
|
-
|
|
480
|
-
|
|
614
|
+
# Re-runs the conflicting-default check for states defined after
|
|
615
|
+
# initialization. Skipped until an initial state exists (the DSL block
|
|
616
|
+
# evaluates before initial_state= during Machine.new).
|
|
617
|
+
#
|
|
618
|
+
# @return [void]
|
|
619
|
+
def recheck_conflicting_attribute_default
|
|
620
|
+
return unless states.detect(&:initial)
|
|
621
|
+
|
|
622
|
+
check_conflicting_attribute_default
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
# Returns true when named states mix explicit integer values with
|
|
626
|
+
# auto-indexed (name-valued) states. Uses value(false) so dynamic
|
|
627
|
+
# (Proc) state values are never evaluated for this metadata decision.
|
|
628
|
+
#
|
|
629
|
+
# @return [Boolean]
|
|
630
|
+
def mixed_integer_state_values?
|
|
631
|
+
named = states.select(&:name)
|
|
632
|
+
explicit = named.count { |s| s.value(false).is_a?(::Integer) }
|
|
633
|
+
explicit.positive? && explicit < named.size
|
|
481
634
|
end
|
|
482
635
|
|
|
483
636
|
# Returns true when the state machine attribute is backed by an integer column
|
|
@@ -497,7 +650,14 @@ module StateMachines
|
|
|
497
650
|
def register_integer_type
|
|
498
651
|
@raw_integer_column_default = owner_class.column_defaults[attribute.to_s]
|
|
499
652
|
@integer_type_registered = true
|
|
500
|
-
|
|
653
|
+
|
|
654
|
+
# When re-registering (e.g. for an STI subclass that inherited the
|
|
655
|
+
# parent's custom type), unwrap it to keep the column's original type
|
|
656
|
+
# as the passthrough delegate.
|
|
657
|
+
current_type = owner_class.type_for_attribute(attribute.to_s)
|
|
658
|
+
raw_type = current_type.is_a?(StateMachines::Type::Integer) ? current_type.raw_type : current_type
|
|
659
|
+
|
|
660
|
+
owner_class.attribute(attribute.to_s, StateMachines::Type::Integer.new(states, raw_type: raw_type))
|
|
501
661
|
end
|
|
502
662
|
|
|
503
663
|
# Detect existing enum methods for this attribute
|
|
@@ -650,6 +810,8 @@ module StateMachines
|
|
|
650
810
|
include MachineMethods
|
|
651
811
|
|
|
652
812
|
class << self
|
|
813
|
+
attr_accessor :auto_convert_integer_state_attributes
|
|
814
|
+
|
|
653
815
|
# Classes that inherit from ActiveRecord::Base will automatically use
|
|
654
816
|
# the ActiveRecord integration.
|
|
655
817
|
def matching_ancestors
|
|
@@ -674,6 +836,22 @@ module StateMachines
|
|
|
674
836
|
owner_class.column_defaults[attribute.to_s]
|
|
675
837
|
end
|
|
676
838
|
|
|
839
|
+
# Checks whether the given state matches the column default. When the
|
|
840
|
+
# custom integer type is registered, auto-indexed states match on their
|
|
841
|
+
# name string while the column default is a raw integer, so the default
|
|
842
|
+
# is also compared through the type's mapping (e.g. 0 matches the first
|
|
843
|
+
# auto-indexed state).
|
|
844
|
+
#
|
|
845
|
+
# @param state [StateMachines::State] the state to compare (the initial state)
|
|
846
|
+
# @return [Boolean] whether the column default represents this state
|
|
847
|
+
def owner_class_attribute_default_matches?(state)
|
|
848
|
+
matches = super
|
|
849
|
+
return matches if matches || !integer_type_registered?
|
|
850
|
+
|
|
851
|
+
default = owner_class_attribute_default
|
|
852
|
+
state.matches?(owner_class.type_for_attribute(attribute.to_s).deserialize(default))
|
|
853
|
+
end
|
|
854
|
+
|
|
677
855
|
def define_state_initializer
|
|
678
856
|
define_helper :instance, <<-END_EVAL, __FILE__, __LINE__ + 1
|
|
679
857
|
def initialize(attributes = nil, *)
|
|
@@ -759,12 +937,6 @@ module StateMachines
|
|
|
759
937
|
|
|
760
938
|
private
|
|
761
939
|
|
|
762
|
-
# Generates the results for the given scope based on one or more states to filter by
|
|
763
|
-
def run_scope(scope, machine, klass, states)
|
|
764
|
-
values = states.flatten.compact.map { |state| machine.states.fetch(state).value }
|
|
765
|
-
scope.call(klass, values)
|
|
766
|
-
end
|
|
767
|
-
|
|
768
940
|
# ActiveModel's use of method_missing / respond_to for attribute methods
|
|
769
941
|
# breaks both ancestor lookups and defined?(super). Need to special-case
|
|
770
942
|
# the existence of query attribute methods.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: state_machines-activerecord
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.200.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abdelkader Boudih
|
|
@@ -30,14 +30,14 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 0.
|
|
33
|
+
version: 0.200.0
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: 0.
|
|
40
|
+
version: 0.200.0
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: appraisal
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -142,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
142
142
|
- !ruby/object:Gem::Version
|
|
143
143
|
version: '0'
|
|
144
144
|
requirements: []
|
|
145
|
-
rubygems_version: 4.0.
|
|
145
|
+
rubygems_version: 4.0.10
|
|
146
146
|
specification_version: 4
|
|
147
147
|
summary: State machines Active Record Integration
|
|
148
148
|
test_files: []
|