validates_timeliness 3.0.0.beta.3 → 3.0.0.beta.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -5,23 +5,23 @@
5
5
 
6
6
  == Description
7
7
 
8
- Validate dates, times and datetimes for Rails 3.x and ActiveModel.
8
+ Complete validation of dates, times and datetimes for Rails 3.x and ActiveModel.
9
9
 
10
10
  If you a looking for the old version for Rails 2.x go here[http://github.com/adzap/validates_timeliness/tree/v2.3].
11
11
 
12
12
 
13
13
  == Features
14
14
 
15
- * Adds ActiveModel validation for dates, times and datetimes
15
+ * Adds validation for dates, times and datetimes to ActiveModel
16
16
 
17
- * Should work with any ORM using ActiveModel
17
+ * Adds extensions to fix Rails date/time select issues
18
+
19
+ * Includes extensible date/time parser
18
20
 
19
21
  * Supports timezone handling
20
22
 
21
23
  * Supports I18n for the error messages
22
24
 
23
- * Adds before_type_cast method on validated attributes, if ORM supports it.
24
-
25
25
 
26
26
  == Installation
27
27
 
@@ -41,24 +41,29 @@ Then run
41
41
 
42
42
  $ rails generate validates_timeliness:install
43
43
 
44
- This creates configuration initializer and locale files. In the initializer, you there are a number of config options to customize the plugin.
44
+ This creates configuration initializer and locale files. In the initializer, you there are a number of config
45
+ options to customize the plugin.
45
46
 
46
- ValidatesTimeliness.setup do |config|
47
47
 
48
- # Add plugin to supported ORMs (only :active_record for now)
49
- # config.extend_orms = [ :active_record ]
48
+ == Examples
50
49
 
51
- end
50
+ validates_datetime :occurred_at
51
+
52
+ validates_date :date_of_birth :before => lambda { 18.years.ago },
53
+ :before_message => "must be at least 18 years old"
52
54
 
53
- By default the plugin extends ActiveRecord if present. Currently ActiveRecord is the only ORM included for extension. If you wish to extend
54
- another ORM then look at the {wiki page}[http://github.com/adzap/validates_timeliness/wiki/ORM-Support] for more information.
55
+ validates_datetime :finish_time, :after => :start_time # Method symbol
55
56
 
56
- To extend other ORMs is pretty straight forward. It's matter of hooking into a couple of methods, being the attribute method generation and
57
- timezone handling of validated attributes. However, the plugin must support the ActiveModel validations system. If you extend an ORM
58
- successfully, please send me a pull request to add the shim to the plugin or let me know where to find it.
57
+ validates_date :booked_at, :on => :create, :on_or_after => :today # See Restriction Shorthand.
58
+ validates_time :booked_at, :between => ['9.00am', '5:00pm']
59
59
 
60
+ validates_time :breakfast_time, :on_or_after => '6:00am',
61
+ :on_or_after_message => 'must be after opening time',
62
+ :before => :lunchtime,
63
+ :before_message => 'must be before lunch time'
60
64
 
61
- == Usage:
65
+
66
+ == Usage
62
67
 
63
68
  To validate a model with a date, time or datetime attribute you just use the
64
69
  validation method
@@ -90,13 +95,13 @@ Temporal options (or restrictions):
90
95
  :after - Attribute must be after this value to be valid
91
96
  :on_or_after - Attribute must be equal to or after this value to be valid
92
97
  :between - Attribute must be between the values to be valid. Range or Array of 2 values.
93
- Uses :on_or_after and :on_of_before for error messages on lower and upper bounds respectively.
94
98
 
95
99
  Regular validation options:
96
100
  :allow_nil - Allow a nil value to be valid
97
101
  :allow_blank - Allows a nil or empty string value to be valid
98
102
  :if - Execute validation when :if evaluates true
99
103
  :unless - Execute validation when :unless evaluates false
104
+ :on - Specify validation context e.g :save, :create or :update. Default is :save.
100
105
 
101
106
  Special options:
102
107
  :ignore_usec - Ignores microsecond value on datetime restrictions
@@ -116,6 +121,29 @@ values are compared as dates.
116
121
 
117
122
  == Configuration
118
123
 
124
+ === ORM/ODM Support
125
+
126
+ The plugin adds date/time validation to ActiveModel for any ORM/ODM that supports the ActiveModel validations component.
127
+ However, there is an issue with most ORM/ODMs which does not allow 100% date/time validation by default. Specifically, when you
128
+ assign an invalid date/time value to an attribute, most ORM/ODMs will only store a nil value for the attribute. This causes an
129
+ issue for date/time validation, since we need to know that a value was assigned but was invalid. To fix this, we need to cache
130
+ the original invalid value to know that the attribute is not just nil.
131
+
132
+ Each ORM/ODM requires a specific shim to fix it. The plugin includes a shim for ActiveRecord and Mongoid. You can activate them
133
+ like so
134
+
135
+ ValidatesTimeliness.setup do |config|
136
+
137
+ # Extend ORM/ODMs for full support (:active_record, :mongoid).
138
+ config.extend_orms = [ :mongoid ]
139
+
140
+ end
141
+
142
+ By default the plugin extends ActiveRecord if loaded. If you wish to extend another ORM then look at the {wiki page}[http://github.com/adzap/validates_timeliness/wiki/ORM-Support] for more information.
143
+
144
+ It is not required that you use a shim, but you will not catch errors when the attribute value is invalid and evaluated to nil.
145
+
146
+
119
147
  === Error Messages
120
148
 
121
149
  Using the I18n system to define new defaults:
@@ -164,7 +192,7 @@ By default the parser is switched off. To switch it on:
164
192
  See the wiki[http://github.com/adzap/validates_timeliness/wiki/Plugin-Parser] for all the details about the parser.
165
193
 
166
194
 
167
- === Restriction Option Shorthand
195
+ === Restriction Shorthand
168
196
 
169
197
  It is common to restrict an attribute to being on or before the current time or current day.
170
198
  To specify this you need to use a lambda as an option value e.g. <tt>lambda { Time.now }</tt>.
@@ -1,5 +1,5 @@
1
1
  ValidatesTimeliness.setup do |config|
2
- # Add plugin to supported ORMs (only :active_record for now)
2
+ # Extend ORM/ODMs for full support (:active_record, :mongoid).
3
3
  # config.extend_orms = [ :active_record ]
4
4
  #
5
5
  # User the plugin date/time parser which is stricter and extendable
@@ -22,4 +22,16 @@ ValidatesTimeliness.setup do |config|
22
22
  # :now => lambda { Time.now },
23
23
  # :today => lambda { Date.today }
24
24
  # )
25
+ #
26
+ # Add one or more formats making them valid. e.g. add_formats(:date, 'd(st|rd|th) of mmm, yyyy')
27
+ # config.parser.add_formats()
28
+ #
29
+ # Remove one or more formats making them invalid. e.g. remove_formats(:date, 'dd/mm/yyy')
30
+ # config.parser.remove_formats()
31
+ #
32
+ # Change the amiguous year threshold when parsing a 2 digit year
33
+ # config.parser.ambiguous_year_threshold = 30
34
+ #
35
+ # Treat ambiguous dates, such as 01/02/1950, as a Non-US date.
36
+ # config.parser.remove_us_formats
25
37
  end
@@ -2,23 +2,42 @@ module ValidatesTimeliness
2
2
  module AttributeMethods
3
3
  extend ActiveSupport::Concern
4
4
 
5
+ included do
6
+ class_inheritable_accessor :timeliness_validated_attributes
7
+ self.timeliness_validated_attributes = []
8
+ end
9
+
5
10
  module ClassMethods
6
11
 
12
+ public
13
+ # Override in ORM shim
14
+ def timeliness_attribute_timezone_aware?(attr_name)
15
+ false
16
+ end
17
+
18
+ # Override in ORM shim
19
+ def timeliness_attribute_type(attr_name)
20
+ :datetime
21
+ end
22
+
23
+ protected
24
+
7
25
  def define_timeliness_methods(before_type_cast=false)
8
26
  return if timeliness_validated_attributes.blank?
9
- timeliness_validated_attributes.each do |attr_name, type|
10
- define_timeliness_write_method(attr_name, type, timeliness_attribute_timezone_aware?(attr_name))
27
+ timeliness_validated_attributes.each do |attr_name|
28
+ define_timeliness_write_method(attr_name)
11
29
  define_timeliness_before_type_cast_method(attr_name) if before_type_cast
12
30
  end
13
31
  end
14
32
 
15
- protected
33
+ def define_timeliness_write_method(attr_name)
34
+ type = timeliness_attribute_type(attr_name)
35
+ timezone_aware = timeliness_attribute_timezone_aware?(attr_name)
16
36
 
17
- def define_timeliness_write_method(attr_name, type, timezone_aware)
18
37
  method_body, line = <<-EOV, __LINE__ + 1
19
38
  def #{attr_name}=(value)
20
- @attributes_cache ||= {}
21
- @attributes_cache["_#{attr_name}_before_type_cast"] = value
39
+ @timeliness_cache ||= {}
40
+ @timeliness_cache["#{attr_name}"] = value
22
41
  #{ "value = ValidatesTimeliness::Parser.parse(value, :#{type}, :timezone_aware => #{timezone_aware}) if value.is_a?(String)" if ValidatesTimeliness.use_plugin_parser }
23
42
  super
24
43
  end
@@ -34,21 +53,16 @@ module ValidatesTimeliness
34
53
  EOV
35
54
  class_eval(method_body, __FILE__, line)
36
55
  end
37
-
38
- public
39
-
40
- def timeliness_attribute_timezone_aware?(attr_name)
41
- false
42
- end
43
-
44
56
  end
45
57
 
46
58
  module InstanceMethods
47
-
48
59
  def _timeliness_raw_value_for(attr_name)
49
- @attributes_cache && @attributes_cache["_#{attr_name}_before_type_cast"]
60
+ @timeliness_cache && @timeliness_cache[attr_name.to_s]
50
61
  end
51
62
 
63
+ def _clear_timeliness_cache
64
+ @timeliness_cache = {}
65
+ end
52
66
  end
53
67
 
54
68
  end
@@ -3,10 +3,10 @@ module ValidatesTimeliness
3
3
  module DateTimeSelect
4
4
  extend ActiveSupport::Concern
5
5
 
6
- # Intercepts the date and time select helpers to reuse the values from the
6
+ # Intercepts the date and time select helpers to reuse the values from
7
7
  # the params rather than the parsed value. This allows invalid date/time
8
8
  # values to be redisplayed instead of blanks to aid correction by the user.
9
- # Its a minor usability improvement which is rarely an issue for the user.
9
+ # It's a minor usability improvement which is rarely an issue for the user.
10
10
 
11
11
  included do
12
12
  alias_method_chain :datetime_selector, :timeliness
@@ -1,15 +1,7 @@
1
- module ValidatesTimeliness
2
- module HelperMethods
3
- extend ActiveSupport::Concern
1
+ module ActiveModel
2
+ module Validations
4
3
 
5
- included do
6
- include ValidationMethods
7
- extend ValidationMethods
8
- class_inheritable_hash :timeliness_validated_attributes
9
- self.timeliness_validated_attributes = {}
10
- end
11
-
12
- module ValidationMethods
4
+ module HelperMethods
13
5
  def validates_date(*attr_names)
14
6
  timeliness_validation_for attr_names, :date
15
7
  end
@@ -23,17 +15,14 @@ module ValidatesTimeliness
23
15
  end
24
16
 
25
17
  def timeliness_validation_for(attr_names, type)
26
- options = _merge_attributes(attr_names)
27
- options[:type] = type
28
- attributes = attr_names.inject({}) {|validated, attr_name|
29
- attr_name = attr_name.to_s
30
- validated[attr_name] = type
31
- validated
32
- }
33
- self.timeliness_validated_attributes = attributes
34
- validates_with Validator, options
18
+ options = _merge_attributes(attr_names).merge(:type => type)
19
+ if respond_to?(:timeliness_validated_attributes)
20
+ self.timeliness_validated_attributes ||= []
21
+ self.timeliness_validated_attributes += (attr_names - self.timeliness_validated_attributes)
22
+ end
23
+ validates_with ValidatesTimeliness::Validator, options
35
24
  end
36
-
37
25
  end
26
+
38
27
  end
39
28
  end
@@ -1,14 +1,37 @@
1
- class ActiveRecord::Base
2
- include ValidatesTimeliness::HelperMethods
3
- include ValidatesTimeliness::AttributeMethods
1
+ module ValidatesTimeliness
2
+ module ORM
3
+ module ActiveRecord
4
+ extend ActiveSupport::Concern
4
5
 
5
- def self.define_attribute_methods
6
- super
7
- # Define write method and before_type_cast method
8
- define_timeliness_methods(true)
9
- end
6
+ module ClassMethods
7
+ def define_attribute_methods
8
+ super
9
+ # Define write method and before_type_cast method
10
+ define_timeliness_methods(true)
11
+ end
12
+
13
+ def timeliness_attribute_timezone_aware?(attr_name)
14
+ attr_name = attr_name.to_s
15
+ create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
16
+ end
17
+
18
+ def timeliness_attribute_type(attr_name)
19
+ columns_hash[attr_name.to_s].type
20
+ end
21
+ end
10
22
 
11
- def self.timeliness_attribute_timezone_aware?(attr_name)
12
- create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
23
+ module InstanceMethods
24
+ def reload(*args)
25
+ _clear_timeliness_cache
26
+ super
27
+ end
28
+ end
29
+
30
+ end
13
31
  end
14
32
  end
33
+
34
+ class ActiveRecord::Base
35
+ include ValidatesTimeliness::AttributeMethods
36
+ include ValidatesTimeliness::ORM::ActiveRecord
37
+ end
@@ -7,29 +7,58 @@ module ValidatesTimeliness
7
7
  # field value in Mongoid. Parser will return nil rather than error.
8
8
 
9
9
  module ClassMethods
10
+ # Mongoid has no bulk attribute method definition hook. It defines
11
+ # them with each field definition. So we likewise define them after
12
+ # each validation is defined.
13
+ #
10
14
  def timeliness_validation_for(attr_names, type)
11
15
  super
12
- attr_names.each { |attr_name| define_timeliness_write_method(attr_name, type, false) }
16
+ attr_names.each { |attr_name| define_timeliness_write_method(attr_name) }
13
17
  end
14
18
 
15
- def define_timeliness_write_method(attr_name, type, timezone_aware)
19
+ def define_timeliness_write_method(attr_name)
20
+ type = timeliness_attribute_type(attr_name)
16
21
  method_body, line = <<-EOV, __LINE__ + 1
17
22
  def #{attr_name}=(value)
18
- @attributes_cache ||= {}
19
- @attributes_cache["_#{attr_name}_before_type_cast"] = value
23
+ @timeliness_cache ||= {}
24
+ @timeliness_cache["#{attr_name}"] = value
20
25
  #{ "value = ValidatesTimeliness::Parser.parse(value, :#{type}) if value.is_a?(String)" if ValidatesTimeliness.use_plugin_parser }
21
26
  write_attribute(:#{attr_name}, value)
22
27
  end
23
28
  EOV
24
29
  class_eval(method_body, __FILE__, line)
25
30
  end
31
+
32
+ def timeliness_attribute_type(attr_name)
33
+ {
34
+ Date => :date,
35
+ Time => :datetime,
36
+ DateTime => :datetime
37
+ }[fields[attr_name.to_s].type] || :datetime
38
+ end
26
39
  end
40
+
27
41
  end
28
42
  end
29
43
  end
30
44
 
31
45
  module Mongoid::Document
32
- include ValidatesTimeliness::HelperMethods
33
- include ValidatesTimeliness::AttributeMethods
34
- include ValidatesTimeliness::ORM::Mongoid
46
+ # Due to how Mongoid misuses ActiveSupport::Concern,
47
+ # the only way to override a core component method is
48
+ # using an append_features hook.
49
+ #
50
+ module TimelinessConcern
51
+ def append_features(base)
52
+ super
53
+ base.send :include, ValidatesTimeliness::AttributeMethods
54
+ base.send :include, ValidatesTimeliness::ORM::Mongoid
55
+ end
56
+ end
57
+ extend TimelinessConcern
58
+
59
+ def reload_with_timeliness
60
+ _clear_timeliness_cache
61
+ reload_without_timeliness
62
+ end
63
+ alias_method_chain :reload, :timeliness
35
64
  end
@@ -23,14 +23,6 @@ module ValidatesTimeliness
23
23
  cattr_accessor :ambiguous_year_threshold
24
24
  self.ambiguous_year_threshold = 30
25
25
 
26
- # Set the dummy date part for a time type value. Should be an array of 3 values
27
- # being year, month and day in that order.
28
- #
29
- # Default: [ 2000, 1, 1 ] same as ActiveRecord
30
- #
31
- cattr_accessor :dummy_date_for_time_type
32
- self.dummy_date_for_time_type = [ 2000, 1, 1 ]
33
-
34
26
  # Format tokens:
35
27
  # y = year
36
28
  # m = month
@@ -14,6 +14,12 @@ module ValidatesTimeliness
14
14
  :on_or_after => :>=,
15
15
  }.freeze
16
16
 
17
+ DEFAULT_ERROR_VALUE_FORMATS = {
18
+ :date => '%Y-%m-%d',
19
+ :time => '%H:%M:%S',
20
+ :datetime => '%Y-%m-%d %H:%M:%S'
21
+ }.freeze
22
+
17
23
  def self.kind
18
24
  :timeliness
19
25
  end
@@ -31,10 +37,10 @@ module ValidatesTimeliness
31
37
  end
32
38
 
33
39
  def validate_each(record, attr_name, value)
34
- raw_value = record._timeliness_raw_value_for(attr_name) || value
40
+ raw_value = attribute_raw_value(record, attr_name) || value
35
41
  return if (@allow_nil && raw_value.nil?) || (@allow_blank && raw_value.blank?)
36
42
 
37
- @timezone_aware = record.class.timeliness_attribute_timezone_aware?(attr_name)
43
+ @timezone_aware = timezone_aware?(record, attr_name)
38
44
  value = parse(raw_value) if value.is_a?(String) || options[:format]
39
45
  value = type_cast_value(value, @type)
40
46
 
@@ -56,10 +62,21 @@ module ValidatesTimeliness
56
62
  end
57
63
 
58
64
  def format_error_value(value)
59
- format = I18n.t(@type, :scope => 'validates_timeliness.error_value_formats')
65
+ format = I18n.t(@type, :default => DEFAULT_ERROR_VALUE_FORMATS[@type], :scope => 'validates_timeliness.error_value_formats')
60
66
  value.strftime(format)
61
67
  end
62
68
 
69
+ def attribute_raw_value(record, attr_name)
70
+ if record.respond_to?(:_timeliness_raw_value_for)
71
+ record._timeliness_raw_value_for(attr_name)
72
+ end
73
+ end
74
+
75
+ def timezone_aware?(record, attr_name)
76
+ record.class.respond_to?(:timeliness_attribute_timezone_aware?) &&
77
+ record.class.timeliness_attribute_timezone_aware?(attr_name)
78
+ end
79
+
63
80
  end
64
81
  end
65
82
 
@@ -1,3 +1,3 @@
1
1
  module ValidatesTimeliness
2
- VERSION = '3.0.0.beta.3'
2
+ VERSION = '3.0.0.beta.4'
3
3
  end
@@ -12,7 +12,7 @@ module ValidatesTimeliness
12
12
  autoload :Parser, 'validates_timeliness/parser'
13
13
  autoload :VERSION, 'validates_timeliness/version'
14
14
 
15
- # Add plugin to supported ORMs (only :active_record for now)
15
+ # Extend ORM/ODMs for full support (:active_record, :mongoid).
16
16
  mattr_accessor :extend_orms
17
17
  @@extend_orms = [ defined?(ActiveRecord) && :active_record ].compact
18
18
 
@@ -39,6 +39,10 @@ module ValidatesTimeliness
39
39
  :today => lambda { Date.today }
40
40
  }
41
41
 
42
+ def self.parser
43
+ Parser
44
+ end
45
+
42
46
  # Setup method for plugin configuration
43
47
  def self.setup
44
48
  yield self
data/spec/spec_helper.rb CHANGED
@@ -24,15 +24,11 @@ LOCALE_PATH = File.expand_path(File.dirname(__FILE__) + '/../lib/generators/vali
24
24
  I18n.load_path.unshift(LOCALE_PATH)
25
25
 
26
26
  # Extend TestModel as you would another ORM/ODM module
27
- module TestModel
28
- include ValidatesTimeliness::HelperMethods
27
+ module TestModelShim
28
+ extend ActiveSupport::Concern
29
29
  include ValidatesTimeliness::AttributeMethods
30
30
 
31
- def self.included(base)
32
- base.extend HookMethods
33
- end
34
-
35
- module HookMethods
31
+ module ClassMethods
36
32
  # Hook method for attribute method generation
37
33
  def define_attribute_methods(attr_names)
38
34
  super
@@ -48,11 +44,17 @@ end
48
44
 
49
45
  class Person
50
46
  include TestModel
51
- self.model_attributes = :birth_date, :birth_time, :birth_datetime
47
+ attribute :birth_date, :date
48
+ attribute :birth_time, :time
49
+ attribute :birth_datetime, :datetime
52
50
  validates_date :birth_date
53
51
  validates_time :birth_time
54
52
  validates_datetime :birth_datetime
55
- define_attribute_methods model_attributes
53
+ define_attribute_methods model_attributes.keys
54
+ end
55
+
56
+ class PersonWithShim < Person
57
+ include TestModelShim
56
58
  end
57
59
 
58
60
  ActiveRecord::Base.time_zone_aware_attributes = true
@@ -62,8 +64,8 @@ ActiveRecord::Schema.define(:version => 1) do
62
64
  create_table :employees, :force => true do |t|
63
65
  t.string :first_name
64
66
  t.string :last_name
65
- t.datetime :birth_date
66
- t.datetime :birth_time
67
+ t.date :birth_date
68
+ t.time :birth_time
67
69
  t.datetime :birth_datetime
68
70
  end
69
71
  end
@@ -80,10 +82,10 @@ Rspec.configure do |c|
80
82
  c.include(RspecTagMatchers)
81
83
  c.before do
82
84
  Person.reset_callbacks(:validate)
83
- Person.timeliness_validated_attributes = {}
85
+ PersonWithShim.timeliness_validated_attributes = []
84
86
  Person._validators.clear
85
87
  Employee.reset_callbacks(:validate)
86
- Employee.timeliness_validated_attributes = {}
88
+ Employee.timeliness_validated_attributes = []
87
89
  Employee._validators.clear
88
90
  end
89
91
  end
data/spec/test_model.rb CHANGED
@@ -1,41 +1,32 @@
1
1
  module TestModel
2
- extend ActiveSupport::Concern
2
+ extend ActiveSupport::Concern
3
+ extend ActiveModel::Translation
4
+ include ActiveModel::Validations
5
+ include ActiveModel::AttributeMethods
3
6
 
4
7
  included do
5
- extend ActiveModel::Translation
6
- include ActiveModel::Validations
7
- include ActiveModel::AttributeMethods
8
- include DynamicMethods
9
-
10
8
  attribute_method_suffix ""
11
9
  attribute_method_suffix "="
12
10
  cattr_accessor :model_attributes
13
11
  end
14
12
 
15
13
  module ClassMethods
14
+ def attribute(name, type)
15
+ self.model_attributes ||= {}
16
+ self.model_attributes[name] = type
17
+ end
18
+
16
19
  def define_method_attribute=(attr_name)
17
- generated_attribute_methods.module_eval("def #{attr_name}=(new_value); @attributes['#{attr_name}']=self.class.type_cast(new_value); end", __FILE__, __LINE__)
20
+ generated_attribute_methods.module_eval("def #{attr_name}=(new_value); @attributes['#{attr_name}']=self.class.type_cast('#{attr_name}', new_value); end", __FILE__, __LINE__)
18
21
  end
19
22
 
20
23
  def define_method_attribute(attr_name)
21
24
  generated_attribute_methods.module_eval("def #{attr_name}; @attributes['#{attr_name}']; end", __FILE__, __LINE__)
22
25
  end
23
26
 
24
- def type_cast(value)
27
+ def type_cast(attr_name, value)
25
28
  return value unless value.is_a?(String)
26
- value.to_time rescue nil
27
- end
28
- end
29
-
30
- module DynamicMethods
31
- def method_missing(method_id, *args, &block)
32
- if !self.class.attribute_methods_generated?
33
- self.class.define_attribute_methods self.class.model_attributes.map(&:to_s)
34
- method_name = method_id.to_s
35
- send(method_id, *args, &block)
36
- else
37
- super
38
- end
29
+ value.send("to_#{model_attributes[attr_name.to_sym]}") rescue nil
39
30
  end
40
31
  end
41
32
 
@@ -57,5 +48,15 @@ module TestModel
57
48
  end
58
49
  end
59
50
 
51
+ def method_missing(method_id, *args, &block)
52
+ if !self.class.attribute_methods_generated?
53
+ self.class.define_attribute_methods self.class.model_attributes.keys.map(&:to_s)
54
+ method_name = method_id.to_s
55
+ send(method_id, *args, &block)
56
+ else
57
+ super
58
+ end
59
+ end
60
+
60
61
  end
61
62
 
@@ -2,13 +2,27 @@ require 'spec_helper'
2
2
 
3
3
  describe ValidatesTimeliness::AttributeMethods do
4
4
  it 'should define _timeliness_raw_value_for instance method' do
5
- Person.instance_methods.should include('_timeliness_raw_value_for')
5
+ PersonWithShim.instance_methods.should include('_timeliness_raw_value_for')
6
+ end
7
+
8
+ describe ".timeliness_validated_attributes" do
9
+ it 'should return attributes validated with plugin validator' do
10
+ PersonWithShim.timeliness_validated_attributes = []
11
+ PersonWithShim.validates_date :birth_date
12
+ PersonWithShim.validates_time :birth_time
13
+ PersonWithShim.validates_datetime :birth_datetime
14
+
15
+ PersonWithShim.timeliness_validated_attributes.should == [ :birth_date, :birth_time, :birth_datetime ]
16
+ end
6
17
  end
7
18
 
8
19
  context "attribute write method" do
9
20
  class PersonWithCache
10
21
  include TestModel
11
- self.model_attributes = :birth_date, :birth_time, :birth_datetime
22
+ include TestModelShim
23
+ attribute :birth_date, :date
24
+ attribute :birth_time, :time
25
+ attribute :birth_datetime, :datetime
12
26
  validates_date :birth_date
13
27
  validates_time :birth_time
14
28
  validates_datetime :birth_datetime
@@ -23,7 +37,10 @@ describe ValidatesTimeliness::AttributeMethods do
23
37
  context "with plugin parser" do
24
38
  class PersonWithParser
25
39
  include TestModel
26
- self.model_attributes = :birth_date, :birth_time, :birth_datetime
40
+ include TestModelShim
41
+ attribute :birth_date, :date
42
+ attribute :birth_time, :time
43
+ attribute :birth_datetime, :datetime
27
44
  validates_date :birth_date
28
45
  validates_time :birth_time
29
46
  validates_datetime :birth_datetime
@@ -53,7 +70,7 @@ describe ValidatesTimeliness::AttributeMethods do
53
70
 
54
71
  context "before_type_cast method" do
55
72
  it 'should not be defined if ORM does not support it' do
56
- Person.instance_methods(false).should_not include("birth_datetime_before_type_cast")
73
+ PersonWithShim.instance_methods(false).should_not include("birth_datetime_before_type_cast")
57
74
  end
58
75
  end
59
76
  end
@@ -1,16 +1,16 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ValidatesTimeliness::HelperMethods do
4
- it 'should define class validation methods on extended classes' do
5
- ActiveRecord::Base.should respond_to(:validates_date)
6
- ActiveRecord::Base.should respond_to(:validates_time)
7
- ActiveRecord::Base.should respond_to(:validates_datetime)
3
+ describe ValidatesTimeliness, 'HelperMethods' do
4
+ it 'should define class validation methods' do
5
+ Person.should respond_to(:validates_date)
6
+ Person.should respond_to(:validates_time)
7
+ Person.should respond_to(:validates_datetime)
8
8
  end
9
9
 
10
- it 'should define instance validation methods on extended classes' do
11
- ActiveRecord::Base.instance_methods.should include('validates_date')
12
- ActiveRecord::Base.instance_methods.should include('validates_time')
13
- ActiveRecord::Base.instance_methods.should include('validates_datetime')
10
+ it 'should define instance validation methods' do
11
+ Person.instance_methods.should include('validates_date')
12
+ Person.instance_methods.should include('validates_time')
13
+ Person.instance_methods.should include('validates_datetime')
14
14
  end
15
15
 
16
16
  it 'should validate instance when validation method called' do
@@ -18,19 +18,4 @@ describe ValidatesTimeliness::HelperMethods do
18
18
  r.validates_date :birth_date
19
19
  r.errors[:birth_date].should_not be_empty
20
20
  end
21
-
22
- describe ".timeliness_validated_attributes" do
23
- it 'should return attributes validated with plugin validator' do
24
- Person.timeliness_validated_attributes = {}
25
- Person.validates_date :birth_date
26
- Person.validates_time :birth_time
27
- Person.validates_datetime :birth_datetime
28
-
29
- Person.timeliness_validated_attributes.should == {
30
- "birth_date" => :date,
31
- "birth_time" => :time,
32
- "birth_datetime" => :datetime
33
- }
34
- end
35
- end
36
21
  end
@@ -1,8 +1,23 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe ValidatesTimeliness, 'ActiveRecord' do
4
- it 'should define _timeliness_raw_value_for instance method' do
5
- Employee.instance_methods.should include('_timeliness_raw_value_for')
4
+
5
+ context "validation methods" do
6
+ it 'should be defined for the class' do
7
+ ActiveRecord::Base.should respond_to(:validates_date)
8
+ ActiveRecord::Base.should respond_to(:validates_time)
9
+ ActiveRecord::Base.should respond_to(:validates_datetime)
10
+ end
11
+
12
+ it 'should defines for the instance' do
13
+ ActiveRecord::Base.instance_methods.should include('validates_date')
14
+ ActiveRecord::Base.instance_methods.should include('validates_time')
15
+ ActiveRecord::Base.instance_methods.should include('validates_datetime')
16
+ end
17
+ end
18
+
19
+ it 'should determine type for attribute' do
20
+ Employee.timeliness_attribute_type(:birth_date).should == :date
6
21
  end
7
22
 
8
23
  context "attribute write method" do
@@ -36,16 +51,26 @@ describe ValidatesTimeliness, 'ActiveRecord' do
36
51
 
37
52
  it 'should parse string as current timezone' do
38
53
  r = EmployeeWithParser.new
39
- r.birth_datetime = '2010-01-01 12:00'
40
- r.birth_datetime.zone == Time.zone.name
54
+ r.birth_datetime = '2010-06-01 12:00'
55
+ r.birth_datetime.utc_offset.should == 10.hours
41
56
  end
42
57
 
43
58
  after :all do
59
+ Time.zone = 'Australia/Melbourne'
44
60
  ValidatesTimeliness.use_plugin_parser = false
45
61
  end
46
62
  end
47
63
  end
48
64
 
65
+ context "cached value" do
66
+ it 'should be cleared on reload' do
67
+ r = Employee.create!
68
+ r.birth_date = '2010-01-01'
69
+ r.reload
70
+ r._timeliness_raw_value_for(:birth_date).should be_nil
71
+ end
72
+ end
73
+
49
74
  context "before_type_cast method" do
50
75
  it 'should be defined on class if ORM supports it' do
51
76
  Employee.instance_methods(false).should include("birth_datetime_before_type_cast")
@@ -10,22 +10,36 @@ Mongoid.configure do |config|
10
10
  config.persist_in_safe_mode = false
11
11
  end
12
12
 
13
+ class Article
14
+ ::ValidatesTimeliness.use_plugin_parser = true
15
+ include Mongoid::Document
16
+ field :publish_date, :type => Date
17
+ field :publish_time, :type => Time
18
+ field :publish_datetime, :type => DateTime
19
+ validates_date :publish_date, :allow_nil => true
20
+ validates_time :publish_time, :allow_nil => true
21
+ validates_datetime :publish_datetime, :allow_nil => true
22
+ ::ValidatesTimeliness.use_plugin_parser = false
23
+ end
24
+
13
25
  describe ValidatesTimeliness, 'Mongoid' do
14
26
 
15
- class Article
16
- ::ValidatesTimeliness.use_plugin_parser = true
17
- include Mongoid::Document
18
- field :publish_date, :type => Date
19
- field :publish_time, :type => Time
20
- field :publish_datetime, :type => DateTime
21
- validates_date :publish_date
22
- validates_time :publish_time
23
- validates_datetime :publish_datetime
24
- ::ValidatesTimeliness.use_plugin_parser = false
27
+ context "validation methods" do
28
+ it 'should be defined on the class' do
29
+ Article.should respond_to(:validates_date)
30
+ Article.should respond_to(:validates_time)
31
+ Article.should respond_to(:validates_datetime)
32
+ end
33
+
34
+ it 'should be defined on the instance' do
35
+ Article.instance_methods.should include('validates_date')
36
+ Article.instance_methods.should include('validates_time')
37
+ Article.instance_methods.should include('validates_datetime')
38
+ end
25
39
  end
26
40
 
27
- it 'should define _timeliness_raw_value_for instance method' do
28
- Article.instance_methods.should include('_timeliness_raw_value_for')
41
+ it 'should determine type for attribute' do
42
+ Article.timeliness_attribute_type(:publish_date).should == :date
29
43
  end
30
44
 
31
45
  context "attribute write method" do
@@ -58,6 +72,15 @@ describe ValidatesTimeliness, 'Mongoid' do
58
72
  end
59
73
  end
60
74
 
75
+ context "cached value" do
76
+ it 'should be cleared on reload' do
77
+ r = Article.create!
78
+ r.publish_date = '2010-01-01'
79
+ r.reload
80
+ r._timeliness_raw_value_for(:publish_date).should be_nil
81
+ end
82
+ end
83
+
61
84
  context "before_type_cast method" do
62
85
  it 'should not be defined if ORM does not support it' do
63
86
  Article.instance_methods(false).should_not include("birth_datetime_before_type_cast")
@@ -177,22 +177,6 @@ describe ValidatesTimeliness::Parser do
177
177
  ValidatesTimeliness::Parser.ambiguous_year_threshold = default
178
178
  end
179
179
  end
180
-
181
- context "with custom dummy date values" do
182
- before(:all) do
183
- @old_dummy_date = ValidatesTimeliness.dummy_date_for_time_type
184
- ValidatesTimeliness.dummy_date_for_time_type = [2009,1,1]
185
- end
186
-
187
- it "should return time array with custom dummy date" do
188
- time_array = formats._parse('12:13:14', :time, :strict => true)
189
- time_array.should == [2009,1,1,12,13,14,0]
190
- end
191
-
192
- after(:all) do
193
- ValidatesTimeliness.dummy_date_for_time_type = @old_dummy_date
194
- end
195
- end
196
180
  end
197
181
 
198
182
  describe "parse" do
@@ -101,7 +101,10 @@ describe ValidatesTimeliness::Validator do
101
101
  describe ":format option" do
102
102
  class PersonWithFormatOption
103
103
  include TestModel
104
- self.model_attributes = :birth_date, :birth_time, :birth_datetime
104
+ include TestModelShim
105
+ attribute :birth_date, :date
106
+ attribute :birth_time, :time
107
+ attribute :birth_datetime, :datetime
105
108
  validates_date :birth_date, :format => 'dd-mm-yyyy'
106
109
  end
107
110
 
@@ -169,6 +172,21 @@ describe ValidatesTimeliness::Validator do
169
172
  validator.format_error_value(Time.mktime(2010,1,1,12,34,56)).should == '2010-01-01 12:34:56'
170
173
  end
171
174
  end
175
+
176
+ describe "with missing translation" do
177
+ before :all do
178
+ I18n.locale = :es
179
+ end
180
+
181
+ it 'should use the default format for the type' do
182
+ validator = ValidatesTimeliness::Validator.new(:attributes => [:birth_date], :type => :date)
183
+ validator.format_error_value(Date.new(2010,1,1)).should == '2010-01-01'
184
+ end
185
+
186
+ after :all do
187
+ I18n.locale = :en
188
+ end
189
+ end
172
190
  end
173
191
 
174
192
  context "custom error message" do
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{validates_timeliness}
5
- s.version = "3.0.0.beta.3"
5
+ s.version = "3.0.0.beta.4"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Adam Meehan"]
9
- s.date = %q{2010-09-22}
9
+ s.date = %q{2010-09-29}
10
10
  s.description = %q{Date and time validation plugin for Rails which allows custom formats}
11
11
  s.email = %q{adam.meehan@gmail.com}
12
12
  s.extra_rdoc_files = ["README.rdoc", "LICENSE", "CHANGELOG"]
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: validates_timeliness
3
3
  version: !ruby/object:Gem::Version
4
- hash: 62196421
4
+ hash: 62196427
5
5
  prerelease: true
6
6
  segments:
7
7
  - 3
8
8
  - 0
9
9
  - 0
10
10
  - beta
11
- - 3
12
- version: 3.0.0.beta.3
11
+ - 4
12
+ version: 3.0.0.beta.4
13
13
  platform: ruby
14
14
  authors:
15
15
  - Adam Meehan
@@ -17,7 +17,7 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2010-09-22 00:00:00 +10:00
20
+ date: 2010-09-29 00:00:00 +10:00
21
21
  default_executable:
22
22
  dependencies: []
23
23