Validation support for ActiveModel::Validations without a shim

move validation helpers into ActiveModel::Validations for default base support
add check if attribute methods shim is being used
refactor specs for helper and attribute methods separation
more mongoid workarounds due to incorrect use of AS::Concern
This commit is contained in:
Adam Meehan 2010-09-29 08:09:43 +10:00
parent 5f31f40413
commit 6e67d45274
12 changed files with 92 additions and 78 deletions

View File

@ -1,5 +1,5 @@
ValidatesTimeliness.setup do |config|
# Add plugin to supported ORMs (:active_record, :mongoid)
# Extend ORM/ODMs for full support (:active_record, :mongoid).
# config.extend_orms = [ :active_record ]
#
# User the plugin date/time parser which is stricter and extendable

View File

@ -2,8 +2,24 @@ module ValidatesTimeliness
module AttributeMethods
extend ActiveSupport::Concern
included do
class_inheritable_accessor :timeliness_validated_attributes
self.timeliness_validated_attributes = []
end
module ClassMethods
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
protected
def define_timeliness_methods(before_type_cast=false)
@ -37,21 +53,9 @@ module ValidatesTimeliness
EOV
class_eval(method_body, __FILE__, line)
end
# 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
def _timeliness_raw_value_for(attr_name)
@timeliness_cache && @timeliness_cache[attr_name.to_s]
end
@ -59,7 +63,6 @@ module ValidatesTimeliness
def _clear_timeliness_cache
@timeliness_cache = {}
end
end
end

View File

@ -3,10 +3,10 @@ module ValidatesTimeliness
module DateTimeSelect
extend ActiveSupport::Concern
# Intercepts the date and time select helpers to reuse the values from the
# Intercepts the date and time select helpers to reuse the values from
# the params rather than the parsed value. This allows invalid date/time
# values to be redisplayed instead of blanks to aid correction by the user.
# Its a minor usability improvement which is rarely an issue for the user.
# It's a minor usability improvement which is rarely an issue for the user.
included do
alias_method_chain :datetime_selector, :timeliness

View File

@ -1,15 +1,7 @@
module ValidatesTimeliness
module HelperMethods
extend ActiveSupport::Concern
module ActiveModel
module Validations
included do
include ValidationMethods
extend ValidationMethods
class_inheritable_accessor :timeliness_validated_attributes
self.timeliness_validated_attributes = []
end
module ValidationMethods
module HelperMethods
def validates_date(*attr_names)
timeliness_validation_for attr_names, :date
end
@ -24,11 +16,13 @@ module ValidatesTimeliness
def timeliness_validation_for(attr_names, type)
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
if respond_to?(:timeliness_validated_attributes)
self.timeliness_validated_attributes ||= []
self.timeliness_validated_attributes += (attr_names - self.timeliness_validated_attributes)
end
validates_with ValidatesTimeliness::Validator, options
end
end
end
end

View File

@ -32,7 +32,6 @@ module ValidatesTimeliness
end
class ActiveRecord::Base
include ValidatesTimeliness::HelperMethods
include ValidatesTimeliness::AttributeMethods
include ValidatesTimeliness::ORM::ActiveRecord
end

View File

@ -43,9 +43,18 @@ module ValidatesTimeliness
end
module Mongoid::Document
include ValidatesTimeliness::HelperMethods
include ValidatesTimeliness::AttributeMethods
include ValidatesTimeliness::ORM::Mongoid
# Due to how Mongoid misuses ActiveSupport::Concern,
# the only way to override a core component method is
# using an append_features hook.
#
module TimelinessConcern
def append_features(base)
super
base.send :include, ValidatesTimeliness::AttributeMethods
base.send :include, ValidatesTimeliness::ORM::Mongoid
end
end
extend TimelinessConcern
def reload_with_timeliness
_clear_timeliness_cache

View File

@ -37,10 +37,10 @@ module ValidatesTimeliness
end
def validate_each(record, attr_name, value)
raw_value = record._timeliness_raw_value_for(attr_name) || value
raw_value = attribute_raw_value(record, attr_name) || value
return if (@allow_nil && raw_value.nil?) || (@allow_blank && raw_value.blank?)
@timezone_aware = record.class.timeliness_attribute_timezone_aware?(attr_name)
@timezone_aware = timezone_aware?(record, attr_name)
value = parse(raw_value) if value.is_a?(String) || options[:format]
value = type_cast_value(value, @type)
@ -66,6 +66,17 @@ module ValidatesTimeliness
value.strftime(format)
end
def attribute_raw_value(record, attr_name)
if record.respond_to?(:_timeliness_raw_value_for)
record._timeliness_raw_value_for(attr_name)
end
end
def timezone_aware?(record, attr_name)
record.class.respond_to?(:timeliness_attribute_timezone_aware?) &&
record.class.timeliness_attribute_timezone_aware?(attr_name)
end
end
end

View File

@ -24,15 +24,11 @@ LOCALE_PATH = File.expand_path(File.dirname(__FILE__) + '/../lib/generators/vali
I18n.load_path.unshift(LOCALE_PATH)
# Extend TestModel as you would another ORM/ODM module
module TestModel
include ValidatesTimeliness::HelperMethods
module TestModelShim
extend ActiveSupport::Concern
include ValidatesTimeliness::AttributeMethods
def self.included(base)
base.extend HookMethods
end
module HookMethods
module ClassMethods
# Hook method for attribute method generation
def define_attribute_methods(attr_names)
super
@ -57,6 +53,10 @@ class Person
define_attribute_methods model_attributes.keys
end
class PersonWithShim < Person
include TestModelShim
end
ActiveRecord::Base.time_zone_aware_attributes = true
ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})
ActiveRecord::Migration.verbose = false
@ -82,7 +82,7 @@ Rspec.configure do |c|
c.include(RspecTagMatchers)
c.before do
Person.reset_callbacks(:validate)
Person.timeliness_validated_attributes = []
PersonWithShim.timeliness_validated_attributes = []
Person._validators.clear
Employee.reset_callbacks(:validate)
Employee.timeliness_validated_attributes = []

View File

@ -1,12 +1,10 @@
module TestModel
extend ActiveSupport::Concern
extend ActiveSupport::Concern
extend ActiveModel::Translation
include ActiveModel::Validations
include ActiveModel::AttributeMethods
included do
extend ActiveModel::Translation
include ActiveModel::Validations
include ActiveModel::AttributeMethods
include DynamicMethods
attribute_method_suffix ""
attribute_method_suffix "="
cattr_accessor :model_attributes
@ -32,18 +30,6 @@ module TestModel
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.keys.map(&:to_s)
method_name = method_id.to_s
send(method_id, *args, &block)
else
super
end
end
end
def initialize(attributes = nil)
@attributes = self.class.model_attributes.inject({}) do |hash, column|
hash[column.to_s] = nil
@ -62,5 +48,15 @@ module TestModel
end
end
def method_missing(method_id, *args, &block)
if !self.class.attribute_methods_generated?
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
super
end
end
end

View File

@ -2,12 +2,24 @@ require 'spec_helper'
describe ValidatesTimeliness::AttributeMethods do
it 'should define _timeliness_raw_value_for instance method' do
Person.instance_methods.should include('_timeliness_raw_value_for')
PersonWithShim.instance_methods.should include('_timeliness_raw_value_for')
end
describe ".timeliness_validated_attributes" do
it 'should return attributes validated with plugin validator' do
PersonWithShim.timeliness_validated_attributes = []
PersonWithShim.validates_date :birth_date
PersonWithShim.validates_time :birth_time
PersonWithShim.validates_datetime :birth_datetime
PersonWithShim.timeliness_validated_attributes.should == [ :birth_date, :birth_time, :birth_datetime ]
end
end
context "attribute write method" do
class PersonWithCache
include TestModel
include TestModelShim
attribute :birth_date, :date
attribute :birth_time, :time
attribute :birth_datetime, :datetime
@ -25,6 +37,7 @@ describe ValidatesTimeliness::AttributeMethods do
context "with plugin parser" do
class PersonWithParser
include TestModel
include TestModelShim
attribute :birth_date, :date
attribute :birth_time, :time
attribute :birth_datetime, :datetime
@ -57,7 +70,7 @@ describe ValidatesTimeliness::AttributeMethods do
context "before_type_cast method" do
it 'should not be defined if ORM does not support it' do
Person.instance_methods(false).should_not include("birth_datetime_before_type_cast")
PersonWithShim.instance_methods(false).should_not include("birth_datetime_before_type_cast")
end
end
end

View File

@ -1,6 +1,6 @@
require 'spec_helper'
describe ValidatesTimeliness::HelperMethods do
describe ValidatesTimeliness, 'HelperMethods' do
it 'should define class validation methods' do
Person.should respond_to(:validates_date)
Person.should respond_to(:validates_time)
@ -18,16 +18,4 @@ describe ValidatesTimeliness::HelperMethods do
r.validates_date :birth_date
r.errors[:birth_date].should_not be_empty
end
describe ".timeliness_validated_attributes" do
it 'should return attributes validated with plugin validator' do
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, :birth_time, :birth_datetime ]
end
end
end

View File

@ -101,6 +101,7 @@ describe ValidatesTimeliness::Validator do
describe ":format option" do
class PersonWithFormatOption
include TestModel
include TestModelShim
attribute :birth_date, :date
attribute :birth_time, :time
attribute :birth_datetime, :datetime