active_fields 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -4
  3. data/CHANGELOG.md +33 -2
  4. data/README.md +478 -90
  5. data/app/models/active_fields/field/boolean.rb +3 -0
  6. data/app/models/active_fields/field/date.rb +3 -0
  7. data/app/models/active_fields/field/date_array.rb +3 -0
  8. data/app/models/active_fields/field/date_time.rb +4 -1
  9. data/app/models/active_fields/field/date_time_array.rb +4 -1
  10. data/app/models/active_fields/field/decimal.rb +6 -1
  11. data/app/models/active_fields/field/decimal_array.rb +6 -1
  12. data/app/models/active_fields/field/enum.rb +3 -0
  13. data/app/models/active_fields/field/enum_array.rb +3 -0
  14. data/app/models/active_fields/field/integer.rb +3 -0
  15. data/app/models/active_fields/field/integer_array.rb +3 -0
  16. data/app/models/active_fields/field/text.rb +3 -0
  17. data/app/models/active_fields/field/text_array.rb +3 -0
  18. data/app/models/active_fields/field.rb +5 -0
  19. data/app/models/concerns/active_fields/customizable_concern.rb +93 -4
  20. data/app/models/concerns/active_fields/field_concern.rb +26 -3
  21. data/db/migrate/20240229230000_create_active_fields_tables.rb +1 -1
  22. data/lib/active_fields/casters/date_time_caster.rb +1 -3
  23. data/lib/active_fields/casters/decimal_caster.rb +2 -5
  24. data/lib/active_fields/constants.rb +55 -0
  25. data/lib/active_fields/engine.rb +7 -0
  26. data/lib/active_fields/finders/array_finder.rb +112 -0
  27. data/lib/active_fields/finders/base_finder.rb +73 -0
  28. data/lib/active_fields/finders/boolean_finder.rb +20 -0
  29. data/lib/active_fields/finders/date_array_finder.rb +65 -0
  30. data/lib/active_fields/finders/date_finder.rb +32 -0
  31. data/lib/active_fields/finders/date_time_array_finder.rb +65 -0
  32. data/lib/active_fields/finders/date_time_finder.rb +32 -0
  33. data/lib/active_fields/finders/decimal_array_finder.rb +65 -0
  34. data/lib/active_fields/finders/decimal_finder.rb +32 -0
  35. data/lib/active_fields/finders/enum_array_finder.rb +41 -0
  36. data/lib/active_fields/finders/enum_finder.rb +20 -0
  37. data/lib/active_fields/finders/integer_array_finder.rb +65 -0
  38. data/lib/active_fields/finders/integer_finder.rb +32 -0
  39. data/lib/active_fields/finders/singular_finder.rb +66 -0
  40. data/lib/active_fields/finders/text_array_finder.rb +47 -0
  41. data/lib/active_fields/finders/text_finder.rb +81 -0
  42. data/lib/active_fields/has_active_fields.rb +3 -4
  43. data/lib/active_fields/registry.rb +38 -0
  44. data/lib/active_fields/version.rb +1 -1
  45. data/lib/active_fields.rb +79 -31
  46. data/lib/generators/active_fields/install/install_generator.rb +1 -1
  47. data/lib/generators/active_fields/scaffold/USAGE +9 -0
  48. data/lib/generators/active_fields/scaffold/scaffold_generator.rb +34 -0
  49. data/lib/generators/active_fields/scaffold/templates/controllers/active_fields_controller.rb +133 -0
  50. data/lib/generators/active_fields/scaffold/templates/controllers/concerns/active_fields_controller_concern.rb +33 -0
  51. data/lib/generators/active_fields/scaffold/templates/helpers/active_fields_helper.rb +100 -0
  52. data/lib/generators/active_fields/scaffold/templates/javascript/controllers/active_fields_finders_form_controller.js +59 -0
  53. data/lib/generators/active_fields/scaffold/templates/javascript/controllers/array_field_controller.js +25 -0
  54. data/lib/generators/active_fields/scaffold/templates/views/active_fields/edit.html.erb +5 -0
  55. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/_form.html.erb +42 -0
  56. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_array_size.html.erb +16 -0
  57. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_boolean.html.erb +21 -0
  58. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_date.html.erb +16 -0
  59. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_date_array.html.erb +16 -0
  60. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_datetime.html.erb +16 -0
  61. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_datetime_array.html.erb +16 -0
  62. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_decimal.html.erb +16 -0
  63. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_decimal_array.html.erb +16 -0
  64. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_enum.html.erb +16 -0
  65. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_enum_array.html.erb +16 -0
  66. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_integer.html.erb +16 -0
  67. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_integer_array.html.erb +16 -0
  68. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_text.html.erb +16 -0
  69. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_text_array.html.erb +16 -0
  70. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_boolean.html.erb +53 -0
  71. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_date.html.erb +58 -0
  72. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_date_array.html.erb +70 -0
  73. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_datetime.html.erb +63 -0
  74. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_datetime_array.html.erb +75 -0
  75. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_decimal.html.erb +63 -0
  76. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_decimal_array.html.erb +76 -0
  77. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_enum.html.erb +61 -0
  78. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_enum_array.html.erb +73 -0
  79. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_integer.html.erb +58 -0
  80. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_integer_array.html.erb +70 -0
  81. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_text.html.erb +53 -0
  82. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_text_array.html.erb +70 -0
  83. data/lib/generators/active_fields/scaffold/templates/views/active_fields/index.html.erb +41 -0
  84. data/lib/generators/active_fields/scaffold/templates/views/active_fields/new.html.erb +5 -0
  85. data/lib/generators/active_fields/scaffold/templates/views/active_fields/show.html.erb +29 -0
  86. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_boolean.html.erb +8 -0
  87. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_date.html.erb +4 -0
  88. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_date_array.html.erb +12 -0
  89. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_datetime.html.erb +4 -0
  90. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_datetime_array.html.erb +12 -0
  91. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_decimal.html.erb +4 -0
  92. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_decimal_array.html.erb +12 -0
  93. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_enum.html.erb +4 -0
  94. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_enum_array.html.erb +4 -0
  95. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_integer.html.erb +4 -0
  96. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_integer_array.html.erb +12 -0
  97. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_text.html.erb +4 -0
  98. data/lib/generators/active_fields/scaffold/templates/views/active_fields/values/inputs/_text_array.html.erb +12 -0
  99. data/lib/generators/active_fields/scaffold/templates/views/shared/_array_field.html.erb +19 -0
  100. metadata +78 -10
  101. data/lib/active_fields/customizable_config.rb +0 -24
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class BooleanFinder < SingularFinder
6
+ operation :eq, operator: OPS[:eq] do |value|
7
+ scope.where(eq(casted_value_field("boolean"), cast(value)))
8
+ end
9
+ operation :not_eq, operator: OPS[:not_eq] do |value|
10
+ scope.where(not_eq(casted_value_field("boolean"), cast(value)))
11
+ end
12
+
13
+ private
14
+
15
+ def cast(value)
16
+ Casters::BooleanCaster.new.deserialize(value)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class DateArrayFinder < ArrayFinder
6
+ operation :include, operator: OPS[:include] do |value|
7
+ scope.where(value_match_any("==", cast(value)))
8
+ end
9
+ operation :not_include, operator: OPS[:not_include] do |value|
10
+ scope.where.not(value_match_any("==", cast(value)))
11
+ end
12
+ operation :any_gt, operator: OPS[:any_gt] do |value|
13
+ scope.where(value_match_any(">", cast(value)))
14
+ end
15
+ operation :any_gteq, operator: OPS[:any_gteq] do |value|
16
+ scope.where(value_match_any(">=", cast(value)))
17
+ end
18
+ operation :any_lt, operator: OPS[:any_lt] do |value|
19
+ scope.where(value_match_any("<", cast(value)))
20
+ end
21
+ operation :any_lteq, operator: OPS[:any_lteq] do |value|
22
+ scope.where(value_match_any("<=", cast(value)))
23
+ end
24
+ operation :all_gt, operator: OPS[:all_gt] do |value|
25
+ scope.where(value_match_all(">", cast(value)))
26
+ end
27
+ operation :all_gteq, operator: OPS[:all_gteq] do |value|
28
+ scope.where(value_match_all(">=", cast(value)))
29
+ end
30
+ operation :all_lt, operator: OPS[:all_lt] do |value|
31
+ scope.where(value_match_all("<", cast(value)))
32
+ end
33
+ operation :all_lteq, operator: OPS[:all_lteq] do |value|
34
+ scope.where(value_match_all("<=", cast(value)))
35
+ end
36
+ operation :size_eq, operator: OPS[:size_eq] do |value|
37
+ scope.where(value_size_eq(value))
38
+ end
39
+ operation :size_not_eq, operator: OPS[:size_not_eq] do |value|
40
+ scope.where(value_size_not_eq(value))
41
+ end
42
+ operation :size_gt, operator: OPS[:size_gt] do |value|
43
+ scope.where(value_size_gt(value))
44
+ end
45
+ operation :size_gteq, operator: OPS[:size_gteq] do |value|
46
+ scope.where(value_size_gteq(value))
47
+ end
48
+ operation :size_lt, operator: OPS[:size_lt] do |value|
49
+ scope.where(value_size_lt(value))
50
+ end
51
+ operation :size_lteq, operator: OPS[:size_lteq] do |value|
52
+ scope.where(value_size_lteq(value))
53
+ end
54
+
55
+ private
56
+
57
+ def cast(value)
58
+ caster = Casters::DateCaster.new
59
+ caster.serialize(caster.deserialize(value))
60
+ end
61
+
62
+ def jsonpath(operator) = "$[*] ? (@.date() #{operator} $value.date())"
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class DateFinder < SingularFinder
6
+ operation :eq, operator: OPS[:eq] do |value|
7
+ scope.where(eq(casted_value_field("date"), cast(value)))
8
+ end
9
+ operation :not_eq, operator: OPS[:not_eq] do |value|
10
+ scope.where(not_eq(casted_value_field("date"), cast(value)))
11
+ end
12
+ operation :gt, operator: OPS[:gt] do |value|
13
+ scope.where(gt(casted_value_field("date"), cast(value)))
14
+ end
15
+ operation :gteq, operator: OPS[:gteq] do |value|
16
+ scope.where(gteq(casted_value_field("date"), cast(value)))
17
+ end
18
+ operation :lt, operator: OPS[:lt] do |value|
19
+ scope.where(lt(casted_value_field("date"), cast(value)))
20
+ end
21
+ operation :lteq, operator: OPS[:lteq] do |value|
22
+ scope.where(lteq(casted_value_field("date"), cast(value)))
23
+ end
24
+
25
+ private
26
+
27
+ def cast(value)
28
+ Casters::DateCaster.new.deserialize(value)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class DateTimeArrayFinder < ArrayFinder
6
+ operation :include, operator: OPS[:include] do |value|
7
+ scope.where(value_match_any("==", cast(value)))
8
+ end
9
+ operation :not_include, operator: OPS[:not_include] do |value|
10
+ scope.where.not(value_match_any("==", cast(value)))
11
+ end
12
+ operation :any_gt, operator: OPS[:any_gt] do |value|
13
+ scope.where(value_match_any(">", cast(value)))
14
+ end
15
+ operation :any_gteq, operator: OPS[:any_gteq] do |value|
16
+ scope.where(value_match_any(">=", cast(value)))
17
+ end
18
+ operation :any_lt, operator: OPS[:any_lt] do |value|
19
+ scope.where(value_match_any("<", cast(value)))
20
+ end
21
+ operation :any_lteq, operator: OPS[:any_lteq] do |value|
22
+ scope.where(value_match_any("<=", cast(value)))
23
+ end
24
+ operation :all_gt, operator: OPS[:all_gt] do |value|
25
+ scope.where(value_match_all(">", cast(value)))
26
+ end
27
+ operation :all_gteq, operator: OPS[:all_gteq] do |value|
28
+ scope.where(value_match_all(">=", cast(value)))
29
+ end
30
+ operation :all_lt, operator: OPS[:all_lt] do |value|
31
+ scope.where(value_match_all("<", cast(value)))
32
+ end
33
+ operation :all_lteq, operator: OPS[:all_lteq] do |value|
34
+ scope.where(value_match_all("<=", cast(value)))
35
+ end
36
+ operation :size_eq, operator: OPS[:size_eq] do |value|
37
+ scope.where(value_size_eq(value))
38
+ end
39
+ operation :size_not_eq, operator: OPS[:size_not_eq] do |value|
40
+ scope.where(value_size_not_eq(value))
41
+ end
42
+ operation :size_gt, operator: OPS[:size_gt] do |value|
43
+ scope.where(value_size_gt(value))
44
+ end
45
+ operation :size_gteq, operator: OPS[:size_gteq] do |value|
46
+ scope.where(value_size_gteq(value))
47
+ end
48
+ operation :size_lt, operator: OPS[:size_lt] do |value|
49
+ scope.where(value_size_lt(value))
50
+ end
51
+ operation :size_lteq, operator: OPS[:size_lteq] do |value|
52
+ scope.where(value_size_lteq(value))
53
+ end
54
+
55
+ private
56
+
57
+ def cast(value)
58
+ caster = Casters::DateTimeCaster.new(precision: active_field.precision)
59
+ caster.serialize(caster.deserialize(value))
60
+ end
61
+
62
+ def jsonpath(operator) = "$[*] ? (@.timestamp_tz() #{operator} $value.timestamp_tz())"
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class DateTimeFinder < SingularFinder
6
+ operation :eq, operator: OPS[:eq] do |value|
7
+ scope.where(eq(casted_value_field("timestamp"), cast(value)))
8
+ end
9
+ operation :not_eq, operator: OPS[:not_eq] do |value|
10
+ scope.where(not_eq(casted_value_field("timestamp"), cast(value)))
11
+ end
12
+ operation :gt, operator: OPS[:gt] do |value|
13
+ scope.where(gt(casted_value_field("timestamp"), cast(value)))
14
+ end
15
+ operation :gteq, operator: OPS[:gteq] do |value|
16
+ scope.where(gteq(casted_value_field("timestamp"), cast(value)))
17
+ end
18
+ operation :lt, operator: OPS[:lt] do |value|
19
+ scope.where(lt(casted_value_field("timestamp"), cast(value)))
20
+ end
21
+ operation :lteq, operator: OPS[:lteq] do |value|
22
+ scope.where(lteq(casted_value_field("timestamp"), cast(value)))
23
+ end
24
+
25
+ private
26
+
27
+ def cast(value)
28
+ Casters::DateTimeCaster.new(precision: active_field.precision).deserialize(value)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class DecimalArrayFinder < ArrayFinder
6
+ operation :include, operator: OPS[:include] do |value|
7
+ scope.where(value_match_any("==", cast(value)))
8
+ end
9
+ operation :not_include, operator: OPS[:not_include] do |value|
10
+ scope.where.not(value_match_any("==", cast(value)))
11
+ end
12
+ operation :any_gt, operator: OPS[:any_gt] do |value|
13
+ scope.where(value_match_any(">", cast(value)))
14
+ end
15
+ operation :any_gteq, operator: OPS[:any_gteq] do |value|
16
+ scope.where(value_match_any(">=", cast(value)))
17
+ end
18
+ operation :any_lt, operator: OPS[:any_lt] do |value|
19
+ scope.where(value_match_any("<", cast(value)))
20
+ end
21
+ operation :any_lteq, operator: OPS[:any_lteq] do |value|
22
+ scope.where(value_match_any("<=", cast(value)))
23
+ end
24
+ operation :all_gt, operator: OPS[:all_gt] do |value|
25
+ scope.where(value_match_all(">", cast(value)))
26
+ end
27
+ operation :all_gteq, operator: OPS[:all_gteq] do |value|
28
+ scope.where(value_match_all(">=", cast(value)))
29
+ end
30
+ operation :all_lt, operator: OPS[:all_lt] do |value|
31
+ scope.where(value_match_all("<", cast(value)))
32
+ end
33
+ operation :all_lteq, operator: OPS[:all_lteq] do |value|
34
+ scope.where(value_match_all("<=", cast(value)))
35
+ end
36
+ operation :size_eq, operator: OPS[:size_eq] do |value|
37
+ scope.where(value_size_eq(value))
38
+ end
39
+ operation :size_not_eq, operator: OPS[:size_not_eq] do |value|
40
+ scope.where(value_size_not_eq(value))
41
+ end
42
+ operation :size_gt, operator: OPS[:size_gt] do |value|
43
+ scope.where(value_size_gt(value))
44
+ end
45
+ operation :size_gteq, operator: OPS[:size_gteq] do |value|
46
+ scope.where(value_size_gteq(value))
47
+ end
48
+ operation :size_lt, operator: OPS[:size_lt] do |value|
49
+ scope.where(value_size_lt(value))
50
+ end
51
+ operation :size_lteq, operator: OPS[:size_lteq] do |value|
52
+ scope.where(value_size_lteq(value))
53
+ end
54
+
55
+ private
56
+
57
+ def cast(value)
58
+ caster = Casters::DecimalCaster.new(precision: active_field.precision)
59
+ caster.serialize(caster.deserialize(value))
60
+ end
61
+
62
+ def jsonpath(operator) = "$[*] ? (@.number() #{operator} $value.number())"
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class DecimalFinder < SingularFinder
6
+ operation :eq, operator: OPS[:eq] do |value|
7
+ scope.where(eq(casted_value_field("decimal"), cast(value)))
8
+ end
9
+ operation :not_eq, operator: OPS[:not_eq] do |value|
10
+ scope.where(not_eq(casted_value_field("decimal"), cast(value)))
11
+ end
12
+ operation :gt, operator: OPS[:gt] do |value|
13
+ scope.where(gt(casted_value_field("decimal"), cast(value)))
14
+ end
15
+ operation :gteq, operator: OPS[:gteq] do |value|
16
+ scope.where(gteq(casted_value_field("decimal"), cast(value)))
17
+ end
18
+ operation :lt, operator: OPS[:lt] do |value|
19
+ scope.where(lt(casted_value_field("decimal"), cast(value)))
20
+ end
21
+ operation :lteq, operator: OPS[:lteq] do |value|
22
+ scope.where(lteq(casted_value_field("decimal"), cast(value)))
23
+ end
24
+
25
+ private
26
+
27
+ def cast(value)
28
+ Casters::DecimalCaster.new(precision: active_field.precision).deserialize(value)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class EnumArrayFinder < ArrayFinder
6
+ operation :include, operator: OPS[:include] do |value|
7
+ scope.where(value_match_any("==", cast(value)))
8
+ end
9
+ operation :not_include, operator: OPS[:not_include] do |value|
10
+ scope.where.not(value_match_any("==", cast(value)))
11
+ end
12
+ operation :size_eq, operator: OPS[:size_eq] do |value|
13
+ scope.where(value_size_eq(value))
14
+ end
15
+ operation :size_not_eq, operator: OPS[:size_not_eq] do |value|
16
+ scope.where(value_size_not_eq(value))
17
+ end
18
+ operation :size_gt, operator: OPS[:size_gt] do |value|
19
+ scope.where(value_size_gt(value))
20
+ end
21
+ operation :size_gteq, operator: OPS[:size_gteq] do |value|
22
+ scope.where(value_size_gteq(value))
23
+ end
24
+ operation :size_lt, operator: OPS[:size_lt] do |value|
25
+ scope.where(value_size_lt(value))
26
+ end
27
+ operation :size_lteq, operator: OPS[:size_lteq] do |value|
28
+ scope.where(value_size_lteq(value))
29
+ end
30
+
31
+ private
32
+
33
+ def cast(value)
34
+ caster = Casters::EnumCaster.new
35
+ caster.serialize(caster.deserialize(value))
36
+ end
37
+
38
+ def jsonpath(operator) = "$[*] ? (@ #{operator} $value)"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class EnumFinder < SingularFinder
6
+ operation :eq, operator: OPS[:eq] do |value|
7
+ scope.where(eq(casted_value_field("text"), cast(value)))
8
+ end
9
+ operation :not_eq, operator: OPS[:not_eq] do |value|
10
+ scope.where(not_eq(casted_value_field("text"), cast(value)))
11
+ end
12
+
13
+ private
14
+
15
+ def cast(value)
16
+ Casters::EnumCaster.new.deserialize(value)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class IntegerArrayFinder < ArrayFinder
6
+ operation :include, operator: OPS[:include] do |value|
7
+ scope.where(value_match_any("==", cast(value)))
8
+ end
9
+ operation :not_include, operator: OPS[:not_include] do |value|
10
+ scope.where.not(value_match_any("==", cast(value)))
11
+ end
12
+ operation :any_gt, operator: OPS[:any_gt] do |value|
13
+ scope.where(value_match_any(">", cast(value)))
14
+ end
15
+ operation :any_gteq, operator: OPS[:any_gteq] do |value|
16
+ scope.where(value_match_any(">=", cast(value)))
17
+ end
18
+ operation :any_lt, operator: OPS[:any_lt] do |value|
19
+ scope.where(value_match_any("<", cast(value)))
20
+ end
21
+ operation :any_lteq, operator: OPS[:any_lteq] do |value|
22
+ scope.where(value_match_any("<=", cast(value)))
23
+ end
24
+ operation :all_gt, operator: OPS[:all_gt] do |value|
25
+ scope.where(value_match_all(">", cast(value)))
26
+ end
27
+ operation :all_gteq, operator: OPS[:all_gteq] do |value|
28
+ scope.where(value_match_all(">=", cast(value)))
29
+ end
30
+ operation :all_lt, operator: OPS[:all_lt] do |value|
31
+ scope.where(value_match_all("<", cast(value)))
32
+ end
33
+ operation :all_lteq, operator: OPS[:all_lteq] do |value|
34
+ scope.where(value_match_all("<=", cast(value)))
35
+ end
36
+ operation :size_eq, operator: OPS[:size_eq] do |value|
37
+ scope.where(value_size_eq(value))
38
+ end
39
+ operation :size_not_eq, operator: OPS[:size_not_eq] do |value|
40
+ scope.where(value_size_not_eq(value))
41
+ end
42
+ operation :size_gt, operator: OPS[:size_gt] do |value|
43
+ scope.where(value_size_gt(value))
44
+ end
45
+ operation :size_gteq, operator: OPS[:size_gteq] do |value|
46
+ scope.where(value_size_gteq(value))
47
+ end
48
+ operation :size_lt, operator: OPS[:size_lt] do |value|
49
+ scope.where(value_size_lt(value))
50
+ end
51
+ operation :size_lteq, operator: OPS[:size_lteq] do |value|
52
+ scope.where(value_size_lteq(value))
53
+ end
54
+
55
+ private
56
+
57
+ def cast(value)
58
+ caster = Casters::IntegerCaster.new
59
+ caster.serialize(caster.deserialize(value))
60
+ end
61
+
62
+ def jsonpath(operator) = "$[*] ? (@ #{operator} $value)"
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class IntegerFinder < SingularFinder
6
+ operation :eq, operator: OPS[:eq] do |value|
7
+ scope.where(eq(casted_value_field("bigint"), cast(value)))
8
+ end
9
+ operation :not_eq, operator: OPS[:not_eq] do |value|
10
+ scope.where(not_eq(casted_value_field("bigint"), cast(value)))
11
+ end
12
+ operation :gt, operator: OPS[:gt] do |value|
13
+ scope.where(gt(casted_value_field("bigint"), cast(value)))
14
+ end
15
+ operation :gteq, operator: OPS[:gteq] do |value|
16
+ scope.where(gteq(casted_value_field("bigint"), cast(value)))
17
+ end
18
+ operation :lt, operator: OPS[:lt] do |value|
19
+ scope.where(lt(casted_value_field("bigint"), cast(value)))
20
+ end
21
+ operation :lteq, operator: OPS[:lteq] do |value|
22
+ scope.where(lteq(casted_value_field("bigint"), cast(value)))
23
+ end
24
+
25
+ private
26
+
27
+ def cast(value)
28
+ Casters::IntegerCaster.new.deserialize(value)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class SingularFinder < BaseFinder
6
+ private
7
+
8
+ # Arel node for `active_fields.value_meta->>const`
9
+ def value_field_text
10
+ Arel::Nodes::InfixOperation.new(
11
+ "->>",
12
+ Arel::Table.new(cte_name)[:value_meta],
13
+ Arel::Nodes.build_quoted("const"),
14
+ )
15
+ end
16
+
17
+ # Arel node with stored value casted to provided type
18
+ # E.g. `CAST active_fields.value_meta->>const AS bigint`
19
+ def casted_value_field(to)
20
+ Arel::Nodes::NamedFunction.new("CAST", [value_field_text.as(to)])
21
+ end
22
+
23
+ # Equal operation, that respects boolean and NULL values
24
+ def eq(target, value)
25
+ if value.is_a?(TrueClass) || value.is_a?(FalseClass) || value.is_a?(NilClass)
26
+ Arel::Nodes::InfixOperation.new("IS", target, Arel::Nodes.build_quoted(value))
27
+ else
28
+ target.eq(value)
29
+ end
30
+ end
31
+
32
+ # Not equal operation, that respects boolean and NULL values
33
+ def not_eq(target, value)
34
+ if value.is_a?(TrueClass) || value.is_a?(FalseClass) || value.is_a?(NilClass)
35
+ Arel::Nodes::InfixOperation.new("IS NOT", target, Arel::Nodes.build_quoted(value))
36
+ else
37
+ # Comparison with NULL always returns NULL.
38
+ # NOT NULL is always NULL as well.
39
+ # We expect them not to be equal to any provided value except TRUE, FALSE and NULL.
40
+ # So we should search for NULL values too.
41
+ target.not_eq(value).or(target.eq(nil))
42
+ end
43
+ end
44
+
45
+ # Greater than operation
46
+ def gt(target, value)
47
+ target.gt(value)
48
+ end
49
+
50
+ # Greater than or equal to operation
51
+ def gteq(target, value)
52
+ target.gteq(value)
53
+ end
54
+
55
+ # Less than operation
56
+ def lt(target, value)
57
+ target.lt(value)
58
+ end
59
+
60
+ # Less than or equal to operation
61
+ def lteq(target, value)
62
+ target.lteq(value)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class TextArrayFinder < ArrayFinder
6
+ operation :include, operator: OPS[:include] do |value|
7
+ scope.where(value_match_any("==", cast(value)))
8
+ end
9
+ operation :not_include, operator: OPS[:not_include] do |value|
10
+ scope.where.not(value_match_any("==", cast(value)))
11
+ end
12
+ operation :any_start_with, operator: OPS[:any_start_with] do |value|
13
+ scope.where(value_match_any("starts with", cast(value)))
14
+ end
15
+ operation :all_start_with, operator: OPS[:all_start_with] do |value|
16
+ scope.where(value_match_all("starts with", cast(value)))
17
+ end
18
+ operation :size_eq, operator: OPS[:size_eq] do |value|
19
+ scope.where(value_size_eq(value))
20
+ end
21
+ operation :size_not_eq, operator: OPS[:size_not_eq] do |value|
22
+ scope.where(value_size_not_eq(value))
23
+ end
24
+ operation :size_gt, operator: OPS[:size_gt] do |value|
25
+ scope.where(value_size_gt(value))
26
+ end
27
+ operation :size_gteq, operator: OPS[:size_gteq] do |value|
28
+ scope.where(value_size_gteq(value))
29
+ end
30
+ operation :size_lt, operator: OPS[:size_lt] do |value|
31
+ scope.where(value_size_lt(value))
32
+ end
33
+ operation :size_lteq, operator: OPS[:size_lteq] do |value|
34
+ scope.where(value_size_lteq(value))
35
+ end
36
+
37
+ private
38
+
39
+ def cast(value)
40
+ caster = Casters::TextCaster.new
41
+ caster.serialize(caster.deserialize(value))
42
+ end
43
+
44
+ def jsonpath(operator) = "$[*] ? (@ #{operator} $value)"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class TextFinder < SingularFinder
6
+ operation :eq, operator: OPS[:eq] do |value|
7
+ scope.where(eq(casted_value_field("text"), cast(value)))
8
+ end
9
+ operation :not_eq, operator: OPS[:not_eq] do |value|
10
+ scope.where(not_eq(casted_value_field("text"), cast(value)))
11
+ end
12
+ operation :start_with, operator: OPS[:start_with] do |value|
13
+ scope.where(like(casted_value_field("text"), "#{escape_pattern(cast(value))}%"))
14
+ end
15
+ operation :end_with, operator: OPS[:end_with] do |value|
16
+ scope.where(like(casted_value_field("text"), "%#{escape_pattern(cast(value))}"))
17
+ end
18
+ operation :contain, operator: OPS[:contain] do |value|
19
+ scope.where(like(casted_value_field("text"), "%#{escape_pattern(cast(value))}%"))
20
+ end
21
+ operation :not_start_with, operator: OPS[:not_start_with] do |value|
22
+ scope.where(not_like(casted_value_field("text"), "#{escape_pattern(cast(value))}%"))
23
+ end
24
+ operation :not_end_with, operator: OPS[:not_end_with] do |value|
25
+ scope.where(not_like(casted_value_field("text"), "%#{escape_pattern(cast(value))}"))
26
+ end
27
+ operation :not_contain, operator: OPS[:not_contain] do |value|
28
+ scope.where(not_like(casted_value_field("text"), "%#{escape_pattern(cast(value))}%"))
29
+ end
30
+ operation :istart_with, operator: OPS[:istart_with] do |value|
31
+ scope.where(ilike(casted_value_field("text"), "#{escape_pattern(cast(value))}%"))
32
+ end
33
+ operation :iend_with, operator: OPS[:iend_with] do |value|
34
+ scope.where(ilike(casted_value_field("text"), "%#{escape_pattern(cast(value))}"))
35
+ end
36
+ operation :icontain, operator: OPS[:icontain] do |value|
37
+ scope.where(ilike(casted_value_field("text"), "%#{escape_pattern(cast(value))}%"))
38
+ end
39
+ operation :not_istart_with, operator: OPS[:not_istart_with] do |value|
40
+ scope.where(not_ilike(casted_value_field("text"), "#{escape_pattern(cast(value))}%"))
41
+ end
42
+ operation :not_iend_with, operator: OPS[:not_iend_with] do |value|
43
+ scope.where(not_ilike(casted_value_field("text"), "%#{escape_pattern(cast(value))}"))
44
+ end
45
+ operation :not_icontain, operator: OPS[:not_icontain] do |value|
46
+ scope.where(not_ilike(casted_value_field("text"), "%#{escape_pattern(cast(value))}%"))
47
+ end
48
+
49
+ private
50
+
51
+ def cast(value)
52
+ Casters::TextCaster.new.deserialize(value)
53
+ end
54
+
55
+ def like(target, value)
56
+ target.matches(value, nil, true)
57
+ end
58
+
59
+ def ilike(target, value)
60
+ target.matches(value, nil, false)
61
+ end
62
+
63
+ def not_like(target, value)
64
+ # We expect NULLs to never match the pattern
65
+ target.does_not_match(value, nil, true).or(target.eq(nil))
66
+ end
67
+
68
+ def not_ilike(target, value)
69
+ # We expect NULLs to never match the pattern
70
+ target.does_not_match(value, nil, false).or(target.eq(nil))
71
+ end
72
+
73
+ # Escape special chars (\, %, _) in LIKE/ILIKE pattern
74
+ def escape_pattern(value)
75
+ return unless value.is_a?(String)
76
+
77
+ value.gsub("\\", "\\\\\\").gsub("%", "\\%").gsub("_", "\\_")
78
+ end
79
+ end
80
+ end
81
+ end