delocalize 0.1.5 → 0.1.6

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,4 +1,4 @@
1
- Copyright (c) 2009 [name of plugin creator]
1
+ Copyright (c) 2009 Clemens Kofler
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -0,0 +1,115 @@
1
+ delocalize
2
+ ==========
3
+
4
+ delocalize provides localized date/time and number parsing functionality for Rails.
5
+
6
+ Installation
7
+ ---------------
8
+
9
+ You can use delocalize either as a gem (preferred) or as a Rails plugin.
10
+
11
+ To use the gem version, put the following gem requirement in your `environment.rb`:
12
+
13
+ config.gem "delocalize", :source => 'http://gemcutter.org'
14
+
15
+ To install it as a plugin, fire up your terminal, go to your Rails app and type:
16
+
17
+ $ ruby script/plugin install git://github.com/clemens/delocalize.git
18
+
19
+
20
+ What does it do? And how do I use it?
21
+ ---------------------------------------------------
22
+
23
+ Delocalize, just as the name suggest, does pretty much the opposite of localize.
24
+
25
+ In the grey past, if you want your users to be able to input localized data, such as dates and numbers, you had to manually override attribute accessors:
26
+
27
+ def price=(price)
28
+ write_attribute(:price, price.gsub(',', '.'))
29
+ end
30
+
31
+ delocalize does this under the covers -- all you need is your regular translation data (as YAML or Ruby file) where you need Rails' standard translations:
32
+
33
+ de:
34
+ number:
35
+ format:
36
+ separator: ','
37
+ delimiter: '.'
38
+ date:
39
+ input:
40
+ formats: [:default, :long, :short] # <- this and ...
41
+
42
+ formats:
43
+ default: "%d.%m.%Y"
44
+ short: "%e. %b"
45
+ long: "%e. %B %Y"
46
+ only_day: "%e"
47
+
48
+ day_names: [Sonntag, Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag]
49
+ abbr_day_names: [So, Mo, Di, Mi, Do, Fr, Sa]
50
+ month_names: [~, Januar, Februar, März, April, Mai, Juni, Juli, August, September, Oktober, November, Dezember]
51
+ abbr_month_names: [~, Jan, Feb, Mär, Apr, Mai, Jun, Jul, Aug, Sep, Okt, Nov, Dez]
52
+ order: [ :day, :month, :year ]
53
+
54
+ time:
55
+ input:
56
+ formats: [:long, :medium, :short, :default, :time] # <- ... this are the only non-standard keys
57
+ formats:
58
+ default: "%A, %e. %B %Y, %H:%M Uhr"
59
+ short: "%e. %B, %H:%M Uhr"
60
+ long: "%A, %e. %B %Y, %H:%M Uhr"
61
+ time: "%H:%M"
62
+
63
+ am: "vormittags"
64
+ pm: "nachmittags"
65
+
66
+ For dates and times, you have to define input formats which are taken from the actual formats. The important thing here is to define input formats sorted by descending complexity; in other words: the format which contains the most (preferably non-numeric) information should be first in the list because it can produce the most reliable match. Exception: If you think there most complex format is not the one that most users will input, you can put the most-used in front so you save unnecessary iterations.
67
+
68
+ Careful with formats containing only numbers: It's very hard to produce reliable matches if you provide multiple strictly numeric formats!
69
+
70
+ delocalize then overrides `to_input_field_tag` in ActionView's `InstanceTag` so you can use localized text fields:
71
+
72
+ <% form_for @product do |f| %>
73
+ <%= f.text_field :name %>
74
+ <%= f.text_field :released_on %>
75
+ <%= f.text_field :price %>
76
+ <% end %>
77
+
78
+ In this example, a user can enter the release date and the price just like he's used to in his language, for example:
79
+
80
+ > Name: "Couch"
81
+ > Released on: "12. Oktober 2009"
82
+ > Price: "2.999,90"
83
+
84
+ When saved, ActiveRecord automatically converts these to a regular Ruby date and number.
85
+
86
+ Edit forms then also show localized dates/numbers. By default, dates and times are localized using the format named :default in your locale file. So with the above locale file, dates would use `%d.%m.%Y` and times would use `%A, %e. %B %Y, %H:%M Uhr`. Numbers are also formatted using your locale's thousands delimiter and decimal separator.
87
+
88
+ You can also customize the output using some options:
89
+
90
+ The price should always show two decimal digits and we don't need the delimiter:
91
+ <%= f.text_field :price, :precision => 2, :delimiter => '' %>
92
+
93
+ The `released_on` date should be shown in the `:full` format:
94
+ <%= f.text_field :released_on, :format => :full %>
95
+
96
+ Since `I18n.localize` supports localizing `strftime` strings, we can also do this:
97
+ <%= f.text_field :released_on, :format => "%B %Y" %>
98
+
99
+ ### Note
100
+
101
+ delocalize is most definitely not enterprise-ready! ;-)
102
+ Or as Yaroslav says: Contains small pieces. Not good for children of age 3 and less. Not enterprise-ready.
103
+
104
+ ### TODO
105
+
106
+ * Improve test coverage
107
+ * Separate Ruby/Rails stuff to make it usable outside Rails
108
+ * Verify correct behavior with time zones
109
+ * Decide on other ActionView hacks (e.g. `text_field_tag`)
110
+ * Implement AM/PM support
111
+ * Cleanup, cleanup, cleanup ...
112
+
113
+ Copyright (c) 2009-2010 Clemens Kofler &lt;clemens@railway.at&gt;
114
+ http://www.railway.at/
115
+ Released under the MIT license
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.5
1
+ 0.1.6
@@ -8,7 +8,8 @@ module Delocalize
8
8
  def parse(value)
9
9
  if value.is_a?(String)
10
10
  separator = I18n.t(:'number.format.separator')
11
- value = value.gsub(/[^0-9\-#{separator}]/, '').gsub(separator, '.')
11
+ delimiter = I18n.t(:'number.format.delimiter')
12
+ value = value.gsub(delimiter, '').gsub(separator, '.')
12
13
  end
13
14
  value
14
15
  end
@@ -22,7 +22,13 @@ ActionView::Helpers::InstanceTag.class_eval do
22
22
  # integers don't need a precision
23
23
  opts.merge!(:precision => 0) if column.type == :integer
24
24
 
25
- options[:value] = number_with_precision(value, opts)
25
+ hidden_for_integer = field_type == 'hidden' && column.type == :integer
26
+
27
+ # the number will be formatted only if it has no errors
28
+ if object.respond_to?(:errors) && !object.errors.invalid?(method_name)
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
26
32
  elsif column.date? || column.time?
27
33
  options[:value] = value ? I18n.l(value, :format => options.delete(:format)) : nil
28
34
  end
@@ -45,4 +51,4 @@ end
45
51
  # end
46
52
  # original_text_field_tag(name, value, options)
47
53
  # end
48
- # end
54
+ # end
@@ -16,8 +16,6 @@ ActiveRecord::Base.class_eval do
16
16
  value = Date.parse_localized(value)
17
17
  elsif column.time?
18
18
  value = Time.parse_localized(value)
19
- elsif column.number?
20
- value = column.type_cast(convert_number_column_value_with_localization(value))
21
19
  end
22
20
  end
23
21
  write_attribute_without_localization(attr_name, value)
@@ -38,6 +36,24 @@ ActiveRecord::Base.class_eval do
38
36
  evaluate_attribute_method attr_name, method_body, "#{attr_name}="
39
37
  end
40
38
 
39
+ # overriding to convert numbers with localization
40
+ # this method belongs to Dirty module
41
+ def field_changed?(attr, old, value)
42
+ if column = column_for_attribute(attr)
43
+ if column.number? && column.null && (old.nil? || old == 0) && value.blank?
44
+ # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
45
+ # Hence we don't record it as a change if the value changes from nil to ''.
46
+ # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
47
+ # be typecast back to 0 (''.to_i => 0)
48
+ value = nil
49
+ else
50
+ value = column.type_cast(convert_number_column_value_with_localization(value))
51
+ end
52
+ end
53
+
54
+ old != value
55
+ end
56
+
41
57
  def convert_number_column_value_with_localization(value)
42
58
  value = convert_number_column_value_without_localization(value)
43
59
  value = Numeric.parse_localized(value) if I18n.delocalization_enabled?
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require File.dirname(__FILE__) + '/test_helper'
2
4
 
3
5
  class DelocalizeActiveRecordTest < ActiveRecord::TestCase
@@ -32,7 +34,7 @@ class DelocalizeActiveRecordTest < ActiveRecord::TestCase
32
34
  end
33
35
 
34
36
  test "delocalizes localized datetime with year" do
35
- time = Time.local(2009, 3, 1, 12, 0, 0)
37
+ time = Time.gm(2009, 3, 1, 11, 0, 0).in_time_zone
36
38
 
37
39
  @product.published_at = 'Sonntag, 1. März 2009, 12:00 Uhr'
38
40
  assert_equal time, @product.published_at
@@ -42,7 +44,7 @@ class DelocalizeActiveRecordTest < ActiveRecord::TestCase
42
44
  end
43
45
 
44
46
  test "delocalizes localized datetime without year" do
45
- time = Time.local(Date.today.year, 3, 1, 12, 0, 0)
47
+ time = Time.gm(Date.today.year, 3, 1, 11, 0, 0).in_time_zone
46
48
 
47
49
  @product.published_at = '1. März, 12:00 Uhr'
48
50
  assert_equal time, @product.published_at
@@ -50,7 +52,7 @@ class DelocalizeActiveRecordTest < ActiveRecord::TestCase
50
52
 
51
53
  test "delocalizes localized time" do
52
54
  now = Time.current
53
- time = Time.local(now.year, now.month, now.day, 9, 0, 0)
55
+ time = Time.gm(now.year, now.month, now.day, 7, 0, 0).in_time_zone
54
56
  @product.cant_think_of_a_sensible_time_field = '09:00 Uhr'
55
57
  assert_equal time, @product.cant_think_of_a_sensible_time_field
56
58
  end
@@ -61,12 +63,12 @@ class DelocalizeActiveRecordTest < ActiveRecord::TestCase
61
63
  @product.released_on = '2009/10/19'
62
64
  assert_equal date, @product.released_on
63
65
 
64
- time = Time.local(2009, 3, 1, 12, 0, 0)
66
+ time = Time.gm(2009, 3, 1, 11, 0, 0).in_time_zone
65
67
  @product.published_at = '2009/03/01 12:00'
66
68
  assert_equal time, @product.published_at
67
69
 
68
70
  now = Time.current
69
- time = Time.local(now.year, now.month, now.day, 9, 0, 0)
71
+ time = Time.gm(now.year, now.month, now.day, 7, 0, 0).in_time_zone
70
72
  @product.cant_think_of_a_sensible_time_field = '09:00'
71
73
  assert_equal time, @product.cant_think_of_a_sensible_time_field
72
74
  end
@@ -131,6 +133,12 @@ class DelocalizeActiveRecordTest < ActiveRecord::TestCase
131
133
  @product.weight = "10,34"
132
134
  assert @product.weight_changed?
133
135
  end
136
+
137
+ test "should remember the value before type cast" do
138
+ @product.price = "asd"
139
+ assert_equal @product.price, 0
140
+ assert_equal @product.price_before_type_cast, "asd"
141
+ end
134
142
  end
135
143
 
136
144
  class DelocalizeActionViewTest < ActionView::TestCase
@@ -195,6 +203,12 @@ class DelocalizeActionViewTest < ActionView::TestCase
195
203
  text_field(:product, :cant_think_of_a_sensible_time_field, :format => :time)
196
204
  end
197
205
 
206
+ test "integer hidden fields shouldn't be formatted" do
207
+ @product.times_sold = 1000
208
+ assert_dom_equal '<input id="product_times_sold" name="product[times_sold]" type="hidden" value="1000" />',
209
+ hidden_field(:product, :times_sold)
210
+ end
211
+
198
212
  test "doesn't raise an exception when object is nil" do
199
213
  assert_nothing_raised {
200
214
  text_field(:not_here, :a_text_field)
@@ -216,6 +230,13 @@ class DelocalizeActionViewTest < ActionView::TestCase
216
230
  text_field(:product, :price, :value => "1.499,90")
217
231
  end
218
232
 
233
+ test "don't convert type if field has errors" do
234
+ @product = ProductWithValidation.new(:price => 'this is not a number')
235
+ @product.valid?
236
+ assert_dom_equal '<div class="fieldWithErrors"><input id="product_price" name="product[price]" size="30" type="text" value="this is not a number" /></div>',
237
+ text_field(:product, :price)
238
+ end
239
+
219
240
  test "doesn't raise an exception when object isn't an ActiveReccord" do
220
241
  @product = NonArProduct.new
221
242
  assert_nothing_raised {
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  ENV["RAILS_ENV"] = "test"
2
4
  #require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
3
5
  require File.expand_path(File.dirname(__FILE__) + "/rails_app/config/environment")
@@ -59,6 +61,11 @@ end
59
61
  class Product < ActiveRecord::Base
60
62
  end
61
63
 
64
+ class ProductWithValidation < Product
65
+ validates_numericality_of :price
66
+ validates_presence_of :price
67
+ end
68
+
62
69
  config = YAML.load_file(File.dirname(__FILE__) + '/database.yml')
63
70
  ActiveRecord::Base.establish_connection(config['test'])
64
71
 
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delocalize
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 6
9
+ version: 0.1.6
5
10
  platform: ruby
6
11
  authors:
7
12
  - Clemens Kofler
@@ -9,7 +14,7 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-01-17 00:00:00 +01:00
17
+ date: 2010-05-13 00:00:00 +02:00
13
18
  default_executable:
14
19
  dependencies: []
15
20
 
@@ -20,10 +25,9 @@ executables: []
20
25
  extensions: []
21
26
 
22
27
  extra_rdoc_files:
23
- - README
28
+ - README.markdown
24
29
  files:
25
30
  - MIT-LICENSE
26
- - README
27
31
  - Rakefile
28
32
  - VERSION
29
33
  - init.rb
@@ -43,6 +47,7 @@ files:
43
47
  - tasks/distribution.rb
44
48
  - tasks/documentation.rb
45
49
  - tasks/testing.rb
50
+ - README.markdown
46
51
  has_rdoc: true
47
52
  homepage: http://github.com/clemens/delocalize
48
53
  licenses: []
@@ -56,18 +61,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
56
61
  requirements:
57
62
  - - ">="
58
63
  - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
59
66
  version: "0"
60
- version:
61
67
  required_rubygems_version: !ruby/object:Gem::Requirement
62
68
  requirements:
63
69
  - - ">="
64
70
  - !ruby/object:Gem::Version
71
+ segments:
72
+ - 0
65
73
  version: "0"
66
- version:
67
74
  requirements: []
68
75
 
69
76
  rubyforge_project:
70
- rubygems_version: 1.3.5
77
+ rubygems_version: 1.3.6
71
78
  signing_key:
72
79
  specification_version: 3
73
80
  summary: Localized date/time and number parsing
data/README DELETED
@@ -1,117 +0,0 @@
1
- delocalize
2
- ==========
3
-
4
- delocalize provides localized date/time and number parsing functionality for Rails.
5
-
6
- Installation
7
- ============
8
-
9
- You can use delocalize either as a gem (preferred) or as a Rails plugin.
10
-
11
- To use the gem version, put the following gem requirement in your environment.rb:
12
-
13
- config.gem "delocalize", :source => 'http://gemcutter.org'
14
-
15
- To install it as a plugin, fire up your terminal, go to your Rails app and type:
16
-
17
- $ ruby script/plugin install git://github.com/clemens/delocalize.git
18
-
19
-
20
- What does it do? And how do I use it?
21
- =====================================
22
-
23
- Delocalize, just as the name suggest, does pretty much the opposite of localize.
24
-
25
- In the grey past, ff you want your users to be able to input localized data, such as dates and numbers, you had to manually override attribute accessors:
26
-
27
- def price=(price)
28
- write_attribute(:price, price.gsub(',', '.'))
29
- end
30
-
31
- delocalize does this under the covers - all you need is your regular translation data (as YAML or Ruby file) where you need Rails' standard translations:
32
-
33
- de:
34
- number:
35
- format:
36
- separator: ','
37
- delimiter: '.'
38
- date:
39
- input:
40
- formats: [:default, :long, :short] # <- this and ...
41
-
42
- formats:
43
- default: "%d.%m.%Y"
44
- short: "%e. %b"
45
- long: "%e. %B %Y"
46
- only_day: "%e"
47
-
48
- day_names: [Sonntag, Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag]
49
- abbr_day_names: [So, Mo, Di, Mi, Do, Fr, Sa]
50
- month_names: [~, Januar, Februar, März, April, Mai, Juni, Juli, August, September, Oktober, November, Dezember]
51
- abbr_month_names: [~, Jan, Feb, Mär, Apr, Mai, Jun, Jul, Aug, Sep, Okt, Nov, Dez]
52
- order: [ :day, :month, :year ]
53
-
54
- time:
55
- input:
56
- formats: [:long, :medium, :short, :default, :time] # <- ... this are the only non-standard keys
57
- formats:
58
- default: "%A, %e. %B %Y, %H:%M Uhr"
59
- short: "%e. %B, %H:%M Uhr"
60
- long: "%A, %e. %B %Y, %H:%M Uhr"
61
- time: "%H:%M"
62
-
63
- am: "vormittags"
64
- pm: "nachmittags"
65
-
66
- For dates and times, you have to define input formats which are taken from the actual formats. The important thing here is to define input formats sorted by descending complexity - in other words: The format which contains the most (preferably non-numeric) information should be first in the list because it can produce the most reliable match. Exception: If you think there most complex format is not the one that most users will input, you can put the most-used in front so you save unnecessary iterations.
67
-
68
- Careful with formats containing only numbers: It's very hard to produce reliable matches if you provide multiple strictly numeric formats!
69
-
70
- delocalize then overrides to_input_field_tag in ActionView's InstanceTag so you can use localized text fields:
71
-
72
- <% form_for @product do |f| %>
73
- <%= f.text_field :name %>
74
- <%= f.text_field :released_on %>
75
- <%= f.text_field :price %>
76
- <% end %>
77
-
78
- In this example, a user can enter the release date and the price just like he's used to in his language, for example:
79
-
80
- Name: "Couch"
81
- Released on: "12. Oktober 2009"
82
- Price: "2.999,90"
83
-
84
- When saved, ActiveRecord automatically converts these to a regular Ruby date and number.
85
-
86
- Edit forms then also show localized dates/numbers. By default, dates and times are localized using the format named :default in your locale file. So with the above locale file, dates would use "%d.%m.%Y" and times would use "%A, %e. %B %Y, %H:%M Uhr". Numbers are also formatted using your locale's thousands delimiter and decimal separator.
87
-
88
- You can also customize the output using some options:
89
-
90
- The price should always show two decimal digits and we don't need the delimiter:
91
- <%= f.text_field :price, :precision => 2, :delimiter => '' %>
92
-
93
- The released_on date should be shown in the :full format:
94
- <%= f.text_field :released_on, :format => :full %>
95
-
96
- Since I18n.localize supportes localizing strftime strings, we can also do this:
97
- <%= f.text_field :released_on, :format => "%B %Y" %>
98
-
99
- Note
100
- ====
101
-
102
- delocalize is most definitely not enterprise-ready! ;-)
103
- Or as Yaroslav says: Contains small pieces. Not good for children of age 3 and less. Not enterprise-ready.
104
-
105
- TODO
106
- ====
107
-
108
- * Improve test coverage
109
- * Separate Ruby/Rails stuff to make it usable outside Rails
110
- * Verify correct behavior with time zones
111
- * Decide on other ActionView hacks (e.g. text_field_tag)
112
- * Implement AM/PM support
113
- * Cleanup, cleanup, cleanup ...
114
-
115
- Copyright (c) 2009 Clemens Kofler <clemens@railway.at>
116
- www.railway.at
117
- Released under the MIT license