Skip to content

Commit a0a6c5f

Browse files
authored
Merge pull request #45 from state-machines/compta
feat: modernize ActiveModel integration with backward compatibility
2 parents 6a15719 + 8b6a779 commit a0a6c5f

File tree

2 files changed

+73
-3
lines changed

2 files changed

+73
-3
lines changed

lib/state_machines/integrations/active_model.rb

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,12 +353,22 @@ def around_validation(object)
353353

354354
def define_state_initializer
355355
define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
356-
def initialize(**params)
357-
params.transform_keys! do |key|
356+
def initialize(params = nil, **kwargs)
357+
# Support both positional hash and keyword arguments
358+
attrs = params.nil? ? kwargs : params
359+
#{' '}
360+
attrs.transform_keys! do |key|
358361
self.class.attribute_aliases[key.to_s] || key.to_s
359362
end if self.class.respond_to?(:attribute_aliases)
360363
361-
self.class.state_machines.initialize_states(self, {}, params) { super }
364+
# Call super with the appropriate arguments based on what we received
365+
self.class.state_machines.initialize_states(self, {}, attrs) do
366+
if params
367+
super(params)
368+
else
369+
super(**kwargs)
370+
end
371+
end
362372
end
363373
end_eval
364374
end
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# frozen_string_literal: true
2+
3+
require_relative 'test_helper'
4+
5+
class MachineInitializationCompatibilityTest < BaseTestCase
6+
def setup
7+
@model = new_model do
8+
include ActiveModel::Validations
9+
attr_accessor :state
10+
end
11+
12+
@machine = StateMachines::Machine.new(@model, initial: :parked)
13+
@machine.state :parked, :idling
14+
@machine.event :ignite
15+
end
16+
17+
def test_should_accept_positional_hash_argument
18+
record = @model.new({ state: 'idling' })
19+
assert_equal 'idling', record.state
20+
end
21+
22+
def test_should_accept_keyword_arguments
23+
record = @model.new(state: 'idling')
24+
assert_equal 'idling', record.state
25+
end
26+
27+
def test_should_accept_empty_initialization
28+
record = @model.new
29+
assert_equal 'parked', record.state
30+
end
31+
32+
def test_should_handle_attribute_aliases
33+
@model.class_eval do
34+
def self.attribute_aliases
35+
{ 'status' => 'state' }
36+
end
37+
end
38+
39+
record = @model.new(status: 'idling')
40+
assert_equal 'idling', record.state
41+
end
42+
43+
def test_should_prefer_positional_hash_over_keywords_when_both_present
44+
# If someone accidentally provides both, positional takes precedence
45+
record = @model.new({ state: 'idling' }, state: 'parked')
46+
assert_equal 'idling', record.state
47+
end
48+
49+
def test_should_handle_empty_positional_hash
50+
# Empty hash should still be treated as positional argument
51+
record = @model.new({})
52+
assert_equal 'parked', record.state # Gets default initial state
53+
end
54+
55+
def test_should_use_keywords_when_empty_hash_and_keywords_present
56+
# With the fix, keywords are ignored even with empty positional hash
57+
record = @model.new({}, state: 'idling')
58+
assert_equal 'parked', record.state # Empty hash takes precedence
59+
end
60+
end

0 commit comments

Comments
 (0)