arel_extensions 1.2.5 → 1.2.8

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -4
  3. data/README.md +7 -2
  4. data/Rakefile +23 -23
  5. data/arel_extensions.gemspec +1 -1
  6. data/functions.html +2 -2
  7. data/gemfiles/rails6.gemfile +30 -0
  8. data/gemspec_v2/arel_extensions-v2.gemspec +28 -0
  9. data/generate_gems.sh +13 -0
  10. data/lib/arel_extensions.rb +4 -1
  11. data/lib/arel_extensions/boolean_functions.rb +0 -2
  12. data/lib/arel_extensions/common_sql_functions.rb +5 -4
  13. data/lib/arel_extensions/insert_manager.rb +24 -24
  14. data/lib/arel_extensions/math.rb +3 -3
  15. data/lib/arel_extensions/math_functions.rb +4 -4
  16. data/lib/arel_extensions/nodes/case.rb +0 -2
  17. data/lib/arel_extensions/nodes/collate.rb +1 -1
  18. data/lib/arel_extensions/nodes/date_diff.rb +1 -3
  19. data/lib/arel_extensions/nodes/duration.rb +0 -2
  20. data/lib/arel_extensions/nodes/formatted_number.rb +20 -20
  21. data/lib/arel_extensions/nodes/json.rb +28 -30
  22. data/lib/arel_extensions/nodes/power.rb +5 -4
  23. data/lib/arel_extensions/nodes/replace.rb +23 -5
  24. data/lib/arel_extensions/nodes/round.rb +5 -5
  25. data/lib/arel_extensions/nodes/soundex.rb +14 -13
  26. data/lib/arel_extensions/nodes/substring.rb +8 -15
  27. data/lib/arel_extensions/nodes/trim.rb +1 -1
  28. data/lib/arel_extensions/nodes/union.rb +0 -1
  29. data/lib/arel_extensions/nodes/union_all.rb +0 -1
  30. data/lib/arel_extensions/predications.rb +16 -17
  31. data/lib/arel_extensions/string_functions.rb +12 -6
  32. data/lib/arel_extensions/tasks.rb +5 -5
  33. data/lib/arel_extensions/version.rb +1 -1
  34. data/lib/arel_extensions/visitors/mssql.rb +13 -12
  35. data/lib/arel_extensions/visitors/mysql.rb +50 -35
  36. data/lib/arel_extensions/visitors/oracle.rb +7 -6
  37. data/lib/arel_extensions/visitors/oracle12.rb +1 -1
  38. data/lib/arel_extensions/visitors/postgresql.rb +45 -27
  39. data/lib/arel_extensions/visitors/sqlite.rb +41 -27
  40. data/lib/arel_extensions/visitors/to_sql.rb +18 -5
  41. data/test/visitors/test_bulk_insert_oracle.rb +6 -6
  42. data/test/visitors/test_bulk_insert_sqlite.rb +5 -5
  43. data/test/visitors/test_to_sql.rb +10 -2
  44. data/test/with_ar/all_agnostic_test.rb +44 -33
  45. data/test/with_ar/test_bulk_sqlite.rb +1 -1
  46. data/test/with_ar/test_math_sqlite.rb +1 -1
  47. data/test/with_ar/test_string_mysql.rb +1 -3
  48. data/test/with_ar/test_string_sqlite.rb +1 -5
  49. data/version_v1.rb +3 -0
  50. data/version_v2.rb +3 -0
  51. metadata +9 -4
@@ -97,26 +97,28 @@ module ArelExtensions
97
97
  end
98
98
 
99
99
  def visit_ArelExtensions_Nodes_Collate o, collector
100
- case o.expressions.first
101
- when Arel::Attributes::Attribute
102
- charset = case o.option
100
+ charset =
101
+ case o.expressions.first
102
+ when Arel::Attributes::Attribute
103
+ case o.option
103
104
  when 'latin1','utf8'
104
105
  o.option
105
106
  else
106
107
  Arel::Table.engine.connection.charset || 'utf8'
107
108
  end
108
- else
109
- charset = (o.option == 'latin1') ? 'latin1' : 'utf8'
110
- end
109
+ else
110
+ (o.option == 'latin1') ? 'latin1' : 'utf8'
111
+ end
111
112
  collector = visit o.expressions.first, collector
112
- if o.ai
113
- collector << " COLLATE #{charset == 'latin1' ? 'latin1_general_ci' : 'utf8_unicode_ci' }"
113
+ collector <<
114
+ if o.ai
115
+ " COLLATE #{charset == 'latin1' ? 'latin1_general_ci' : 'utf8_unicode_ci' }"
114
116
  #doesn't work in latin1
115
- elsif o.ci
116
- collector << " COLLATE #{charset == 'latin1' ? 'latin1_general_ci' : 'utf8_unicode_ci' }"
117
- else
118
- collector << " COLLATE #{charset}_bin"
119
- end
117
+ elsif o.ci
118
+ " COLLATE #{charset == 'latin1' ? 'latin1_general_ci' : 'utf8_unicode_ci' }"
119
+ else
120
+ " COLLATE #{charset}_bin"
121
+ end
120
122
  collector
121
123
  end
122
124
 
@@ -262,7 +264,7 @@ module ArelExtensions
262
264
  else
263
265
  if o.with_interval
264
266
  case o.left
265
- when 'd','m','y'
267
+ when 'd','m','y'
266
268
  interval = 'DAY'
267
269
  when 'h','mn','s'
268
270
  interval = 'SECOND'
@@ -362,20 +364,21 @@ module ArelExtensions
362
364
  else(o.flags.include?('+') ? '+' : (o.flags.include?(' ') ? ' ' : ''))
363
365
  sign_length = ArelExtensions::Nodes::Length.new([sign])
364
366
 
365
- if o.scientific_notation
366
- number = ArelExtensions::Nodes::Concat.new([
367
- Arel::Nodes::NamedFunction.new('FORMAT',[
368
- col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor)
369
- ]+params),
370
- o.type,
371
- Arel::Nodes::NamedFunction.new('FORMAT',[
372
- col.abs.log10.floor,
373
- 0
367
+ number =
368
+ if o.scientific_notation
369
+ ArelExtensions::Nodes::Concat.new([
370
+ Arel::Nodes::NamedFunction.new('FORMAT',[
371
+ col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor)
372
+ ]+params),
373
+ o.type,
374
+ Arel::Nodes::NamedFunction.new('FORMAT',[
375
+ col.abs.log10.floor,
376
+ 0
377
+ ])
374
378
  ])
375
- ])
376
- else
377
- number = Arel::Nodes::NamedFunction.new('FORMAT',[col.abs]+params)
378
- end
379
+ else
380
+ Arel::Nodes::NamedFunction.new('FORMAT',[col.abs]+params)
381
+ end
379
382
 
380
383
  repeated_char = (o.width == 0) ? Arel::Nodes.build_quoted('') : ArelExtensions::Nodes::Case.new().
381
384
  when(Arel::Nodes.build_quoted(o.width).abs-(number.length+sign_length)>0).
@@ -399,19 +402,19 @@ module ArelExtensions
399
402
  end
400
403
 
401
404
  def visit_Aggregate_For_AggregateFunction o, collector
402
- if !(Arel::Table.engine.connection.send(:version) >= (Arel::Table.engine.connection.send(:mariadb?) ? '10.2.3' : '8.0'))
405
+ if !window_supported?
403
406
  warn("Warning : ArelExtensions: Window Functions are not available in the current version on the DBMS.")
404
407
  return collector
405
408
  end
406
409
 
407
- if o.order || o.group
410
+ if !o.order.empty? || !o.group.empty?
408
411
  collector << " OVER ("
409
- if o.group
412
+ if !o.group.empty?
410
413
  collector << " PARTITION BY ("
411
414
  visit o.group, collector
412
415
  collector << ")"
413
416
  end
414
- if o.order
417
+ if !o.order.empty?
415
418
  collector << " ORDER BY ("
416
419
  visit o.order, collector
417
420
  collector << ")"
@@ -433,15 +436,27 @@ module ArelExtensions
433
436
  collector << (o.unbiased_estimator ? "VAR_SAMP(" : "VAR_POP(")
434
437
  visit o.left, collector
435
438
  collector << ")"
439
+ visit_Aggregate_For_AggregateFunction o, collector
436
440
  collector
437
441
  end
438
442
 
439
443
  # JSON if implemented only after 10.2.3 in MariaDb and 5.7 in MySql
440
444
  def json_supported?
441
- Arel::Table.engine.connection.send(:mariadb?) &&
442
- Arel::Table.engine.connection.send(:version) >= '10.2.3' ||
443
- !Arel::Table.engine.connection.send(:mariadb?) &&
444
- Arel::Table.engine.connection.send(:version) >= '5.7.0'
445
+ version_supported?('10.2.3', '5.7.0')
446
+ end
447
+
448
+ def window_supported?
449
+ version_supported?('10.2.3', '8.0')
450
+ end
451
+
452
+ def version_supported?(mysql_v = '10.2.3',mariadb_v = '5.7.0')
453
+ conn = Arel::Table.engine.connection
454
+ conn.send(:mariadb?) &&
455
+ (conn.respond_to?(:get_database_version) && conn.send(:get_database_version) >= mysql_v ||
456
+ conn.respond_to?(:version) && conn.send(:version) >= mysql_v) ||
457
+ !Arel::Table.engine.connection.send(:mariadb?) &&
458
+ (conn.respond_to?(:get_database_version) && conn.send(:get_database_version) >= mariadb_v ||
459
+ conn.respond_to?(:version) && conn.send(:version) >= mariadb_v)
445
460
  end
446
461
 
447
462
  def visit_ArelExtensions_Nodes_Json o,collector
@@ -131,11 +131,12 @@ module ArelExtensions
131
131
  collector << "(LISTAGG("
132
132
  collector = visit o.left, collector
133
133
  collector << Arel::Visitors::Oracle::COMMA
134
- if o.separator && o.separator != 'NULL'
135
- collector = visit o.separator, collector
136
- else
137
- collector = visit Arel::Nodes.build_quoted(','), collector
138
- end
134
+ collector =
135
+ if o.separator && o.separator != 'NULL'
136
+ visit o.separator, collector
137
+ else
138
+ visit Arel::Nodes.build_quoted(','), collector
139
+ end
139
140
  collector << ") WITHIN GROUP (ORDER BY "
140
141
  if !o.order.blank?
141
142
  o.order.each_with_index do |order,i|
@@ -154,7 +155,7 @@ module ArelExtensions
154
155
  o.expressions.each_with_index { |arg, i|
155
156
  collector << Arel::Visitors::Oracle::COMMA unless i == 0
156
157
  if i > 0 && o.left_node_type == :text
157
- if arg == '' || (arg.is_a?(Arel::Nodes::Quoted) && (arg.expr == ''))
158
+ if arg == '' || (arg.is_a?(Arel::Nodes::Quoted) && (arg.expr == ''))
158
159
  collector << "NULL"
159
160
  else
160
161
  collector << 'TO_CLOB('
@@ -68,7 +68,7 @@ module ArelExtensions
68
68
  if i != 0
69
69
  collector << Arel::Visitors::MySQL::COMMA
70
70
  end
71
- collector = visit v, collector
71
+ collector = visit v, collector
72
72
  end
73
73
  collector << ')'
74
74
  when Hash
@@ -120,11 +120,12 @@ module ArelExtensions
120
120
  o.order = nil
121
121
  visit_Aggregate_For_AggregateFunction o, collector
122
122
  collector << Arel::Visitors::PostgreSQL::COMMA
123
- if o.separator && o.separator != 'NULL'
124
- collector = visit o.separator, collector
125
- else
126
- collector = visit Arel::Nodes.build_quoted(','), collector
127
- end
123
+ collector =
124
+ if o.separator && o.separator != 'NULL'
125
+ visit o.separator, collector
126
+ else
127
+ visit Arel::Nodes.build_quoted(','), collector
128
+ end
128
129
  collector << ")"
129
130
  collector
130
131
  end
@@ -302,6 +303,22 @@ module ArelExtensions
302
303
  collector
303
304
  end
304
305
 
306
+ def visit_ArelExtensions_Nodes_RegexpReplace o, collector
307
+ collector << "REGEXP_REPLACE("
308
+ visit o.left, collector
309
+ collector << Arel::Visitors::ToSql::COMMA
310
+ tab = o.pattern.inspect+ 'g' # Make it always global
311
+ pattern = tab.split('/')[1..-2].join('/')
312
+ flags = tab.split('/')[-1]
313
+ visit Arel::Nodes.build_quoted(pattern), collector
314
+ collector << Arel::Visitors::ToSql::COMMA
315
+ visit o.substitute, collector
316
+ collector << Arel::Visitors::ToSql::COMMA
317
+ visit Arel::Nodes.build_quoted(flags+"g"), collector
318
+ collector << ")"
319
+ collector
320
+ end
321
+
305
322
  def visit_ArelExtensions_Nodes_IsNull o, collector
306
323
  collector = visit o.expr, collector
307
324
  collector << ' IS NULL'
@@ -371,27 +388,28 @@ module ArelExtensions
371
388
  else(o.flags.include?('+') ? '+' : (o.flags.include?(' ') ? ' ' : ''))
372
389
  sign_length = ArelExtensions::Nodes::Length.new([sign])
373
390
 
374
- if o.scientific_notation
375
- number = ArelExtensions::Nodes::Concat.new([
376
- Arel::Nodes::NamedFunction.new('TRIM',[
377
- Arel::Nodes::NamedFunction.new('TO_CHAR',[
378
- col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor),
379
- Arel::Nodes.build_quoted('FM'+nines_before+'"'+comma+'"V'+nines_after)
380
- ])]),
381
- o.type,
382
- Arel::Nodes::NamedFunction.new('TRIM',[
383
- Arel::Nodes::NamedFunction.new('TO_CHAR',[
384
- col.abs.log10.floor,
385
- Arel::Nodes.build_quoted('FM'+nines_before)
386
- ])])
387
- ])
388
- else
389
- number = Arel::Nodes::NamedFunction.new('TRIM',[
390
- Arel::Nodes::NamedFunction.new('TO_CHAR',[
391
- Arel::Nodes.build_quoted(col.abs),
392
- Arel::Nodes.build_quoted('FM'+nines_before+'"'+comma+'"V'+nines_after)
393
- ])])
394
- end
391
+ number =
392
+ if o.scientific_notation
393
+ ArelExtensions::Nodes::Concat.new([
394
+ Arel::Nodes::NamedFunction.new('TRIM',[
395
+ Arel::Nodes::NamedFunction.new('TO_CHAR',[
396
+ col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor),
397
+ Arel::Nodes.build_quoted('FM'+nines_before+'"'+comma+'"V'+nines_after)
398
+ ])]),
399
+ o.type,
400
+ Arel::Nodes::NamedFunction.new('TRIM',[
401
+ Arel::Nodes::NamedFunction.new('TO_CHAR',[
402
+ col.abs.log10.floor,
403
+ Arel::Nodes.build_quoted('FM'+nines_before)
404
+ ])])
405
+ ])
406
+ else
407
+ Arel::Nodes::NamedFunction.new('TRIM',[
408
+ Arel::Nodes::NamedFunction.new('TO_CHAR',[
409
+ Arel::Nodes.build_quoted(col.abs),
410
+ Arel::Nodes.build_quoted('FM'+nines_before+'"'+comma+'"V'+nines_after)
411
+ ])])
412
+ end
395
413
 
396
414
  repeated_char = (o.width == 0) ? Arel::Nodes.build_quoted('') : ArelExtensions::Nodes::Case.new().
397
415
  when(Arel::Nodes.build_quoted(o.width).abs-(number.length+sign_length)>0).
@@ -466,7 +484,7 @@ module ArelExtensions
466
484
  if i != 0
467
485
  collector << Arel::Visitors::MySQL::COMMA
468
486
  end
469
- collector = visit v, collector
487
+ collector = visit v, collector
470
488
  end
471
489
  collector << '])'
472
490
  when Hash
@@ -193,18 +193,28 @@ module ArelExtensions
193
193
  collector
194
194
  end
195
195
 
196
- # CASE WHEN ROUND(3.42,1) > round(3.42) THEN round(3.42) ELSE round(3.42)-1 END
197
- # OR CAST(3.14 AS INTEGER)
196
+ # CAST(
197
+ # CASE
198
+ # WHEN 3.42 >= 0 THEN CAST(3.42 AS INT)
199
+ # WHEN CAST(3.42 AS INT) = 3.42 THEN CAST(3.42 AS INT)
200
+ # ELSE CAST((3.42 - 1.0) AS INT)
201
+ # END
202
+ # AS FLOAT
203
+ # )
198
204
  def visit_ArelExtensions_Nodes_Floor o, collector
199
- collector << "CASE WHEN ROUND("
205
+ collector << "CAST(CASE WHEN "
200
206
  collector = visit o.left, collector
201
- collector << ", 1) > ROUND("
207
+ collector << " >= 0 THEN CAST("
202
208
  collector = visit o.left, collector
203
- collector << ") THEN ROUND("
209
+ collector << " AS INT) WHEN CAST("
210
+ collector = visit o.left, collector
211
+ collector << " AS INT) = "
204
212
  collector = visit o.left, collector
205
- collector << ") ELSE ROUND("
213
+ collector << " THEN CAST("
206
214
  collector = visit o.left, collector
207
- collector << ") - 1 END"
215
+ collector << " AS INT) ELSE CAST(("
216
+ collector = visit o.left, collector
217
+ collector << " - 1.0) AS INT) END AS FLOAT)"
208
218
  collector
209
219
  end
210
220
 
@@ -270,32 +280,36 @@ module ArelExtensions
270
280
  end
271
281
 
272
282
  def visit_ArelExtensions_Nodes_Union o, collector
273
- if o.left.is_a?(Arel::SelectManager)
274
- collector = visit o.left.ast, collector
275
- else
276
- collector = visit o.left, collector
277
- end
283
+ collector =
284
+ if o.left.is_a?(Arel::SelectManager)
285
+ visit o.left.ast, collector
286
+ else
287
+ visit o.left, collector
288
+ end
278
289
  collector << " UNION "
279
- if o.right.is_a?(Arel::SelectManager)
280
- collector = visit o.right.ast, collector
281
- else
282
- collector = visit o.right, collector
283
- end
290
+ collector =
291
+ if o.right.is_a?(Arel::SelectManager)
292
+ visit o.right.ast, collector
293
+ else
294
+ visit o.right, collector
295
+ end
284
296
  collector
285
297
  end
286
298
 
287
299
  def visit_ArelExtensions_Nodes_UnionAll o, collector
288
- if o.left.is_a?(Arel::SelectManager)
289
- collector = visit o.left.ast, collector
290
- else
291
- collector = visit o.left, collector
292
- end
300
+ collector =
301
+ if o.left.is_a?(Arel::SelectManager)
302
+ visit o.left.ast, collector
303
+ else
304
+ visit o.left, collector
305
+ end
293
306
  collector << " UNION ALL "
294
- if o.right.is_a?(Arel::SelectManager)
295
- collector = visit o.right.ast, collector
296
- else
297
- collector = visit o.right, collector
298
- end
307
+ collector =
308
+ if o.right.is_a?(Arel::SelectManager)
309
+ visit o.right.ast, collector
310
+ else
311
+ visit o.right, collector
312
+ end
299
313
  collector
300
314
  end
301
315
 
@@ -1,6 +1,7 @@
1
1
  module ArelExtensions
2
2
  module Visitors
3
- Arel::Visitors::ToSql.class_eval do
3
+ Arel::Visitors::ToSql.class_eval do
4
+ Arel::Visitors::ToSql::COMMA = ', ' unless defined?(Arel::Visitors::ToSql::COMMA)
4
5
 
5
6
  # Math Functions
6
7
  def visit_ArelExtensions_Nodes_Abs o, collector
@@ -134,10 +135,22 @@ module ArelExtensions
134
135
 
135
136
  def visit_ArelExtensions_Nodes_Replace o, collector
136
137
  collector << "REPLACE("
137
- o.expressions.each_with_index { |arg, i|
138
- collector << Arel::Visitors::ToSql::COMMA unless i == 0
139
- collector = visit arg, collector
140
- }
138
+ visit o.left, collector
139
+ collector << Arel::Visitors::ToSql::COMMA
140
+ visit o.pattern, collector
141
+ collector << Arel::Visitors::ToSql::COMMA
142
+ visit o.substitute, collector
143
+ collector << ")"
144
+ collector
145
+ end
146
+
147
+ def visit_ArelExtensions_Nodes_RegexpReplace o, collector
148
+ collector << "REGEXP_REPLACE("
149
+ visit o.left, collector
150
+ collector << Arel::Visitors::ToSql::COMMA
151
+ visit Arel::Nodes.build_quoted(o.pattern.to_s), collector
152
+ collector << Arel::Visitors::ToSql::COMMA
153
+ visit o.substitute, collector
141
154
  collector << ")"
142
155
  collector
143
156
  end
@@ -9,8 +9,8 @@ module ArelExtensions
9
9
  @table = Arel::Table.new(:users)
10
10
  @cols = ['name', 'comments', 'created_at']
11
11
  @data = [
12
- ['nom1', "sdfdsfdsfsdf", '2016-01-01'],
13
- ['nom2', "sdfdsfdsfsdf", '2016-01-01']
12
+ ['nom1', "sdfdsfdsfsdf", '2016-01-01'],
13
+ ['nom2', "sdfdsfdsfsdf", '2016-01-01']
14
14
  ]
15
15
  end
16
16
 
@@ -24,11 +24,11 @@ module ArelExtensions
24
24
 
25
25
  it "should import large set of data in Oracle" do
26
26
  insert_manager = Arel::VERSION.to_i > 6 ? Arel::InsertManager.new().into(@table) : Arel::InsertManager.new(@conn).into(@table)
27
- insert_manager.bulk_insert(@cols, @data)
28
- sql = compile(insert_manager.ast)
29
- sql.must_be_like %Q[INSERT INTO "users" ("name", "comments", "created_at") ((SELECT 'nom1', 'sdfdsfdsfsdf', '2016-01-01' FROM DUAL) UNION ALL (SELECT 'nom2', 'sdfdsfdsfsdf', '2016-01-01' FROM DUAL))]
27
+ insert_manager.bulk_insert(@cols, @data)
28
+ sql = compile(insert_manager.ast)
29
+ sql.must_be_like %Q[INSERT INTO "users" ("name", "comments", "created_at") ((SELECT 'nom1', 'sdfdsfdsfsdf', '2016-01-01' FROM DUAL) UNION ALL (SELECT 'nom2', 'sdfdsfdsfsdf', '2016-01-01' FROM DUAL))]
30
30
  end
31
31
 
32
- end
32
+ end
33
33
  end
34
34
  end
@@ -11,8 +11,8 @@ module ArelExtensions
11
11
  Arel::Table.engine = @conn
12
12
  @cols = ['id', 'name', 'comments', 'created_at']
13
13
  @data = [
14
- [23, 'nom1', "sdfdsfdsfsdf", '2016-01-01'],
15
- [25, 'nom2', "sdfdsfdsfsdf", '2016-01-01']
14
+ [23, 'nom1', "sdfdsfdsfsdf", '2016-01-01'],
15
+ [25, 'nom2', "sdfdsfdsfsdf", '2016-01-01']
16
16
  ]
17
17
  end
18
18
 
@@ -28,9 +28,9 @@ module ArelExtensions
28
28
  insert_manager = Arel::VERSION.to_i > 6 ? Arel::InsertManager.new().into(@table) : Arel::InsertManager.new(@conn).into(@table)
29
29
  insert_manager.bulk_insert(@cols, @data)
30
30
  sql = compile(insert_manager.ast)
31
- sql.must_be_like %Q[INSERT INTO "users" ("id", "name", "comments", "created_at") SELECT 23 AS 'id', 'nom1' AS 'name', 'sdfdsfdsfsdf' AS 'comments', '2016-01-01' AS 'created_at' UNION ALL SELECT 25, 'nom2', 'sdfdsfdsfsdf', '2016-01-01']
31
+ sql.must_be_like %Q[INSERT INTO "users" ("id", "name", "comments", "created_at") SELECT 23 AS 'id', 'nom1' AS 'name', 'sdfdsfdsfsdf' AS 'comments', '2016-01-01' AS 'created_at' UNION ALL SELECT 25, 'nom2', 'sdfdsfdsfsdf', '2016-01-01']
32
32
  end
33
33
 
34
- end
34
+ end
35
35
  end
36
- end
36
+ end