diff options
Diffstat (limited to 'activemodel/lib/active_model')
-rw-r--r-- | activemodel/lib/active_model/dirty.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/errors.rb | 14 | ||||
-rw-r--r-- | activemodel/lib/active_model/naming.rb | 2 | ||||
-rw-r--r-- | activemodel/lib/active_model/observing.rb | 27 | ||||
-rw-r--r-- | activemodel/lib/active_model/secure_password.rb | 11 | ||||
-rw-r--r-- | activemodel/lib/active_model/serialization.rb | 19 | ||||
-rw-r--r-- | activemodel/lib/active_model/translation.rb | 17 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations.rb | 17 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations/with.rb | 6 |
9 files changed, 69 insertions, 48 deletions
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb index d327913824..7f7fb90d87 100644 --- a/activemodel/lib/active_model/dirty.rb +++ b/activemodel/lib/active_model/dirty.rb @@ -151,13 +151,15 @@ module ActiveModel # Handle <tt>*_will_change!</tt> for +method_missing+. def attribute_will_change!(attr) + return if attribute_changed?(attr) + begin value = __send__(attr) value = value.duplicable? ? value.clone : value rescue TypeError, NoMethodError end - changed_attributes[attr] = value unless changed_attributes.include?(attr) + changed_attributes[attr] = value end # Handle <tt>reset_*!</tt> for +method_missing+. diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 0c628c33c2..aba6618b56 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -3,7 +3,6 @@ require 'active_support/core_ext/array/conversions' require 'active_support/core_ext/string/inflections' require 'active_support/core_ext/object/blank' -require 'active_support/core_ext/hash/reverse_merge' module ActiveModel # == Active Model Errors @@ -202,12 +201,12 @@ module ActiveModel # # <error>name must be specified</error> # # </errors> def to_xml(options={}) - to_a.to_xml options.reverse_merge(:root => "errors", :skip_types => true) + to_a.to_xml({ :root => "errors", :skip_types => true }.merge!(options)) end # Returns an Hash that can be used as the JSON representation for this object. # Options: - # * <tt>:full_messages</tt> - determines if json object should contain + # * <tt>:full_messages</tt> - determines if json object should contain # full messages or not. Default: <tt>false</tt>. def as_json(options=nil) to_hash(options && options[:full_messages]) @@ -217,7 +216,7 @@ module ActiveModel if full_messages messages = {} self.messages.each do |attribute, array| - messages[attribute] = array.map{|message| full_message(attribute, message) } + messages[attribute] = array.map { |message| full_message(attribute, message) } end messages else @@ -347,7 +346,7 @@ module ActiveModel :model => @base.class.model_name.human, :attribute => @base.class.human_attribute_name(attribute), :value => value - }.merge(options) + }.merge!(options) I18n.translate(key, options) end @@ -356,9 +355,10 @@ module ActiveModel def normalize_message(attribute, message, options) message ||= :invalid - if message.is_a?(Symbol) + case message + when Symbol generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS)) - elsif message.is_a?(Proc) + when Proc message.call else message diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index 5665e10002..2b5fc57a3a 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -54,7 +54,7 @@ module ActiveModel defaults << options[:default] if options[:default] defaults << @human - options = {:scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults}.merge(options.except(:default)) + options = { :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults }.merge!(options.except(:default)) I18n.translate(defaults.shift, options) end diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index fc84b52dd9..cc60c9ac72 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -4,6 +4,8 @@ require 'active_support/core_ext/module/aliasing' require 'active_support/core_ext/module/remove_method' require 'active_support/core_ext/string/inflections' require 'active_support/core_ext/enumerable' +require 'active_support/deprecation' +require 'active_support/core_ext/object/try' require 'active_support/descendants_tracker' module ActiveModel @@ -74,22 +76,29 @@ module ActiveModel end # Total number of observers. - def count_observers + def observers_count observer_instances.size end + def count_observers + msg = "count_observers is deprecated in favor of observers_count" + ActiveSupport::Deprecation.warn(msg) + observers_count + end + protected def instantiate_observer(observer) #:nodoc: # string/symbol if observer.respond_to?(:to_sym) - observer.to_s.camelize.constantize.instance - elsif observer.respond_to?(:instance) + observer = observer.to_s.camelize.constantize + end + if observer.respond_to?(:instance) observer.instance else raise ArgumentError, - "#{observer} must be a lowercase, underscored class name (or an " + - "instance of the class itself) responding to the instance " + - "method. Example: Person.observers = :big_brother # calls " + + "#{observer} must be a lowercase, underscored class name (or " + + "the class itself) responding to the method :instance. " + + "Example: Person.observers = :big_brother # calls " + "BigBrother.instance" end end @@ -205,11 +214,7 @@ module ActiveModel # The class observed by default is inferred from the observer's class name: # assert_equal Person, PersonObserver.observed_class def observed_class - if observed_class_name = name[/(.*)Observer/, 1] - observed_class_name.constantize - else - nil - end + name[/(.*)Observer/, 1].try :constantize end end diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index e7a57cf691..8711b24124 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -55,17 +55,14 @@ module ActiveModel module InstanceMethodsOnActivation # Returns self if the password is correct, otherwise false. def authenticate(unencrypted_password) - if BCrypt::Password.new(password_digest) == unencrypted_password - self - else - false - end + BCrypt::Password.new(password_digest) == unencrypted_password && self end - # Encrypts the password into the password_digest attribute. + # Encrypts the password into the password_digest attribute, only if the + # new password is not blank. def password=(unencrypted_password) - @password = unencrypted_password unless unencrypted_password.blank? + @password = unencrypted_password self.password_digest = BCrypt::Password.create(unencrypted_password) end end diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb index 4323ee1e09..4403ef060b 100644 --- a/activemodel/lib/active_model/serialization.rb +++ b/activemodel/lib/active_model/serialization.rb @@ -26,17 +26,18 @@ module ActiveModel # person.serializable_hash # => {"name"=>"Bob"} # # You need to declare an attributes hash which contains the attributes - # you want to serialize. When called, serializable hash will use + # you want to serialize. Attributes must be strings, not symbols. + # When called, serializable hash will use # instance methods that match the name of the attributes hash's keys. # In order to override this behavior, take a look at the private - # method read_attribute_for_serialization. + # method +read_attribute_for_serialization+. # # Most of the time though, you will want to include the JSON or XML # serializations. Both of these modules automatically include the - # ActiveModel::Serialization module, so there is no need to explicitly + # <tt>ActiveModel::Serialization</tt> module, so there is no need to explicitly # include it. # - # So a minimal implementation including XML and JSON would be: + # A minimal implementation including XML and JSON would be: # # class Person # include ActiveModel::Serializers::JSON @@ -63,7 +64,12 @@ module ActiveModel # person.to_json # => "{\"name\":\"Bob\"}" # person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person... # - # Valid options are <tt>:only</tt>, <tt>:except</tt> and <tt>:methods</tt> . + # Valid options are <tt>:only</tt>, <tt>:except</tt>, <tt>:methods</tt> and <tt>include</tt>. + # The following are all valid examples: + # + # person.serializable_hash(:only => 'name') + # person.serializable_hash(:include => :address) + # person.serializable_hash(:include => { :address => { :only => 'city' }}) module Serialization def serializable_hash(options = nil) options ||= {} @@ -78,8 +84,7 @@ module ActiveModel hash = {} attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) } - method_names = Array(options[:methods]).select { |n| respond_to?(n) } - method_names.each { |n| hash[n.to_s] = send(n) } + Array(options[:methods]).each { |m| hash[m.to_s] = send(m) if respond_to?(m) } serializable_add_includes(options) do |association, records, opts| hash[association.to_s] = if records.is_a?(Enumerable) diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb index 02b7c54d61..6f0ca92e2a 100644 --- a/activemodel/lib/active_model/translation.rb +++ b/activemodel/lib/active_model/translation.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/hash/reverse_merge' - module ActiveModel # == Active Model Translation @@ -43,19 +41,20 @@ module ActiveModel # # Specify +options+ with additional translating options. def human_attribute_name(attribute, options = {}) - defaults = [] + options = { :count => 1 }.merge!(options) parts = attribute.to_s.split(".", 2) attribute = parts.pop namespace = parts.pop + attributes_scope = "#{self.i18n_scope}.attributes" if namespace - lookup_ancestors.each do |klass| - defaults << :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}" + defaults = lookup_ancestors.map do |klass| + :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}" end - defaults << :"#{self.i18n_scope}.attributes.#{namespace}.#{attribute}" + defaults << :"#{attributes_scope}.#{namespace}.#{attribute}" else - lookup_ancestors.each do |klass| - defaults << :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}" + defaults = lookup_ancestors.map do |klass| + :"#{attributes_scope}.#{klass.model_name.i18n_key}.#{attribute}" end end @@ -63,7 +62,7 @@ module ActiveModel defaults << options.delete(:default) if options[:default] defaults << attribute.humanize - options.reverse_merge! :count => 1, :default => defaults + options[:default] = defaults I18n.translate(defaults.shift, options) end end diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index 0e15155b85..3ed72bae3b 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -65,7 +65,7 @@ module ActiveModel # # attr_accessor :first_name, :last_name # - # validates_each :first_name, :last_name do |record, attr, value| + # validates_each :first_name, :last_name, :allow_blank => true do |record, attr, value| # record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z # end # end @@ -128,6 +128,19 @@ module ActiveModel # end # end # + # Options: + # * <tt>:on</tt> - Specifies the context where this validation is active + # (e.g. <tt>:on => :create</tt> or <tt>:on => :custom_validation_context</tt>) + # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+. + # * <tt>:allow_blank</tt> - Skip validation if attribute is blank. + # * <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. + # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should + # not occur (e.g. <tt>:unless => :skip_validation</tt>, or + # <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The + # method, proc or string should return or evaluate to a true or false value. def validate(*args, &block) options = args.extract_options! if options.key?(:on) @@ -145,7 +158,7 @@ module ActiveModel _validators.values.flatten.uniq end - # List all validators that being used to validate a specific attribute. + # List all validators that are being used to validate a specific attribute. def validators_on(*attributes) attributes.map do |attribute| _validators[attribute.to_sym] diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb index 72b8562b93..991c5f7b82 100644 --- a/activemodel/lib/active_model/validations/with.rb +++ b/activemodel/lib/active_model/validations/with.rb @@ -126,12 +126,12 @@ module ActiveModel # end # # Standard configuration options (:on, :if and :unless), which are - # available on the class version of validates_with, should instead be - # placed on the <tt>validates</tt> method as these are applied and tested + # available on the class version of +validates_with+, should instead be + # placed on the +validates+ method as these are applied and tested # in the callback # # If you pass any additional configuration options, they will be passed - # to the class and available as <tt>options</tt>, please refer to the + # to the class and available as +options+, please refer to the # class version of this method for more information # def validates_with(*args, &block) |