validates_timeliness 4.1.1 → 5.0.0.alpha1

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.
@@ -1,74 +1,55 @@
1
- ActiveRecord::AttributeAssignment::MultiparameterAttribute.class_eval do
2
- private
1
+ module ValidatesTimeliness
2
+ module Extensions
3
+ class AcceptsMultiparameterTime < Module
4
+
5
+ def initialize(defaults: {})
6
+
7
+ define_method(:cast) do |value|
8
+ if value.is_a?(Hash)
9
+ value_from_multiparameter_assignment(value)
10
+ else
11
+ super(value)
12
+ end
13
+ end
14
+
15
+ define_method(:assert_valid_value) do |value|
16
+ if value.is_a?(Hash)
17
+ value_from_multiparameter_assignment(value)
18
+ else
19
+ super(value)
20
+ end
21
+ end
22
+
23
+ define_method(:value_from_multiparameter_assignment) do |values_hash|
24
+ defaults.each do |k, v|
25
+ values_hash[k] ||= v
26
+ end
27
+ return unless values_hash.values_at(1,2,3).all?{ |v| v.present? } &&
28
+ Date.valid_civil?(*values_hash.values_at(1,2,3))
29
+
30
+ values = values_hash.sort.map(&:last)
31
+ ::Time.send(default_timezone, *values)
32
+ end
33
+ private :value_from_multiparameter_assignment
3
34
 
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
12
-
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
18
-
19
- def instantiate_time_object(set_values)
20
- raise if set_values.any?(&:nil?)
21
-
22
- validate_multiparameter_date_values(set_values) {
23
- set_values = set_values.map {|v| v.is_a?(String) ? v.strip : v }
24
-
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)
29
- end
30
- }
31
- rescue
32
- invalid_multiparameter_date_or_time_as_string(set_values)
33
- end
34
-
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
42
35
  end
43
- end
44
-
45
- max_position = extract_max_param(6)
46
- set_values = values.values_at(*(1..max_position))
47
36
 
48
- instantiate_time_object(set_values)
49
- end
50
-
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
37
  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
64
-
65
- # Cast type is v4.2 and column before
66
- def cast_type_or_column
67
- @cast_type || @column
68
38
  end
39
+ end
69
40
 
70
- def timezone_conversion_attribute?
71
- object.class.send(:create_time_zone_conversion_attribute?, name, column)
72
- end
41
+ ActiveModel::Type::Date.class_eval do
42
+ include ValidatesTimeliness::Extensions::AcceptsMultiparameterTime.new
43
+ end
73
44
 
45
+ ActiveModel::Type::Time.class_eval do
46
+ include ValidatesTimeliness::Extensions::AcceptsMultiparameterTime.new(
47
+ defaults: { 1 => 1970, 2 => 1, 3 => 1, 4 => 0, 5 => 0 }
48
+ )
74
49
  end
50
+
51
+ ActiveModel::Type::DateTime.class_eval do
52
+ include ValidatesTimeliness::Extensions::AcceptsMultiparameterTime.new(
53
+ defaults: { 4 => 0, 5 => 0 }
54
+ )
55
+ end
@@ -7,14 +7,65 @@ module ValidatesTimeliness
7
7
  public
8
8
 
9
9
  def define_attribute_methods(*attr_names)
10
- super.tap { define_timeliness_methods}
10
+ super.tap { define_timeliness_methods }
11
11
  end
12
12
 
13
13
  def undefine_attribute_methods
14
14
  super.tap { undefine_timeliness_attribute_methods }
15
15
  end
16
+
17
+ def define_timeliness_methods(before_type_cast=false)
18
+ return if timeliness_validated_attributes.blank?
19
+ timeliness_validated_attributes.each do |attr_name|
20
+ define_attribute_timeliness_methods(attr_name, before_type_cast)
21
+ end
22
+ end
23
+
24
+ def generated_timeliness_methods
25
+ @generated_timeliness_methods ||= Module.new { |m|
26
+ extend Mutex_m
27
+ }.tap { |mod| include mod }
28
+ end
29
+
30
+ def undefine_timeliness_attribute_methods
31
+ generated_timeliness_methods.module_eval do
32
+ instance_methods.each { |m| undef_method(m) }
33
+ end
34
+ end
35
+
36
+ def define_attribute_timeliness_methods(attr_name, before_type_cast=false)
37
+ define_timeliness_write_method(attr_name)
38
+ define_timeliness_before_type_cast_method(attr_name) if before_type_cast
39
+ end
40
+
41
+ def define_timeliness_write_method(attr_name)
42
+ generated_timeliness_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
43
+ def #{attr_name}=(value)
44
+ @timeliness_cache ||= {}
45
+ @timeliness_cache['#{attr_name}'] = value
46
+
47
+ @attributes['#{attr_name}'] = super
48
+ end
49
+ STR
50
+ end
51
+
52
+ def define_timeliness_before_type_cast_method(attr_name)
53
+ generated_timeliness_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
54
+ def #{attr_name}_before_type_cast
55
+ read_timeliness_attribute_before_type_cast('#{attr_name}')
56
+ end
57
+ STR
58
+ end
16
59
  end
17
-
60
+
61
+ def read_timeliness_attribute_before_type_cast(attr_name)
62
+ @timeliness_cache && @timeliness_cache[attr_name] || @attributes[attr_name]
63
+ end
64
+
65
+ def _clear_timeliness_cache
66
+ @timeliness_cache = {}
67
+ end
68
+
18
69
  end
19
70
  end
20
71
  end
@@ -3,90 +3,8 @@ module ValidatesTimeliness
3
3
  module ActiveRecord
4
4
  extend ActiveSupport::Concern
5
5
 
6
- module ClassMethods
7
- public
8
-
9
- def timeliness_attribute_timezone_aware?(attr_name)
10
- create_time_zone_conversion_attribute?(attr_name, timeliness_column_for_attribute(attr_name))
11
- end
12
-
13
- def timeliness_attribute_type(attr_name)
14
- timeliness_column_for_attribute(attr_name).type
15
- end
16
-
17
- if ::ActiveModel.version >= Gem::Version.new('4.2')
18
- def timeliness_column_for_attribute(attr_name)
19
- columns_hash.fetch(attr_name.to_s) do |key|
20
- validation_type = _validators[key.to_sym].find {|v| v.kind == :timeliness }.type.to_s
21
- ::ActiveRecord::ConnectionAdapters::Column.new(key, nil, lookup_cast_type(validation_type), validation_type)
22
- end
23
- end
24
-
25
- def lookup_cast_type(sql_type)
26
- case sql_type
27
- when 'datetime' then ::ActiveRecord::Type::DateTime.new
28
- when 'date' then ::ActiveRecord::Type::Date.new
29
- when 'time' then ::ActiveRecord::Type::Time.new
30
- end
31
- end
32
- else
33
- def timeliness_column_for_attribute(attr_name)
34
- columns_hash.fetch(attr_name.to_s) do |key|
35
- validation_type = _validators[key.to_sym].find {|v| v.kind == :timeliness }.type.to_s
36
- ::ActiveRecord::ConnectionAdapters::Column.new(key, nil, validation_type)
37
- end
38
- end
39
- end
40
-
41
- def define_attribute_methods
42
- super.tap {
43
- generated_timeliness_methods.synchronize do
44
- return if @timeliness_methods_generated
45
- define_timeliness_methods true
46
- @timeliness_methods_generated = true
47
- end
48
- }
49
- end
50
-
51
- def undefine_attribute_methods
52
- super.tap {
53
- generated_timeliness_methods.synchronize do
54
- return unless @timeliness_methods_generated
55
- undefine_timeliness_attribute_methods
56
- @timeliness_methods_generated = false
57
- end
58
- }
59
- end
60
- # Override to overwrite methods in ActiveRecord attribute method module because in AR 4+
61
- # there is curious code which calls the method directly from the generated methods module
62
- # via bind inside method_missing. This means our method in the formerly custom timeliness
63
- # methods module was never reached.
64
- def generated_timeliness_methods
65
- generated_attribute_methods
66
- end
67
- end
68
-
69
- def write_timeliness_attribute(attr_name, value)
70
- @timeliness_cache ||= {}
71
- @timeliness_cache[attr_name] = value
72
-
73
- if ValidatesTimeliness.use_plugin_parser
74
- type = self.class.timeliness_attribute_type(attr_name)
75
- timezone = :current if self.class.timeliness_attribute_timezone_aware?(attr_name)
76
- value = Timeliness::Parser.parse(value, type, :zone => timezone)
77
- value = value.to_date if value && type == :date
78
- end
79
-
80
- write_attribute(attr_name, value)
81
- end
82
-
83
6
  def read_timeliness_attribute_before_type_cast(attr_name)
84
- @timeliness_cache && @timeliness_cache[attr_name] || read_attribute_before_type_cast(attr_name)
85
- end
86
-
87
- def reload(*args)
88
- _clear_timeliness_cache
89
- super
7
+ read_attribute_before_type_cast(attr_name)
90
8
  end
91
9
 
92
10
  end
@@ -11,13 +11,5 @@ module ValidatesTimeliness
11
11
  initializer "validates_timeliness.initialize_restriction_errors" do
12
12
  ValidatesTimeliness.ignore_restriction_errors = !Rails.env.test?
13
13
  end
14
-
15
- initializer "validates_timeliness.initialize_timeliness_ambiguous_date_format", :after => 'load_config_initializers' do
16
- if Timeliness.respond_to?(:ambiguous_date_format) # i.e. v0.4+
17
- # Set default for each new thread if you have changed the default using
18
- # the format switching methods.
19
- Timeliness.configuration.ambiguous_date_format = Timeliness::Definitions.current_date_format
20
- end
21
- end
22
14
  end
23
15
  end
@@ -3,9 +3,7 @@ require 'active_model/validator'
3
3
 
4
4
  module ValidatesTimeliness
5
5
  class Validator < ActiveModel::EachValidator
6
- include Conversion
7
-
8
- attr_reader :type, :attributes
6
+ attr_reader :type, :attributes, :converter
9
7
 
10
8
  RESTRICTIONS = {
11
9
  :is_at => :==,
@@ -55,18 +53,14 @@ module ValidatesTimeliness
55
53
  end
56
54
  end
57
55
 
58
- # Rails 4.0 compatibility for old #setup method with class as arg
59
- if Gem::Version.new(ActiveModel::VERSION::STRING) <= Gem::Version.new('4.1')
60
- alias_method(:setup, :setup_timeliness_validated_attributes)
61
- end
62
-
63
56
  def validate_each(record, attr_name, value)
64
57
  raw_value = attribute_raw_value(record, attr_name) || value
65
58
  return if (@allow_nil && raw_value.nil?) || (@allow_blank && raw_value.blank?)
66
59
 
67
- @timezone_aware = timezone_aware?(record, attr_name)
68
- value = parse(raw_value) if value.is_a?(String) || options[:format]
69
- value = type_cast_value(value, @type)
60
+ @converter = initialize_converter(record, attr_name)
61
+
62
+ value = @converter.parse(raw_value) if value.is_a?(String) || options[:format]
63
+ value = @converter.type_cast_value(value)
70
64
 
71
65
  add_error(record, attr_name, :"invalid_#{@type}") and return if value.blank?
72
66
 
@@ -76,7 +70,7 @@ module ValidatesTimeliness
76
70
  def validate_restrictions(record, attr_name, value)
77
71
  @restrictions_to_check.each do |restriction|
78
72
  begin
79
- restriction_value = type_cast_value(evaluate_option_value(options[restriction], record), @type)
73
+ restriction_value = @converter.type_cast_value(@converter.evaluate(options[restriction], record))
80
74
  unless value.send(RESTRICTIONS[restriction], restriction_value)
81
75
  add_error(record, attr_name, restriction, restriction_value) and break
82
76
  end
@@ -105,9 +99,18 @@ module ValidatesTimeliness
105
99
  record.read_timeliness_attribute_before_type_cast(attr_name.to_s)
106
100
  end
107
101
 
108
- def timezone_aware?(record, attr_name)
109
- record.class.respond_to?(:timeliness_attribute_timezone_aware?) &&
110
- record.class.timeliness_attribute_timezone_aware?(attr_name)
102
+ def time_zone_aware?(record, attr_name)
103
+ record.class.respond_to?(:skip_time_zone_conversion_for_attributes) &&
104
+ !record.class.skip_time_zone_conversion_for_attributes.include?(attr_name.to_sym)
105
+ end
106
+
107
+ def initialize_converter(record, attr_name)
108
+ ValidatesTimeliness::Converter.new(
109
+ type: @type,
110
+ time_zone_aware: time_zone_aware?(record, attr_name),
111
+ format: options[:format],
112
+ ignore_usec: options[:ignore_usec]
113
+ )
111
114
  end
112
115
 
113
116
  end
@@ -1,3 +1,3 @@
1
1
  module ValidatesTimeliness
2
- VERSION = '4.1.1'
2
+ VERSION = '5.0.0.alpha1'
3
3
  end
@@ -1,5 +1,6 @@
1
1
  require 'rspec'
2
2
 
3
+ require 'byebug'
3
4
  require 'active_model'
4
5
  require 'active_model/validations'
5
6
  require 'active_record'
@@ -9,8 +10,6 @@ require 'timecop'
9
10
  require 'validates_timeliness'
10
11
  require 'validates_timeliness/orm/active_model'
11
12
 
12
- require 'rails/railtie'
13
-
14
13
  require 'support/test_model'
15
14
  require 'support/model_helpers'
16
15
  require 'support/config_helper'
@@ -59,6 +58,7 @@ end
59
58
  ActiveRecord::Base.default_timezone = :utc
60
59
  ActiveRecord::Base.time_zone_aware_attributes = true
61
60
  ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})
61
+ ActiveRecord::Base.time_zone_aware_types = [:datetime, :time]
62
62
  ActiveRecord::Migration.verbose = false
63
63
  ActiveRecord::Schema.define(:version => 1) do
64
64
  create_table :employees, :force => true do |t|
@@ -25,7 +25,9 @@ module TestModel
25
25
 
26
26
  def type_cast(attr_name, value)
27
27
  return value unless value.is_a?(String)
28
- value.send("to_#{model_attributes[attr_name.to_sym]}") rescue nil
28
+ type_name = model_attributes[attr_name.to_sym]
29
+ type = ActiveModel::Type.lookup(type_name)
30
+ type.cast(value)
29
31
  end
30
32
  end
31
33
 
@@ -48,7 +50,7 @@ module TestModel
48
50
  end
49
51
 
50
52
  def method_missing(method_id, *args, &block)
51
- if match_attribute_method?(method_id.to_s)
53
+ if !matched_attribute_method(method_id.to_s).nil?
52
54
  self.class.define_attribute_methods self.class.model_attributes.keys
53
55
  send(method_id, *args, &block)
54
56
  else
@@ -38,21 +38,6 @@ RSpec.describe ValidatesTimeliness::AttributeMethods do
38
38
  expect(e.redefined_birth_date_called).to be_truthy
39
39
  end
40
40
 
41
- it 'should be undefined if model class has dynamic attribute methods reset' do
42
- # Force method definitions
43
- PersonWithShim.validates_date :birth_date
44
- r = PersonWithShim.new
45
- r.birth_date = Time.now
46
-
47
- write_method = :birth_date=
48
-
49
- expect(PersonWithShim.send(:generated_timeliness_methods).instance_methods).to include(write_method)
50
-
51
- PersonWithShim.undefine_attribute_methods
52
-
53
- expect(PersonWithShim.send(:generated_timeliness_methods).instance_methods).not_to include(write_method)
54
- end
55
-
56
41
  context "with plugin parser" do
57
42
  with_config(:use_plugin_parser, true)
58
43
 
@@ -1,94 +1,109 @@
1
- RSpec.describe ValidatesTimeliness::Conversion do
2
- include ValidatesTimeliness::Conversion
1
+ RSpec.describe ValidatesTimeliness::Converter do
2
+ subject(:converter) { described_class.new(type: type, time_zone_aware: time_zone_aware, ignore_usec: ignore_usec) }
3
3
 
4
4
  let(:options) { Hash.new }
5
+ let(:type) { :date }
6
+ let(:time_zone_aware) { false }
7
+ let(:ignore_usec) { false }
5
8
 
6
9
  before do
7
10
  Timecop.freeze(Time.mktime(2010, 1, 1, 0, 0, 0))
8
11
  end
9
12
 
13
+ delegate :type_cast_value, :evaluate, :parse, :dummy_time, to: :converter
14
+
10
15
  describe "#type_cast_value" do
11
16
  describe "for date type" do
17
+ let(:type) { :date }
18
+
12
19
  it "should return same value for date value" do
13
- expect(type_cast_value(Date.new(2010, 1, 1), :date)).to eq(Date.new(2010, 1, 1))
20
+ expect(type_cast_value(Date.new(2010, 1, 1))).to eq(Date.new(2010, 1, 1))
14
21
  end
15
22
 
16
23
  it "should return date part of time value" do
17
- expect(type_cast_value(Time.mktime(2010, 1, 1, 0, 0, 0), :date)).to eq(Date.new(2010, 1, 1))
24
+ expect(type_cast_value(Time.mktime(2010, 1, 1, 0, 0, 0))).to eq(Date.new(2010, 1, 1))
18
25
  end
19
26
 
20
27
  it "should return date part of datetime value" do
21
- expect(type_cast_value(DateTime.new(2010, 1, 1, 0, 0, 0), :date)).to eq(Date.new(2010, 1, 1))
28
+ expect(type_cast_value(DateTime.new(2010, 1, 1, 0, 0, 0))).to eq(Date.new(2010, 1, 1))
22
29
  end
23
30
 
24
31
  it 'should return nil for invalid value types' do
25
- expect(type_cast_value(12, :date)).to eq(nil)
32
+ expect(type_cast_value(12)).to eq(nil)
26
33
  end
27
34
  end
28
35
 
29
36
  describe "for time type" do
37
+ let(:type) { :time }
38
+
30
39
  it "should return same value for time value matching dummy date part" do
31
- expect(type_cast_value(Time.utc(2000, 1, 1, 0, 0, 0), :time)).to eq(Time.utc(2000, 1, 1, 0, 0, 0))
40
+ expect(type_cast_value(Time.utc(2000, 1, 1, 0, 0, 0))).to eq(Time.utc(2000, 1, 1, 0, 0, 0))
32
41
  end
33
42
 
34
43
  it "should return dummy time value with same time part for time value with different date" do
35
- expect(type_cast_value(Time.utc(2010, 1, 1, 0, 0, 0), :time)).to eq(Time.utc(2000, 1, 1, 0, 0, 0))
44
+ expect(type_cast_value(Time.utc(2010, 1, 1, 0, 0, 0))).to eq(Time.utc(2000, 1, 1, 0, 0, 0))
36
45
  end
37
46
 
38
47
  it "should return dummy time only for date value" do
39
- expect(type_cast_value(Date.new(2010, 1, 1), :time)).to eq(Time.utc(2000, 1, 1, 0, 0, 0))
48
+ expect(type_cast_value(Date.new(2010, 1, 1))).to eq(Time.utc(2000, 1, 1, 0, 0, 0))
40
49
  end
41
50
 
42
51
  it "should return dummy date with time part for datetime value" do
43
- expect(type_cast_value(DateTime.civil_from_format(:utc, 2010, 1, 1, 12, 34, 56), :time)).to eq(Time.utc(2000, 1, 1, 12, 34, 56))
52
+ expect(type_cast_value(DateTime.civil_from_format(:utc, 2010, 1, 1, 12, 34, 56))).to eq(Time.utc(2000, 1, 1, 12, 34, 56))
44
53
  end
45
54
 
46
55
  it 'should return nil for invalid value types' do
47
- expect(type_cast_value(12, :time)).to eq(nil)
56
+ expect(type_cast_value(12)).to eq(nil)
48
57
  end
49
58
  end
50
59
 
51
60
  describe "for datetime type" do
61
+ let(:type) { :datetime }
62
+ let(:time_zone_aware) { true }
63
+
52
64
  it "should return Date as Time value" do
53
- expect(type_cast_value(Date.new(2010, 1, 1), :datetime)).to eq(Time.local(2010, 1, 1, 0, 0, 0))
65
+ expect(type_cast_value(Date.new(2010, 1, 1))).to eq(Time.local(2010, 1, 1, 0, 0, 0))
54
66
  end
55
67
 
56
68
  it "should return same Time value" do
57
69
  value = Time.utc(2010, 1, 1, 12, 34, 56)
58
- expect(type_cast_value(Time.utc(2010, 1, 1, 12, 34, 56), :datetime)).to eq(value)
70
+ expect(type_cast_value(Time.utc(2010, 1, 1, 12, 34, 56))).to eq(value)
59
71
  end
60
72
 
61
73
  it "should return as Time with same component values" do
62
- expect(type_cast_value(DateTime.civil_from_format(:utc, 2010, 1, 1, 12, 34, 56), :datetime)).to eq(Time.utc(2010, 1, 1, 12, 34, 56))
74
+ expect(type_cast_value(DateTime.civil_from_format(:utc, 2010, 1, 1, 12, 34, 56))).to eq(Time.utc(2010, 1, 1, 12, 34, 56))
63
75
  end
64
76
 
65
77
  it "should return same Time in correct zone if timezone aware" do
66
- @timezone_aware = true
67
78
  value = Time.utc(2010, 1, 1, 12, 34, 56)
68
- result = type_cast_value(value, :datetime)
79
+ result = type_cast_value(value)
69
80
  expect(result).to eq(Time.zone.local(2010, 1, 1, 23, 34, 56))
70
81
  expect(result.zone).to eq('AEDT')
71
82
  end
72
83
 
73
84
  it 'should return nil for invalid value types' do
74
- expect(type_cast_value(12, :datetime)).to eq(nil)
85
+ expect(type_cast_value(12)).to eq(nil)
75
86
  end
76
87
  end
77
88
 
78
89
  describe "ignore_usec option" do
79
- let(:options) { {:ignore_usec => true} }
90
+ let(:type) { :datetime }
91
+ let(:ignore_usec) { true }
80
92
 
81
93
  it "should ignore usec on time values when evaluated" do
82
94
  value = Time.utc(2010, 1, 1, 12, 34, 56, 10000)
83
- expect(type_cast_value(value, :datetime)).to eq(Time.utc(2010, 1, 1, 12, 34, 56))
95
+ expect(type_cast_value(value)).to eq(Time.utc(2010, 1, 1, 12, 34, 56))
84
96
  end
85
97
 
86
- it "should ignore usec and return time in correct zone if timezone aware" do
87
- @timezone_aware = true
88
- value = Time.utc(2010, 1, 1, 12, 34, 56, 10000)
89
- result = type_cast_value(value, :datetime)
90
- expect(result).to eq(Time.zone.local(2010, 1, 1, 23, 34, 56))
91
- expect(result.zone).to eq('AEDT')
98
+ context do
99
+ let(:time_zone_aware) { true }
100
+
101
+ it "should ignore usec and return time in correct zone if timezone aware" do
102
+ value = Time.utc(2010, 1, 1, 12, 34, 56, 10000)
103
+ result = type_cast_value(value)
104
+ expect(result).to eq(Time.zone.local(2010, 1, 1, 23, 34, 56))
105
+ expect(result.zone).to eq('AEDT')
106
+ end
92
107
  end
93
108
  end
94
109
  end
@@ -103,7 +118,6 @@ RSpec.describe ValidatesTimeliness::Conversion do
103
118
  end
104
119
 
105
120
  it 'should return time component values shifted to current zone if timezone aware' do
106
- @timezone_aware = true
107
121
  expect(dummy_time(Time.utc(2000, 1, 1, 12, 34, 56))).to eq(Time.zone.local(2000, 1, 1, 23, 34, 56))
108
122
  end
109
123
 
@@ -120,61 +134,64 @@ RSpec.describe ValidatesTimeliness::Conversion do
120
134
  end
121
135
  end
122
136
 
123
- describe "#evaluate_option_value" do
137
+ describe "#evaluate" do
124
138
  let(:person) { Person.new }
125
139
 
126
140
  it 'should return Date object as is' do
127
141
  value = Date.new(2010,1,1)
128
- expect(evaluate_option_value(value, person)).to eq(value)
142
+ expect(evaluate(value, person)).to eq(value)
129
143
  end
130
144
 
131
145
  it 'should return Time object as is' do
132
146
  value = Time.mktime(2010,1,1)
133
- expect(evaluate_option_value(value, person)).to eq(value)
147
+ expect(evaluate(value, person)).to eq(value)
134
148
  end
135
149
 
136
150
  it 'should return DateTime object as is' do
137
151
  value = DateTime.new(2010,1,1,0,0,0)
138
- expect(evaluate_option_value(value, person)).to eq(value)
152
+ expect(evaluate(value, person)).to eq(value)
139
153
  end
140
154
 
141
155
  it 'should return Time value returned from proc with 0 arity' do
142
156
  value = Time.mktime(2010,1,1)
143
- expect(evaluate_option_value(lambda { value }, person)).to eq(value)
157
+ expect(evaluate(lambda { value }, person)).to eq(value)
144
158
  end
145
159
 
146
160
  it 'should return Time value returned by record attribute call in proc arity of 1' do
147
161
  value = Time.mktime(2010,1,1)
148
162
  person.birth_time = value
149
- expect(evaluate_option_value(lambda {|r| r.birth_time }, person)).to eq(value)
163
+ expect(evaluate(lambda {|r| r.birth_time }, person)).to eq(value)
150
164
  end
151
165
 
152
166
  it 'should return Time value for attribute method symbol which returns Time' do
153
167
  value = Time.mktime(2010,1,1)
154
- person.birth_time = value
155
- expect(evaluate_option_value(:birth_time, person)).to eq(value)
168
+ person.birth_datetime = value
169
+ expect(evaluate(:birth_datetime, person)).to eq(value)
156
170
  end
157
171
 
158
172
  it 'should return Time value is default zone from string time value' do
159
173
  value = '2010-01-01 12:00:00'
160
- expect(evaluate_option_value(value, person)).to eq(Time.utc(2010,1,1,12,0,0))
174
+ expect(evaluate(value, person)).to eq(Time.utc(2010,1,1,12,0,0))
161
175
  end
162
176
 
163
- it 'should return Time value is current zone from string time value if timezone aware' do
164
- @timezone_aware = true
165
- value = '2010-01-01 12:00:00'
166
- expect(evaluate_option_value(value, person)).to eq(Time.zone.local(2010,1,1,12,0,0))
177
+ context do
178
+ let(:converter) { described_class.new(type: :date, time_zone_aware: true) }
179
+
180
+ it 'should return Time value is current zone from string time value if timezone aware' do
181
+ value = '2010-01-01 12:00:00'
182
+ expect(evaluate(value, person)).to eq(Time.zone.local(2010,1,1,12,0,0))
183
+ end
167
184
  end
168
185
 
169
186
  it 'should return Time value in default zone from proc which returns string time' do
170
- value = '2010-01-01 12:00:00'
171
- expect(evaluate_option_value(lambda { value }, person)).to eq(Time.utc(2010,1,1,12,0,0))
187
+ value = '2010-11-12 13:00:00'
188
+ expect(evaluate(lambda { value }, person)).to eq(Time.utc(2010,11,12,13,0,0))
172
189
  end
173
190
 
174
191
  it 'should return Time value for attribute method symbol which returns string time value' do
175
- value = '2010-01-01 12:00:00'
192
+ value = '13:00:00'
176
193
  person.birth_time = value
177
- expect(evaluate_option_value(:birth_time, person)).to eq(Time.local(2010,1,1,12,0,0))
194
+ expect(evaluate(:birth_time, person)).to eq(Time.utc(2000,1,1,13,0,0))
178
195
  end
179
196
 
180
197
  context "restriction shorthand" do
@@ -183,17 +200,17 @@ RSpec.describe ValidatesTimeliness::Conversion do
183
200
  end
184
201
 
185
202
  it 'should evaluate :now as current time' do
186
- expect(evaluate_option_value(:now, person)).to eq(Time.now)
203
+ expect(evaluate(:now, person)).to eq(Time.now)
187
204
  end
188
205
 
189
206
  it 'should evaluate :today as current time' do
190
- expect(evaluate_option_value(:today, person)).to eq(Date.today)
207
+ expect(evaluate(:today, person)).to eq(Date.today)
191
208
  end
192
209
 
193
210
  it 'should not use shorthand if symbol if is record method' do
194
211
  time = 1.day.from_now
195
212
  allow(person).to receive(:now).and_return(time)
196
- expect(evaluate_option_value(:now, person)).to eq(time)
213
+ expect(evaluate(:now, person)).to eq(time)
197
214
  end
198
215
  end
199
216
  end
@@ -212,13 +229,11 @@ RSpec.describe ValidatesTimeliness::Conversion do
212
229
  with_config(:use_plugin_parser, false)
213
230
 
214
231
  it 'should use Time.zone.parse attribute is timezone aware' do
215
- @timezone_aware = true
216
- expect(Time.zone).to receive(:parse)
232
+ expect(Timeliness::Parser).to_not receive(:parse)
217
233
  parse('2000-01-01')
218
234
  end
219
235
 
220
236
  it 'should use value#to_time if use_plugin_parser setting is false and attribute is not timezone aware' do
221
- @timezone_aware = false
222
237
  value = '2000-01-01'
223
238
  expect(value).to receive(:to_time)
224
239
  parse(value)