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
This commit is contained in:
Adam Meehan 2010-09-24 12:00:15 +10:00
parent 2efcff2fd4
commit f41903a769
11 changed files with 79 additions and 48 deletions

View File

@ -4,17 +4,20 @@ module ValidatesTimeliness
module ClassMethods module ClassMethods
protected
def define_timeliness_methods(before_type_cast=false) def define_timeliness_methods(before_type_cast=false)
return if timeliness_validated_attributes.blank? return if timeliness_validated_attributes.blank?
timeliness_validated_attributes.each do |attr_name, type| timeliness_validated_attributes.each do |attr_name|
define_timeliness_write_method(attr_name, type, timeliness_attribute_timezone_aware?(attr_name)) define_timeliness_write_method(attr_name)
define_timeliness_before_type_cast_method(attr_name) if before_type_cast define_timeliness_before_type_cast_method(attr_name) if before_type_cast
end end
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 method_body, line = <<-EOV, __LINE__ + 1
def #{attr_name}=(value) def #{attr_name}=(value)
@attributes_cache ||= {} @attributes_cache ||= {}
@ -35,12 +38,16 @@ module ValidatesTimeliness
class_eval(method_body, __FILE__, line) class_eval(method_body, __FILE__, line)
end end
public # Override in ORM shim
def timeliness_attribute_timezone_aware?(attr_name) def timeliness_attribute_timezone_aware?(attr_name)
false false
end end
# Override in ORM shim
def timeliness_attribute_type(attr_name)
:datetime
end
end end
module InstanceMethods module InstanceMethods

View File

@ -5,8 +5,8 @@ module ValidatesTimeliness
included do included do
include ValidationMethods include ValidationMethods
extend ValidationMethods extend ValidationMethods
class_inheritable_hash :timeliness_validated_attributes class_inheritable_accessor :timeliness_validated_attributes
self.timeliness_validated_attributes = {} self.timeliness_validated_attributes = []
end end
module ValidationMethods module ValidationMethods
@ -23,14 +23,9 @@ module ValidatesTimeliness
end end
def timeliness_validation_for(attr_names, type) def timeliness_validation_for(attr_names, type)
options = _merge_attributes(attr_names) options = _merge_attributes(attr_names).merge(:type => type)
options[:type] = type self.timeliness_validated_attributes ||= []
attributes = attr_names.inject({}) {|validated, attr_name| self.timeliness_validated_attributes += (attr_names - self.timeliness_validated_attributes)
attr_name = attr_name.to_s
validated[attr_name] = type
validated
}
self.timeliness_validated_attributes = attributes
validates_with Validator, options validates_with Validator, options
end end

View File

@ -2,13 +2,19 @@ class ActiveRecord::Base
include ValidatesTimeliness::HelperMethods include ValidatesTimeliness::HelperMethods
include ValidatesTimeliness::AttributeMethods include ValidatesTimeliness::AttributeMethods
def self.define_attribute_methods class << self
super def define_attribute_methods
# Define write method and before_type_cast method super
define_timeliness_methods(true) # Define write method and before_type_cast method
end define_timeliness_methods(true)
end
def self.timeliness_attribute_timezone_aware?(attr_name) def timeliness_attribute_timezone_aware?(attr_name)
create_time_zone_conversion_attribute?(attr_name, columns_hash[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
end end

View File

@ -7,12 +7,17 @@ module ValidatesTimeliness
# field value in Mongoid. Parser will return nil rather than error. # field value in Mongoid. Parser will return nil rather than error.
module ClassMethods 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) def timeliness_validation_for(attr_names, type)
super 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 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 method_body, line = <<-EOV, __LINE__ + 1
def #{attr_name}=(value) def #{attr_name}=(value)
@attributes_cache ||= {} @attributes_cache ||= {}
@ -23,7 +28,16 @@ module ValidatesTimeliness
EOV EOV
class_eval(method_body, __FILE__, line) class_eval(method_body, __FILE__, line)
end 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
end end
end end

View File

@ -48,11 +48,13 @@ end
class Person class Person
include TestModel 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_date :birth_date
validates_time :birth_time validates_time :birth_time
validates_datetime :birth_datetime validates_datetime :birth_datetime
define_attribute_methods model_attributes define_attribute_methods model_attributes.keys
end end
ActiveRecord::Base.time_zone_aware_attributes = true ActiveRecord::Base.time_zone_aware_attributes = true
@ -62,8 +64,8 @@ ActiveRecord::Schema.define(:version => 1) do
create_table :employees, :force => true do |t| create_table :employees, :force => true do |t|
t.string :first_name t.string :first_name
t.string :last_name t.string :last_name
t.datetime :birth_date t.date :birth_date
t.datetime :birth_time t.time :birth_time
t.datetime :birth_datetime t.datetime :birth_datetime
end end
end end
@ -80,10 +82,10 @@ Rspec.configure do |c|
c.include(RspecTagMatchers) c.include(RspecTagMatchers)
c.before do c.before do
Person.reset_callbacks(:validate) Person.reset_callbacks(:validate)
Person.timeliness_validated_attributes = {} Person.timeliness_validated_attributes = []
Person._validators.clear Person._validators.clear
Employee.reset_callbacks(:validate) Employee.reset_callbacks(:validate)
Employee.timeliness_validated_attributes = {} Employee.timeliness_validated_attributes = []
Employee._validators.clear Employee._validators.clear
end end
end end

View File

@ -13,24 +13,29 @@ module TestModel
end end
module ClassMethods module ClassMethods
def attribute(name, type)
self.model_attributes ||= {}
self.model_attributes[name] = type
end
def define_method_attribute=(attr_name) 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 end
def define_method_attribute(attr_name) def define_method_attribute(attr_name)
generated_attribute_methods.module_eval("def #{attr_name}; @attributes['#{attr_name}']; end", __FILE__, __LINE__) generated_attribute_methods.module_eval("def #{attr_name}; @attributes['#{attr_name}']; end", __FILE__, __LINE__)
end end
def type_cast(value) def type_cast(attr_name, value)
return value unless value.is_a?(String) 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
end end
module DynamicMethods module DynamicMethods
def method_missing(method_id, *args, &block) def method_missing(method_id, *args, &block)
if !self.class.attribute_methods_generated? 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 method_name = method_id.to_s
send(method_id, *args, &block) send(method_id, *args, &block)
else else

View File

@ -8,7 +8,9 @@ describe ValidatesTimeliness::AttributeMethods do
context "attribute write method" do context "attribute write method" do
class PersonWithCache class PersonWithCache
include TestModel 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_date :birth_date
validates_time :birth_time validates_time :birth_time
validates_datetime :birth_datetime validates_datetime :birth_datetime
@ -23,7 +25,9 @@ describe ValidatesTimeliness::AttributeMethods do
context "with plugin parser" do context "with plugin parser" do
class PersonWithParser class PersonWithParser
include TestModel 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_date :birth_date
validates_time :birth_time validates_time :birth_time
validates_datetime :birth_datetime validates_datetime :birth_datetime

View File

@ -21,16 +21,12 @@ describe ValidatesTimeliness::HelperMethods do
describe ".timeliness_validated_attributes" do describe ".timeliness_validated_attributes" do
it 'should return attributes validated with plugin validator' 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_date :birth_date
Person.validates_time :birth_time Person.validates_time :birth_time
Person.validates_datetime :birth_datetime Person.validates_datetime :birth_datetime
Person.timeliness_validated_attributes.should == { Person.timeliness_validated_attributes.should == [ :birth_date, :birth_time, :birth_datetime ]
"birth_date" => :date,
"birth_time" => :time,
"birth_datetime" => :datetime
}
end end
end end

View File

@ -16,8 +16,8 @@ describe ValidatesTimeliness, 'ActiveRecord' do
end end
end end
it 'should define _timeliness_raw_value_for instance method' do it 'should determine type for attribute' do
Employee.instance_methods.should include('_timeliness_raw_value_for') Employee.timeliness_attribute_type(:birth_date).should == :date
end end
context "attribute write method" do context "attribute write method" do

View File

@ -38,8 +38,8 @@ describe ValidatesTimeliness, 'Mongoid' do
end end
end end
it 'should define _timeliness_raw_value_for instance method' do it 'should determine type for attribute' do
Article.instance_methods.should include('_timeliness_raw_value_for') Article.timeliness_attribute_type(:publish_date).should == :date
end end
context "attribute write method" do context "attribute write method" do

View File

@ -101,7 +101,9 @@ describe ValidatesTimeliness::Validator do
describe ":format option" do describe ":format option" do
class PersonWithFormatOption class PersonWithFormatOption
include TestModel 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' validates_date :birth_date, :format => 'dd-mm-yyyy'
end end