From f41903a76911790adf99fe520cd816672e778088 Mon Sep 17 00:00:00 2001 From: Adam Meehan Date: Fri, 24 Sep 2010 12:00:15 +1000 Subject: [PATCH] refactored to get attribute type from model types not validation type reverts to behaviour of old version which allows you to define validations of any type for sake of the values it is validating against --- lib/validates_timeliness/attribute_methods.rb | 19 ++++++++++++------ lib/validates_timeliness/helper_methods.rb | 15 +++++--------- lib/validates_timeliness/orm/active_record.rb | 20 ++++++++++++------- lib/validates_timeliness/orm/mongoid.rb | 18 +++++++++++++++-- spec/spec_helper.rb | 14 +++++++------ spec/test_model.rb | 13 ++++++++---- .../attribute_methods_spec.rb | 8 ++++++-- .../helper_methods_spec.rb | 8 ++------ .../orm/active_record_spec.rb | 4 ++-- spec/validates_timeliness/orm/mongoid_spec.rb | 4 ++-- spec/validates_timeliness/validator_spec.rb | 4 +++- 11 files changed, 79 insertions(+), 48 deletions(-) diff --git a/lib/validates_timeliness/attribute_methods.rb b/lib/validates_timeliness/attribute_methods.rb index d67e59b..8e0350e 100644 --- a/lib/validates_timeliness/attribute_methods.rb +++ b/lib/validates_timeliness/attribute_methods.rb @@ -4,17 +4,20 @@ module ValidatesTimeliness module ClassMethods + protected + def define_timeliness_methods(before_type_cast=false) return if timeliness_validated_attributes.blank? - timeliness_validated_attributes.each do |attr_name, type| - define_timeliness_write_method(attr_name, type, timeliness_attribute_timezone_aware?(attr_name)) + timeliness_validated_attributes.each do |attr_name| + define_timeliness_write_method(attr_name) define_timeliness_before_type_cast_method(attr_name) if before_type_cast end end - protected + def define_timeliness_write_method(attr_name) + type = timeliness_attribute_type(attr_name) + timezone_aware = timeliness_attribute_timezone_aware?(attr_name) - def define_timeliness_write_method(attr_name, type, timezone_aware) method_body, line = <<-EOV, __LINE__ + 1 def #{attr_name}=(value) @attributes_cache ||= {} @@ -35,12 +38,16 @@ module ValidatesTimeliness class_eval(method_body, __FILE__, line) end - public - + # Override in ORM shim def timeliness_attribute_timezone_aware?(attr_name) false end + # Override in ORM shim + def timeliness_attribute_type(attr_name) + :datetime + end + end module InstanceMethods diff --git a/lib/validates_timeliness/helper_methods.rb b/lib/validates_timeliness/helper_methods.rb index d5d5bb1..601169d 100644 --- a/lib/validates_timeliness/helper_methods.rb +++ b/lib/validates_timeliness/helper_methods.rb @@ -5,8 +5,8 @@ module ValidatesTimeliness included do include ValidationMethods extend ValidationMethods - class_inheritable_hash :timeliness_validated_attributes - self.timeliness_validated_attributes = {} + class_inheritable_accessor :timeliness_validated_attributes + self.timeliness_validated_attributes = [] end module ValidationMethods @@ -23,14 +23,9 @@ module ValidatesTimeliness end def timeliness_validation_for(attr_names, type) - options = _merge_attributes(attr_names) - options[:type] = type - attributes = attr_names.inject({}) {|validated, attr_name| - attr_name = attr_name.to_s - validated[attr_name] = type - validated - } - self.timeliness_validated_attributes = attributes + options = _merge_attributes(attr_names).merge(:type => type) + self.timeliness_validated_attributes ||= [] + self.timeliness_validated_attributes += (attr_names - self.timeliness_validated_attributes) validates_with Validator, options end diff --git a/lib/validates_timeliness/orm/active_record.rb b/lib/validates_timeliness/orm/active_record.rb index 88bb7ee..aa4d1bf 100644 --- a/lib/validates_timeliness/orm/active_record.rb +++ b/lib/validates_timeliness/orm/active_record.rb @@ -2,13 +2,19 @@ class ActiveRecord::Base include ValidatesTimeliness::HelperMethods include ValidatesTimeliness::AttributeMethods - def self.define_attribute_methods - super - # Define write method and before_type_cast method - define_timeliness_methods(true) - end + class << self + def define_attribute_methods + super + # Define write method and before_type_cast method + define_timeliness_methods(true) + end - def self.timeliness_attribute_timezone_aware?(attr_name) - create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name]) + def timeliness_attribute_timezone_aware?(attr_name) + create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name]) + end + + def timeliness_attribute_type(attr_name) + columns_hash[attr_name.to_s].type + end end end diff --git a/lib/validates_timeliness/orm/mongoid.rb b/lib/validates_timeliness/orm/mongoid.rb index f027291..850b24e 100644 --- a/lib/validates_timeliness/orm/mongoid.rb +++ b/lib/validates_timeliness/orm/mongoid.rb @@ -7,12 +7,17 @@ module ValidatesTimeliness # field value in Mongoid. Parser will return nil rather than error. module ClassMethods + # Mongoid has no bulk attribute method definition hook. It defines + # them with each field definition. So we likewise define them after + # each validation is defined. + # def timeliness_validation_for(attr_names, type) super - attr_names.each { |attr_name| define_timeliness_write_method(attr_name, type, false) } + attr_names.each { |attr_name| define_timeliness_write_method(attr_name) } end - def define_timeliness_write_method(attr_name, type, timezone_aware) + def define_timeliness_write_method(attr_name) + type = timeliness_attribute_type(attr_name) method_body, line = <<-EOV, __LINE__ + 1 def #{attr_name}=(value) @attributes_cache ||= {} @@ -23,7 +28,16 @@ module ValidatesTimeliness EOV class_eval(method_body, __FILE__, line) end + + def timeliness_attribute_type(attr_name) + { + Date => :date, + Time => :datetime, + DateTime => :datetime + }[fields[attr_name.to_s].type] || :datetime + end end + end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 72cb5d5..13d2d0f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -48,11 +48,13 @@ end class Person include TestModel - self.model_attributes = :birth_date, :birth_time, :birth_datetime + attribute :birth_date, :date + attribute :birth_time, :time + attribute :birth_datetime, :datetime validates_date :birth_date validates_time :birth_time validates_datetime :birth_datetime - define_attribute_methods model_attributes + define_attribute_methods model_attributes.keys end ActiveRecord::Base.time_zone_aware_attributes = true @@ -62,8 +64,8 @@ ActiveRecord::Schema.define(:version => 1) do create_table :employees, :force => true do |t| t.string :first_name t.string :last_name - t.datetime :birth_date - t.datetime :birth_time + t.date :birth_date + t.time :birth_time t.datetime :birth_datetime end end @@ -80,10 +82,10 @@ Rspec.configure do |c| c.include(RspecTagMatchers) c.before do Person.reset_callbacks(:validate) - Person.timeliness_validated_attributes = {} + Person.timeliness_validated_attributes = [] Person._validators.clear Employee.reset_callbacks(:validate) - Employee.timeliness_validated_attributes = {} + Employee.timeliness_validated_attributes = [] Employee._validators.clear end end diff --git a/spec/test_model.rb b/spec/test_model.rb index 169d6fa..dd7801c 100644 --- a/spec/test_model.rb +++ b/spec/test_model.rb @@ -13,24 +13,29 @@ module TestModel end module ClassMethods + def attribute(name, type) + self.model_attributes ||= {} + self.model_attributes[name] = type + end + def define_method_attribute=(attr_name) - generated_attribute_methods.module_eval("def #{attr_name}=(new_value); @attributes['#{attr_name}']=self.class.type_cast(new_value); end", __FILE__, __LINE__) + generated_attribute_methods.module_eval("def #{attr_name}=(new_value); @attributes['#{attr_name}']=self.class.type_cast('#{attr_name}', new_value); end", __FILE__, __LINE__) end def define_method_attribute(attr_name) generated_attribute_methods.module_eval("def #{attr_name}; @attributes['#{attr_name}']; end", __FILE__, __LINE__) end - def type_cast(value) + def type_cast(attr_name, value) return value unless value.is_a?(String) - value.to_time rescue nil + value.send("to_#{model_attributes[attr_name.to_sym]}") rescue nil end end module DynamicMethods def method_missing(method_id, *args, &block) if !self.class.attribute_methods_generated? - self.class.define_attribute_methods self.class.model_attributes.map(&:to_s) + self.class.define_attribute_methods self.class.model_attributes.keys.map(&:to_s) method_name = method_id.to_s send(method_id, *args, &block) else diff --git a/spec/validates_timeliness/attribute_methods_spec.rb b/spec/validates_timeliness/attribute_methods_spec.rb index 55e6161..1de25ce 100644 --- a/spec/validates_timeliness/attribute_methods_spec.rb +++ b/spec/validates_timeliness/attribute_methods_spec.rb @@ -8,7 +8,9 @@ describe ValidatesTimeliness::AttributeMethods do context "attribute write method" do class PersonWithCache include TestModel - self.model_attributes = :birth_date, :birth_time, :birth_datetime + attribute :birth_date, :date + attribute :birth_time, :time + attribute :birth_datetime, :datetime validates_date :birth_date validates_time :birth_time validates_datetime :birth_datetime @@ -23,7 +25,9 @@ describe ValidatesTimeliness::AttributeMethods do context "with plugin parser" do class PersonWithParser include TestModel - self.model_attributes = :birth_date, :birth_time, :birth_datetime + attribute :birth_date, :date + attribute :birth_time, :time + attribute :birth_datetime, :datetime validates_date :birth_date validates_time :birth_time validates_datetime :birth_datetime diff --git a/spec/validates_timeliness/helper_methods_spec.rb b/spec/validates_timeliness/helper_methods_spec.rb index 4a5a96b..cb0ab97 100644 --- a/spec/validates_timeliness/helper_methods_spec.rb +++ b/spec/validates_timeliness/helper_methods_spec.rb @@ -21,16 +21,12 @@ describe ValidatesTimeliness::HelperMethods do describe ".timeliness_validated_attributes" do it 'should return attributes validated with plugin validator' do - Person.timeliness_validated_attributes = {} + Person.timeliness_validated_attributes = [] Person.validates_date :birth_date Person.validates_time :birth_time Person.validates_datetime :birth_datetime - Person.timeliness_validated_attributes.should == { - "birth_date" => :date, - "birth_time" => :time, - "birth_datetime" => :datetime - } + Person.timeliness_validated_attributes.should == [ :birth_date, :birth_time, :birth_datetime ] end end diff --git a/spec/validates_timeliness/orm/active_record_spec.rb b/spec/validates_timeliness/orm/active_record_spec.rb index 27402c9..e35946a 100644 --- a/spec/validates_timeliness/orm/active_record_spec.rb +++ b/spec/validates_timeliness/orm/active_record_spec.rb @@ -16,8 +16,8 @@ describe ValidatesTimeliness, 'ActiveRecord' do end end - it 'should define _timeliness_raw_value_for instance method' do - Employee.instance_methods.should include('_timeliness_raw_value_for') + it 'should determine type for attribute' do + Employee.timeliness_attribute_type(:birth_date).should == :date end context "attribute write method" do diff --git a/spec/validates_timeliness/orm/mongoid_spec.rb b/spec/validates_timeliness/orm/mongoid_spec.rb index 8d6139e..992e834 100644 --- a/spec/validates_timeliness/orm/mongoid_spec.rb +++ b/spec/validates_timeliness/orm/mongoid_spec.rb @@ -38,8 +38,8 @@ describe ValidatesTimeliness, 'Mongoid' do end end - it 'should define _timeliness_raw_value_for instance method' do - Article.instance_methods.should include('_timeliness_raw_value_for') + it 'should determine type for attribute' do + Article.timeliness_attribute_type(:publish_date).should == :date end context "attribute write method" do diff --git a/spec/validates_timeliness/validator_spec.rb b/spec/validates_timeliness/validator_spec.rb index 08ce3e9..4d8522d 100644 --- a/spec/validates_timeliness/validator_spec.rb +++ b/spec/validates_timeliness/validator_spec.rb @@ -101,7 +101,9 @@ describe ValidatesTimeliness::Validator do describe ":format option" do class PersonWithFormatOption include TestModel - self.model_attributes = :birth_date, :birth_time, :birth_datetime + attribute :birth_date, :date + attribute :birth_time, :time + attribute :birth_datetime, :datetime validates_date :birth_date, :format => 'dd-mm-yyyy' end