diff options
Diffstat (limited to 'activemodel')
-rw-r--r-- | activemodel/README.rdoc | 2 | ||||
-rw-r--r-- | activemodel/lib/active_model/attribute_methods.rb | 13 | ||||
-rw-r--r-- | activemodel/lib/active_model/conversion.rb | 2 | ||||
-rw-r--r-- | activemodel/lib/active_model/lint.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/mass_assignment_security.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/observing.rb | 2 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations.rb | 6 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations/length.rb | 3 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations/with.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/validator.rb | 31 | ||||
-rw-r--r-- | activemodel/test/cases/attribute_methods_test.rb | 59 | ||||
-rw-r--r-- | activemodel/test/cases/validations/length_validation_test.rb | 11 | ||||
-rw-r--r-- | activemodel/test/cases/validations/numericality_validation_test.rb | 2 |
13 files changed, 102 insertions, 41 deletions
diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc index d9a9bdae3e..b5b5edd52a 100644 --- a/activemodel/README.rdoc +++ b/activemodel/README.rdoc @@ -8,7 +8,7 @@ the Rails framework. Prior to Rails 3.0, if a plugin or gem developer wanted to have an object interact with Action Pack helpers, it was required to either copy chunks of code from Rails, or monkey patch entire helpers to make them handle objects -that did not exacly conform to the Active Record interface. This would result +that did not exactly conform to the Active Record interface. This would result in code duplication and fragile applications that broke on upgrades. Active Model solves this. You can include functionality from the following diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index 2a99450a3d..be55581c66 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -106,11 +106,14 @@ module ActiveModel if block_given? sing.send :define_method, name, &block else - # use eval instead of a block to work around a memory leak in dev - # mode in fcgi - sing.class_eval <<-eorb, __FILE__, __LINE__ + 1 - def #{name}; #{value.nil? ? 'nil' : value.to_s.inspect}; end - eorb + if name =~ /^[a-zA-Z_]\w*[!?=]?$/ + sing.class_eval <<-eorb, __FILE__, __LINE__ + 1 + def #{name}; #{value.nil? ? 'nil' : value.to_s.inspect}; end + eorb + else + value = value.to_s if value + sing.send(:define_method, name) { value } + end end end diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb index ae0ab93e97..e3992e842a 100644 --- a/activemodel/lib/active_model/conversion.rb +++ b/activemodel/lib/active_model/conversion.rb @@ -3,8 +3,6 @@ module ActiveModel # # Handles default conversions: to_model, to_key and to_param. # - # == Example - # # Let's take for example this non persisted object. # # class ContactMessage diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb index 957d1b9d70..b71ef4b22e 100644 --- a/activemodel/lib/active_model/lint.rb +++ b/activemodel/lib/active_model/lint.rb @@ -23,7 +23,7 @@ module ActiveModel def test_to_key assert model.respond_to?(:to_key), "The model should respond to to_key" def model.persisted?() false end - assert model.to_key.nil? + assert model.to_key.nil?, "to_key should return nil when `persisted?` returns false" end # == Responds to <tt>to_param</tt> @@ -40,7 +40,7 @@ module ActiveModel assert model.respond_to?(:to_param), "The model should respond to to_param" def model.to_key() [1] end def model.persisted?() false end - assert model.to_param.nil? + assert model.to_param.nil?, "to_param should return nil when `persisted?` returns false" end # == Responds to <tt>valid?</tt> diff --git a/activemodel/lib/active_model/mass_assignment_security.rb b/activemodel/lib/active_model/mass_assignment_security.rb index 97e31d4243..be48415739 100644 --- a/activemodel/lib/active_model/mass_assignment_security.rb +++ b/activemodel/lib/active_model/mass_assignment_security.rb @@ -54,9 +54,7 @@ module ActiveModel # Mass-assignment to these attributes will simply be ignored, to assign # to them you can use direct writer methods. This is meant to protect # sensitive attributes from being overwritten by malicious users - # tampering with URLs or forms. - # - # == Example + # tampering with URLs or forms. Example: # # class Customer # include ActiveModel::MassAssignmentSecurity diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index af036b560e..ef36f80bec 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -72,7 +72,7 @@ module ActiveModel def instantiate_observer(observer) #:nodoc: # string/symbol if observer.respond_to?(:to_sym) - observer = observer.to_s.camelize.constantize.instance + observer.to_s.camelize.constantize.instance elsif observer.respond_to?(:instance) observer.instance else diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index 98af88b5a0..d968609e67 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -36,8 +36,8 @@ module ActiveModel # person.invalid? # => true # person.errors # => #<OrderedHash {:first_name=>["starts with z."]}> # - # Note that ActiveModel::Validations automatically adds an +errors+ method - # to your instances initialized with a new ActiveModel::Errors object, so + # Note that <tt>ActiveModel::Validations</tt> automatically adds an +errors+ method + # to your instances initialized with a new <tt>ActiveModel::Errors</tt> object, so # there is no need for you to do this manually. # module Validations @@ -165,7 +165,7 @@ module ActiveModel end end - # Returns the Errors object that holds all information about attribute error messages. + # Returns the +Errors+ object that holds all information about attribute error messages. def errors @errors ||= Errors.new(self) end diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb index 7af6c83460..72735cfb89 100644 --- a/activemodel/lib/active_model/validations/length.rb +++ b/activemodel/lib/active_model/validations/length.rb @@ -43,7 +43,8 @@ module ActiveModel value ||= [] if key == :maximum - next if value && value.size.send(validity_check, check_value) + value_length = value.respond_to?(:length) ? value.length : value.to_s.length + next if value_length.send(validity_check, check_value) errors_options = options.except(*RESERVED_OPTIONS) errors_options[:count] = check_value diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb index 1663697727..65ae18a769 100644 --- a/activemodel/lib/active_model/validations/with.rb +++ b/activemodel/lib/active_model/validations/with.rb @@ -50,9 +50,9 @@ module ActiveModel # end # # Configuration options: - # * <tt>on</tt> - Specifies when this validation is active + # * <tt>:on</tt> - Specifies when this validation is active # (<tt>:create</tt> or <tt>:update</tt> - # * <tt>if</tt> - Specifies a method, proc or string to call to determine + # * <tt>:if</tt> - Specifies a method, proc or string to call to determine # if the validation should occur (e.g. <tt>:if => :allow_validation</tt>, # or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). # The method, proc or string should return or evaluate to a true or false value. diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb index 0168233fce..c5ed8d22d3 100644 --- a/activemodel/lib/active_model/validator.rb +++ b/activemodel/lib/active_model/validator.rb @@ -63,7 +63,7 @@ module ActiveModel #:nodoc: # end # # The easiest way to add custom validators for validating individual attributes - # is with the convenient ActiveModel::EachValidator for example: + # is with the convenient <tt>ActiveModel::EachValidator</tt>. For example: # # class TitleValidator < ActiveModel::EachValidator # def validate_each(record, attribute, value) @@ -72,18 +72,18 @@ module ActiveModel #:nodoc: # end # # This can now be used in combination with the +validates+ method - # (see ActiveModel::Validations::ClassMethods.validates for more on this) + # (see <tt>ActiveModel::Validations::ClassMethods.validates</tt> for more on this) # # class Person # include ActiveModel::Validations # attr_accessor :title # - # validates :title, :presence => true, :title => true + # validates :title, :presence => true # end # # Validator may also define a +setup+ instance method which will get called - # with the class that using that validator as it's argument. This can be - # useful when there are prerequisites such as an attr_accessor being present + # with the class that using that validator as its argument. This can be + # useful when there are prerequisites such as an +attr_accessor+ being present # for example: # # class MyValidator < ActiveModel::Validator @@ -98,9 +98,7 @@ module ActiveModel #:nodoc: class Validator attr_reader :options - # Returns the kind of the validator. - # - # == Examples + # Returns the kind of the validator. Examples: # # PresenceValidator.kind # => :presence # UniquenessValidator.kind # => :uniqueness @@ -122,15 +120,15 @@ module ActiveModel #:nodoc: # Override this method in subclasses with validation logic, adding errors # to the records +errors+ array where necessary. def validate(record) - raise NotImplementedError + raise NotImplementedError, "Subclasses must implement a validate(record) method." end end - # EachValidator is a validator which iterates through the attributes given - # in the options hash invoking the validate_each method passing in the + # +EachValidator+ is a validator which iterates through the attributes given + # in the options hash invoking the <tt>validate_each</tt> method passing in the # record, attribute and value. # - # All Active Model validations are built on top of this Validator. + # All Active Model validations are built on top of this validator. class EachValidator < Validator attr_reader :attributes @@ -158,19 +156,18 @@ module ActiveModel #:nodoc: # Override this method in subclasses with the validation logic, adding # errors to the records +errors+ array where necessary. def validate_each(record, attribute, value) - raise NotImplementedError + raise NotImplementedError, "Subclasses must implement a validate_each(record, attribute, value) method" end # Hook method that gets called by the initializer allowing verification # that the arguments supplied are valid. You could for example raise an - # ArgumentError when invalid options are supplied. + # +ArgumentError+ when invalid options are supplied. def check_validity! end end - # BlockValidator is a special EachValidator which receives a block on initialization - # and call this block for each attribute being validated. +validates_each+ uses this - # Validator. + # +BlockValidator+ is a special +EachValidator+ which receives a block on initialization + # and call this block for each attribute being validated. +validates_each+ uses this validator. class BlockValidator < EachValidator def initialize(options, &block) @block = block diff --git a/activemodel/test/cases/attribute_methods_test.rb b/activemodel/test/cases/attribute_methods_test.rb index b001adb35a..022c6716bd 100644 --- a/activemodel/test/cases/attribute_methods_test.rb +++ b/activemodel/test/cases/attribute_methods_test.rb @@ -5,6 +5,12 @@ class ModelWithAttributes attribute_method_suffix '' + class << self + define_method(:bar) do + 'original bar' + end + end + def attributes { :foo => 'value of foo' } end @@ -36,6 +42,27 @@ private end end +class ModelWithWeirdNamesAttributes + include ActiveModel::AttributeMethods + + attribute_method_suffix '' + + class << self + define_method(:'c?d') do + 'original c?d' + end + end + + def attributes + { :'a?b' => 'value of a?b' } + end + +private + def attribute(name) + attributes[name.to_sym] + end +end + class AttributeMethodsTest < ActiveModel::TestCase test 'unrelated classes should not share attribute method matchers' do assert_not_equal ModelWithAttributes.send(:attribute_method_matchers), @@ -49,6 +76,14 @@ class AttributeMethodsTest < ActiveModel::TestCase assert_equal "value of foo", ModelWithAttributes.new.foo end + test '#define_attribute_method generates attribute method with invalid identifier characters' do + ModelWithWeirdNamesAttributes.define_attribute_method(:'a?b') + ModelWithWeirdNamesAttributes.define_attribute_method(:'a?b') + + assert_respond_to ModelWithWeirdNamesAttributes.new, :'a?b' + assert_equal "value of a?b", ModelWithWeirdNamesAttributes.new.send('a?b') + end + test '#define_attribute_methods generates attribute methods' do ModelWithAttributes.define_attribute_methods([:foo]) @@ -58,15 +93,33 @@ class AttributeMethodsTest < ActiveModel::TestCase test '#define_attribute_methods generates attribute methods with spaces in their names' do ModelWithAttributesWithSpaces.define_attribute_methods([:'foo bar']) - + assert_respond_to ModelWithAttributesWithSpaces.new, :'foo bar' assert_equal "value of foo bar", ModelWithAttributesWithSpaces.new.send(:'foo bar') end - + + test '#define_attr_method generates attribute method' do + ModelWithAttributes.define_attr_method(:bar, 'bar') + + assert_respond_to ModelWithAttributes, :bar + assert_equal "original bar", ModelWithAttributes.original_bar + assert_equal "bar", ModelWithAttributes.bar + ModelWithAttributes.define_attr_method(:bar) + assert !ModelWithAttributes.bar + end + + test '#define_attr_method generates attribute method with invalid identifier characters' do + ModelWithWeirdNamesAttributes.define_attr_method(:'c?d', 'c?d') + + assert_respond_to ModelWithWeirdNamesAttributes, :'c?d' + assert_equal "original c?d", ModelWithWeirdNamesAttributes.send('original_c?d') + assert_equal "c?d", ModelWithWeirdNamesAttributes.send('c?d') + end + test '#alias_attribute works with attributes with spaces in their names' do ModelWithAttributesWithSpaces.define_attribute_methods([:'foo bar']) ModelWithAttributesWithSpaces.alias_attribute(:'foo_bar', :'foo bar') - + assert_equal "value of foo bar", ModelWithAttributesWithSpaces.new.foo_bar end diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb index 1e6180a938..f02514639b 100644 --- a/activemodel/test/cases/validations/length_validation_test.rb +++ b/activemodel/test/cases/validations/length_validation_test.rb @@ -342,6 +342,17 @@ class LengthValidationTest < ActiveModel::TestCase assert_equal ["Your essay must be at least 5 words."], t.errors[:content] end + def test_validates_length_of_for_fixnum + Topic.validates_length_of(:approved, :is => 4) + + t = Topic.new("title" => "uhohuhoh", "content" => "whatever", :approved => 1) + assert t.invalid? + assert t.errors[:approved].any? + + t = Topic.new("title" => "uhohuhoh", "content" => "whatever", :approved => 1234) + assert t.valid? + end + def test_validates_length_of_for_ruby_class Person.validates_length_of :karma, :minimum => 5 diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb index e1d7d40c47..08f6169ca5 100644 --- a/activemodel/test/cases/validations/numericality_validation_test.rb +++ b/activemodel/test/cases/validations/numericality_validation_test.rb @@ -14,7 +14,7 @@ class NumericalityValidationTest < ActiveModel::TestCase NIL = [nil] BLANK = ["", " ", " \t \r \n"] - BIGDECIMAL_STRINGS = %w(12345678901234567890.1234567890) # 30 significent digits + BIGDECIMAL_STRINGS = %w(12345678901234567890.1234567890) # 30 significant digits FLOAT_STRINGS = %w(0.0 +0.0 -0.0 10.0 10.5 -10.5 -0.0001 -090.1 90.1e1 -90.1e5 -90.1e-5 90e-5) INTEGER_STRINGS = %w(0 +0 -0 10 +10 -10 0090 -090) FLOATS = [0.0, 10.0, 10.5, -10.5, -0.0001] + FLOAT_STRINGS |