Skip to content

Commit afecc8f

Browse files
authored
Permit frozen models to be validated (rails#47969)
* Permit frozen models to be validated * Remove initialize method since it's not needed  Co-authored-by: Rafael Mendonça França <[email protected]>
1 parent c0effe0 commit afecc8f

File tree

2 files changed

+50
-23
lines changed

2 files changed

+50
-23
lines changed

activemodel/lib/active_model/validations.rb

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -45,27 +45,6 @@ module Validations
4545
extend HelperMethods
4646
include HelperMethods
4747

48-
##
49-
# :method: validation_context
50-
# Returns the context when running validations.
51-
#
52-
# This is useful when running validations except a certain context (opposite to the +on+ option).
53-
#
54-
# class Person
55-
# include ActiveModel::Validations
56-
#
57-
# attr_accessor :name
58-
# validates :name, presence: true, if: -> { validation_context != :custom }
59-
# end
60-
#
61-
# person = Person.new
62-
# person.valid? #=> false
63-
# person.valid?(:new) #=> false
64-
# person.valid?(:custom) #=> true
65-
66-
##
67-
attr_accessor :validation_context
68-
private :validation_context=
6948
define_callbacks :validate, scope: :name
7049

7150
class_attribute :_validators, instance_writer: false, default: Hash.new { |h, k| h[k] = [] }
@@ -361,15 +340,23 @@ def errors
361340
# person.valid? # => true
362341
# person.valid?(:new) # => false
363342
def valid?(context = nil)
364-
current_context, self.validation_context = validation_context, context
343+
current_context = validation_context
344+
context_for_validation.context = context
365345
errors.clear
366346
run_validations!
367347
ensure
368-
self.validation_context = current_context
348+
context_for_validation.context = current_context
369349
end
370350

371351
alias_method :validate, :valid?
372352

353+
def freeze
354+
errors
355+
context_for_validation
356+
357+
super
358+
end
359+
373360
# Performs the opposite of <tt>valid?</tt>. Returns +true+ if errors were
374361
# added, +false+ otherwise.
375362
#
@@ -430,7 +417,34 @@ def validate!(context = nil)
430417
# end
431418
alias :read_attribute_for_validation :send
432419

420+
# Returns the context when running validations.
421+
#
422+
# This is useful when running validations except a certain context (opposite to the +on+ option).
423+
#
424+
# class Person
425+
# include ActiveModel::Validations
426+
#
427+
# attr_accessor :name
428+
# validates :name, presence: true, if: -> { validation_context != :custom }
429+
# end
430+
#
431+
# person = Person.new
432+
# person.valid? #=> false
433+
# person.valid?(:new) #=> false
434+
# person.valid?(:custom) #=> true
435+
def validation_context
436+
context_for_validation.context
437+
end
438+
433439
private
440+
def validation_context=(context)
441+
context_for_validation.context = context
442+
end
443+
444+
def context_for_validation
445+
@context_for_validation ||= ValidationContext.new
446+
end
447+
434448
def init_internals
435449
super
436450
@errors = nil
@@ -466,6 +480,10 @@ def initialize(model)
466480
super(I18n.t(:"#{@model.class.i18n_scope}.errors.messages.model_invalid", errors: errors, default: :"errors.messages.model_invalid"))
467481
end
468482
end
483+
484+
class ValidationContext # :nodoc:
485+
attr_accessor :context
486+
end
469487
end
470488

471489
Dir[File.expand_path("validations/*.rb", __dir__)].each { |file| require file }

activemodel/test/cases/validations_test.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require "cases/helper"
44

55
require "models/topic"
6+
require "models/person"
67
require "models/reply"
78
require "models/custom_reader"
89

@@ -14,6 +15,7 @@ class CustomStrictValidationException < StandardError; end
1415

1516
def teardown
1617
Topic.clear_validators!
18+
Person.clear_validators!
1719
end
1820

1921
def test_single_field_validation
@@ -453,4 +455,11 @@ def test_validation_with_message_as_proc_that_takes_record_and_data_as_a_paramet
453455
assert_predicate t, :invalid?
454456
assert_equal ["Title is missing. You have failed me for the last time, Admiral."], t.errors[:title]
455457
end
458+
459+
def test_frozen_models_can_be_validated
460+
Person.validates :title, presence: true
461+
person = Person.new.freeze
462+
assert_predicate person, :frozen?
463+
assert_not person.valid?
464+
end
456465
end

0 commit comments

Comments
 (0)