diff options
-rw-r--r-- | activemodel/lib/active_model/validations/acceptance.rb | 55 | ||||
-rw-r--r-- | activerecord/CHANGELOG.md | 5 | ||||
-rw-r--r-- | activerecord/test/cases/validations_test.rb | 11 |
3 files changed, 67 insertions, 4 deletions
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb index 1bcfedb35d..c5c0cd4636 100644 --- a/activemodel/lib/active_model/validations/acceptance.rb +++ b/activemodel/lib/active_model/validations/acceptance.rb @@ -14,16 +14,63 @@ module ActiveModel end private + def setup!(klass) - attr_readers = attributes.reject { |name| klass.attribute_method?(name) } - attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") } - klass.send(:attr_reader, *attr_readers) - klass.send(:attr_writer, *attr_writers) + klass.include(LazilyDefineAttributes.new(AttributeDefinition.new(attributes))) end def acceptable_option?(value) Array(options[:accept]).include?(value) end + + class LazilyDefineAttributes < Module + def initialize(attribute_definition) + define_method(:respond_to_missing?) do |method_name, include_private=false| + super(method_name, include_private) || attribute_definition.matches?(method_name) + end + + define_method(:method_missing) do |method_name, *args, &block| + if attribute_definition.matches?(method_name) + attribute_definition.define_on(self.class) + send(method_name, *args, &block) + else + super(method_name, *args, &block) + end + end + end + end + + class AttributeDefinition + def initialize(attributes) + @attributes = attributes.map(&:to_s) + end + + def matches?(method_name) + attr_name = convert_to_reader_name(method_name) + attributes.include?(attr_name) + end + + def define_on(klass) + attr_readers = attributes.reject { |name| klass.attribute_method?(name) } + attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") } + klass.send(:attr_reader, *attr_readers) + klass.send(:attr_writer, *attr_writers) + end + + protected + + attr_reader :attributes + + private + + def convert_to_reader_name(method_name) + attr_name = method_name.to_s + if attr_name.end_with?("=") + attr_name = attr_name[0..-2] + end + attr_name + end + end end module HelperMethods diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 45c330b8af..6c0722e43c 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* Don't require a database connection to load a class which uses acceptance + validations. + + *Sean Griffin* + * Correctly apply `unscope` when preloading through associations. *Jimmy Bourassa* diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index a429d06aad..d04f4f7ce7 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -168,4 +168,15 @@ class ValidationsTest < ActiveRecord::TestCase ensure Topic.reset_column_information end + + def test_acceptance_validator_doesnt_require_db_connection + klass = Class.new(ActiveRecord::Base) do + self.table_name = 'posts' + end + klass.reset_column_information + + assert_no_queries do + klass.validates_acceptance_of(:foo) + end + end end |