validates_timeliness 3.0.15 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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