arel_extensions 1.2.5 → 1.2.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -4
  3. data/.travis.yml +54 -86
  4. data/README.md +7 -2
  5. data/Rakefile +38 -27
  6. data/arel_extensions.gemspec +1 -1
  7. data/functions.html +2 -2
  8. data/gemfiles/rails4.gemfile +1 -1
  9. data/gemfiles/rails6.gemfile +30 -0
  10. data/gemspec_v2/arel_extensions-v2.gemspec +28 -0
  11. data/generate_gems.sh +14 -0
  12. data/lib/arel_extensions.rb +49 -21
  13. data/lib/arel_extensions/attributes.rb +0 -1
  14. data/lib/arel_extensions/boolean_functions.rb +38 -13
  15. data/lib/arel_extensions/common_sql_functions.rb +5 -4
  16. data/lib/arel_extensions/insert_manager.rb +26 -24
  17. data/lib/arel_extensions/math.rb +3 -3
  18. data/lib/arel_extensions/math_functions.rb +4 -4
  19. data/lib/arel_extensions/nodes/abs.rb +0 -0
  20. data/lib/arel_extensions/nodes/case.rb +8 -4
  21. data/lib/arel_extensions/nodes/ceil.rb +0 -0
  22. data/lib/arel_extensions/nodes/coalesce.rb +0 -0
  23. data/lib/arel_extensions/nodes/collate.rb +1 -1
  24. data/lib/arel_extensions/nodes/concat.rb +0 -0
  25. data/lib/arel_extensions/nodes/date_diff.rb +1 -3
  26. data/lib/arel_extensions/nodes/duration.rb +0 -2
  27. data/lib/arel_extensions/nodes/find_in_set.rb +0 -0
  28. data/lib/arel_extensions/nodes/floor.rb +0 -0
  29. data/lib/arel_extensions/nodes/formatted_number.rb +20 -20
  30. data/lib/arel_extensions/nodes/function.rb +0 -0
  31. data/lib/arel_extensions/nodes/is_null.rb +0 -0
  32. data/lib/arel_extensions/nodes/json.rb +43 -30
  33. data/lib/arel_extensions/nodes/length.rb +0 -0
  34. data/lib/arel_extensions/nodes/locate.rb +0 -0
  35. data/lib/arel_extensions/nodes/power.rb +5 -4
  36. data/lib/arel_extensions/nodes/rand.rb +0 -0
  37. data/lib/arel_extensions/nodes/replace.rb +23 -5
  38. data/lib/arel_extensions/nodes/round.rb +5 -5
  39. data/lib/arel_extensions/nodes/soundex.rb +14 -13
  40. data/lib/arel_extensions/nodes/substring.rb +8 -15
  41. data/lib/arel_extensions/nodes/trim.rb +1 -1
  42. data/lib/arel_extensions/nodes/union.rb +0 -1
  43. data/lib/arel_extensions/nodes/union_all.rb +0 -1
  44. data/lib/arel_extensions/nodes/wday.rb +0 -0
  45. data/lib/arel_extensions/predications.rb +35 -33
  46. data/lib/arel_extensions/set_functions.rb +2 -2
  47. data/lib/arel_extensions/string_functions.rb +25 -6
  48. data/lib/arel_extensions/tasks.rb +5 -5
  49. data/lib/arel_extensions/version.rb +1 -1
  50. data/lib/arel_extensions/visitors.rb +1 -1
  51. data/lib/arel_extensions/visitors/ibm_db.rb +1 -1
  52. data/lib/arel_extensions/visitors/mssql.rb +13 -12
  53. data/lib/arel_extensions/visitors/mysql.rb +67 -37
  54. data/lib/arel_extensions/visitors/oracle.rb +14 -14
  55. data/lib/arel_extensions/visitors/oracle12.rb +1 -1
  56. data/lib/arel_extensions/visitors/postgresql.rb +46 -28
  57. data/lib/arel_extensions/visitors/sqlite.rb +52 -44
  58. data/lib/arel_extensions/visitors/to_sql.rb +73 -59
  59. data/test/arelx_test_helper.rb +28 -0
  60. data/test/support/fake_record.rb +4 -0
  61. data/test/test_comparators.rb +8 -7
  62. data/test/visitors/test_bulk_insert_oracle.rb +8 -7
  63. data/test/visitors/test_bulk_insert_sqlite.rb +8 -7
  64. data/test/visitors/test_bulk_insert_to_sql.rb +3 -3
  65. data/test/visitors/test_oracle.rb +41 -41
  66. data/test/visitors/test_to_sql.rb +367 -199
  67. data/test/with_ar/all_agnostic_test.rb +63 -41
  68. data/test/with_ar/insert_agnostic_test.rb +1 -1
  69. data/test/with_ar/test_bulk_sqlite.rb +5 -4
  70. data/test/with_ar/test_math_sqlite.rb +2 -2
  71. data/test/with_ar/test_string_mysql.rb +2 -4
  72. data/test/with_ar/test_string_sqlite.rb +2 -6
  73. data/version_v1.rb +3 -0
  74. data/version_v2.rb +3 -0
  75. metadata +10 -5
  76. data/test/helper.rb +0 -18
@@ -1,6 +1,6 @@
1
1
  module ArelExtensions
2
2
  module Visitors
3
- Arel::Visitors::SQLite.class_eval do
3
+ class Arel::Visitors::SQLite
4
4
  Arel::Visitors::SQLite::DATE_MAPPING = {'d' => '%d', 'm' => '%m', 'w' => '%W', 'y' => '%Y', 'wd' => '%w', 'M' => '%M', 'h' => '%H', 'mn' => '%M', 's' => '%S'}
5
5
  Arel::Visitors::SQLite::DATE_FORMAT_DIRECTIVES = { # ISO C / POSIX
6
6
  '%Y' => '%Y', '%C' => '', '%y' => '%y', '%m' => '%m', '%B' => '%M', '%b' => '%b', '%^b' => '%b', # year, month
@@ -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) = "
212
+ collector = visit o.left, collector
213
+ collector << " THEN CAST("
204
214
  collector = visit o.left, collector
205
- collector << ") ELSE ROUND("
215
+ collector << " AS INT) ELSE CAST(("
206
216
  collector = visit o.left, collector
207
- collector << ") - 1 END"
217
+ collector << " - 1.0) AS INT) END AS FLOAT)"
208
218
  collector
209
219
  end
210
220
 
@@ -225,9 +235,8 @@ module ArelExtensions
225
235
  def visit_ArelExtensions_InsertManager_BulkValues o, collector
226
236
  o.left.each_with_index do |row, idx|
227
237
  collector << 'SELECT '
228
- v = Arel::Nodes::Values.new(row, o.cols)
229
- len = v.expressions.length - 1
230
- v.expressions.zip(v.columns).each_with_index { |(value, attr), i|
238
+ len = row.length - 1
239
+ row.zip(o.cols).each_with_index { |(value, attr), i|
231
240
  case value
232
241
  when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
233
242
  collector = visit value.as(attr.name), collector
@@ -248,12 +257,17 @@ module ArelExtensions
248
257
  def visit_ArelExtensions_InsertManager_BulkValues o, collector
249
258
  o.left.each_with_index do |row, idx|
250
259
  collector << 'SELECT '
251
- v = Arel::Nodes::Values.new(row, o.cols)
252
- len = v.expressions.length - 1
253
- v.expressions.zip(v.columns).each_with_index { |(value, attr), i|
260
+ len = row.length - 1
261
+ row.zip(o.cols).each_with_index { |(value, attr), i|
254
262
  case value
255
263
  when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
256
264
  collector = visit value.as(attr.name), collector
265
+ when Integer
266
+ collector << value.to_s
267
+ if idx == 0
268
+ collector << " AS "
269
+ collector << quote(attr.name)
270
+ end
257
271
  else
258
272
  collector << (attr && attr.able_to_type_cast? ? quote(attr.type_cast_for_database(value)) : quote(value).to_s)
259
273
  if idx == 0
@@ -270,32 +284,36 @@ module ArelExtensions
270
284
  end
271
285
 
272
286
  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
287
+ collector =
288
+ if o.left.is_a?(Arel::SelectManager)
289
+ visit o.left.ast, collector
290
+ else
291
+ visit o.left, collector
292
+ end
278
293
  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
294
+ collector =
295
+ if o.right.is_a?(Arel::SelectManager)
296
+ visit o.right.ast, collector
297
+ else
298
+ visit o.right, collector
299
+ end
284
300
  collector
285
301
  end
286
302
 
287
303
  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
304
+ collector =
305
+ if o.left.is_a?(Arel::SelectManager)
306
+ visit o.left.ast, collector
307
+ else
308
+ visit o.left, collector
309
+ end
293
310
  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
311
+ collector =
312
+ if o.right.is_a?(Arel::SelectManager)
313
+ visit o.right.ast, collector
314
+ else
315
+ visit o.right, collector
316
+ end
299
317
  collector
300
318
  end
301
319
 
@@ -346,16 +364,6 @@ module ArelExtensions
346
364
  collector
347
365
  end
348
366
 
349
-
350
- alias_method :old_visit_Arel_Nodes_SelectStatement, :visit_Arel_Nodes_SelectStatement
351
- def visit_Arel_Nodes_SelectStatement o, collector
352
- if !collector.value.blank? && o.limit.blank?
353
- o = o.dup
354
- o.orders = []
355
- end
356
- old_visit_Arel_Nodes_SelectStatement(o,collector)
357
- end
358
-
359
367
  alias_method :old_visit_Arel_Nodes_As, :visit_Arel_Nodes_As
360
368
  def visit_Arel_Nodes_As o, collector
361
369
  if o.left.is_a?(Arel::Nodes::Binary)
@@ -1,6 +1,7 @@
1
1
  module ArelExtensions
2
2
  module Visitors
3
- Arel::Visitors::ToSql.class_eval do
3
+ class Arel::Visitors::ToSql
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
@@ -415,16 +428,15 @@ module ArelExtensions
415
428
  row_nb = o.left.length
416
429
  o.left.each_with_index do |row, idx|
417
430
  collector << '('
418
- v = Arel::Nodes::Values.new(row, o.cols)
419
- len = v.expressions.length - 1
420
- v.expressions.zip(v.columns).each_with_index { |(value, attr), i|
421
- case value
422
- when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
423
- collector = visit value, collector
424
- else
425
- collector << quote(value, attr && column_for(attr)).to_s
426
- end
427
- collector << Arel::Visitors::ToSql::COMMA unless i == len
431
+ len = row.length - 1
432
+ row.zip(o.cols).each_with_index { |(value, attr), i|
433
+ case value
434
+ when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
435
+ collector = visit value, collector
436
+ else
437
+ collector << quote(value, attr && column_for(attr)).to_s
438
+ end
439
+ collector << Arel::Visitors::ToSql::COMMA unless i == len
428
440
  }
429
441
  collector << (idx == row_nb-1 ? ')' : '), ')
430
442
  end
@@ -436,16 +448,17 @@ module ArelExtensions
436
448
  row_nb = o.left.length
437
449
  o.left.each_with_index do |row, idx|
438
450
  collector << '('
439
- v = Arel::Nodes::Values.new(row, o.cols)
440
- len = v.expressions.length - 1
441
- v.expressions.zip(v.columns).each_with_index { |(value, attr), i|
442
- case value
443
- when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
444
- collector = visit value, collector
445
- else
446
- collector << (attr && attr.able_to_type_cast? ? quote(attr.type_cast_for_database(value)) : quote(value).to_s)
447
- end
448
- collector << Arel::Visitors::ToSql::COMMA unless i == len
451
+ len = row.length - 1
452
+ row.zip(o.cols).each_with_index { |(value, attr), i|
453
+ case value
454
+ when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
455
+ collector = visit value, collector
456
+ when Integer
457
+ collector << value.to_s
458
+ else
459
+ collector << (attr && attr.able_to_type_cast? ? quote(attr.type_cast_for_database(value)) : quote(value).to_s)
460
+ end
461
+ collector << Arel::Visitors::ToSql::COMMA unless i == len
449
462
  }
450
463
  collector << (idx == row_nb-1 ? ')' : '), ')
451
464
  end
@@ -530,53 +543,66 @@ module ArelExtensions
530
543
  collector
531
544
  end
532
545
 
546
+ # Boolean logic.
547
+
533
548
  alias_method :old_visit_Arel_Nodes_And, :visit_Arel_Nodes_And
534
549
  def visit_Arel_Nodes_And o, collector
535
- collector << '('
536
550
  case o.children.length
537
551
  when 0
538
- collector << '1=1' # but this should not happen
552
+ collector << '1 = 1' # but this should not happen
539
553
  when 1
540
554
  collector = visit o.children[0], collector
541
555
  else
556
+ collector << '('
542
557
  o.children.each_with_index { |arg, i|
543
558
  if i != 0
544
559
  collector << ') AND ('
545
560
  end
546
561
  collector = visit arg, collector
547
562
  }
563
+ collector << ')'
548
564
  end
549
- collector << ')'
550
565
  collector
551
566
  end
552
567
 
553
- def visit_ArelExtensions_Nodes_Or o, collector
554
- collector << '('
568
+ alias_method :old_visit_Arel_Nodes_Or, :visit_Arel_Nodes_Or
569
+ def visit_Arel_Nodes_Or o, collector
555
570
  case o.children.length
556
571
  when 0
557
- collector << '0=1' # but this should not happen
572
+ collector << '1 = 0' # but this should not happen
558
573
  when 1
559
574
  collector = visit o.children[0], collector
560
575
  else
576
+ collector << '('
561
577
  o.children.each_with_index { |arg, i|
562
578
  if i != 0
563
579
  collector << ') OR ('
564
580
  end
565
581
  collector = visit arg, collector
566
582
  }
583
+ collector << ')'
567
584
  end
568
- collector << ')'
569
585
  collector
570
586
  end
571
587
 
572
- alias_method :old_visit_Arel_Nodes_Or, :visit_Arel_Nodes_Or
573
- def visit_Arel_Nodes_Or o, collector
574
- collector << '('
575
- collector = visit o.left, collector
576
- collector << ') OR ('
577
- collector = visit o.right, collector
578
- collector << ')'
579
- collector
588
+ def json_value(o,v)
589
+ case o.type_of_node(v)
590
+ when :string
591
+ Arel.when(v.is_null).then(Arel::Nodes.build_quoted("null")).else(Arel::Nodes.build_quoted('"') + v.replace('\\','\\\\').replace('"','\"') + '"')
592
+ when :date
593
+ s = v.format('%Y-%m-%d')
594
+ Arel.when(s.is_null).then(Arel::Nodes.build_quoted("null")).else(Arel::Nodes.build_quoted('"') + s + '"')
595
+ when :datetime
596
+ s = v.format('%Y-%m-%dT%H:%M:%S')
597
+ Arel.when(s.is_null).then(Arel::Nodes.build_quoted("null")).else(Arel::Nodes.build_quoted('"') + s + '"')
598
+ when :time
599
+ s = v.format('%H:%M:%S')
600
+ Arel.when(s.is_null).then(Arel::Nodes.build_quoted("null")).else(Arel::Nodes.build_quoted('"') + s + '"')
601
+ when :nil
602
+ Arel::Nodes.build_quoted("null")
603
+ else
604
+ ArelExtensions::Nodes::Cast.new([v, :string]).coalesce("null")
605
+ end
580
606
  end
581
607
 
582
608
  def visit_ArelExtensions_Nodes_Json o,collector
@@ -587,11 +613,7 @@ module ArelExtensions
587
613
  if i != 0
588
614
  res += ', '
589
615
  end
590
- if (v.is_a?(Arel::Attributes::Attribute) && o.type_of_attribute(v) == :string) || (v.return_type == :string)
591
- res = res + '"' + v + '"'
592
- else
593
- res += v
594
- end
616
+ res += json_value(o,v)
595
617
  end
596
618
  res += ']'
597
619
  collector = visit res, collector
@@ -601,12 +623,8 @@ module ArelExtensions
601
623
  if i != 0
602
624
  res += ', '
603
625
  end
604
- res += Arel::Nodes.build_quoted('"')+k + '": '
605
- if (v.is_a?(Arel::Attributes::Attribute) && o.type_of_attribute(v) == :string) || (v.respond_to?(:return_type) && v.return_type == :string)
606
- res = res + '"' + v + '"'
607
- else
608
- res += v
609
- end
626
+ res += Arel::Nodes.build_quoted('"') + ArelExtensions::Nodes::Cast.new([k, :string]).coalesce("").replace('\\','\\\\').replace('"','\"') + '": '
627
+ res += json_value(o,v)
610
628
  end
611
629
  res += '}'
612
630
  collector = visit res, collector
@@ -618,7 +636,7 @@ module ArelExtensions
618
636
 
619
637
  def visit_ArelExtensions_Nodes_JsonGroup o, collector
620
638
  if o.as_array
621
- res = Arel::Nodes.build_quoted('[') + (o.orders ? o.dict.group_concat(', ',o.orders) : o.dict.group_concat(', ')) + ']'
639
+ res = Arel::Nodes.build_quoted('[') + (o.orders ? o.dict.group_concat(', ', order: Array(o.orders)) : o.dict.group_concat(', ')).coalesce('') + ']'
622
640
  collector = visit res, collector
623
641
  else
624
642
  res = Arel::Nodes.build_quoted('{')
@@ -627,13 +645,9 @@ module ArelExtensions
627
645
  if i != 0
628
646
  res = res + ', '
629
647
  end
630
- kv = Arel::Nodes.build_quoted('"')+k + '": '
631
- if (v.is_a?(Arel::Attributes::Attribute) && o.type_of_attribute(v) == :string) || (v.respond_to?(:return_type) && v.return_type == :string)
632
- kv = kv + '"' + v + '"'
633
- else
634
- kv += v
635
- end
636
- res = res + kv.group_concat(', ',orders)
648
+ kv = Arel::Nodes.build_quoted('"') + ArelExtensions::Nodes::Cast.new([k, :string]).coalesce("").replace('\\','\\\\').replace('"','\"') + '": '
649
+ kv += json_value(o,v)
650
+ res = res + kv.group_concat(', ', order: Array(orders)).coalesce('')
637
651
  end
638
652
  res = res + '}'
639
653
  collector = visit res, collector
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'minitest/autorun'
3
+ require 'fileutils'
4
+ require 'arel'
5
+ require 'active_record'
6
+
7
+ require 'support/fake_record'
8
+
9
+ require 'arel_extensions'
10
+ Arel::Table.engine = FakeRecord::Base.new
11
+
12
+ $arel_silence_type_casting_deprecation = true
13
+
14
+ module Minitest::Assertions
15
+ #
16
+ # Fails unless +expected and +actual are the same string, modulo extraneous spaces.
17
+ #
18
+ def assert_like(expected, actual, msg = nil)
19
+ msg ||= "Expected #{expected.inspect} and #{actual.inspect} to be alike"
20
+ assert_equal expected.gsub(/\s+/, ' ').strip, actual.gsub(/\s+/, ' ').strip
21
+ end
22
+ end
23
+
24
+ module Minitest::Expectations
25
+
26
+ infect_an_assertion :assert_like, :must_be_like
27
+
28
+ end
@@ -65,6 +65,10 @@ module FakeRecord
65
65
  self
66
66
  end
67
67
 
68
+ def in_clause_length
69
+ 10000
70
+ end
71
+
68
72
  def quote thing, column = nil
69
73
  if column && !thing.nil?
70
74
  case column.type
@@ -1,4 +1,4 @@
1
- require 'helper'
1
+ require 'arelx_test_helper'
2
2
 
3
3
  module ArelExtensions
4
4
  module Nodes
@@ -20,26 +20,27 @@ module ArelExtensions
20
20
  end
21
21
 
22
22
  it "< is equal lt" do
23
- compile(@table[:id] < 10).must_be_like('"users"."id" < 10')
23
+ _(compile(@table[:id] < 10)).must_be_like('"users"."id" < 10')
24
24
  end
25
25
 
26
26
  it "<= is equal lteq" do
27
- compile(@table[:id] <= 10).must_be_like('"users"."id" <= 10')
27
+ _(compile(@table[:id] <= 10)).must_be_like('"users"."id" <= 10')
28
28
  end
29
29
 
30
30
  it "> is equal gt" do
31
- compile(@table[:id] > 10).must_be_like('"users"."id" > 10')
31
+ _(compile(@table[:id] > 10)).must_be_like('"users"."id" > 10')
32
32
  end
33
33
 
34
34
  it "< is equal gteq" do
35
- compile(@table[:id] >= 10).must_be_like('"users"."id" >= 10')
35
+ _(compile(@table[:id] >= 10)).must_be_like('"users"."id" >= 10')
36
36
  end
37
37
 
38
38
  it "should compare with dates" do
39
- compile(@table[:created_at] >= Date.new(2016, 3, 31)).must_be_like %{"users"."created_at" >= '2016-03-31'}
39
+ _(compile(@table[:created_at] >= Date.new(2016, 3, 31)))
40
+ .must_be_like %{"users"."created_at" >= '2016-03-31'}
40
41
  end
41
42
 
42
43
  end
43
44
 
44
45
  end
45
- end
46
+ end