delocalize 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bf99ef08d5088f5fd791efc0acf489fa51ea5417
4
- data.tar.gz: 013d7f081dea1507d1819b129ffe1697059abea5
3
+ metadata.gz: 59bab63abe22f6890fbbe6d2d754285d0897b0bf
4
+ data.tar.gz: d6463916eeb2d036930ec25447ba1ac3e4e92d6d
5
5
  SHA512:
6
- metadata.gz: a6c5091da9618f1886ddc60472593f84e42bfa2d9392c75d77b069774384811764e0899344f8e37a0cfefcef2291cc464687c7f83d55d1315d896b43de291786
7
- data.tar.gz: 18f7bbf54dfc495b934b6a7e8253fbe763dda21b72231a5ecbac47de21e3189ec388edbac91c21137999bbd07fbca681eeb922762e4de356905bd7ffa64b3991
6
+ metadata.gz: e098d8423fde0322dac888e933df34313e4bd595ddc308dac2e66b9aa2a5fed2c973b6526477a130e62aefa7cca5f2dea045296fee3612dd71de0e2a41afc765
7
+ data.tar.gz: b9c601788e8fb90a21fde7156c63e7b3630beafe4c96a185e5e3361dc15a4a72df434df38c0b84642664860d4fa767c088e5b6f02ebe0d8f66041d10a2c6a32e
@@ -4,24 +4,20 @@
4
4
 
5
5
  delocalize provides localized date/time and number parsing functionality for Rails.
6
6
 
7
- ## Demo Application
8
-
9
- Find a demo application [here](https://github.com/clemens/delocalize_demo).
10
-
11
- ## Compability
7
+ ## Compatibility
12
8
 
13
9
  This gem requires the following versions:
14
10
 
15
- * Ruby >= 1.9.2
16
- * Rails >= 3.0 (Rails 2 and probably even Rails 1 *should* work but aren't officially supported)
11
+ * Ruby >= 2.1.10 (Ruby >= 1.9.2 *should* work but isn't officially supported)
12
+ * Rails >= 4.2 (earlier versions including probably even Rails 1 *should* work but aren't officially supported)
17
13
 
18
- Check [the Travis configuration](https://github.com/clemens/delocalize/blob/1-0-beta/.travis.yml) in order to see which configurations we are testing.
14
+ Check [the Travis configuration](https://github.com/clemens/delocalize/blob/master/.travis.yml) in order to see which configurations we are testing.
19
15
 
20
16
  ## Installation
21
17
 
22
18
  You can use delocalize as a gem. Using delocalize as a Rails plugin has been discontinued and is no supported. If you want/need to use delocalize as a plugin (I really don't see a reason why you'd want to), consider using the `0-2-stable` branch.
23
19
 
24
- ### Rails 3
20
+ ### Rails 3 and above
25
21
 
26
22
  To use delocalize, put the following gem requirement in your `Gemfile`:
27
23
 
@@ -163,6 +159,6 @@ For dates and times, you have to define input formats which are taken from the a
163
159
 
164
160
  [Here](https://github.com/clemens/delocalize/graphs/contributors) is a list of all people who ever contributed to delocalize.
165
161
 
166
- Copyright (c) 2009-2015 Clemens Kofler <clemens@railway.at>
162
+ Copyright (c) 2009-2017 Clemens Kofler <clemens@railway.at>
167
163
  <http://www.railway.at/>
168
164
  Released under the MIT license
@@ -24,7 +24,12 @@ module Delocalize
24
24
 
25
25
  def delocalize_parse(options, key_stack, value)
26
26
  parser = delocalize_parser_for(options, key_stack)
27
- parser ? parser.parse(value) : value
27
+ return value unless parser
28
+ begin
29
+ parser.parse(value)
30
+ rescue ArgumentError
31
+ value
32
+ end
28
33
  end
29
34
 
30
35
  def delocalize_parser_for(options, key_stack)
@@ -36,7 +36,7 @@ module Delocalize
36
36
  input_formats(type).each do |original_format|
37
37
  next unless datetime =~ /^#{apply_regex(original_format)}$/
38
38
 
39
- datetime = ::DateTime.strptime(datetime, original_format)
39
+ datetime = ::DateTime.strptime(datetime, original_format) rescue break
40
40
  return Date == type ?
41
41
  datetime.to_date :
42
42
  Time.zone.local(datetime.year, datetime.mon, datetime.mday, datetime.hour, datetime.min, datetime.sec)
@@ -51,13 +51,16 @@ module Delocalize
51
51
 
52
52
  today = Date.current
53
53
  parsed = Date._parse(datetime)
54
- return if parsed.empty? # the datetime value is invalid
54
+ raise ArgumentError, "invalid date: #{datetime}" if parsed.empty? # the datetime value is invalid
55
55
 
56
56
  # set default year, month and day if not found
57
57
  parsed.reverse_merge!(:year => today.year, :mon => today.mon, :mday => today.mday)
58
- datetime = Time.zone.local(*parsed.values_at(:year, :mon, :mday, :hour, :min, :sec))
59
58
 
60
- Date == type ? datetime.to_date : datetime
59
+ if Date == type
60
+ Date.civil(*parsed.values_at(:year, :mon, :mday))
61
+ else
62
+ Time.zone.local(*parsed.values_at(:year, :mon, :mday, :hour, :min, :sec))
63
+ end
61
64
  end
62
65
 
63
66
  def translate_month_and_day_names(datetime)
@@ -1,3 +1,3 @@
1
1
  module Delocalize
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0".freeze
3
3
  end
@@ -5,6 +5,7 @@ require 'active_support/time'
5
5
 
6
6
  describe Delocalize::Parsers::DateTime do
7
7
  before do
8
+ I18n.locale = I18n.default_locale
8
9
  Time.zone = 'Berlin' # make sure everything works as expected with TimeWithZone
9
10
  @time_parser = Delocalize::Parsers::DateTime.new(Time)
10
11
  @date_parser = Delocalize::Parsers::DateTime.new(Date)
@@ -14,6 +15,23 @@ describe Delocalize::Parsers::DateTime do
14
15
  Timecop.return
15
16
  end
16
17
 
18
+ it "parses a date/time using default parsing" do
19
+ I18n.locale = :en # force default parsing since we don't have a locale file for en
20
+
21
+ date = Date.civil(2009, 2, 28)
22
+ time = Time.zone.local(2009, 2, 28, 12, 30, 45)
23
+ @date_parser.parse('2009-02-28').must_equal date
24
+ @time_parser.parse('2009-02-28 12:30:45').must_equal time
25
+ end
26
+
27
+ it "doesn't parse date/time-like objects" do
28
+ date = Date.civil(2009, 10, 19)
29
+ time = Time.zone.local(2009, 3, 1, 12, 0, 0)
30
+
31
+ @date_parser.parse(date).must_equal date
32
+ @time_parser.parse(time).must_equal time
33
+ end
34
+
17
35
  # date
18
36
  it "parses a date from a string" do
19
37
  date = Date.civil(2009, 10, 19)
@@ -27,6 +45,23 @@ describe Delocalize::Parsers::DateTime do
27
45
  @date_parser.parse('19.10.').must_equal date
28
46
  end
29
47
 
48
+ it "fails for an invalid date (same behavior as without delocalize)" do
49
+ must_raise_invalid_date { @date_parser.parse('29. Februar 2009') }
50
+ end
51
+
52
+ it "rejects invalid dates when using default parsing (Ruby default behavior)" do
53
+ I18n.locale = :en # force default parsing since we don't have a locale file for en
54
+
55
+ must_raise_invalid_date { @date_parser.parse('32.10.2009') }
56
+ end
57
+
58
+
59
+ it "rejects 29th February in a non leap year when using default parsing (Ruby default behavior)" do
60
+ I18n.locale = :en # force default parsing since we don't have a locale file for en
61
+
62
+ must_raise_invalid_date { @date_parser.parse('29.02.2009') }
63
+ end
64
+
30
65
  # datetime
31
66
  it "parses a datetime from a string" do
32
67
  time = Time.zone.local(2009, 3, 1, 12, 0, 0)
@@ -39,6 +74,25 @@ describe Delocalize::Parsers::DateTime do
39
74
  @time_parser.parse('1. März, 12:00 Uhr').must_equal time
40
75
  end
41
76
 
77
+ it "doesn't fail for an invalid datetime (same behavior as without delocalize)" do
78
+ time = Time.zone.local(2009, 3, 1, 12, 0)
79
+ @time_parser.parse('29. Februar 2009, 12:00 Uhr').must_equal time
80
+ end
81
+
82
+ it "shifts invalid dates to the next month when using default parse (mimicking Ruby's default behavior)" do
83
+ I18n.locale = :en # force default parsing since we don't have a locale file for en
84
+
85
+ time = Time.zone.local(2009, 5, 1, 12, 0, 0)
86
+ @time_parser.parse('31.04.2009 12:00').must_equal time
87
+ end
88
+
89
+ it "shifts 29th February to 1st March in a non leap year when using default parse (mimicking Ruby's default behavior)" do
90
+ I18n.locale = :en # force default parsing since we don't have a locale file for en
91
+
92
+ time = Time.zone.local(2009, 3, 1, 12, 0, 0)
93
+ @time_parser.parse('29.02.2009 12:00').must_equal time
94
+ end
95
+
42
96
  # time
43
97
  it "parses a time from a string, defaulting to the current day" do
44
98
  Timecop.freeze(Time.zone.local(2009, 3, 1, 12, 0, 0)) # prevent DST issues
@@ -46,11 +100,21 @@ describe Delocalize::Parsers::DateTime do
46
100
  @time_parser.parse('9:00 Uhr').must_equal time
47
101
  end
48
102
 
49
- it "doesn't parse date/time-like objects" do
50
- date = Date.civil(2009, 10, 19)
51
- time = Time.zone.local(2009, 3, 1, 12, 0, 0)
103
+ it "raises ArgumentError when parsing a bad string" do
104
+ err = proc { @date_parser.parse("asdf") }.must_raise ArgumentError
105
+ err.message.must_equal "invalid date: asdf"
106
+ end
52
107
 
53
- @date_parser.parse(date).must_equal date
54
- @time_parser.parse(time).must_equal time
108
+ it "raises ArgumentError when parsing a bad date" do
109
+ must_raise_invalid_date { @date_parser.parse("02.0.2017") }
110
+ must_raise_invalid_date { @date_parser.parse("02.99.2017") }
111
+ end
112
+
113
+ def must_raise_invalid_date
114
+ begin
115
+ yield
116
+ rescue => ex
117
+ ex.message.must_equal 'invalid date'
118
+ end
55
119
  end
56
120
  end
@@ -2,6 +2,7 @@ require 'test_helper'
2
2
 
3
3
  describe Delocalize::Parsers::Number do
4
4
  before do
5
+ I18n.locale = I18n.default_locale
5
6
  @parser = Delocalize::Parsers::Number.new
6
7
  end
7
8
 
@@ -23,8 +24,8 @@ describe Delocalize::Parsers::Number do
23
24
 
24
25
  it "doesn't change a number if it's already a numeric type" do
25
26
  @parser.parse(1299.99).must_equal 1299.99
26
- @parser.parse(-1299.99).must_equal -1299.99
27
+ @parser.parse(-1299.99).must_equal(-1299.99)
27
28
  @parser.parse(1299).must_equal 1299
28
- @parser.parse(-1299).must_equal -1299
29
+ @parser.parse(-1299).must_equal(-1299)
29
30
  end
30
31
  end
@@ -14,6 +14,7 @@ puts "Testing parameter classes: #{parameters_classes.inspect}"
14
14
  parameters_classes.each do |parameters_class|
15
15
  describe parameters_class do
16
16
  before do
17
+ I18n.locale = I18n.default_locale
17
18
  Time.zone = 'Berlin' # make sure everything works as expected with TimeWithZone
18
19
  end
19
20
 
@@ -159,7 +160,7 @@ parameters_classes.each do |parameters_class|
159
160
  params = parameters_class.new(:parent => { :parent_date => '21. Mai 2004' })
160
161
 
161
162
  ## Should not throw an error:
162
- delocalized_params = params.delocalize({})
163
+ params.delocalize({})
163
164
  end
164
165
 
165
166
  it "delocalizes arrays" do
@@ -170,5 +171,15 @@ parameters_classes.each do |parameters_class|
170
171
  delocalized_params[:location].must_equal ['13.456', '51.234']
171
172
  delocalized_params[:interval].must_equal [Date.civil(2013, 12, 25), Date.civil(2014, 1, 31)]
172
173
  end
174
+
175
+ it "keeps invalid dates in the params hash" do
176
+ params = parameters_class.new(:first_date => "asdf", :second_date => "02.0.2017", :third_date => "02.123.2017")
177
+
178
+ delocalized_params = params.delocalize(:first_date => :date, :second_date => :date, :third_date => :date)
179
+
180
+ delocalized_params[:first_date].must_equal "asdf"
181
+ delocalized_params[:second_date].must_equal "02.0.2017"
182
+ delocalized_params[:third_date].must_equal "02.123.2017"
183
+ end
173
184
  end
174
185
  end
@@ -7,9 +7,11 @@ Bundler.require(:default, :development)
7
7
 
8
8
  require 'minitest/autorun'
9
9
  require 'minitest/spec'
10
- require 'mocha/setup'
11
10
 
12
11
  require 'rails'
12
+ # We need to explicitly load ActiveSupport's version of Hash#slice since Rails 3.2 somehow loads
13
+ # i18n's version first which is different from Rails' (see https://github.com/svenfuchs/i18n/commit/24e71a9a4901ed18c9cab5c53109fd9bf2416bcb).
14
+ require 'active_support/core_ext/hash/slice'
13
15
 
14
16
  de = {
15
17
  :date => {
@@ -65,4 +67,5 @@ tt[:date][:formats][:default] = '%d|%m|%Y'
65
67
  I18n.backend.store_translations :de, de
66
68
  I18n.backend.store_translations :tt, tt
67
69
 
70
+ I18n.default_locale = :de
68
71
  I18n.locale = :de
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delocalize
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clemens Kofler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-20 00:00:00.000000000 Z
11
+ date: 2017-06-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2'
27
- - !ruby/object:Gem::Dependency
28
- name: mocha
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: timecop
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -68,10 +54,6 @@ files:
68
54
  - lib/delocalize/parsers.rb
69
55
  - lib/delocalize/parsers/date_time.rb
70
56
  - lib/delocalize/parsers/number.rb
71
- - lib/delocalize/rails_ext/action_view_rails3.rb
72
- - lib/delocalize/rails_ext/action_view_rails4.rb
73
- - lib/delocalize/rails_ext/active_record_rails3.rb
74
- - lib/delocalize/rails_ext/active_record_rails4.rb
75
57
  - lib/delocalize/railtie.rb
76
58
  - lib/delocalize/version.rb
77
59
  - test/database.yml
@@ -100,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
100
82
  version: '0'
101
83
  requirements: []
102
84
  rubyforge_project:
103
- rubygems_version: 2.4.5.1
85
+ rubygems_version: 2.6.11
104
86
  signing_key:
105
87
  specification_version: 4
106
88
  summary: Localized date/time and number parsing
@@ -1,53 +0,0 @@
1
- require 'action_view'
2
-
3
- # TODO: also override other methods like to_check_box_tag since they might contain numeric values?
4
- # ActionView needs some patching too
5
-
6
- ActionView::Helpers::InstanceTag.class_eval do
7
- include ActionView::Helpers::NumberHelper
8
-
9
- alias original_to_input_field_tag to_input_field_tag
10
- def to_input_field_tag(field_type, options = {})
11
- options.symbolize_keys!
12
- # numbers and dates/times should be localized unless value is already defined
13
- if object && (options[:value].blank? || !options[:value].is_a?(String)) && object.respond_to?(:column_for_attribute) && column = object.column_for_attribute(method_name)
14
- value = options[:value] || object.send(method_name)
15
-
16
- if column.number?
17
- number_options = I18n.t(:'number.format')
18
- separator = options.delete(:separator) || number_options[:separator]
19
- delimiter = options.delete(:delimiter) || number_options[:delimiter]
20
- precision = options.delete(:precision) || number_options[:precision]
21
- opts = { :separator => separator, :delimiter => delimiter, :precision => precision }
22
- # integers don't need a precision
23
- opts.merge!(:precision => 0) if column.type == :integer
24
-
25
- hidden_for_integer = field_type == 'hidden' && column.type == :integer
26
-
27
- # the number will be formatted only if it has no numericality errors
28
- if object.respond_to?(:errors) && !Array(object.errors[method_name]).try(:include?, 'is not a number')
29
- # we don't format integer hidden fields because this breaks nested_attributes
30
- options[:value] = number_with_precision(value, opts) unless hidden_for_integer
31
- end
32
- elsif column.date? || column.time?
33
- options[:value] = value ? I18n.l(value, :format => options.delete(:format)) : nil
34
- end
35
- end
36
-
37
- original_to_input_field_tag(field_type, options)
38
- end
39
- end
40
-
41
- # TODO: does it make sense to also override FormTagHelper methods?
42
- # ActionView::Helpers::FormTagHelper.class_eval do
43
- # include ActionView::Helpers::NumberHelper
44
- #
45
- # alias original_text_field_tag text_field_tag
46
- # def text_field_tag(name, value = nil, options = {})
47
- # value = options.delete(:value) if options.key?(:value)
48
- # if value.is_a?(Numeric)
49
- # value = number_with_delimiter(value)
50
- # end
51
- # original_text_field_tag(name, value, options)
52
- # end
53
- # end
@@ -1,38 +0,0 @@
1
- require 'action_view'
2
-
3
- # TODO: also override other methods like to_check_box_tag since they might contain numeric values?
4
- # ActionView needs some patching too
5
-
6
- ActionView::Helpers::Tags::TextField.class_eval do
7
- include ActionView::Helpers::NumberHelper
8
-
9
- def render_with_localization
10
- if object && (@options[:value].blank? || !@options[:value].is_a?(String)) && object.respond_to?(:column_for_attribute) && column = object.column_for_attribute(@method_name)
11
- value = @options[:value] || object.send(@method_name)
12
-
13
- if column.number?
14
- number_options = I18n.t(:'number.format')
15
- separator = @options.delete(:separator) || number_options[:separator]
16
- delimiter = @options.delete(:delimiter) || number_options[:delimiter]
17
- precision = @options.delete(:precision) || number_options[:precision]
18
- opts = { :separator => separator, :delimiter => delimiter, :precision => precision }
19
- # integers don't need a precision
20
- opts.merge!(:precision => 0) if column.type == :integer
21
-
22
- hidden_for_integer = field_type == 'hidden' && column.type == :integer
23
-
24
- # the number will be formatted only if it has no numericality errors
25
- if object.respond_to?(:errors) && !Array(object.errors[@method_name]).try(:include?, 'is not a number')
26
- # we don't format integer hidden fields because this breaks nested_attributes
27
- @options[:value] = number_with_precision(value, opts) unless hidden_for_integer
28
- end
29
- elsif column.date? || column.time?
30
- @options[:value] = value ? I18n.l(value, :format => @options.delete(:format)) : nil
31
- end
32
- end
33
-
34
- render_without_localization
35
- end
36
-
37
- alias_method_chain :render, :localization
38
- end
@@ -1,80 +0,0 @@
1
- require 'active_record'
2
-
3
- require 'active_record/connection_adapters/abstract/schema_definitions'
4
- begin
5
- require 'active_record/connection_adapters/column'
6
- rescue LoadError
7
- # Not Rails 3.1, it seems
8
- end
9
-
10
- # let's hack into ActiveRecord a bit - everything at the lowest possible level, of course, so we minimalize side effects
11
- ActiveRecord::ConnectionAdapters::Column.class_eval do
12
- def date?
13
- klass == Date
14
- end
15
-
16
- def time?
17
- klass == Time
18
- end
19
- end
20
-
21
- ActiveRecord::Base.class_eval do
22
- def write_attribute_with_localization(attr_name, original_value)
23
- new_value = original_value
24
- if column = column_for_attribute(attr_name.to_s)
25
- if column.date?
26
- new_value = Date.parse_localized(original_value) rescue original_value
27
- elsif column.time?
28
- new_value = Time.parse_localized(original_value) rescue original_value
29
- end
30
- end
31
- write_attribute_without_localization(attr_name, new_value)
32
- end
33
- alias_method_chain :write_attribute, :localization
34
-
35
- def convert_number_column_value_with_localization(value)
36
- value = convert_number_column_value_without_localization(value)
37
- value = Numeric.parse_localized(value) if I18n.delocalization_enabled?
38
- value
39
- end
40
- alias_method_chain :convert_number_column_value, :localization
41
-
42
-
43
- define_method( (Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new('3.2.9')) ? :field_changed? : :_field_changed? ) do |attr, old, value|
44
- if column = column_for_attribute(attr)
45
- if column.number? && column.null && (old.nil? || old == 0) && value.blank?
46
- # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
47
- # Hence we don't record it as a change if the value changes from nil to ''.
48
- # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
49
- # be typecast back to 0 (''.to_i => 0)
50
- value = nil
51
- elsif column.number?
52
- value = column.type_cast(convert_number_column_value_with_localization(value))
53
- else
54
- value = column.type_cast(value)
55
- end
56
- end
57
- old != value
58
- end
59
- end
60
-
61
- ActiveRecord::Base.instance_eval do
62
- def define_method_attribute=(attr_name)
63
- if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
64
- method_body, line = <<-EOV, __LINE__ + 1
65
- def #{attr_name}=(original_time)
66
- time = original_time
67
- unless time.acts_like?(:time)
68
- time = time.is_a?(String) ? (I18n.delocalization_enabled? ? Time.zone.parse_localized(time) : Time.zone.parse(time)) : time.to_time rescue time
69
- end
70
- time = time.in_time_zone rescue nil if time
71
- write_attribute(:#{attr_name}, original_time)
72
- @attributes_cache["#{attr_name}"] = time
73
- end
74
- EOV
75
- generated_attribute_methods.module_eval(method_body, __FILE__, line)
76
- else
77
- super
78
- end
79
- end
80
- end
@@ -1,72 +0,0 @@
1
- require 'active_record'
2
-
3
- # let's hack into ActiveRecord a bit - everything at the lowest possible level, of course, so we minimalize side effects
4
- ActiveRecord::ConnectionAdapters::Column.class_eval do
5
- def date?
6
- klass == Date
7
- end
8
-
9
- def time?
10
- klass == Time
11
- end
12
- end
13
-
14
- module ActiveRecord::AttributeMethods::Write
15
- def type_cast_attribute_for_write(column, value)
16
- return value unless column
17
-
18
- value = Numeric.parse_localized(value) if column.number? && I18n.delocalization_enabled?
19
- column.type_cast_for_write value
20
- end
21
- end
22
-
23
- ActiveRecord::Base.class_eval do
24
- def write_attribute_with_localization(attr_name, original_value)
25
- new_value = original_value
26
- if column = column_for_attribute(attr_name.to_s)
27
- if column.date?
28
- new_value = Date.parse_localized(original_value) rescue original_value
29
- elsif column.time?
30
- new_value = Time.parse_localized(original_value) rescue original_value
31
- end
32
- end
33
- write_attribute_without_localization(attr_name, new_value)
34
- end
35
- alias_method_chain :write_attribute, :localization
36
-
37
- define_method :_field_changed? do |attr, old, value|
38
- if column = column_for_attribute(attr)
39
- if column.number? && column.null && (old.nil? || old == 0) && value.blank?
40
- # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
41
- # Hence we don't record it as a change if the value changes from nil to ''.
42
- # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
43
- # be typecast back to 0 (''.to_i => 0)
44
- value = nil
45
- elsif column.number?
46
- value = column.type_cast(Numeric.parse_localized(value))
47
- else
48
- value = column.type_cast(value)
49
- end
50
- end
51
- old != value
52
- end
53
-
54
- def define_method_attribute=(attr_name)
55
- if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
56
- method_body, line = <<-EOV, __LINE__ + 1
57
- def #{attr_name}=(original_time)
58
- time = original_time
59
- unless time.acts_like?(:time)
60
- time = time.is_a?(String) ? (I18n.delocalization_enabled? ? Time.zone.parse_localized(time) : Time.zone.parse(time)) : time.to_time rescue time
61
- end
62
- time = time.in_time_zone rescue nil if time
63
- write_attribute(:#{attr_name}, original_time)
64
- @attributes_cache["#{attr_name}"] = time
65
- end
66
- EOV
67
- generated_attribute_methods.module_eval(method_body, __FILE__, line)
68
- else
69
- super
70
- end
71
- end
72
- end