Skip to content

Instantly share code, notes, and snippets.

@faust45
Forked from stengland/event.rb
Created March 18, 2010 17:01
Show Gist options
  • Save faust45/336567 to your computer and use it in GitHub Desktop.
Save faust45/336567 to your computer and use it in GitHub Desktop.
class Event
include MongoMapper::Document
include MultiParameterAttributes
key :name, String, :required => true
key :start_date, Date, :required => true
key :start_time, Time, :required => true
end
require 'spec_helper'
describe Event do
before(:each) do
@valid_attributes = {
:name => "Event name"
}
end
it "should allow multi_attibutes" do
@valid_attributes.delete(:start_date)
event = Event.create!(@valid_attributes.merge({
"start_date(1i)" => "2009",
"start_date(2i)" => "10",
"start_date(3i)" => "5",
"start_time(1i)" => "2009",
"start_time(2i)" => "10",
"start_time(3i)" => "5",
"start_time(4i)" => "14",
"start_time(5i)" => "35"
}))
event.start_date.should == Date.civil(2009,10,5)
event.start_time.should == Time.local(2009,10,5,14,35)
end
end
module MultiParameterAttributes
def attributes=(attrs)
multi_parameter_attributes = []
attrs.each do |name, value|
return if attrs.blank?
if name.to_s.include?("(")
multi_parameter_attributes << [ name, value ]
else
writer_method = "#{name}="
if respond_to?(writer_method)
self.send(writer_method, value)
else
self[name.to_s] = value
end
end
end
assign_multiparameter_attributes(multi_parameter_attributes)
end
def assign_multiparameter_attributes(pairs)
execute_callstack_for_multiparameter_attributes(
extract_callstack_for_multiparameter_attributes(pairs)
)
end
def execute_callstack_for_multiparameter_attributes(callstack)
callstack.each do |name, values_with_empty_parameters|
# in order to allow a date to be set without a year, we must keep the empty values.
# Otherwise, we wouldn't be able to distinguish it from a date with an empty day.
values = values_with_empty_parameters.reject(&:nil?)
if values.any?
key = self.class.keys[name]
raise ArgumentError, "Unknown key #{name}" if key.nil?
klass = key.type
value = if Time == klass
Time.zone.local(*values)
elsif Date == klass
begin
values = values_with_empty_parameters.collect do |v| v.nil? ? 1 : v end
Date.new(*values)
rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
Time.zone.local(*values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
end
else
klass.new(*values)
end
writer_method = "#{name}="
if respond_to?(writer_method)
self.send(writer_method, value)
else
self[name.to_s] = value
end
end
end
end
def extract_callstack_for_multiparameter_attributes(pairs)
attributes = { }
for pair in pairs
multiparameter_name, value = pair
attribute_name = multiparameter_name.split("(").first
attributes[attribute_name] = [] unless attributes.include?(attribute_name)
attributes[attribute_name] << [ find_parameter_position(multiparameter_name), value ]
end
attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
end
def find_parameter_position(multiparameter_name)
multiparameter_name.scan(/\(([0-9]*).*\)/).first.first
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment