这个Ruby代码的细分?

时间:2010-04-23 22:56:39

标签: ruby-on-rails ruby

有人会善意地解剖合并!方法?它对条件和变量赋值的使用看起来相当简洁,而且我很难跟上它。很想听到一位精通Ruby的开发人员打破这种局面。

module ActiveRecord
  class Errors

    def merge!(errors, options={})
      fields_to_merge = if only=options[:only]
        only
      elsif except=options[:except]
        except = [except] unless except.is_a?(Array)
        except.map!(&:to_sym)
        errors.entries.map(&:first).select do |field|
          !except.include?(field.to_sym)
        end
      else
        errors.entries.map(&:first)
      end
      fields_to_merge = [fields_to_merge] unless fields_to_merge.is_a?(Array)
      fields_to_merge.map!(&:to_sym)

      errors.entries.each do |field, msg|
        add field, msg if fields_to_merge.include?(field.to_sym)
      end
    end
  end
end

1 个答案:

答案 0 :(得分:7)

module ActiveRecord
  class Errors

    def merge!(errors, options={})


      # This method has two major sections.

      # 1. Figure out which fields we want to merge, based on the options.

      # First check to see if we're supposed to whitelist or blacklist 
      # any fields.
      # fields_to_merge is simple being assigned the return value of 
      # the if/elsif/else block.
      #
      fields_to_merge = if only=options[:only]
        # Whitelist
        # Implies that options[:only] looks like :only => :foo, but that 
        # seems short-sighted and should probably be refactored to work 
        # like the blacklist, complete with to_sym conversions.
        only

      elsif except=options[:except]
        # Blacklist
        # You can pass in a single field or an array.
        # :except => [:foo, :bar, :yourmom]  or  :except => :foo
        # In the latter case, this is about to be converted to [:foo] so that
        # it's an array either way.
        except = [except] unless except.is_a?(Array)

        # For each array element, convert that element to a symbol. 
        # "foo" becomes :foo
        # This is a short hand for "map" which is equivalent to the following:
        #   except.map!{|field| field.to_sym}
        except.map!(&:to_sym)

        # The "entries" method comes from Enumerable and works like to_a. 
        # In the case of a hash, you end up with an array of arrays.
        # Assuming there are already errors, the output of that will look 
        # something like:
        # [['user', 'can't be blank'], ['some_other_field', 'has problems']]
        # 
        # Mapping this to :first means "send the 'first' message to each array 
        # element. Since the elements are themselves arrays, you end up 
        # with ['user', 'some_other_field']
        #
        # Now we do a 'select' on that array, looking for any field names that 
        # aren't in our 'except' array.
        errors.entries.map(&:first).select do |field|
          !except.include?(field.to_sym)
        end
      else
        # No filters. Just dump out an array of all field names.
        errors.entries.map(&:first)
      end

      # Make sure that we have an array, which at this point is only possible 
      # if we're using an :only => :foo.
      fields_to_merge = [fields_to_merge] unless fields_to_merge.is_a?(Array)

      # Make sure that they are all symbols (again with the :only, see the 
      # note above about refactoring).
      fields_to_merge.map!(&:to_sym)


      # 2. Now we know what fields we care about, yank in any errors with a 
      # matching field name from the errors object.

      errors.entries.each do |field, msg|
        # 'add' is a method on ActiveRecord::Errors that adds a new error 
        # message for the given attribute (field).
        # In this case, any error from the errors object passed into this 
        # method with a field name that appears in the 'fields_to_merge' 
        # array is added to the existing errors object.
        #
        add field, msg if fields_to_merge.include?(field.to_sym)
      end
    end
  end
end