active_fields 1.0.0 → 2.0.0

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 (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