Skip to content

Commit

Permalink
Merge fefb0b1 into c3da497
Browse files Browse the repository at this point in the history
  • Loading branch information
jstrocel committed Jun 11, 2013
2 parents c3da497 + fefb0b1 commit 3048a24
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/mongoid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
require "mongoid/sessions"
require "mongoid/document"
require "mongoid/unit_of_work"
require "mongoid/multi_parameter_attributes"

# If we are using Rails then we will include the Mongoid railtie. This has all
# the nifty initializers that Mongoid needs.
Expand Down
102 changes: 102 additions & 0 deletions lib/mongoid/multi_parameter_attributes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# encoding: utf-8
module Mongoid

# Adds Rails' multi-parameter attribute support to Mongoid.
#
# @todo: Durran: This module needs an overhaul.
module MultiParameterAttributes

module Errors

# Raised when an error occurred while doing a mass assignment to an
# attribute through the <tt>attributes=</tt> method. The exception
# has an +attribute+ property that is the name of the offending attribute.
class AttributeAssignmentError < Mongoid::Errors::MongoidError
attr_reader :exception, :attribute

def initialize(message, exception, attribute)
@exception = exception
@attribute = attribute
@message = message
end
end

# Raised when there are multiple errors while doing a mass assignment
# through the +attributes+ method. The exception has an +errors+
# property that contains an array of AttributeAssignmentError
# objects, each corresponding to the error while assigning to an
# attribute.
class MultiparameterAssignmentErrors < Mongoid::Errors::MongoidError
attr_reader :errors

def initialize(errors)
@errors = errors
end
end
end

# Process the provided attributes casting them to their proper values if a
# field exists for them on the document. This will be limited to only the
# attributes provided in the suppied +Hash+ so that no extra nil values get
# put into the document's attributes.
#
# @example Process the attributes.
# person.process_attributes(:title => "sir", :age => 40)
#
# @param [ Hash ] attrs The attributes to set.
#
# @since 2.0.0.rc.7
def process_attributes(attrs = nil)
if attrs
errors = []
attributes = attrs.class.new
attributes.permit! if attrs.respond_to?(:permitted?) && attrs.permitted?
multi_parameter_attributes = {}

attrs.each_pair do |key, value|
if key =~ /\A([^\(]+)\((\d+)([if])\)$/
key, index = $1, $2.to_i
(multi_parameter_attributes[key] ||= {})[index] = value.empty? ? nil : value.send("to_#{$3}")
else
attributes[key] = value
end
end

multi_parameter_attributes.each_pair do |key, values|
begin
values = (values.keys.min..values.keys.max).map { |i| values[i] }
field = self.class.fields[database_field_name(key)]
attributes[key] = instantiate_object(field, values)
rescue => e
errors << Errors::AttributeAssignmentError.new(
"error on assignment #{values.inspect} to #{key}", e, key
)
end
end

unless errors.empty?
raise Errors::MultiparameterAssignmentErrors.new(errors),
"#{errors.size} error(s) on assignment of multiparameter attributes"
end
super(attributes)
else
super
end
end

protected

def instantiate_object(field, values_with_empty_parameters)
return nil if values_with_empty_parameters.all? { |v| v.nil? }
values = values_with_empty_parameters.collect { |v| v.nil? ? 1 : v }
klass = field.type
if klass == DateTime || klass == Date || klass == Time
field.mongoize(values)
elsif klass
klass.new(*values)
else
values
end
end
end
end

0 comments on commit 3048a24

Please sign in to comment.