validates_timeliness 3.0.15 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +21 -0
  3. data/CHANGELOG.rdoc +0 -5
  4. data/README.rdoc +5 -5
  5. data/gemfiles/rails_4_0.gemfile +19 -0
  6. data/gemfiles/rails_4_1.gemfile +19 -0
  7. data/gemfiles/rails_4_2.gemfile +19 -0
  8. data/lib/generators/validates_timeliness/templates/validates_timeliness.rb +2 -2
  9. data/lib/validates_timeliness.rb +1 -1
  10. data/lib/validates_timeliness/attribute_methods.rb +36 -36
  11. data/lib/validates_timeliness/extensions.rb +3 -4
  12. data/lib/validates_timeliness/extensions/date_time_select.rb +2 -9
  13. data/lib/validates_timeliness/extensions/multiparameter_handler.rb +59 -65
  14. data/lib/validates_timeliness/helper_methods.rb +8 -2
  15. data/lib/validates_timeliness/orm/active_record.rb +59 -13
  16. data/lib/validates_timeliness/validator.rb +13 -5
  17. data/lib/validates_timeliness/version.rb +1 -1
  18. data/spec/spec_helper.rb +3 -2
  19. data/spec/support/model_helpers.rb +4 -4
  20. data/spec/support/tag_matcher.rb +35 -0
  21. data/spec/support/test_model.rb +0 -1
  22. data/spec/validates_timeliness/attribute_methods_spec.rb +10 -10
  23. data/spec/validates_timeliness/conversion_spec.rb +41 -41
  24. data/spec/validates_timeliness/extensions/date_time_select_spec.rb +5 -4
  25. data/spec/validates_timeliness/extensions/multiparameter_handler_spec.rb +8 -7
  26. data/spec/validates_timeliness/helper_methods_spec.rb +8 -8
  27. data/spec/validates_timeliness/orm/active_record_spec.rb +37 -37
  28. data/spec/validates_timeliness/validator/after_spec.rb +3 -3
  29. data/spec/validates_timeliness/validator/before_spec.rb +3 -3
  30. data/spec/validates_timeliness/validator/is_at_spec.rb +4 -4
  31. data/spec/validates_timeliness/validator/on_or_after_spec.rb +3 -3
  32. data/spec/validates_timeliness/validator/on_or_before_spec.rb +3 -3
  33. data/spec/validates_timeliness/validator_spec.rb +26 -26
  34. data/spec/validates_timeliness_spec.rb +8 -8
  35. metadata +7 -11
  36. data/gemfiles/mongoid_2_1.gemfile +0 -16
  37. data/gemfiles/mongoid_2_2.gemfile +0 -16
  38. data/gemfiles/mongoid_2_3.gemfile +0 -16
  39. data/gemfiles/mongoid_2_4.gemfile +0 -16
  40. data/gemfiles/rails_3_0.gemfile +0 -15
  41. data/gemfiles/rails_3_1.gemfile +0 -15
  42. data/gemfiles/rails_3_2.gemfile +0 -15
  43. data/lib/validates_timeliness/orm/mongoid.rb +0 -49
  44. data/spec/validates_timeliness/orm/mongoid_spec.rb +0 -223
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a60055680e49760bf31b2a820e30d35c125701e5
4
- data.tar.gz: 846e163f120d01337b2e55356cc1a01fca513ea7
3
+ metadata.gz: c730e7683c67e1fce0a8a2e0b6dcba331b812e36
4
+ data.tar.gz: 904f0cfe44ca9d764e051d1d656cf34cbb6a5dba
5
5
  SHA512:
6
- metadata.gz: 46b5e4bfba055d99c8e6ba8dc3e122bc8e3dc872efb35ee957802f7b3f827b92ec5efd19d64f2054f2597656b91409432a7dcc731b48090cd91603e6323e5fb4
7
- data.tar.gz: 93ed8ea91b878022a29aee4c5ad0b0586e4c1cff6cc531168c7bfe2aab4e8a232b26c4dfe683bdc99c7d3897dc97b4d91f2c4af0d5e4dffdf90f6362aea3f581
6
+ metadata.gz: fd5326ac226351463fab7d317fadfb96bab6858b0231cfa1ee2b3fb49b974e6aeacf9cff498a22088132cd675d823054db6f0b622ea6db034d0884f0cdfab1e1
7
+ data.tar.gz: fe62be44d7d398c17d5762f91335e11187afdd8e2cb3f15e69de95feb8b764b03773f038c009ca72e299a793378ee680e8ea7ebcb62aaec341ef17b02fa1f747
data/.travis.yml ADDED
@@ -0,0 +1,21 @@
1
+ language: ruby
2
+ cache: bundler
3
+
4
+ gemfile:
5
+ - gemfiles/rails_4_0.gemfile
6
+ - gemfiles/rails_4_1.gemfile
7
+ - gemfiles/rails_4_2.gemfile
8
+
9
+ rvm:
10
+ - "2.0.0"
11
+ - "2.2.0"
12
+ - "2.3.0"
13
+
14
+ script: 'bundle exec rake'
15
+
16
+ notifications:
17
+ email:
18
+ recipients:
19
+ - adam.meehan@gmail.com
20
+ on_failure: change
21
+ on_success: never
data/CHANGELOG.rdoc CHANGED
@@ -1,8 +1,3 @@
1
- = 3.0.15 [2015-12-29]
2
- * Fixes mongoid 3 support and removes mongoid 2 support(johnnyshields)
3
- * Some documentation/comments tidying
4
- * Some general tidying up
5
-
6
1
  = 3.0.14 [2012-08-23]
7
2
  * Fix for using validates :timeliness => {} form to correctly add attributes to timeliness validated attributes.
8
3
 
data/README.rdoc CHANGED
@@ -5,9 +5,9 @@
5
5
 
6
6
  == Description
7
7
 
8
- Complete validation of dates, times and datetimes for Rails 3.x and ActiveModel.
8
+ Complete validation of dates, times and datetimes for Rails 4.x and ActiveModel.
9
9
 
10
- If you a looking for the old version for Rails 2.x go here[http://github.com/adzap/validates_timeliness/tree/v2.3].
10
+ If you a looking for the old version for Rails 3.x go here[http://github.com/adzap/validates_timeliness/tree/v3.x].
11
11
 
12
12
 
13
13
  == Features
@@ -30,7 +30,7 @@ If you a looking for the old version for Rails 2.x go here[http://github.com/adz
30
30
  == Installation
31
31
 
32
32
  # in Gemfile
33
- gem 'validates_timeliness', '~> 3.0'
33
+ gem 'validates_timeliness', '~> 4.0'
34
34
 
35
35
  # Run bundler
36
36
  $ bundle install
@@ -137,8 +137,8 @@ like so
137
137
 
138
138
  ValidatesTimeliness.setup do |config|
139
139
 
140
- # Extend ORM/ODMs for full support (:active_record, :mongoid).
141
- config.extend_orms = [ :mongoid ]
140
+ # Extend ORM/ODMs for full support (:active_record).
141
+ config.extend_orms = [ :active_record ]
142
142
 
143
143
  end
144
144
 
@@ -0,0 +1,19 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 4.0.0"
6
+ gem "rspec", "~> 3.0.0"
7
+ gem "rspec-rails", "~> 3.0.0"
8
+ gem "timecop"
9
+ gem "rspec_tag_matchers"
10
+ gem "byebug"
11
+ gem "appraisal"
12
+ gem "sqlite3"
13
+ gem "nokogiri", "1.6.7"
14
+
15
+ group :active_record do
16
+ gem "sqlite3-ruby", :require => "sqlite3"
17
+ end
18
+
19
+ gemspec :path => "../"
@@ -0,0 +1,19 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 4.1.0"
6
+ gem "rspec", "~> 3.0.0"
7
+ gem "rspec-rails", "~> 3.0.0"
8
+ gem "timecop"
9
+ gem "rspec_tag_matchers"
10
+ gem "byebug"
11
+ gem "appraisal"
12
+ gem "sqlite3"
13
+ gem "nokogiri", "1.6.7"
14
+
15
+ group :active_record do
16
+ gem "sqlite3-ruby", :require => "sqlite3"
17
+ end
18
+
19
+ gemspec :path => "../"
@@ -0,0 +1,19 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 4.2.0"
6
+ gem "rspec", "~> 3.0.0"
7
+ gem "rspec-rails", "~> 3.0.0"
8
+ gem "timecop"
9
+ gem "rspec_tag_matchers"
10
+ gem "byebug"
11
+ gem "appraisal"
12
+ gem "sqlite3"
13
+ gem "nokogiri", "1.6.7"
14
+
15
+ group :active_record do
16
+ gem "sqlite3-ruby", :require => "sqlite3"
17
+ end
18
+
19
+ gemspec :path => "../"
@@ -1,6 +1,6 @@
1
1
  ValidatesTimeliness.setup do |config|
2
- # Extend ORM/ODMs for full support (:active_record, :mongoid).
3
- # config.extend_orms = [ :active_record ]
2
+ # Extend ORM/ODMs for full support (:active_record included).
3
+ config.extend_orms = [ :active_record ]
4
4
  #
5
5
  # Default timezone
6
6
  # config.default_timezone = :utc
@@ -28,7 +28,7 @@ module ValidatesTimeliness
28
28
  attr_accessor :extend_orms, :ignore_restriction_errors, :restriction_shorthand_symbols, :use_plugin_parser
29
29
  end
30
30
 
31
- # Extend ORM/ODMs for full support (:active_record, :mongoid).
31
+ # Extend ORM/ODMs for full support (:active_record).
32
32
  self.extend_orms = []
33
33
 
34
34
  # Ignore errors when restriction options are evaluated
@@ -20,13 +20,6 @@ module ValidatesTimeliness
20
20
  :datetime
21
21
  end
22
22
 
23
- def undefine_attribute_methods
24
- super
25
- undefine_timeliness_attribute_methods
26
- end
27
-
28
- protected
29
-
30
23
  def define_timeliness_methods(before_type_cast=false)
31
24
  return if timeliness_validated_attributes.blank?
32
25
  timeliness_validated_attributes.each do |attr_name|
@@ -34,55 +27,62 @@ module ValidatesTimeliness
34
27
  end
35
28
  end
36
29
 
30
+ def generated_timeliness_methods
31
+ @generated_timeliness_methods ||= Module.new { |m|
32
+ extend Mutex_m
33
+ }.tap { |mod| include mod }
34
+ end
35
+
36
+ def undefine_attribute_methods
37
+ super.tap { undefine_timeliness_attribute_methods }
38
+ end
39
+
40
+ def undefine_timeliness_attribute_methods
41
+ generated_timeliness_methods.synchronize do
42
+ generated_timeliness_methods.module_eval do
43
+ instance_methods.each { |m| undef_method(m) }
44
+ end
45
+ end
46
+ end
47
+
48
+ protected
49
+
37
50
  def define_attribute_timeliness_methods(attr_name, before_type_cast=false)
38
51
  define_timeliness_write_method(attr_name)
39
52
  define_timeliness_before_type_cast_method(attr_name) if before_type_cast
40
53
  end
41
54
 
42
55
  def define_timeliness_write_method(attr_name)
43
- method_body, line = <<-EOV, __LINE__ + 1
56
+ generated_timeliness_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
44
57
  def #{attr_name}=(value)
45
- original_value = value
46
- @timeliness_cache ||= {}
47
- @timeliness_cache["#{attr_name}"] = original_value
48
- #{ "if value.is_a?(String)\n#{timeliness_type_cast_code(attr_name, 'value')}\nend" if ValidatesTimeliness.use_plugin_parser }
49
-
50
- super(value)
58
+ write_timeliness_attribute('#{attr_name}', value)
51
59
  end
52
- EOV
53
- generated_timeliness_methods.module_eval(method_body, __FILE__, line)
60
+ STR
54
61
  end
55
62
 
56
63
  def define_timeliness_before_type_cast_method(attr_name)
57
- method_body, line = <<-EOV, __LINE__ + 1
64
+ generated_timeliness_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
58
65
  def #{attr_name}_before_type_cast
59
- _timeliness_raw_value_for('#{attr_name}') || @attributes['#{attr_name}']
66
+ read_timeliness_attribute_before_type_cast('#{attr_name}')
60
67
  end
61
- EOV
62
- generated_timeliness_methods.module_eval(method_body, __FILE__, line)
68
+ STR
63
69
  end
70
+ end
64
71
 
65
- def timeliness_type_cast_code(attr_name, var_name)
66
- type = timeliness_attribute_type(attr_name)
67
- timezone_aware = timeliness_attribute_timezone_aware?(attr_name)
68
- timezone = :current if timezone_aware
69
-
70
- "#{var_name} = Timeliness::Parser.parse(#{var_name}, :#{type}, :zone => #{timezone.inspect})"
71
- end
72
+ def write_timeliness_attribute(attr_name, value)
73
+ @timeliness_cache ||= {}
74
+ @timeliness_cache[attr_name] = value
72
75
 
73
- def generated_timeliness_methods
74
- @generated_timeliness_methods ||= Module.new.tap { |m| include(m) }
76
+ if ValidatesTimeliness.use_plugin_parser
77
+ timezone = :current if self.class.timeliness_attribute_timezone_aware?(attr_name)
78
+ value = Timeliness::Parser.parse(value, self.class.timeliness_attribute_type(attr_name), :zone => timezone)
75
79
  end
76
80
 
77
- def undefine_timeliness_attribute_methods
78
- generated_timeliness_methods.module_eval do
79
- instance_methods.each { |m| undef_method(m) }
80
- end
81
- end
81
+ @attributes[attr_name] = value
82
82
  end
83
83
 
84
- def _timeliness_raw_value_for(attr_name)
85
- @timeliness_cache && @timeliness_cache[attr_name]
84
+ def read_timeliness_attribute_before_type_cast(attr_name)
85
+ @timeliness_cache && @timeliness_cache[attr_name] || @attributes[attr_name]
86
86
  end
87
87
 
88
88
  def _clear_timeliness_cache
@@ -1,14 +1,13 @@
1
1
  module ValidatesTimeliness
2
2
  module Extensions
3
- autoload :DateTimeSelect, 'validates_timeliness/extensions/date_time_select'
4
- autoload :MultiparameterHandler, 'validates_timeliness/extensions/multiparameter_handler'
3
+ autoload :DateTimeSelect, 'validates_timeliness/extensions/date_time_select'
5
4
  end
6
5
 
7
6
  def self.enable_date_time_select_extension!
8
- ::ActionView::Helpers::InstanceTag.send(:include, ValidatesTimeliness::Extensions::DateTimeSelect)
7
+ ::ActionView::Helpers::Tags::DateSelect.send(:include, ValidatesTimeliness::Extensions::DateTimeSelect)
9
8
  end
10
9
 
11
10
  def self.enable_multiparameter_extension!
12
- ::ActiveRecord::Base.send(:include, ValidatesTimeliness::Extensions::MultiparameterHandler)
11
+ require 'validates_timeliness/extensions/multiparameter_handler'
13
12
  end
14
13
  end
@@ -9,7 +9,6 @@ module ValidatesTimeliness
9
9
  # It's a minor usability improvement which is rarely an issue for the user.
10
10
 
11
11
  included do
12
- alias_method_chain :datetime_selector, :timeliness
13
12
  alias_method_chain :value, :timeliness
14
13
  end
15
14
 
@@ -33,15 +32,8 @@ module ValidatesTimeliness
33
32
  end
34
33
  end
35
34
 
36
- def datetime_selector_with_timeliness(*args)
37
- @timeliness_date_or_time_tag = true
38
- datetime_selector_without_timeliness(*args)
39
- end
40
-
41
35
  def value_with_timeliness(object)
42
- unless @timeliness_date_or_time_tag && @template_object.params[@object_name]
43
- return value_without_timeliness(object)
44
- end
36
+ return value_without_timeliness(object) unless @template_object.params[@object_name]
45
37
 
46
38
  @template_object.params[@object_name]
47
39
 
@@ -56,6 +48,7 @@ module ValidatesTimeliness
56
48
 
57
49
  TimelinessDateTime.new(*values)
58
50
  end
51
+
59
52
  end
60
53
  end
61
54
  end
@@ -1,80 +1,74 @@
1
- module ValidatesTimeliness
2
- module Extensions
3
- module MultiparameterHandler
4
- extend ActiveSupport::Concern
1
+ ActiveRecord::AttributeAssignment::MultiparameterAttribute.class_eval do
2
+ private
5
3
 
6
- # Stricter handling of date and time values from multiparameter
7
- # assignment from the date/time select view helpers
4
+ # Yield if date values are valid
5
+ def validate_multiparameter_date_values(set_values)
6
+ if set_values[0..2].all?{ |v| v.present? } && Date.valid_civil?(*set_values[0..2])
7
+ yield
8
+ else
9
+ invalid_multiparameter_date_or_time_as_string(set_values)
10
+ end
11
+ end
8
12
 
9
- included do
10
- alias_method_chain :instantiate_time_object, :timeliness
11
- alias_method :execute_callstack_for_multiparameter_attributes, :execute_callstack_for_multiparameter_attributes_with_timeliness
12
- alias_method :read_value_from_parameter, :read_value_from_parameter_with_timeliness
13
- end
13
+ def invalid_multiparameter_date_or_time_as_string(values)
14
+ value = [values[0], *values[1..2].map {|s| s.to_s.rjust(2,"0")} ].join("-")
15
+ value += ' ' + values[3..5].map {|s| s.to_s.rjust(2, "0") }.join(":") unless values[3..5].empty?
16
+ value
17
+ end
14
18
 
15
- private
19
+ def instantiate_time_object(set_values)
20
+ raise if set_values.any?(&:nil?)
16
21
 
17
- def invalid_multiparameter_date_or_time_as_string(values)
18
- value = [values[0], *values[1..2].map {|s| s.to_s.rjust(2,"0")} ].join("-")
19
- value += ' ' + values[3..5].map {|s| s.to_s.rjust(2, "0") }.join(":") unless values[3..5].empty?
20
- value
21
- end
22
+ validate_multiparameter_date_values(set_values) {
23
+ set_values = set_values.map {|v| v.is_a?(String) ? v.strip : v }
22
24
 
23
- def instantiate_time_object_with_timeliness(name, values)
24
- validate_multiparameter_date_values(values) {
25
- instantiate_time_object_without_timeliness(name, values)
26
- }
25
+ if object.class.send(:create_time_zone_conversion_attribute?, name, cast_type_or_column)
26
+ Time.zone.local(*set_values)
27
+ else
28
+ Time.send(object.class.default_timezone, *set_values)
27
29
  end
30
+ }
31
+ rescue
32
+ invalid_multiparameter_date_or_time_as_string(set_values)
33
+ end
28
34
 
29
- def instantiate_date_object(name, values)
30
- validate_multiparameter_date_values(values) {
31
- Date.new(*values)
32
- }
35
+ def read_time
36
+ # If column is a :time (and not :date or :timestamp) there is no need to validate if
37
+ # there are year/month/day fields
38
+ if cast_type_or_column.type == :time
39
+ # if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil
40
+ { 1 => 1970, 2 => 1, 3 => 1 }.each do |key,value|
41
+ values[key] ||= value
33
42
  end
43
+ end
34
44
 
35
- # Yield if date values are valid
36
- def validate_multiparameter_date_values(values)
37
- if values[0..2].all?{ |v| v.present? } && Date.valid_civil?(*values[0..2])
38
- yield
39
- else
40
- invalid_multiparameter_date_or_time_as_string(values)
41
- end
42
- end
45
+ max_position = extract_max_param(6)
46
+ set_values = values.values_at(*(1..max_position))
43
47
 
44
- def read_value_from_parameter_with_timeliness(name, values_from_param)
45
- klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
46
- values = values_from_param.is_a?(Hash) ? values_from_param.to_a.sort_by(&:first).map(&:last) : values_from_param
48
+ instantiate_time_object(set_values)
49
+ end
47
50
 
48
- if values.empty? || values.all?{ |v| v.nil? }
49
- nil
50
- elsif klass == Time
51
- instantiate_time_object(name, values)
52
- elsif klass == Date
53
- instantiate_date_object(name, values)
54
- else
55
- if respond_to?(:read_other_parameter_value)
56
- read_date_parameter_value(name, values_from_param)
57
- else
58
- klass.new(*values)
59
- end
60
- end
61
- end
51
+ def read_date
52
+ set_values = values.values_at(1,2,3).map {|v| v.is_a?(String) ? v.strip : v }
53
+
54
+ if set_values.any? { |v| v.is_a?(String) }
55
+ Timeliness.parse(set_values.join('-'), :date).try(:to_date) or raise TypeError
56
+ else
57
+ Date.new(*set_values)
58
+ end
59
+ rescue TypeError, ArgumentError, NoMethodError => ex # if Date.new raises an exception on an invalid date
60
+ # Date.new with nil values throws NoMethodError
61
+ raise ex if ex.is_a?(NoMethodError) && ex.message !~ /undefined method `div' for/
62
+ invalid_multiparameter_date_or_time_as_string(set_values)
63
+ end
62
64
 
63
- def execute_callstack_for_multiparameter_attributes_with_timeliness(callstack)
64
- errors = []
65
- callstack.each do |name, values_with_empty_parameters|
66
- begin
67
- send(name + "=", read_value_from_parameter(name, values_with_empty_parameters))
68
- rescue => ex
69
- values = values_with_empty_parameters.is_a?(Hash) ? values_with_empty_parameters.values : values_with_empty_parameters
70
- errors << ActiveRecord::AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
71
- end
72
- end
73
- unless errors.empty?
74
- raise ActiveRecord::MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
75
- end
76
- end
65
+ # Cast type is v4.2 and column before
66
+ def cast_type_or_column
67
+ @cast_type || @column
68
+ end
77
69
 
78
- end
70
+ def timezone_conversion_attribute?
71
+ object.class.send(:create_time_zone_conversion_attribute?, name, column)
79
72
  end
73
+
80
74
  end