forked from mongodb/mongoid
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
103 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |