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
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

View File

@ -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

View File

@ -2,13 +2,19 @@ class ActiveRecord::Base
include ValidatesTimeliness::HelperMethods
include ValidatesTimeliness::AttributeMethods
def self.define_attribute_methods
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)
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

View File

@ -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,8 +28,17 @@ 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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