Skip to content

Commit dfb0e4b

Browse files
committed
add strict argument checking to ActiveRecord callbacks
This ends up adding it to all save-related callbacks defined in `ActiveRecord::DefineCallbacks`, including e.g. `after_create`. Which should be fine: they didn't support `:on` in the first place.
1 parent 9732195 commit dfb0e4b

File tree

2 files changed

+32
-7
lines changed

2 files changed

+32
-7
lines changed

activemodel/lib/active_model/callbacks.rb

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,26 +127,28 @@ def define_model_callbacks(*callbacks)
127127
private
128128

129129
def _define_before_model_callback(klass, callback)
130-
klass.define_singleton_method("before_#{callback}") do |*args, &block|
131-
set_callback(:"#{callback}", :before, *args, &block)
130+
klass.define_singleton_method("before_#{callback}") do |*args, **options, &block|
131+
options.assert_valid_keys :if, :unless, :prepend
132+
set_callback(:"#{callback}", :before, *args, **options, &block)
132133
end
133134
end
134135

135136
def _define_around_model_callback(klass, callback)
136-
klass.define_singleton_method("around_#{callback}") do |*args, &block|
137-
set_callback(:"#{callback}", :around, *args, &block)
137+
klass.define_singleton_method("around_#{callback}") do |*args, **options, &block|
138+
options.assert_valid_keys :if, :unless, :prepend
139+
set_callback(:"#{callback}", :around, *args, **options, &block)
138140
end
139141
end
140142

141143
def _define_after_model_callback(klass, callback)
142-
klass.define_singleton_method("after_#{callback}") do |*args, &block|
143-
options = args.extract_options!
144+
klass.define_singleton_method("after_#{callback}") do |*args, **options, &block|
145+
options.assert_valid_keys :if, :unless, :prepend
144146
options[:prepend] = true
145147
conditional = ActiveSupport::Callbacks::Conditionals::Value.new { |v|
146148
v != false
147149
}
148150
options[:if] = Array(options[:if]) << conditional
149-
set_callback(:"#{callback}", :after, *(args << options), &block)
151+
set_callback(:"#{callback}", :after, *args, **options, &block)
150152
end
151153
end
152154
end

activerecord/test/cases/callbacks_test.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,4 +476,27 @@ def test_inheritance_of_callbacks
476476
child.save
477477
assert child.after_save_called
478478
end
479+
480+
def test_on_isnt_allowed
481+
exception = assert_raises ArgumentError do
482+
Class.new(ActiveRecord::Base) do
483+
before_save(on: :create) {}
484+
end
485+
end
486+
assert_equal "Unknown key: :on. Valid keys are: :if, :unless, :prepend", exception.message
487+
488+
exception = assert_raises ArgumentError do
489+
Class.new(ActiveRecord::Base) do
490+
around_save(on: :create) {}
491+
end
492+
end
493+
assert_equal "Unknown key: :on. Valid keys are: :if, :unless, :prepend", exception.message
494+
495+
exception = assert_raises ArgumentError do
496+
Class.new(ActiveRecord::Base) do
497+
after_save(on: :create) {}
498+
end
499+
end
500+
assert_equal "Unknown key: :on. Valid keys are: :if, :unless, :prepend", exception.message
501+
end
479502
end

0 commit comments

Comments
 (0)