normalizy 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +379 -32
  4. data/lib/normalizy/config.rb +7 -3
  5. data/lib/normalizy/extensions.rb +11 -7
  6. data/lib/normalizy/filters/date.rb +44 -0
  7. data/lib/normalizy/filters/money.rb +38 -0
  8. data/lib/normalizy/filters/number.rb +4 -3
  9. data/lib/normalizy/filters/percent.rb +38 -0
  10. data/lib/normalizy/rspec/matcher.rb +2 -0
  11. data/lib/normalizy/version.rb +1 -1
  12. data/spec/normalizy/config/add_spec.rb +3 -0
  13. data/spec/normalizy/config/alias_spec.rb +7 -13
  14. data/spec/normalizy/config/initialize_spec.rb +5 -2
  15. data/spec/normalizy/config/normalizy_raws_spec.rb +1 -1
  16. data/spec/normalizy/extensions/{apply_normalizations_spec.rb → apply_normalizy_spec.rb} +36 -28
  17. data/spec/normalizy/extensions/filters/date_spec.rb +34 -0
  18. data/spec/normalizy/extensions/filters/money_spec.rb +40 -0
  19. data/spec/normalizy/extensions/filters/number_spec.rb +13 -0
  20. data/spec/normalizy/extensions/filters/percent_spec.rb +40 -0
  21. data/spec/normalizy/extensions/filters/strip_spec.rb +21 -0
  22. data/spec/normalizy/extensions/normalizy_rules_spec.rb +1 -1
  23. data/spec/normalizy/filters/date_spec.rb +56 -0
  24. data/spec/normalizy/filters/money_spec.rb +155 -0
  25. data/spec/normalizy/filters/number_spec.rb +25 -9
  26. data/spec/normalizy/filters/percent_spec.rb +155 -0
  27. data/spec/rails_helper.rb +4 -0
  28. data/spec/support/db/schema.rb +8 -3
  29. data/spec/support/filters/blacklist_1.rb +11 -0
  30. data/spec/support/filters/blacklist_2.rb +13 -0
  31. data/spec/support/filters/blacklist_block.rb +11 -0
  32. data/spec/support/models/user.rb +1 -1
  33. metadata +29 -8
  34. data/spec/normalizy/extensions/normalizy_spec.rb +0 -11
  35. data/spec/support/filters/blacklist_filter.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 781b68ea361e5e2a698bba7230a2a2459f2cb758
4
- data.tar.gz: 01d9c17b15ee6ec08c5dda0da645897a43f9370d
3
+ metadata.gz: 76436253c67c497cba120a39bd71667b34cad65f
4
+ data.tar.gz: c5bb1d390b33e655ea7e5e9c865cf4bf8d903fc6
5
5
  SHA512:
6
- metadata.gz: cef08c0e44de84158e91659510c0d4bcd50715739a977452c9c7037b44dbaa06acfe29993388b357a1c0e863e7cafa22e8b5acbc8f995b740340c8efa7aeef76
7
- data.tar.gz: 632e6116a884839be1e018f905599766ecf9223ef9336ae5ba79c503bd549c41242fdcab975a489372f75a530f97b495ec71e0f53d649e0c8cf844c870788341
6
+ metadata.gz: 488852bd2f5c2ce038533f7dcd984b537b5e27cd185cccafa560553cf7f50a13d8a7bac0e15670c6647fff376957b91a53463b966713f887ed5e566f68d32415
7
+ data.tar.gz: 7205c44545020100f4663e2ca7e9d9af224dfda0e3bb6645acee7defb8798c76bec57582678a25670a5f4c07d2f6d07fed151d089e81f72e154ca6fd20afb806
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## v0.2.0
2
+
3
+ - Changes:
4
+ - `number` filter no more make cast automatically.
5
+ - `number` now accept `cast` options with method to be used on cast type;
6
+
7
+ - Features:
8
+ - add `date` filter with `format`, `time_zone` and I18n error message support;
9
+ - add `money` filter with `cast`, `type` and I18n support;
10
+ - add `percent` filter with `cast`, `type` and I18n support.
11
+
1
12
  ## v0.1.0
2
13
 
3
14
  - First release.
data/README.md CHANGED
@@ -7,7 +7,7 @@ Attribute normalizer for ActiveRecord.
7
7
 
8
8
  ## Description
9
9
 
10
- If you know the obvious format of an input, why not normalize it instead of raise an validation error to your use? Make the follow email ` myemail@example.org ` valid like `email@example.com` with no need to override acessors methods.
10
+ If you know the obvious format of an input, why not normalize it instead of raise an validation error to your use? Make the follow email ` Email@example.com ` valid like `email@example.com` with no need to override acessors methods.
11
11
 
12
12
  ## install
13
13
 
@@ -35,22 +35,319 @@ class User < ApplicationRecord
35
35
  end
36
36
  ```
37
37
 
38
- Now some email like ` myemail@example.org ` will be saved as `email@example.com`.
38
+ Now some email like ` myemail@example.com ` will be saved as `email@example.com`.
39
39
 
40
40
  ## Filters
41
41
 
42
- We have a couple of built-in filters. The most one is just a wrapper o the original String methods:
42
+ We have a couple of built-in filters.
43
+
44
+ ### Date
45
+
46
+ Transform a value to date format.
47
+
48
+ ```ruby
49
+ normalizy :birthday, with: :date
50
+
51
+ '1984-10-23'
52
+ # Tue, 23 Oct 1984 00:00:00 UTC +00:00
53
+ ```
54
+
55
+ By default, the date is treat as `%F` format and as `UTC` time.
56
+
57
+ #### format
58
+
59
+ You can change the format using the `format` options:
60
+
61
+ ```ruby
62
+ normalizy :birthday, with: date: { format: '%y/%m/%d' }
63
+
64
+ '84/10/23'
65
+ # Tue, 23 Oct 1984 00:00:00 UTC +00:00
66
+ ```
67
+
68
+ #### time zone
69
+
70
+ To convert the date on your time zone, just provide the `time_zone` option:
71
+
72
+ ```ruby
73
+ normalizy :birthday, with: date: { time_zone: '%y/%m/%d' }
74
+
75
+ '1984-10-23'
76
+ # Tue, 23 Oct 1984 00:00:00 EDT -04:00
77
+ ```
78
+
79
+ #### error message
80
+
81
+ If an invalid date is provided, Normalizy will add an error on attribute of the related object.
82
+ You can customize the error via I18n config:
83
+
84
+ ```yml
85
+ en:
86
+ normalizy:
87
+ errors:
88
+ date:
89
+ user:
90
+ birthday: '%{value} is an invalid date.'
91
+ ```
92
+
93
+ If no configuration is provided, the default message will be `'%{value} is an invalid date.`.
94
+
95
+ #### adjust
96
+
97
+ If your model receive a `Time` or `DateTime`, you can provide `adjust` options to change you time to begin o the day:
98
+
99
+ ```ruby
100
+ normalizy :birthday, with: date: { adjust: :begin }
101
+
102
+ Tue, 23 Oct 1984 11:30:00 EDT -04:00
103
+ # Tue, 23 Oct 1984 00:00:00 EDT -04:00
104
+ ```
105
+
106
+ Or to the end of the day:
107
+
108
+ ```ruby
109
+ normalizy :birthday, with: date: { adjust: :end }
110
+
111
+ Tue, 23 Oct 1984 00:00:00 EDT -04:00
112
+ # Tue, 23 Oct 1984 11:59:59 EDT -04:00
113
+ ```
114
+
115
+ By default, `number` works with value before [Type Cast](#type-cast).
116
+
117
+ ### Money
118
+
119
+ Transform a value to money format.
120
+
121
+ ```ruby
122
+ normalizy :amount, with: :money
123
+
124
+ '$ 42.00'
125
+ # '42.00'
126
+ ```
127
+
128
+ #### separator
129
+
130
+ The `separator` will be keeped on value to be possible cast the right integer value.
131
+ You can change this separator:
132
+
133
+ ```ruby
134
+ normalizy :amount, with: money: { separator: ',' }
135
+
136
+ 'R$ 42,00'
137
+ # '42,00'
138
+ ```
139
+
140
+ If you do not want pass it as options, Normalizy will fetch your I18n config:
141
+
142
+ ```yaml
143
+ en:
144
+ currency:
145
+ format:
146
+ separator: '.'
147
+ ```
148
+
149
+ And if it does not exists, `.` will be used as default.
150
+
151
+ #### type
152
+
153
+ You can retrieve the value in *cents* format, use the `type` options as `cents`:
154
+
155
+ ```ruby
156
+ normalizy :amount, with: money: { type: :cents }
157
+
158
+ '$ 42.00'
159
+ # '4200'
160
+ ```
161
+
162
+ #### precision
163
+
164
+ As you could see on the last example, when using `type: :cents` is important the number of decimal digits.
165
+ So, you can configure it to avoid the following error:
166
+
167
+ ```ruby
168
+ normalizy :amount, with: money: { type: :cents }
169
+
170
+ '$ 42.0'
171
+ # 420
172
+ ```
173
+
174
+ When you parse it back, the value need to be divided by `100` to be presented, but it will result in a value you do not want: `4.2` instead of the original `42.0`. Just provide a `precision`:
175
+
176
+ ```ruby
177
+ normalizy :amount, with: money: { precision: 2 }
178
+
179
+ '$ 42.0'
180
+ # 42.00
181
+ ```
182
+
183
+ ```ruby
184
+ normalizy :amount, with: money: { precision: 2, type: :cents }
185
+
186
+ '$ 42.0'
187
+ # 4200
188
+ ```
189
+
190
+ If you do not want pass it as options, Normalizy will fetch your I18n config:
191
+
192
+ ```yaml
193
+ en:
194
+ currency:
195
+ format:
196
+ separator: 2
197
+ ```
198
+
199
+ And if it does not exists, `2` will be used as default.
200
+
201
+ #### cast
202
+
203
+ If you need get a number over a normalized string in a number style, provide `cast` option with desired cast method:
204
+
205
+ ```ruby
206
+ normalizy :amount, with: money: { cast: :to_i }
207
+
208
+ '$ 42.00'
209
+ # 4200
210
+ ```
211
+
212
+ Just pay attention to avoid to use `type: :cents` together `cast` with float parses.
213
+ Since `type` runs first, you will add decimal in a number that already is represented with decimal, but as integer:
214
+
215
+ ```ruby
216
+ normalizy :amount, with: money: { cast: :to_f, type: :cents }
217
+
218
+ '$ 42.00'
219
+ # 4200.0
220
+ ```
221
+
222
+ By default, `money` works with value before [Type Cast](#type-cast).
43
223
 
44
224
  ### Number
45
225
 
46
226
  ```ruby
47
227
  normalizy :age, with: :number
48
228
 
49
- ' 32'
229
+ ' 32x'
230
+ # '32'
231
+ ```
232
+
233
+ If you want cast the value, provide `cast` option with desired cast method:
234
+
235
+ ```ruby
236
+ normalizy :age, with: number: { cast: :to_i }
237
+
238
+ ' 32x'
50
239
  # 32
51
240
  ```
52
241
 
53
- By default, `number` works with input value before [Type Cast](#type-cast)
242
+ By default, `number` works with value before [Type Cast](#type-cast).
243
+
244
+ ### Percent
245
+
246
+ Transform a value to percent format.
247
+
248
+ ```ruby
249
+ normalizy :amount, with: :percent
250
+
251
+ '42.00 %'
252
+ # '42.00'
253
+ ```
254
+
255
+ #### separator
256
+
257
+ The `separator` will be keeped on value to be possible cast the right integer value.
258
+ You can change this separator:
259
+
260
+ ```ruby
261
+ normalizy :amount, with: percent: { separator: ',' }
262
+
263
+ '42,00 %'
264
+ # '42,00'
265
+ ```
266
+
267
+ If you do not want pass it as options, Normalizy will fetch your I18n config:
268
+
269
+ ```yaml
270
+ en:
271
+ percentage:
272
+ format:
273
+ separator: '.'
274
+ ```
275
+
276
+ And if it does not exists, `.` will be used as default.
277
+
278
+ #### type
279
+
280
+ You can retrieve the value in *integer* format, use the `type` options as `integer`:
281
+
282
+ ```ruby
283
+ normalizy :amount, with: percent: { type: :integer }
284
+
285
+ '42.00 %'
286
+ # '4200'
287
+ ```
288
+
289
+ #### precision
290
+
291
+ As you could see on the last example, when using `type: :integer` is important the number of decimal digits.
292
+ So, you can configure it to avoid the following error:
293
+
294
+ ```ruby
295
+ normalizy :amount, with: percent: { type: :integer }
296
+
297
+ '42.0 %'
298
+ # 420
299
+ ```
300
+
301
+ When you parse it back, the value need to be divided by `100` to be presented, but it will result in a value you do not want: `4.2` instead of the original `42.0`. Just provide a `precision`:
302
+
303
+ ```ruby
304
+ normalizy :amount, with: percent: { precision: 2 }
305
+
306
+ '42.0 %'
307
+ # 42.00
308
+ ```
309
+
310
+ ```ruby
311
+ normalizy :amount, with: percent: { precision: 2, type: :integer }
312
+
313
+ '42.0 %'
314
+ # 4200
315
+ ```
316
+
317
+ If you do not want pass it as options, Normalizy will fetch your I18n config:
318
+
319
+ ```yaml
320
+ en:
321
+ percentage:
322
+ format:
323
+ separator: 2
324
+ ```
325
+
326
+ And if it does not exists, `2` will be used as default.
327
+
328
+ #### cast
329
+
330
+ If you need get a number over a normalized string in a number style, provide `cast` option with desired cast method:
331
+
332
+ ```ruby
333
+ normalizy :amount, with: percent: { cast: :to_i }
334
+
335
+ '42.00 %'
336
+ # 4200
337
+ ```
338
+
339
+ Just pay attention to avoid to use `type: :integer` together `cast` with float parses.
340
+ Since `type` runs first, you will add decimal in a number that already is represented with decimal, but as integer:
341
+
342
+ ```ruby
343
+ normalizy :amount, with: percent: { cast: :to_f, type: :integer }
344
+
345
+ '42.00 %'
346
+ # 4200.0
347
+ ```
348
+
349
+ By default, `percent` works with value before [Type Cast](#type-cast).
350
+
54
351
  ### Strip
55
352
 
56
353
  Options:
@@ -101,7 +398,7 @@ You can normalize more than one attribute at once too, with one or muiltiple fil
101
398
  normalizy :email, :username, with: :downcase
102
399
  ```
103
400
 
104
- Of course you can declare muiltiples attribute and multiple filters together.
401
+ Of course you can declare muiltiples attribute and multiple filters, either.
105
402
  It is possible to make sequential normalizy calls:
106
403
 
107
404
  ```ruby
@@ -113,7 +410,7 @@ In this case, each line will be evaluated from the top to the bottom.
113
410
 
114
411
  ## Default Filters
115
412
 
116
- You can configure some default filters to be runned. Edit you initializer at `config/initializers/normalizy.rb`:
413
+ You can configure some default filters to be runned. Edit initializer at `config/initializers/normalizy.rb`:
117
414
 
118
415
  ```ruby
119
416
  Normalizy.configure do |config|
@@ -168,7 +465,9 @@ normalizy :name, with: :blacklist
168
465
  # 'Washington *** Botelho'
169
466
  ```
170
467
 
171
- If you want to pass options to your filter, just call it as hash and the value will be passed to the custom filter:
468
+ #### options
469
+
470
+ If you want to pass options to your filter, just call it as a hash and the value will be send to the custom filter:
172
471
 
173
472
  ```ruby
174
473
  module Normalizy
@@ -189,6 +488,13 @@ normalizy :name, with: blacklist: { replacement: '---' }
189
488
  # 'Washington --- Botelho'
190
489
  ```
191
490
 
491
+ ### options value
492
+
493
+ By default, Modules and instance methods of class will receveis the following attributes on `options` argument:
494
+
495
+ - `object`: The object that Normalizy is acting;
496
+ - `attribute`: The attribute of the object that Normalizy is acting.
497
+
192
498
  You can pass a block and it will be received on filter:
193
499
 
194
500
  ```ruby
@@ -214,16 +520,14 @@ normalizy :name, with: :blacklist, &->(value) { value.sub('filtered', '(filtered
214
520
  # 'Washington (filtered 2x) Botelho'
215
521
  ```
216
522
 
217
- The block
218
-
219
523
  ## Method Filters
220
524
 
221
- If a built-in filter is not found, Normalizy will try to find a method to suply the normalize with the same name of the given filter:
525
+ If a built-in filter is not found, Normalizy will try to find a method in the current class.
222
526
 
223
527
  ```ruby
224
528
  normalizy :birthday, with: :parse_date
225
529
 
226
- def parse_date(input, options = {})
530
+ def parse_date(input)
227
531
  Time.zone.parse(input).strftime '%Y/%m/%d'
228
532
  end
229
533
 
@@ -231,7 +535,7 @@ end
231
535
  # '1984/10/23'
232
536
  ```
233
537
 
234
- If you gives an option, it will be passed to the function too:
538
+ If you gives an option, it will be passed to the function:
235
539
 
236
540
  ```ruby
237
541
  normalizy :birthday, with: { parse_date: { format: '%Y/%m/%d' }
@@ -244,7 +548,7 @@ end
244
548
  # '1984/10/23'
245
549
  ```
246
550
 
247
- Block methods works here too.
551
+ Block methods works here either.
248
552
 
249
553
  ## Native Filter
250
554
 
@@ -262,7 +566,7 @@ normalizy :name, with: :reverse
262
566
  Maybe you want to declare an inline filter, in this case, just use a Lambda or Proc:
263
567
 
264
568
  ```ruby
265
- normalizy :age, with: ->(input) { input.abs }
569
+ normalizy :age, with: ->(input) { input.to_i.abs }
266
570
 
267
571
  -32
268
572
  # 32
@@ -272,60 +576,68 @@ You can use it on filters declaration too:
272
576
 
273
577
  ```ruby
274
578
  Normalizy.configure do |config|
275
- config.add :age, ->(input) { input.abs }
579
+ config.add :age, ->(input) { input.to_i.abs }
276
580
  end
277
581
  ```
278
582
 
279
583
  ## Type Cast
280
584
 
281
- An input field with `$ 42.00` dollars when sent to model with a field with `integer` type,
282
- will be converted to `0`, since the type does not match. But you want to use the value before Rails do this cast the type.
585
+ An input field with `= 42` value when sent to model with a field as `integer` type,
586
+ will be converted to `0`, since the type does not match. But you want to use the value before Rails cast the type.
283
587
 
284
588
  To receive the value before type cast, just pass a `raw` options as `true`:
285
589
 
286
590
  ```ruby
287
591
  normalizy :amount, with: :number, raw: true
288
592
 
289
- '$ 42.00'
290
- # 4200
593
+ '= 42'
594
+ # 42
291
595
  ```
292
596
 
293
- To avoid repeat the `raw: true` where you will always to use, you can register a filter with this options:
597
+ To avoid repeat the `raw: true` when you have multiple uses, you can register a filter:
294
598
 
295
599
  ```ruby
296
600
  Normalizy.configure do |config|
297
- config.add :money, ->(input) { input.gsub(/\D/, '') }, raw: true
601
+ config.add :raw_number, ->(input) { input.gsub(/\D/, '') }, raw: true
298
602
  end
299
603
  ```
300
604
 
605
+ And use it in short version:
606
+
607
+ ```ruby
608
+ normalizy :amount, with: :raw_number
609
+
610
+ '= 42'
611
+ # 42
612
+ ```
613
+
301
614
  ## Alias
302
615
 
303
616
  Sometimes you want to give a better name to your filter, just to keep the things semantic.
304
- But duplicates the code just to redefine a new name is not a good idea, so, just create an alias:
617
+ Duplicates the code, as you know, is not a good idea, so, create an alias:
305
618
 
306
619
  ```ruby
307
620
  Normalizy.configure do |config|
308
- config.alias :money, :number
621
+ config.alias :age, :number
309
622
  end
310
623
  ```
311
624
 
312
- Now, `money` will delegate to `number` filter.
313
- Since we already know the need of `raw` options, we can declare it here too:
625
+ Now, `age` will delegate to `number` filter.
626
+ Since we already know the need of `raw` options, we can declare it here:
314
627
 
315
628
  ```ruby
316
629
  Normalizy.configure do |config|
317
- config.alias :money, :number, raw: true
630
+ config.alias :age, :number, raw: true
318
631
  end
319
632
  ```
320
633
 
321
- But `number` filter already works with `raw: true`, don't need to tell it again.
322
- An our previous example, about `amount`, was refactored to:
634
+ And now, the aliased filter will work fine:
323
635
 
324
636
  ```ruby
325
- normalizy :amount, with: :money
637
+ normalizy :age, with: :age
326
638
 
327
- '$ 42.00'
328
- # 4200
639
+ '= 42'
640
+ # 42
329
641
  ```
330
642
 
331
643
  If you need to alias multiple filters, just provide an array of them:
@@ -336,6 +648,41 @@ Normalizy.configure do |config|
336
648
  end
337
649
  ```
338
650
 
651
+ ## RSpec
652
+
653
+ If you use [RSpec](http://rspec.info), we did built-in matchers for you.
654
+ Add the following code to your `rails_helper.rb`
655
+
656
+ ```ruby
657
+ RSpec.configure do |config|
658
+ config.include Normalizy::RSpec
659
+ end
660
+ ```
661
+
662
+ And now you can use some of the matchers:
663
+
664
+ ##### Result Matcher
665
+
666
+ ```ruby
667
+ it { is_expected.to normalizy(:email).from(' Email@example.com ').to 'email@example.com' }
668
+ ```
669
+
670
+ ##### Filter Matcher
671
+
672
+ It will match the given filter literally:
673
+
674
+ ```ruby
675
+ it { is_expected.to normalizy(:email).with :downcase }
676
+ ```
677
+
678
+ ```ruby
679
+ it { is_expected.to normalizy(:email).with %i[downcase squish] }
680
+ ```
681
+
682
+ ```ruby
683
+ it { is_expected.to normalizy(:email).with(trim: { side: :left }) }
684
+ ```
685
+
339
686
  ## Love it!
340
687
 
341
688
  Via [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=X8HEP2878NDEG&item_name=normalizy) or [Gratipay](https://gratipay.com/~wbotelhos). Thanks! (:
@@ -9,6 +9,7 @@ module Normalizy
9
9
 
10
10
  def add(name, value, raw: false)
11
11
  @filters[name] = value
12
+
12
13
  @normalizy_raws << name if raw
13
14
 
14
15
  self
@@ -24,11 +25,14 @@ module Normalizy
24
25
  def initialize
25
26
  @default_filters = {}
26
27
  @normalizy_aliases = {}
27
- @normalizy_raws = [:number]
28
+ @normalizy_raws = %i[date money number percent]
28
29
 
29
30
  @filters = {
30
- number: Normalizy::Filters::Number,
31
- strip: Normalizy::Filters::Strip
31
+ date: Normalizy::Filters::Date,
32
+ money: Normalizy::Filters::Money,
33
+ number: Normalizy::Filters::Number,
34
+ percent: Normalizy::Filters::Percent,
35
+ strip: Normalizy::Filters::Strip
32
36
  }
33
37
  end
34
38
  end
@@ -51,17 +51,21 @@ module Normalizy
51
51
  value = nil
52
52
 
53
53
  [rules].flatten.compact.each do |rule|
54
- value = original_value(attribute, rule, options)
55
- aliased_rules = [aliases.key?(rule) ? aliases[rule] : rule]
54
+ result_rules = [aliases.key?(rule) ? aliases[rule] : rule]
56
55
 
57
- aliased_rules.flatten.compact.each do |alias_rule|
58
- filter, filter_options = extract_filter(alias_rule)
59
- value = extract_value(value, filter, filter_options, block)
56
+ result_rules.flatten.compact.each do |result_rule|
57
+ filter, filter_options = extract_filter(result_rule)
58
+
59
+ if filter.respond_to?(:name)
60
+ rule_name = filter.name.tableize.split('/').last.singularize.to_sym
61
+ end
62
+
63
+ original = original_value(attribute, rule_name, options)
64
+ full_options = filter_options.merge(attribute: attribute, object: self)
65
+ value = extract_value(original, filter, full_options, block)
60
66
  end
61
67
  end
62
68
 
63
- return unless value
64
-
65
69
  write attribute, value
66
70
  end
67
71
 
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Normalizy
4
+ module Filters
5
+ module Date
6
+ class << self
7
+ def call(input, options = {})
8
+ if input.is_a?(String)
9
+ return input if input.blank?
10
+
11
+ Time.use_zone(time_zone(options)) do
12
+ input = Time.zone.strptime(input, format(options))
13
+ end
14
+ end
15
+
16
+ if input.is_a?(Time) || input.is_a?(DateTime)
17
+ input = input.beginning_of_day if options[:adjust] == :begin
18
+ input = input.end_of_day if options[:adjust] == :end
19
+ end
20
+
21
+ input
22
+ rescue ArgumentError
23
+ options[:object].errors.add options[:attribute], error_message(input, options)
24
+ end
25
+
26
+ private
27
+
28
+ def error_message(input, options)
29
+ I18n.t options[:attribute], scope: [
30
+ 'normalizy.errors.date', options[:object].class.name.underscore
31
+ ], value: input, default: '%{value} is an invalid date.'
32
+ end
33
+
34
+ def format(options)
35
+ options.fetch :format, '%F'
36
+ end
37
+
38
+ def time_zone(options)
39
+ options.fetch :time_zone, 'UTC'
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Normalizy
4
+ module Filters
5
+ module Money
6
+ class << self
7
+ def call(input, options = {})
8
+ return input unless input.is_a?(String)
9
+
10
+ value = input.gsub(/[^[0-9]#{separator(options)}]/, '')
11
+
12
+ return nil if value.blank?
13
+
14
+ value = "%0.#{precision(options)}f" % [value.sub(separator(options), '.')]
15
+ value = value.delete('.') if cents?(options)
16
+
17
+ return value.send(options[:cast]) if options[:cast]
18
+
19
+ value
20
+ end
21
+
22
+ private
23
+
24
+ def cents?(options)
25
+ options[:type]&.to_sym == :cents
26
+ end
27
+
28
+ def precision(options)
29
+ options.fetch :precision, I18n.t('currency.format.precision', default: 2)
30
+ end
31
+
32
+ def separator(options)
33
+ options.fetch :separator, I18n.t('currency.format.separator', default: '.')
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end