validates_timeliness 4.1.1 → 5.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)