active_fields 1.1.0 → 2.0.1

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -4
  3. data/CHANGELOG.md +32 -2
  4. data/README.md +411 -38
  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 +89 -5
  20. data/app/models/concerns/active_fields/field_concern.rb +26 -5
  21. data/app/models/concerns/active_fields/value_concern.rb +0 -2
  22. data/db/migrate/20240229230000_create_active_fields_tables.rb +1 -1
  23. data/lib/active_fields/casters/date_time_caster.rb +1 -3
  24. data/lib/active_fields/casters/decimal_caster.rb +2 -5
  25. data/lib/active_fields/constants.rb +55 -0
  26. data/lib/active_fields/engine.rb +2 -1
  27. data/lib/active_fields/finders/array_finder.rb +112 -0
  28. data/lib/active_fields/finders/base_finder.rb +73 -0
  29. data/lib/active_fields/finders/boolean_finder.rb +20 -0
  30. data/lib/active_fields/finders/date_array_finder.rb +65 -0
  31. data/lib/active_fields/finders/date_finder.rb +32 -0
  32. data/lib/active_fields/finders/date_time_array_finder.rb +65 -0
  33. data/lib/active_fields/finders/date_time_finder.rb +32 -0
  34. data/lib/active_fields/finders/decimal_array_finder.rb +65 -0
  35. data/lib/active_fields/finders/decimal_finder.rb +32 -0
  36. data/lib/active_fields/finders/enum_array_finder.rb +41 -0
  37. data/lib/active_fields/finders/enum_finder.rb +20 -0
  38. data/lib/active_fields/finders/integer_array_finder.rb +65 -0
  39. data/lib/active_fields/finders/integer_finder.rb +32 -0
  40. data/lib/active_fields/finders/singular_finder.rb +66 -0
  41. data/lib/active_fields/finders/text_array_finder.rb +47 -0
  42. data/lib/active_fields/finders/text_finder.rb +81 -0
  43. data/lib/active_fields/has_active_fields.rb +3 -4
  44. data/lib/active_fields/registry.rb +38 -0
  45. data/lib/active_fields/version.rb +1 -1
  46. data/lib/active_fields.rb +29 -1
  47. data/lib/generators/active_fields/scaffold/scaffold_generator.rb +9 -0
  48. data/lib/generators/active_fields/scaffold/templates/controllers/active_fields_controller.rb +0 -10
  49. data/lib/generators/active_fields/scaffold/templates/controllers/concerns/active_fields_controller_concern.rb +33 -0
  50. data/lib/generators/active_fields/scaffold/templates/helpers/active_fields_helper.rb +67 -0
  51. data/lib/generators/active_fields/scaffold/templates/javascript/controllers/active_fields_finders_form_controller.js +59 -0
  52. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/_form.html.erb +42 -0
  53. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_array_size.html.erb +16 -0
  54. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_boolean.html.erb +21 -0
  55. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_date.html.erb +16 -0
  56. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_date_array.html.erb +16 -0
  57. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_datetime.html.erb +16 -0
  58. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_datetime_array.html.erb +16 -0
  59. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_decimal.html.erb +16 -0
  60. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_decimal_array.html.erb +16 -0
  61. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_enum.html.erb +16 -0
  62. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_enum_array.html.erb +16 -0
  63. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_integer.html.erb +16 -0
  64. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_integer_array.html.erb +16 -0
  65. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_text.html.erb +16 -0
  66. data/lib/generators/active_fields/scaffold/templates/views/active_fields/finders/inputs/_text_array.html.erb +16 -0
  67. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_boolean.html.erb +1 -1
  68. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_date.html.erb +1 -1
  69. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_date_array.html.erb +1 -1
  70. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_datetime.html.erb +2 -2
  71. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_datetime_array.html.erb +2 -2
  72. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_decimal.html.erb +2 -2
  73. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_decimal_array.html.erb +2 -2
  74. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_enum.html.erb +1 -1
  75. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_enum_array.html.erb +1 -1
  76. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_integer.html.erb +1 -1
  77. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_integer_array.html.erb +1 -1
  78. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_text.html.erb +1 -1
  79. data/lib/generators/active_fields/scaffold/templates/views/active_fields/forms/_text_array.html.erb +1 -1
  80. data/lib/generators/active_fields/scaffold/templates/views/shared/_array_field.html.erb +1 -1
  81. metadata +42 -10
  82. data/lib/active_fields/customizable_config.rb +0 -24
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveFields
4
+ module Finders
5
+ class BaseFinder
6
+ class << self
7
+ # Define search operation
8
+ def operation(name, operator:, &block)
9
+ name = name.to_sym
10
+ operator = operator.to_sym
11
+
12
+ __operations__[name] = {
13
+ operator: operator.to_sym,
14
+ block: block,
15
+ }
16
+ __operators__[operator] = name
17
+ end
18
+
19
+ # Returns operator for provided search operation name
20
+ def operator_for(operation_name)
21
+ __operations__.dig(operation_name.to_sym, :operator)
22
+ end
23
+
24
+ # Returns search operation name for provided operator
25
+ def operation_for(operator)
26
+ __operators__[operator.to_sym]
27
+ end
28
+
29
+ # Returns all defined search operations names
30
+ def operations
31
+ __operations__.keys
32
+ end
33
+
34
+ # Storage for defined operations. Private.
35
+ def __operations__
36
+ @__operations__ ||= {}
37
+ end
38
+
39
+ # Index for finding operation by operator. Private.
40
+ def __operators__
41
+ @__operators__ ||= {}
42
+ end
43
+ end
44
+
45
+ attr_reader :active_field
46
+
47
+ def initialize(active_field:)
48
+ @active_field = active_field
49
+ end
50
+
51
+ # Perform query operation
52
+ # @param op [String, Symbol] Operation name or operator
53
+ # @param value [Any] The value to search for
54
+ def search(op:, value:)
55
+ op = op.to_sym if op.respond_to?(:to_sym)
56
+ operation = self.class.__operations__.key?(op) ? op : self.class.__operators__[op]
57
+ return if operation.nil?
58
+
59
+ instance_exec(value, &self.class.__operations__[operation][:block])
60
+ end
61
+
62
+ private
63
+
64
+ # Name of the CTE, that is used in queries. It is the original values table name.
65
+ def cte_name = ActiveFields.config.value_class.table_name
66
+
67
+ # Base scope for querying
68
+ def scope
69
+ ActiveFields.config.value_class.with(cte_name => active_field.active_values)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -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