delocalize 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +147 -126
- data/lib/delocalize/localized_date_time_parser.rb +8 -2
- data/lib/delocalize/rails_ext/action_view.rb +22 -24
- data/lib/delocalize/rails_ext/active_record.rb +2 -2
- data/test/delocalize_test.rb +52 -17
- data/test/test_helper.rb +18 -0
- metadata +23 -20
- data/Rakefile +0 -14
- data/VERSION +0 -1
data/README.markdown
CHANGED
@@ -14,7 +14,9 @@ You can use delocalize as a gem (preferred). Using delocalize as a Rails plugin
|
|
14
14
|
|
15
15
|
To use delocalize, put the following gem requirement in your `Gemfile`:
|
16
16
|
|
17
|
-
|
17
|
+
```ruby
|
18
|
+
gem "delocalize"
|
19
|
+
```
|
18
20
|
|
19
21
|
### Rails 2
|
20
22
|
|
@@ -22,7 +24,9 @@ Note: Support for Rails 2 has been discontinued. This version is only considered
|
|
22
24
|
|
23
25
|
To use delocalize, put the following gem requirement in your `environment.rb`:
|
24
26
|
|
25
|
-
|
27
|
+
```ruby
|
28
|
+
config.gem "delocalize", :source => 'http://gemcutter.org'
|
29
|
+
```
|
26
30
|
|
27
31
|
In Rails 2.3, alternatively, you can use it with Bundler. See http://gembundler.com/rails23.html for instructions.
|
28
32
|
|
@@ -33,44 +37,48 @@ Delocalize, just as the name suggest, does pretty much the opposite of localize.
|
|
33
37
|
|
34
38
|
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:
|
35
39
|
|
36
|
-
|
37
|
-
|
38
|
-
|
40
|
+
```ruby
|
41
|
+
def price=(price)
|
42
|
+
write_attribute(:price, price.gsub(',', '.'))
|
43
|
+
end
|
44
|
+
```
|
39
45
|
|
40
46
|
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:
|
41
47
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
48
|
+
```yml
|
49
|
+
de:
|
50
|
+
number:
|
51
|
+
format:
|
52
|
+
separator: ','
|
53
|
+
delimiter: '.'
|
54
|
+
date:
|
55
|
+
input:
|
56
|
+
formats: [:default, :long, :short] # <- this and ...
|
57
|
+
|
58
|
+
formats:
|
59
|
+
default: "%d.%m.%Y"
|
60
|
+
short: "%e. %b"
|
61
|
+
long: "%e. %B %Y"
|
62
|
+
only_day: "%e"
|
63
|
+
|
64
|
+
day_names: [Sonntag, Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag]
|
65
|
+
abbr_day_names: [So, Mo, Di, Mi, Do, Fr, Sa]
|
66
|
+
month_names: [~, Januar, Februar, März, April, Mai, Juni, Juli, August, September, Oktober, November, Dezember]
|
67
|
+
abbr_month_names: [~, Jan, Feb, Mär, Apr, Mai, Jun, Jul, Aug, Sep, Okt, Nov, Dez]
|
68
|
+
order: [ :day, :month, :year ]
|
69
|
+
|
70
|
+
time:
|
71
|
+
input:
|
72
|
+
formats: [:long, :medium, :short, :default, :time] # <- ... this are the only non-standard keys
|
73
|
+
formats:
|
74
|
+
default: "%A, %e. %B %Y, %H:%M Uhr"
|
75
|
+
short: "%e. %B, %H:%M Uhr"
|
76
|
+
long: "%A, %e. %B %Y, %H:%M Uhr"
|
77
|
+
time: "%H:%M"
|
78
|
+
|
79
|
+
am: "vormittags"
|
80
|
+
pm: "nachmittags"
|
81
|
+
```
|
74
82
|
|
75
83
|
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.
|
76
84
|
|
@@ -78,12 +86,14 @@ Careful with formats containing only numbers: It's very hard to produce reliable
|
|
78
86
|
|
79
87
|
delocalize then overrides `to_input_field_tag` in ActionView's `InstanceTag` so you can use localized text fields:
|
80
88
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
89
|
+
```erb
|
90
|
+
<% form_for @product do |f| %>
|
91
|
+
<%= f.text_field :name %>
|
92
|
+
<%= f.text_field :released_on %>
|
93
|
+
<%= f.text_field :price %>
|
94
|
+
<% end %>
|
95
|
+
```
|
96
|
+
|
87
97
|
In this example, a user can enter the release date and the price just like he's used to in his language, for example:
|
88
98
|
|
89
99
|
> Name: "Couch"
|
@@ -97,13 +107,22 @@ Edit forms then also show localized dates/numbers. By default, dates and times a
|
|
97
107
|
You can also customize the output using some options:
|
98
108
|
|
99
109
|
The price should always show two decimal digits and we don't need the delimiter:
|
100
|
-
<%= f.text_field :price, :precision => 2, :delimiter => '' %>
|
101
110
|
|
102
|
-
|
103
|
-
|
111
|
+
```erb
|
112
|
+
<%= f.text_field :price, :precision => 2, :delimiter => '' %>
|
113
|
+
```
|
104
114
|
|
115
|
+
The `released_on` date should be shown in the `:full` format:
|
116
|
+
|
117
|
+
```erb
|
118
|
+
<%= f.text_field :released_on, :format => :full %>
|
119
|
+
```
|
120
|
+
|
105
121
|
Since `I18n.localize` supports localizing `strftime` strings, we can also do this:
|
106
|
-
|
122
|
+
|
123
|
+
```erb
|
124
|
+
<%= f.text_field :released_on, :format => "%B %Y" %>
|
125
|
+
```
|
107
126
|
|
108
127
|
### Ruby 1.9 + Psych YAML Parser
|
109
128
|
|
@@ -121,85 +140,87 @@ http://pivotallabs.com/users/mkocher/blog/articles/1692-yaml-psych-and-ruby-1-9-
|
|
121
140
|
|
122
141
|
__Psych Preferred Formatting:__
|
123
142
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
143
|
+
```yml
|
144
|
+
en:
|
145
|
+
number:
|
146
|
+
format:
|
147
|
+
separator: '.'
|
148
|
+
delimiter: ','
|
149
|
+
precision: 2
|
150
|
+
date:
|
151
|
+
input:
|
152
|
+
formats:
|
153
|
+
- :default
|
154
|
+
- :long
|
155
|
+
- :short
|
156
|
+
formats:
|
157
|
+
default: "%m/%d/%Y"
|
158
|
+
short: "%b %e"
|
159
|
+
long: "%B %e, %Y"
|
160
|
+
only_day: "%e"
|
161
|
+
day_names:
|
162
|
+
- Sunday
|
163
|
+
- Monday
|
164
|
+
- Tuesday
|
165
|
+
- Wednesday
|
166
|
+
- Thursday
|
167
|
+
- Friday
|
168
|
+
- Saturday
|
169
|
+
abbr_day_names:
|
170
|
+
- Sun
|
171
|
+
- Mon
|
172
|
+
- Tue
|
173
|
+
- Wed
|
174
|
+
- Thur
|
175
|
+
- Fri
|
176
|
+
- Sat
|
177
|
+
month_names:
|
178
|
+
- ~
|
179
|
+
- January
|
180
|
+
- February
|
181
|
+
- March
|
182
|
+
- April
|
183
|
+
- May
|
184
|
+
- June
|
185
|
+
- July
|
186
|
+
- August
|
187
|
+
- September
|
188
|
+
- October
|
189
|
+
- November
|
190
|
+
- December
|
191
|
+
abbr_month_names:
|
192
|
+
- ~
|
193
|
+
- Jan
|
194
|
+
- Feb
|
195
|
+
- Mar
|
196
|
+
- Apr
|
197
|
+
- May
|
198
|
+
- Jun
|
199
|
+
- Jul
|
200
|
+
- Aug
|
201
|
+
- Sep
|
202
|
+
- Oct
|
203
|
+
- Nov
|
204
|
+
- Dec
|
205
|
+
order:
|
206
|
+
- :month
|
207
|
+
- :day
|
208
|
+
- :year
|
209
|
+
time:
|
210
|
+
input:
|
211
|
+
formats:
|
212
|
+
- :default
|
213
|
+
- :long
|
214
|
+
- :short
|
215
|
+
- :time
|
216
|
+
formats:
|
217
|
+
default: "%m/%d/%Y %I:%M%p"
|
218
|
+
short: "%B %e %I:%M %p"
|
219
|
+
long: "%A, %B %e, %Y %I:%M%p"
|
220
|
+
time: "%l:%M%p"
|
221
|
+
am: "am"
|
222
|
+
pm: "pm"
|
223
|
+
```
|
203
224
|
|
204
225
|
### Compatibility
|
205
226
|
|
@@ -53,9 +53,15 @@ module Delocalize
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def translate_month_and_day_names(datetime)
|
56
|
-
|
56
|
+
# Note: This should be a bulk lookup but due to a bug in i18n it doesn't work properly with fallbacks.
|
57
|
+
# See https://github.com/svenfuchs/i18n/issues/104.
|
58
|
+
# TODO Make it a bulk lookup again at some point in the future when the bug is fixed in i18n.
|
59
|
+
translated = [:month_names, :abbr_month_names, :day_names, :abbr_day_names].map do |key|
|
60
|
+
I18n.t(key, :scope => :date)
|
61
|
+
end.flatten.compact
|
62
|
+
|
57
63
|
original = (Date::MONTHNAMES + Date::ABBR_MONTHNAMES + Date::DAYNAMES + Date::ABBR_DAYNAMES).compact
|
58
|
-
translated.each_with_index { |name, i| datetime.gsub!(name
|
64
|
+
translated.each_with_index { |name, i| datetime.gsub!(/\b#{name}\b/, original[i]) }
|
59
65
|
end
|
60
66
|
|
61
67
|
def input_formats(type)
|
@@ -10,30 +10,27 @@ ActionView::Helpers::InstanceTag.class_eval do
|
|
10
10
|
def to_input_field_tag(field_type, options = {})
|
11
11
|
options.symbolize_keys!
|
12
12
|
# numbers and dates/times should be localized unless value is already defined
|
13
|
-
if object && options[:value].blank? && object.respond_to?(:column_for_attribute) && column = object.column_for_attribute(method_name)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
#
|
30
|
-
|
31
|
-
# we don't format integer hidden fields because this breaks nested_attributes
|
32
|
-
options[:value] = number_with_precision(value, opts) unless hidden_for_integer
|
33
|
-
end
|
34
|
-
elsif column.date? || column.time?
|
35
|
-
options[:value] = value ? I18n.l(value, :format => options.delete(:format)) : nil
|
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
|
36
31
|
end
|
32
|
+
elsif column.date? || column.time?
|
33
|
+
options[:value] = value ? I18n.l(value, :format => options.delete(:format)) : nil
|
37
34
|
end
|
38
35
|
end
|
39
36
|
|
@@ -44,7 +41,7 @@ end
|
|
44
41
|
# TODO: does it make sense to also override FormTagHelper methods?
|
45
42
|
# ActionView::Helpers::FormTagHelper.class_eval do
|
46
43
|
# include ActionView::Helpers::NumberHelper
|
47
|
-
#
|
44
|
+
#
|
48
45
|
# alias original_text_field_tag text_field_tag
|
49
46
|
# def text_field_tag(name, value = nil, options = {})
|
50
47
|
# value = options.delete(:value) if options.key?(:value)
|
@@ -54,3 +51,4 @@ end
|
|
54
51
|
# original_text_field_tag(name, value, options)
|
55
52
|
# end
|
56
53
|
# end
|
54
|
+
|
@@ -39,7 +39,8 @@ ActiveRecord::Base.class_eval do
|
|
39
39
|
end
|
40
40
|
alias_method_chain :convert_number_column_value, :localization
|
41
41
|
|
42
|
-
|
42
|
+
|
43
|
+
define_method( (Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new('3.2.9')) ? :field_changed? : :_field_changed? ) do |attr, old, value|
|
43
44
|
if column = column_for_attribute(attr)
|
44
45
|
if column.number? && column.null && (old.nil? || old == 0) && value.blank?
|
45
46
|
# For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
|
@@ -53,7 +54,6 @@ ActiveRecord::Base.class_eval do
|
|
53
54
|
value = column.type_cast(value)
|
54
55
|
end
|
55
56
|
end
|
56
|
-
|
57
57
|
old != value
|
58
58
|
end
|
59
59
|
end
|
data/test/delocalize_test.rb
CHANGED
@@ -11,6 +11,10 @@ class DelocalizeActiveRecordTest < ActiveRecord::TestCase
|
|
11
11
|
@product = Product.new
|
12
12
|
end
|
13
13
|
|
14
|
+
def teardown
|
15
|
+
Timecop.return
|
16
|
+
end
|
17
|
+
|
14
18
|
test "delocalizes localized number" do
|
15
19
|
@product.price = '1.299,99'
|
16
20
|
assert_equal 1299.99, @product.price
|
@@ -43,6 +47,7 @@ class DelocalizeActiveRecordTest < ActiveRecord::TestCase
|
|
43
47
|
end
|
44
48
|
|
45
49
|
test "delocalizes localized date without year" do
|
50
|
+
Timecop.return
|
46
51
|
date = Date.civil(Date.today.year, 10, 19)
|
47
52
|
|
48
53
|
@product.released_on = '19. Okt'
|
@@ -59,6 +64,17 @@ class DelocalizeActiveRecordTest < ActiveRecord::TestCase
|
|
59
64
|
assert_equal time, @product.published_at
|
60
65
|
end
|
61
66
|
|
67
|
+
test "delocalizes with fallback locale" do
|
68
|
+
I18n::Backend::Simple.include(I18n::Backend::Fallbacks)
|
69
|
+
I18n.fallbacks[:xx] = [:xx, :tt]
|
70
|
+
|
71
|
+
I18n.with_locale :xx do
|
72
|
+
@product.released_on = '10|11|2009'
|
73
|
+
date = Date.civil(2009, 11, 10)
|
74
|
+
assert_equal date, @product.released_on
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
62
78
|
test "delocalizes localized datetime without year" do
|
63
79
|
time = Time.zone.local(Date.today.year, 3, 1, 12, 0, 0)
|
64
80
|
|
@@ -66,21 +82,9 @@ class DelocalizeActiveRecordTest < ActiveRecord::TestCase
|
|
66
82
|
assert_equal time, @product.published_at
|
67
83
|
end
|
68
84
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
now = Date.today
|
73
|
-
time = Time.zone.local(now.year, now.month, now.day, 9, 0, 0)
|
74
|
-
@product.cant_think_of_a_sensible_time_field = '09:00 Uhr'
|
75
|
-
assert_equal time, @product.cant_think_of_a_sensible_time_field
|
76
|
-
end
|
77
|
-
else
|
78
|
-
test "delocalizes localized time (non-DST)" do
|
79
|
-
now = Date.today
|
80
|
-
time = Time.zone.local(now.year, now.month, now.day, 8, 0, 0)
|
81
|
-
@product.cant_think_of_a_sensible_time_field = '08:00 Uhr'
|
82
|
-
assert_equal time, @product.cant_think_of_a_sensible_time_field
|
83
|
-
end
|
85
|
+
test "delocalizes localized time (DST)" do
|
86
|
+
@product.cant_think_of_a_sensible_time_field = '09:00 Uhr'
|
87
|
+
assert_equal '09:00', @product.cant_think_of_a_sensible_time_field.strftime('%H:%M')
|
84
88
|
end
|
85
89
|
|
86
90
|
test "invalid dates should be delocalized to nil" do
|
@@ -179,6 +183,23 @@ class DelocalizeActiveRecordTest < ActiveRecord::TestCase
|
|
179
183
|
assert_equal @product.price, 0
|
180
184
|
assert_equal @product.price_before_type_cast, "asd"
|
181
185
|
end
|
186
|
+
|
187
|
+
test 'it should gsub only whole translated words and not mess up the original string' do
|
188
|
+
orig_march = I18n.t('date.month_names')[3]
|
189
|
+
orig_monday = I18n.t('date.abbr_day_names')[1]
|
190
|
+
|
191
|
+
#Simulate Dutch
|
192
|
+
I18n.t('date.month_names')[3] = 'Maart'
|
193
|
+
I18n.t('date.abbr_day_names')[1] = 'Ma'
|
194
|
+
|
195
|
+
subject = '30 Maart 2011'
|
196
|
+
Delocalize::LocalizedDateTimeParser.send(:translate_month_and_day_names, subject)
|
197
|
+
|
198
|
+
assert_equal subject, '30 March 2011'
|
199
|
+
|
200
|
+
I18n.t('date.month_names')[3] = orig_march
|
201
|
+
I18n.t('date.abbr_day_names')[1] = orig_monday
|
202
|
+
end
|
182
203
|
end
|
183
204
|
|
184
205
|
class DelocalizeActionViewTest < ActionView::TestCase
|
@@ -264,19 +285,32 @@ class DelocalizeActionViewTest < ActionView::TestCase
|
|
264
285
|
}
|
265
286
|
end
|
266
287
|
|
267
|
-
test "
|
288
|
+
test "delocalizes a given non-string :value" do
|
289
|
+
@product.price = 1299.9
|
290
|
+
assert_dom_equal '<input id="product_price" name="product[price]" size="30" type="text" value="1.499,90" />',
|
291
|
+
text_field(:product, :price, :value => 1499.90)
|
292
|
+
end
|
293
|
+
|
294
|
+
test "doesn't override given string :value" do
|
268
295
|
@product.price = 1299.9
|
269
296
|
assert_dom_equal '<input id="product_price" name="product[price]" size="30" type="text" value="1.499,90" />',
|
270
297
|
text_field(:product, :price, :value => "1.499,90")
|
271
298
|
end
|
272
299
|
|
273
|
-
test "doesn't convert the value if field has errors" do
|
300
|
+
test "doesn't convert the value if field has numericality errors" do
|
274
301
|
@product = ProductWithValidation.new(:price => 'this is not a number')
|
275
302
|
@product.valid?
|
276
303
|
assert_dom_equal %(<div class="field_with_errors"><input id="product_price" name="product[price]" size="30" type="text" value="this is not a number" /></div>),
|
277
304
|
text_field(:product, :price)
|
278
305
|
end
|
279
306
|
|
307
|
+
test "should convert the value if field have non-numericality errors, but have other errors, e.g. business rules" do
|
308
|
+
@product = ProductWithBusinessValidation.new(:price => '1.337,66')
|
309
|
+
@product.valid?
|
310
|
+
assert_dom_equal %(<div class="field_with_errors"><input id="product_price" name="product[price]" size="30" type="text" value="1.337,66" /></div>),
|
311
|
+
text_field(:product, :price)
|
312
|
+
end
|
313
|
+
|
280
314
|
test "doesn't raise an exception when object isn't an ActiveReccord" do
|
281
315
|
@product = NonArProduct.new
|
282
316
|
assert_nothing_raised {
|
@@ -294,3 +328,4 @@ class DelocalizeActionViewTest < ActionView::TestCase
|
|
294
328
|
text_field(:product, :some_value_with_default)
|
295
329
|
end
|
296
330
|
end
|
331
|
+
|
data/test/test_helper.rb
CHANGED
@@ -5,6 +5,8 @@ require 'bundler'
|
|
5
5
|
|
6
6
|
Bundler.require(:default, :development)
|
7
7
|
|
8
|
+
require 'rails/all'
|
9
|
+
|
8
10
|
require 'test/unit'
|
9
11
|
|
10
12
|
require 'delocalize/rails_ext/action_view'
|
@@ -47,6 +49,13 @@ de = {
|
|
47
49
|
:separator => ',',
|
48
50
|
:delimiter => '.'
|
49
51
|
}
|
52
|
+
},
|
53
|
+
:activerecord => {
|
54
|
+
:errors => {
|
55
|
+
:messages => {
|
56
|
+
:not_a_number => 'is not a number'
|
57
|
+
}
|
58
|
+
}
|
50
59
|
}
|
51
60
|
}
|
52
61
|
|
@@ -73,6 +82,14 @@ class ProductWithValidation < Product
|
|
73
82
|
validates_presence_of :price
|
74
83
|
end
|
75
84
|
|
85
|
+
class ProductWithBusinessValidation < Product
|
86
|
+
validate do |record|
|
87
|
+
if record.price > 10
|
88
|
+
record.errors.add(:price, :invalid)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
76
93
|
config = YAML.load_file(File.dirname(__FILE__) + '/database.yml')
|
77
94
|
ActiveRecord::Base.establish_connection(config['test'])
|
78
95
|
|
@@ -86,3 +103,4 @@ ActiveRecord::Base.connection.create_table :products do |t|
|
|
86
103
|
t.integer :times_sold
|
87
104
|
t.decimal :some_value_with_default, :default => 0, :precision => 20, :scale => 2
|
88
105
|
end
|
106
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delocalize
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,33 +9,33 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-09-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '3.0'
|
22
|
+
- - <
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '4'
|
22
25
|
type: :runtime
|
23
26
|
prerelease: false
|
24
|
-
version_requirements:
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: sqlite3
|
27
|
-
requirement: &2160175980 !ruby/object:Gem::Requirement
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
|
-
- -
|
30
|
+
- - ! '>='
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
version: '3.0'
|
33
|
+
- - <
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '4'
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: timecop
|
38
|
-
requirement:
|
38
|
+
requirement: !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,7 +43,12 @@ dependencies:
|
|
43
43
|
version: 0.3.5
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements:
|
46
|
+
version_requirements: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ~>
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: 0.3.5
|
47
52
|
description: Delocalize is a tool for parsing localized dates/times and numbers.
|
48
53
|
email: clemens@railway.at
|
49
54
|
executables: []
|
@@ -51,10 +56,6 @@ extensions: []
|
|
51
56
|
extra_rdoc_files:
|
52
57
|
- README.markdown
|
53
58
|
files:
|
54
|
-
- MIT-LICENSE
|
55
|
-
- Rakefile
|
56
|
-
- VERSION
|
57
|
-
- lib/delocalize.rb
|
58
59
|
- lib/delocalize/i18n_ext.rb
|
59
60
|
- lib/delocalize/localized_date_time_parser.rb
|
60
61
|
- lib/delocalize/localized_numeric_parser.rb
|
@@ -62,12 +63,14 @@ files:
|
|
62
63
|
- lib/delocalize/rails_ext/active_record.rb
|
63
64
|
- lib/delocalize/rails_ext/time_zone.rb
|
64
65
|
- lib/delocalize/railtie.rb
|
65
|
-
- lib/delocalize/ruby_ext.rb
|
66
66
|
- lib/delocalize/ruby_ext/date.rb
|
67
67
|
- lib/delocalize/ruby_ext/datetime.rb
|
68
68
|
- lib/delocalize/ruby_ext/numeric.rb
|
69
69
|
- lib/delocalize/ruby_ext/time.rb
|
70
|
+
- lib/delocalize/ruby_ext.rb
|
71
|
+
- lib/delocalize.rb
|
70
72
|
- README.markdown
|
73
|
+
- MIT-LICENSE
|
71
74
|
- test/database.yml
|
72
75
|
- test/delocalize_test.rb
|
73
76
|
- test/test_helper.rb
|
@@ -92,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
95
|
version: '0'
|
93
96
|
requirements: []
|
94
97
|
rubyforge_project:
|
95
|
-
rubygems_version: 1.8.
|
98
|
+
rubygems_version: 1.8.23
|
96
99
|
signing_key:
|
97
100
|
specification_version: 3
|
98
101
|
summary: Localized date/time and number parsing
|
data/Rakefile
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'rake'
|
2
|
-
require 'rake/testtask'
|
3
|
-
require 'rdoc/task'
|
4
|
-
|
5
|
-
desc 'Test the delocalize gem.'
|
6
|
-
Rake::TestTask.new(:test) do |t|
|
7
|
-
t.libs << 'lib'
|
8
|
-
t.libs << 'test'
|
9
|
-
t.pattern = 'test/delocalize_test.rb'
|
10
|
-
t.verbose = true
|
11
|
-
end
|
12
|
-
|
13
|
-
desc 'Default: run unit tests.'
|
14
|
-
task :default => :test
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.3.1
|