state_machines-activerecord 0.100.0 → 0.103.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: 8da92c0d0872bf79ae81017dd5d16459e5222d10ee304390ddff3af14f1e1e05
4
- data.tar.gz: c7ebd9fbdff4ee5bd2b0cca81b5daad186bb92157eddcb912829685571aa84eb
3
+ metadata.gz: 571fa09486dd051901ae113941b3ae04e9867da62da325c609f97f44f1272f66
4
+ data.tar.gz: 22513e4df2238588d70dcff91f79fb00dfc18dac3a7b36a8a971765355939f8a
5
5
  SHA512:
6
- metadata.gz: 66d176f9efbc9d44ac2d5a5786f078e593d3590a99d855804ff4775649b18abae64264781f4ddc36a60ac253366aea0a9c02d3d1712d680d78fe443fffd437e7
7
- data.tar.gz: f5e7a924679499d596439009b9bad2633af5389069959016595d31c44243f7551893cd2e53769e5e1b3a99b979f8d6ad1fa24d86caaa9134f0aa63528739b108
6
+ metadata.gz: a15cf9abb68277ce52ab35e139b15fa8ce6b28fb68424bcbcf840de0216267f7f58b62483c01b2cbd62df9694ba117dfee3b090e2b64692cfe9114396d64c0f2
7
+ data.tar.gz: 97cb794b7e45222503e682641e80da05282f3593453979650431f35fa687831e9349dabe46ea7f2969dc2f95e9b3bc181cfc5860af17e3d57aefb72a8144a485
data/README.md CHANGED
@@ -77,7 +77,108 @@ Vehicle.with_state(params[:state]) # Returns all vehicles if par
77
77
  Vehicle.where(color: 'red').with_state(nil) # Returns all red vehicles (chainable)
78
78
  ```
79
79
 
80
- ### State driven validations
80
+ ## Rails Enum Integration
81
+
82
+ When your ActiveRecord model uses Rails enums and defines a state machine on the same attribute, this gem automatically detects the conflict and provides seamless integration. This prevents method name collisions between Rails enum methods and state machine methods.
83
+
84
+ ### Auto-Detection and Conflict Resolution
85
+
86
+ ```ruby
87
+ class Order < ApplicationRecord
88
+ # Rails enum definition
89
+ enum :status, { pending: 0, processing: 1, completed: 2, cancelled: 3 }
90
+
91
+ # State machine on the same attribute
92
+ state_machine :status do
93
+ state :pending, :processing, :completed, :cancelled
94
+
95
+ event :process do
96
+ transition pending: :processing
97
+ end
98
+
99
+ event :complete do
100
+ transition processing: :completed
101
+ end
102
+
103
+ event :cancel do
104
+ transition [:pending, :processing] => :cancelled
105
+ end
106
+ end
107
+ end
108
+ ```
109
+
110
+ When enum integration is detected, the gem automatically:
111
+ - Preserves original Rails enum methods (`pending?`, `processing?`, etc.)
112
+ - Generates prefixed state machine methods to avoid conflicts (`status_pending?`, `status_processing?`, etc.)
113
+ - Creates prefixed scope methods (`Order.status_pending`, `Order.status_processing`, etc.)
114
+
115
+ ### Available Methods
116
+
117
+ **Original Rails enum methods (preserved):**
118
+ ```ruby
119
+ order = Order.create(status: :pending)
120
+ order.pending? # => true (Rails enum method)
121
+ order.processing? # => false (Rails enum method)
122
+ order.processing! # Sets status to :processing (Rails enum method)
123
+
124
+ Order.pending # Rails enum scope
125
+ Order.processing # Rails enum scope
126
+ ```
127
+
128
+ **Generated state machine methods (prefixed):**
129
+ ```ruby
130
+ # Predicate methods
131
+ order.status_pending? # => true (state machine method)
132
+ order.status_processing? # => false (state machine method)
133
+ order.status_completed? # => false (state machine method)
134
+
135
+ # Bang methods (for conflict resolution only)
136
+ # These are placeholders and raise runtime errors
137
+ order.status_processing! # => raises RuntimeError
138
+
139
+ # Scope methods
140
+ Order.status_pending # State machine scope
141
+ Order.status_processing # State machine scope
142
+ Order.not_status_pending # Negative state machine scope
143
+ ```
144
+
145
+ ### Introspection API
146
+
147
+ The integration provides a comprehensive introspection API for advanced use cases:
148
+
149
+ ```ruby
150
+ machine = Order.state_machine(:status)
151
+
152
+ # Check if enum integration is enabled
153
+ machine.enum_integrated? # => true
154
+
155
+ # Get the Rails enum mapping
156
+ machine.enum_mapping # => {"pending"=>0, "processing"=>1, "completed"=>2, "cancelled"=>3}
157
+
158
+ # Get original Rails enum methods that were preserved
159
+ machine.original_enum_methods
160
+ # => ["pending?", "processing?", "completed?", "cancelled?", "pending!", "processing!", ...]
161
+
162
+ # Get state machine methods that were generated
163
+ machine.state_machine_methods
164
+ # => ["status_pending?", "status_processing?", "status_completed?", "status_cancelled?", ...]
165
+ ```
166
+
167
+
168
+ ### Requirements for Enum Integration
169
+
170
+ - The state machine attribute must match an existing Rails enum attribute
171
+ - Auto-detection is enabled by default when this condition is met
172
+
173
+ ### Configuration Options
174
+
175
+ The enum integration supports several configuration options:
176
+
177
+ - `prefix` (default: true) - Adds a prefix to generated methods to avoid conflicts
178
+ - `suffix` (default: false) - Alternative naming strategy using suffixes instead of prefixes
179
+ - `scopes` (default: true) - Controls whether state machine scopes are generated
180
+
181
+ ## State driven validations
81
182
 
82
183
  As mentioned in `StateMachines::Machine#state`, you can define behaviors,
83
184
  like validations, that only execute for certain states. One *important*
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StateMachines
4
+ module Type
5
+ # Custom ActiveRecord attribute type for state machine attributes backed by
6
+ # integer columns. Handles bidirectional conversion between state name strings
7
+ # (used internally by the state machine) and integer values (stored in the DB).
8
+ #
9
+ # States without explicit integer values are mapped by their index position
10
+ # in the states collection (0, 1, 2, …). States with an explicit integer
11
+ # value (e.g. state :pending, value: 2) use that value directly.
12
+ class Integer < ::ActiveRecord::Type::Value
13
+ def initialize(states)
14
+ @states = states
15
+ super()
16
+ end
17
+
18
+ # integer from DB → state name string
19
+ def deserialize(value)
20
+ return nil if value.nil?
21
+ int_val = value.to_i
22
+ state = named_states.detect { |s| state_integer(s) == int_val }
23
+ state ? state.name.to_s : value
24
+ end
25
+
26
+ # assignment (symbol / string / integer) → state name string (in-memory)
27
+ def cast(value)
28
+ return nil if value.nil?
29
+ state = named_states.detect { |s| s.name.to_s == value.to_s }
30
+ state ||= named_states.detect { |s| state_integer(s) == value.to_i } if value.respond_to?(:to_i)
31
+ state ? state.name.to_s : value.to_s
32
+ end
33
+
34
+ # state name string → integer for DB write
35
+ def serialize(value)
36
+ return nil if value.nil?
37
+ state = named_states.detect { |s| s.name.to_s == value.to_s }
38
+ state ? state_integer(state) : value
39
+ end
40
+
41
+ def type
42
+ :integer
43
+ end
44
+
45
+ private
46
+
47
+ # All non-nil states in definition order — not memoized because states are
48
+ # added to the collection after the type is instantiated.
49
+ def named_states
50
+ @states.reject { |s| s.name.nil? }
51
+ end
52
+
53
+ # Returns the integer to use for storage:
54
+ # - explicit integer value if set (e.g. state :pending, value: 2)
55
+ # - otherwise the index position among named states
56
+ def state_integer(state)
57
+ state.value.is_a?(::Integer) ? state.value : named_states.index(state)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -3,7 +3,7 @@
3
3
  module StateMachines
4
4
  module Integrations
5
5
  module ActiveRecord
6
- VERSION = '0.100.0'
6
+ VERSION = '0.103.0'
7
7
  end
8
8
  end
9
9
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'state_machines-activemodel'
2
4
  require 'active_record'
3
5
  require 'state_machines/integrations/active_record/version'
6
+ require 'state_machines/integrations/active_record/type/integer'
4
7
 
5
8
  module StateMachines
6
9
  module Integrations # :nodoc:
@@ -194,7 +197,8 @@ module StateMachines
194
197
  # example, assuming there's a validation on a field called +name+ on the class:
195
198
  #
196
199
  # vehicle = Vehicle.new
197
- # vehicle.ignite! # => StateMachines::InvalidTransition: Cannot transition state via :ignite from :parked (Reason(s): Name cannot be blank)
200
+ # vehicle.ignite! # => StateMachines::InvalidTransition: Cannot transition state via :ignite from :parked
201
+ # # (Reason(s): Name cannot be blank)
198
202
  #
199
203
  # == Scopes
200
204
  #
@@ -368,6 +372,283 @@ module StateMachines
368
372
 
369
373
  # The default options to use for state machines using this integration
370
374
  @defaults = { action: :save, use_transactions: true }
375
+
376
+ # Machine-specific methods for enum integration
377
+ module MachineMethods
378
+ # Enum integration metadata storage
379
+ attr_accessor :enum_integration
380
+
381
+ # Hook called after machine initialization
382
+ def after_initialize
383
+ super
384
+ initialize_enum_integration
385
+ register_integer_type if integer_column? && !enum_integrated?
386
+ end
387
+
388
+ # Check if enum integration should be enabled for this machine
389
+ def detect_enum_integration
390
+ return nil unless owner_class.defined_enums.key?(attribute.to_s)
391
+
392
+ # For now, auto-detect enum and enable basic integration
393
+ # Later we can add explicit configuration options
394
+ {
395
+ enabled: true,
396
+ prefix: true,
397
+ suffix: false,
398
+ scopes: true,
399
+ enum_values: owner_class.defined_enums[attribute.to_s] || {},
400
+ original_enum_methods: detect_existing_enum_methods,
401
+ state_machine_methods: []
402
+ }
403
+ end
404
+
405
+ # Initialize enum integration if enum is detected
406
+ def initialize_enum_integration
407
+ detected_config = detect_enum_integration
408
+ return unless detected_config
409
+
410
+ # Store enum integration metadata
411
+ self.enum_integration = detected_config
412
+ end
413
+
414
+ # Override state method to trigger method generation after states are defined
415
+ def state(*, &)
416
+ result = super
417
+
418
+ # Generate methods after each state addition if enum integration is enabled
419
+ generate_state_machine_methods if enum_integrated?
420
+
421
+ result
422
+ end
423
+
424
+ # Check if this machine has enum integration enabled
425
+ def enum_integrated?
426
+ enum_integration && enum_integration[:enabled]
427
+ end
428
+
429
+ # Get the enum mapping for this attribute
430
+ def enum_mapping
431
+ return {} unless enum_integrated?
432
+
433
+ enum_integration[:enum_values] || {}
434
+ end
435
+
436
+ # Get list of original enum methods that were preserved
437
+ def original_enum_methods
438
+ return [] unless enum_integrated?
439
+
440
+ enum_integration[:original_enum_methods] || []
441
+ end
442
+
443
+ # Get list of state machine methods that were generated
444
+ def state_machine_methods
445
+ return [] unless enum_integrated?
446
+
447
+ enum_integration[:state_machine_methods] || []
448
+ end
449
+
450
+ def integer_type_registered?
451
+ !!@integer_type_registered
452
+ end
453
+
454
+ # Machine internals (state matching, validations) call read() to get the
455
+ # current state value and compare it against state.value. Only override
456
+ # when states have explicit integer values (e.g. state :pending, value: 0).
457
+ # In that case the custom type returns a state name string but machine
458
+ # internals need the raw integer for value-based lookup.
459
+ #
460
+ # For auto-indexed states (no explicit value), state.value is the string
461
+ # name so super() returns the right thing already.
462
+ def read(object, attr_sym, ivar = false)
463
+ return super unless integer_type_registered? && attr_sym == :state
464
+ return super unless states_with_explicit_integer_values?
465
+
466
+ raw = object.read_attribute_before_type_cast(attribute.to_s)
467
+ if raw.is_a?(::String) || raw.is_a?(::Symbol)
468
+ matched = states.detect { |s| s.name && s.name.to_s == raw.to_s }
469
+ return matched ? matched.value : raw
470
+ end
471
+ raw
472
+ end
473
+
474
+ private
475
+
476
+ # Returns true when at least one named state has an explicit integer value.
477
+ # Used to decide whether read() needs to return raw integers for machine
478
+ # internals rather than relying on the custom type's string representation.
479
+ def states_with_explicit_integer_values?
480
+ states.any? { |s| s.name && s.value.is_a?(::Integer) }
481
+ end
482
+
483
+ # Returns true when the state machine attribute is backed by an integer column
484
+ def integer_column?
485
+ return false unless owner_class.respond_to?(:type_for_attribute)
486
+ return false unless owner_class.connected? && owner_class.table_exists?
487
+
488
+ owner_class.type_for_attribute(attribute.to_s).type == :integer
489
+ rescue ::ActiveRecord::StatementInvalid, ::ActiveRecord::ConnectionNotEstablished
490
+ false
491
+ end
492
+
493
+ # Registers a custom AR attribute type so that integer columns transparently
494
+ # convert between state name strings and stored integers.
495
+ # Saves the raw column default first so the conflicting-default check
496
+ # (which fires later, during initial_state=) still compares raw integers.
497
+ def register_integer_type
498
+ @raw_integer_column_default = owner_class.column_defaults[attribute.to_s]
499
+ @integer_type_registered = true
500
+ owner_class.attribute(attribute.to_s, StateMachines::Type::Integer.new(states))
501
+ end
502
+
503
+ # Detect existing enum methods for this attribute
504
+ def detect_existing_enum_methods
505
+ return [] unless owner_class.defined_enums.key?(attribute.to_s)
506
+
507
+ enum_values = owner_class.defined_enums[attribute.to_s]
508
+ methods = []
509
+
510
+ enum_values.each_key do |value|
511
+ # Predicate methods like 'active?'
512
+ predicate = "#{value}?"
513
+ methods << predicate if owner_class.method_defined?(predicate)
514
+
515
+ # Bang methods like 'active!'
516
+ bang_method = "#{value}!"
517
+ methods << bang_method if owner_class.method_defined?(bang_method)
518
+
519
+ # Scope methods (class-level)
520
+ methods << value.to_s if owner_class.respond_to?(value)
521
+ methods << "not_#{value}" if owner_class.respond_to?("not_#{value}")
522
+ end
523
+
524
+ methods
525
+ end
526
+
527
+ # Generate method name with prefix/suffix based on configuration
528
+ def generate_state_method_name(state_name, method_type)
529
+ return state_name unless enum_integrated?
530
+
531
+ config = enum_integration
532
+ base_name = case method_type
533
+ when :predicate
534
+ "#{state_name}?"
535
+ when :bang
536
+ "#{state_name}!"
537
+ else
538
+ state_name.to_s
539
+ end
540
+
541
+ # Apply prefix
542
+ if config[:prefix]
543
+ prefix = config[:prefix] == true ? "#{attribute}_" : "#{config[:prefix]}_"
544
+ base_name = "#{prefix}#{base_name}"
545
+ end
546
+
547
+ # Apply suffix
548
+ if config[:suffix]
549
+ suffix = config[:suffix] == true ? "_#{attribute}" : "_#{config[:suffix]}"
550
+ base_name = base_name.gsub(/(\?|!)$/, "#{suffix}\\1")
551
+ base_name = "#{base_name}#{suffix}" unless base_name.end_with?('?', '!')
552
+ end
553
+
554
+ base_name
555
+ end
556
+
557
+ # Generate state machine methods with conflict resolution
558
+ def generate_state_machine_methods
559
+ return unless enum_integrated?
560
+
561
+ # Initialize tracking if not already done
562
+ @processed_states ||= Set.new
563
+ enum_integration[:state_machine_methods] ||= []
564
+
565
+ # Get all states for this machine
566
+ states.each do |state|
567
+ state_name = state.name.to_s
568
+ next if state.nil? # Skip nil state
569
+ next if @processed_states.include?(state_name) # Skip already processed states
570
+
571
+ # Generate predicate method (e.g., status_pending?)
572
+ predicate_method = generate_state_method_name(state_name, :predicate)
573
+ if predicate_method != "#{state_name}?"
574
+ define_state_predicate_method(state_name, predicate_method)
575
+ track_generated_method(predicate_method)
576
+ end
577
+
578
+ # Generate bang method (e.g., status_pending!)
579
+ bang_method = generate_state_method_name(state_name, :bang)
580
+ if bang_method != "#{state_name}!"
581
+ define_state_bang_method(state_name, bang_method)
582
+ track_generated_method(bang_method)
583
+ end
584
+
585
+ # Generate scope methods (e.g., status_pending) if scopes are enabled
586
+ if enum_integration[:scopes]
587
+ scope_method = generate_state_method_name(state_name, :scope)
588
+ if scope_method != state_name
589
+ define_state_scope_method(state_name, scope_method)
590
+ track_generated_method(scope_method)
591
+ end
592
+ end
593
+
594
+ # Mark this state as processed
595
+ @processed_states.add(state_name)
596
+ end
597
+ end
598
+
599
+ # Define a prefixed predicate method for a state
600
+ def define_state_predicate_method(state_name, method_name)
601
+ machine_attribute = attribute
602
+ target_state_name = state_name.to_sym
603
+ owner_class.define_method(method_name) do
604
+ machine = self.class.state_machine(machine_attribute)
605
+ machine.states.matches?(self, target_state_name)
606
+ end
607
+ end
608
+
609
+ # Define a prefixed bang method for a state
610
+ def define_state_bang_method(state_name, method_name)
611
+ owner_class.define_method(method_name) do
612
+ # Raise an error with actionable guidance
613
+ raise "#{method_name} is a conflict-resolution placeholder. " \
614
+ "Use the original enum method '#{state_name}!' or state machine events instead."
615
+ end
616
+ end
617
+
618
+ # Define a prefixed scope method for a state
619
+ def define_state_scope_method(state_name, method_name)
620
+ machine_attribute = attribute
621
+ scope_lambda = lambda do |value = true|
622
+ machine = state_machine(machine_attribute)
623
+ state_value = machine.states[state_name.to_sym].value
624
+ if value
625
+ where(machine_attribute => state_value)
626
+ else
627
+ where.not(machine_attribute => state_value)
628
+ end
629
+ end
630
+
631
+ owner_class.define_singleton_method(method_name, &scope_lambda)
632
+ owner_class.define_singleton_method("not_#{method_name}") do
633
+ public_send(method_name, false)
634
+ end
635
+ end
636
+
637
+ # Track generated state machine methods for introspection
638
+ def track_generated_method(method_name)
639
+ return unless enum_integrated?
640
+
641
+ # Use a Set to ensure no duplicates
642
+ enum_integration[:state_machine_methods] ||= []
643
+ return if enum_integration[:state_machine_methods].include?(method_name)
644
+
645
+ enum_integration[:state_machine_methods] << method_name
646
+ end
647
+ end
648
+
649
+ # Include MachineMethods to make enum integration methods available on machine instances
650
+ include MachineMethods
651
+
371
652
  class << self
372
653
  # Classes that inherit from ActiveRecord::Base will automatically use
373
654
  # the ActiveRecord integration.
@@ -383,8 +664,11 @@ module StateMachines
383
664
  action == :save
384
665
  end
385
666
 
386
- # Gets the db default for the machine's attribute
667
+ # Gets the db default for the machine's attribute.
668
+ # For integer columns the raw pre-type-registration default is returned so
669
+ # that check_conflicting_attribute_default can compare integers to integers.
387
670
  def owner_class_attribute_default
671
+ return @raw_integer_column_default if defined?(@raw_integer_column_default)
388
672
  return unless owner_class.connected? && owner_class.table_exists?
389
673
 
390
674
  owner_class.column_defaults[attribute.to_s]
@@ -434,7 +718,7 @@ module StateMachines
434
718
 
435
719
  # Creates a scope for finding records *with* a particular state or
436
720
  # states for the attribute
437
- def create_with_scope(name)
721
+ def create_with_scope(_name)
438
722
  attr_name = attribute
439
723
  lambda do |klass, values|
440
724
  if values.present?
@@ -447,7 +731,7 @@ module StateMachines
447
731
 
448
732
  # Creates a scope for finding records *without* a particular state or
449
733
  # states for the attribute
450
- def create_without_scope(name)
734
+ def create_without_scope(_name)
451
735
  attr_name = attribute
452
736
  lambda do |klass, values|
453
737
  if values.present?
@@ -458,14 +742,13 @@ module StateMachines
458
742
  end
459
743
  end
460
744
 
461
-
462
745
  # Runs a new database transaction, rolling back any changes by raising
463
746
  # an ActiveRecord::Rollback exception if the yielded block fails
464
747
  # (i.e. returns false).
465
748
  def transaction(object)
466
749
  result = nil
467
750
  object.class.transaction do
468
- raise ::ActiveRecord::Rollback unless result = yield
751
+ raise ::ActiveRecord::Rollback unless (result = yield)
469
752
  end
470
753
  result
471
754
  end
@@ -476,7 +759,6 @@ module StateMachines
476
759
 
477
760
  private
478
761
 
479
-
480
762
  # Generates the results for the given scope based on one or more states to filter by
481
763
  def run_scope(scope, machine, klass, states)
482
764
  values = states.flatten.compact.map { |state| machine.states.fetch(state).value }
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.100.0
4
+ version: 0.103.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.100.0
33
+ version: 0.102.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.100.0
40
+ version: 0.102.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: appraisal
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -56,16 +56,16 @@ dependencies:
56
56
  name: minitest
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: 5.4.0
61
+ version: 5.27.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - '='
67
67
  - !ruby/object:Gem::Version
68
- version: 5.4.0
68
+ version: 5.27.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: minitest-reporters
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '1.3'
103
+ version: '2.1'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '1.3'
110
+ version: '2.1'
111
111
  description: Adds support for creating state machines for attributes on ActiveRecord
112
112
  email:
113
113
  - terminale@gmail.com
@@ -121,6 +121,7 @@ files:
121
121
  - lib/state_machines-activerecord.rb
122
122
  - lib/state_machines/integrations/active_record.rb
123
123
  - lib/state_machines/integrations/active_record/locale.rb
124
+ - lib/state_machines/integrations/active_record/type/integer.rb
124
125
  - lib/state_machines/integrations/active_record/version.rb
125
126
  homepage: https://github.com/state-machines/state_machines-activerecord/
126
127
  licenses:
@@ -141,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
142
  - !ruby/object:Gem::Version
142
143
  version: '0'
143
144
  requirements: []
144
- rubygems_version: 3.6.9
145
+ rubygems_version: 4.0.3
145
146
  specification_version: 4
146
147
  summary: State machines Active Record Integration
147
148
  test_files: []