Skip to content
This repository has been archived by the owner on Apr 17, 2018. It is now read-only.

Commit

Permalink
fixes #1222 dm-rails doesn't play nice with date_select
Browse files Browse the repository at this point in the history
  • Loading branch information
probablykabari committed Dec 22, 2010
1 parent d501dd9 commit 0b868c1
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 2 deletions.
29 changes: 28 additions & 1 deletion Gemfile
Expand Up @@ -26,7 +26,7 @@ group :development do

gem 'rake', '~> 0.8.7'
gem 'jeweler', '~> 1.4.0'

gem 'rspec', '~> 1.3.1'
end

group :quality do # These gems contain rake tasks that check the quality of the source code
Expand All @@ -36,3 +36,30 @@ group :quality do # These gems contain rake tasks that check the quality of the
gem 'yardstick', '~> 0.1'

end

group :datamapper do
adapters = ENV['ADAPTER'] || ENV['ADAPTERS']
adapters = adapters.to_s.tr(',', ' ').split.uniq - %w[ in_memory ]

DO_VERSION = '~> 0.10.2'
DM_DO_ADAPTERS = %w[ sqlite postgres mysql oracle sqlserver ]

if (do_adapters = DM_DO_ADAPTERS & adapters).any?
options = {}
options[:git] = "#{DATAMAPPER}/do#{REPO_POSTFIX}" if ENV['DO_GIT'] == 'true'

gem 'data_objects', DO_VERSION, options.dup

do_adapters.each do |adapter|
adapter = 'sqlite3' if adapter == 'sqlite'
gem "do_#{adapter}", DO_VERSION, options.dup
end

gem 'dm-do-adapter', DM_VERSION, SOURCE => "#{DATAMAPPER}/dm-do-adapter#{REPO_POSTFIX}"
gem 'dm-migrations', DM_VERSION, SOURCE => "#{DATAMAPPER}/dm-migrations#{REPO_POSTFIX}"
end

adapters.each do |adapter|
gem "dm-#{adapter}-adapter", DM_VERSION, SOURCE => "#{DATAMAPPER}/dm-#{adapter}-adapter#{REPO_POSTFIX}"
end
end
3 changes: 2 additions & 1 deletion Rakefile
Expand Up @@ -29,5 +29,6 @@ rescue LoadError
puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
end

task(:spec) {} # stub out the spec task for as long as we don't have any specs
require "spec/rake/spectask"
Spec::Rake::SpecTask.new

104 changes: 104 additions & 0 deletions lib/dm-rails/multiparameter_attribute_support.rb
@@ -0,0 +1,104 @@
module Rails
module DataMapper
module MultiparameterAttribute
def self.included(model)
model.alias_method_chain :attributes=, :multiparameter
end

protected
def attributes_with_multiparameter=(values_hash)
attribs = values_hash.dup
multi_parameter_attributes = []
attribs.each do |k, v|
if k.to_s.include?("(")
multi_parameter_attributes << [ k, v ]
attribs.delete(k)
# else
# respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(ArgumentError, "unknown property: #{k}")
end
end

attribs = attribs.merge(assign_multiparameter_attributes(multi_parameter_attributes))

self.attributes_without_multiparameter = attribs
end

def assign_multiparameter_attributes(pairs)
execute_callstack_for_multiparameter_attributes(
extract_callstack_for_multiparameter_attributes(pairs)
)
end

def instantiate_time_object(name, values)
if properties[name].name.demodulize =~ /utc/i
Time.zone.local(*values)
else
Time.time_with_datetime_fallback(Rails.configuration.time_zone, *values)
end
end

def execute_callstack_for_multiparameter_attributes(callstack)
errors = []
attribs = {}
callstack.each do |name, values|
klass = properties[name].primitive
if values.empty?
attribs[name] = nil
else
begin
value = if Time == klass || DateTime == klass
instantiate_time_object(name, values)
elsif Date == klass
begin
Date.new(*values)
rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
instantiate_time_object(name, 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

attribs[name] = value
rescue => ex
errors << "error on assignment #{values.inspect} to #{name}"
end
end
end
unless errors.empty?
raise(ArgumentError, "#{errors.size} error(s) on assignment of multiparameter attributes")
end
attribs
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)

unless value.empty?
attributes[attribute_name] <<
[ find_parameter_position(multiparameter_name), type_cast_attribute_value(multiparameter_name, value) ]
end
end

attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
end


def type_cast_attribute_value(multiparameter_name, value)
multiparameter_name =~ /\([0-9]*([a-z])\)/ ? value.send("to_" + $1) : value
end

def find_parameter_position(multiparameter_name)
multiparameter_name.scan(/\(([0-9]*).*\)/).first.first
end

end # Model
end

end

DataMapper::Model.append_inclusions(Rails::DataMapper::MultiparameterAttribute)
1 change: 1 addition & 0 deletions lib/dm-rails/railtie.rb
Expand Up @@ -20,6 +20,7 @@
# but users will still need to include it into the
# models they want it to use it in.
require 'dm-rails/mass_assignment_security'
require 'dm-rails/multiparameter_attribute_support'

module Rails
module DataMapper
Expand Down
3 changes: 3 additions & 0 deletions spec/spec.opts
@@ -0,0 +1,3 @@
--format progress
--color
--backtrace
17 changes: 17 additions & 0 deletions spec/spec_helper.rb
@@ -0,0 +1,17 @@
$:.unshift File.expand_path File.dirname(__FILE__) + '../lib'
require "dm-migrations"
require 'dm-core/spec/setup'
require 'dm-core/spec/lib/adapter_helpers'
require 'dm-core/spec/lib/spec_helper'
require 'dm-core/spec/lib/pending_helpers'

Spec::Runner.configure do |config|

config.extend(DataMapper::Spec::Adapters::Helpers)
config.include(DataMapper::Spec::PendingHelpers)

config.after :all do
DataMapper::Spec.cleanup_models
end

end
59 changes: 59 additions & 0 deletions spec/unit/multiparameter_attribute_spec.rb
@@ -0,0 +1,59 @@
require 'spec_helper'
require "dm-rails/multiparameter_attribute_support"

describe "Multiparameter attributes (ie dates) in rails forms" do

supported_by :all do
before(:all) do
class MuliparameterRecource
include DataMapper::Resource

property :id, Serial
property :name, String
property :birthday, Date
end
MuliparameterRecource.auto_migrate!
end

it "should not blow up" do
lambda { create_mpr }.should_not raise_error
end

it "should assign multiparameter property correctly" do
mpr = create_mpr
mpr.birthday.should == Date.new(1989,3,26)
end

it "should still assign the regular values" do
mpr = create_mpr
mpr.name.should == "joe"
end

it "should not change the attributes hash" do
mpr = create_mpr
mpr.attributes.should == {:birthday => Date.new(1989,3,26), :name => "joe", :id => mpr.id}
end

it "should not change non-multiparameter attributes" do
mpr = MuliparameterRecource.new
attributes = { "birthday" => Date.new(1989,3,26), "name" => "joe" }
mpr.should_receive(:attributes_without_multiparameter=).with(attributes.dup) # dup to make sure original hash isn't modified
mpr.send(:attributes_with_multiparameter=, multiparameter_hash).should == attributes
end
end

def multiparameter_hash
{
"birthday(2i)"=>"3",
"birthday(3i)"=>"26",
"birthday(1i)"=>"1989",
"name" => "joe"
}
end

def create_mpr
MuliparameterRecource.create(multiparameter_hash)
end

end

0 comments on commit 0b868c1

Please sign in to comment.