shiny_json_logic 0.2.16 → 0.3.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +1 -5
  5. data/lib/shiny_json_logic/comparisons/comparable.rb +27 -4
  6. data/lib/shiny_json_logic/engine.rb +11 -5
  7. data/lib/shiny_json_logic/numericals/min_max_collection.rb +9 -14
  8. data/lib/shiny_json_logic/numericals/numerify.rb +5 -5
  9. data/lib/shiny_json_logic/numericals/with_error_handling.rb +2 -0
  10. data/lib/shiny_json_logic/operations/addition.rb +5 -23
  11. data/lib/shiny_json_logic/operations/all.rb +1 -3
  12. data/lib/shiny_json_logic/operations/and.rb +3 -4
  13. data/lib/shiny_json_logic/operations/base.rb +13 -31
  14. data/lib/shiny_json_logic/operations/coalesce.rb +2 -2
  15. data/lib/shiny_json_logic/operations/concatenation.rb +4 -4
  16. data/lib/shiny_json_logic/operations/different.rb +2 -17
  17. data/lib/shiny_json_logic/operations/division.rb +6 -14
  18. data/lib/shiny_json_logic/operations/double_not.rb +3 -3
  19. data/lib/shiny_json_logic/operations/equal.rb +2 -16
  20. data/lib/shiny_json_logic/operations/exists.rb +4 -4
  21. data/lib/shiny_json_logic/operations/filter.rb +2 -4
  22. data/lib/shiny_json_logic/operations/greater.rb +2 -17
  23. data/lib/shiny_json_logic/operations/greater_equal.rb +2 -17
  24. data/lib/shiny_json_logic/operations/if.rb +5 -16
  25. data/lib/shiny_json_logic/operations/inclusion.rb +3 -3
  26. data/lib/shiny_json_logic/operations/iterable/base.rb +31 -61
  27. data/lib/shiny_json_logic/operations/max.rb +2 -4
  28. data/lib/shiny_json_logic/operations/merge.rb +3 -3
  29. data/lib/shiny_json_logic/operations/min.rb +2 -4
  30. data/lib/shiny_json_logic/operations/missing.rb +10 -10
  31. data/lib/shiny_json_logic/operations/missing_some.rb +7 -6
  32. data/lib/shiny_json_logic/operations/modulo.rb +6 -14
  33. data/lib/shiny_json_logic/operations/none.rb +1 -3
  34. data/lib/shiny_json_logic/operations/not.rb +2 -2
  35. data/lib/shiny_json_logic/operations/or.rb +3 -4
  36. data/lib/shiny_json_logic/operations/preserve.rb +8 -25
  37. data/lib/shiny_json_logic/operations/product.rb +7 -20
  38. data/lib/shiny_json_logic/operations/reduce.rb +24 -30
  39. data/lib/shiny_json_logic/operations/smaller.rb +2 -17
  40. data/lib/shiny_json_logic/operations/smaller_equal.rb +2 -17
  41. data/lib/shiny_json_logic/operations/some.rb +1 -3
  42. data/lib/shiny_json_logic/operations/strict_different.rb +6 -12
  43. data/lib/shiny_json_logic/operations/strict_equal.rb +6 -12
  44. data/lib/shiny_json_logic/operations/substring.rb +4 -4
  45. data/lib/shiny_json_logic/operations/subtraction.rb +7 -20
  46. data/lib/shiny_json_logic/operations/throw.rb +5 -7
  47. data/lib/shiny_json_logic/operations/try.rb +2 -3
  48. data/lib/shiny_json_logic/operations/val.rb +17 -24
  49. data/lib/shiny_json_logic/operations/var.rb +18 -16
  50. data/lib/shiny_json_logic/truthy.rb +9 -7
  51. data/lib/shiny_json_logic/utils/data_hash.rb +18 -0
  52. data/lib/shiny_json_logic/version.rb +1 -1
  53. data/lib/shiny_json_logic.rb +0 -2
  54. metadata +3 -2
@@ -5,9 +5,9 @@ require "shiny_json_logic/operations/base"
5
5
  module ShinyJsonLogic
6
6
  module Operations
7
7
  class Inclusion < Base
8
- def call
9
- needle = evaluate(rules.first)
10
- haystack = evaluate(rules.last)
8
+ def self.execute(rules, scope_stack)
9
+ needle = evaluate(rules.first, scope_stack)
10
+ haystack = evaluate(rules.last, scope_stack)
11
11
  haystack.include?(needle)
12
12
  end
13
13
  end
@@ -6,93 +6,63 @@ module ShinyJsonLogic
6
6
  module Operations
7
7
  module Iterable
8
8
  class Base < Operations::Base
9
- def initialize(rules, scope_stack)
10
- super
11
-
12
- return handle_nil if dynamic_args?
13
- return handle_nil unless rules.is_a?(Array)
14
-
15
- @filter = rules[1]
16
- return handle_nil if @filter.nil? && self.class.raise_on_nil_filter?
17
-
18
- collection = rules.any? ? rules[0] : rules
19
- return handle_nil if collection.nil?
9
+ def self.raise_on_nil_filter!
10
+ @raise_on_nil_filter = true
11
+ end
20
12
 
21
- setup_collection(collection)
13
+ def self.raise_on_nil_filter?
14
+ @raise_on_nil_filter
22
15
  end
23
16
 
24
- def call
25
- on_before
17
+ def self.call(rules, scope_stack)
18
+ rules = resolve_rules(rules, scope_stack)
26
19
 
27
- results = collection.each_with_index.each_with_object([]) do |(item, index), results|
28
- on_before_each(item, index)
20
+ collection, filter = setup_collection(rules, scope_stack)
21
+
22
+ on_before(scope_stack)
23
+ results = collection.each_with_index.each_with_object([]) do |(item, index), acc|
24
+ scope_stack.push({ "index" => index }, index: index)
25
+ scope_stack.push(item, index: index)
29
26
  begin
30
- solved = on_each(item)
31
- results << solved
32
- on_after_each
27
+ solved = on_each(item, filter, scope_stack)
28
+ acc << solved
29
+ scope_stack.pop
30
+ scope_stack.pop
33
31
  rescue => e
34
- # Clean up scopes before re-raising
35
32
  scope_stack.pop # item scope
36
33
  scope_stack.pop # iterator context scope
37
34
  raise e
38
35
  end
39
36
  end
40
-
41
- on_after(results)
37
+ on_after(results, scope_stack)
42
38
  end
43
39
 
44
- protected
40
+ def self.setup_collection(rules, scope_stack)
41
+ return handle_nil unless rules.is_a?(Array)
45
42
 
46
- private
43
+ filter = rules[1]
44
+ return handle_nil if filter.nil? && raise_on_nil_filter?
47
45
 
48
- def on_each(_item)
49
- Engine.call(filter, scope_stack)
50
- end
46
+ collection_rule = rules.any? ? rules[0] : rules
47
+ return handle_nil if collection_rule.nil?
51
48
 
52
- def on_before_each(item, index = 0)
53
- # Push the iterator context first (with index)
54
- # This creates the intermediate level for [[1], "index"] access
55
- scope_stack.push({ "index" => index }, index: index)
56
-
57
- # Then push the current item
58
- scope_stack.push(item, index: index)
49
+ collection = Utils::Array.wrap(Engine.call(collection_rule, scope_stack))
50
+ [collection, filter]
59
51
  end
60
52
 
61
- def on_after_each
62
- # Pop the item scope
63
- scope_stack.pop
64
- # Pop the iterator context scope
65
- scope_stack.pop
66
- end
53
+ def self.on_before(_scope_stack); end
67
54
 
68
- def on_before
55
+ def self.on_each(_item, filter, scope_stack)
56
+ Engine.call(filter, scope_stack)
69
57
  end
70
58
 
71
- def on_after(results)
59
+ def self.on_after(results, _scope_stack)
72
60
  results
73
61
  end
74
62
 
75
- def handle_nil
63
+ def self.handle_nil
76
64
  raise Errors::InvalidArguments
77
65
  end
78
-
79
- def setup_collection(collection)
80
- if collection.nil?
81
- @collection = []
82
- else
83
- @collection = wrap(evaluate(collection))
84
- end
85
- end
86
-
87
- def self.raise_on_nil_filter!
88
- @raise_on_nil_filter = true
89
- end
90
-
91
- def self.raise_on_nil_filter?
92
- @raise_on_nil_filter
93
- end
94
-
95
- attr_reader :collection, :filter
96
66
  end
97
67
  end
98
68
  end
@@ -6,10 +6,8 @@ require "shiny_json_logic/numericals/min_max_collection"
6
6
  module ShinyJsonLogic
7
7
  module Operations
8
8
  class Max < Base
9
- include Numericals::MinMaxCollection
10
-
11
- def call
12
- collect_numeric_values.max
9
+ def self.execute(rules, scope_stack)
10
+ Numericals::MinMaxCollection.collect_numeric_values(rules, scope_stack).max
13
11
  end
14
12
  end
15
13
  end
@@ -5,9 +5,9 @@ require "shiny_json_logic/operations/base"
5
5
  module ShinyJsonLogic
6
6
  module Operations
7
7
  class Merge < Base
8
- def call
9
- wrap_nil(rules).map do |rule|
10
- wrap_nil(evaluate(rule))
8
+ def self.execute(rules, scope_stack)
9
+ Utils::Array.wrap_nil(rules).map do |rule|
10
+ Utils::Array.wrap_nil(evaluate(rule, scope_stack))
11
11
  end.reduce([], :+)
12
12
  end
13
13
  end
@@ -6,10 +6,8 @@ require "shiny_json_logic/numericals/min_max_collection"
6
6
  module ShinyJsonLogic
7
7
  module Operations
8
8
  class Min < Base
9
- include Numericals::MinMaxCollection
10
-
11
- def call
12
- collect_numeric_values.min
9
+ def self.execute(rules, scope_stack)
10
+ Numericals::MinMaxCollection.collect_numeric_values(rules, scope_stack).min
13
11
  end
14
12
  end
15
13
  end
@@ -6,25 +6,25 @@ require "shiny_json_logic/operations/base"
6
6
  module ShinyJsonLogic
7
7
  module Operations
8
8
  class Missing < Base
9
- def call
10
- items = wrap_nil(rules)
9
+ def self.execute(rules, scope_stack)
10
+ items = Utils::Array.wrap_nil(rules)
11
11
  keys = []
12
12
  items.each do |rule|
13
- evaluated = evaluate(rule)
14
- keys.concat(wrap_nil(evaluated).map(&:to_s))
13
+ evaluated = evaluate(rule, scope_stack)
14
+ keys.concat(Utils::Array.wrap_nil(evaluated).map(&:to_s))
15
15
  end
16
- return keys unless data.is_a?(Hash)
16
+ current_data = scope_stack.current
17
+ return keys unless current_data.is_a?(Hash)
17
18
 
18
- keys - deep_keys(data)
19
+ keys - deep_keys(current_data)
19
20
  end
20
21
 
21
- private
22
-
23
- def deep_keys(hash)
22
+ def self.deep_keys(hash)
24
23
  return unless hash.is_a?(Hash)
25
24
 
26
- hash.keys.map{|key| ([key.to_s] << deep_keys(hash[key])).compact.join(".") }
25
+ hash.keys.map { |key| ([key.to_s] << deep_keys(hash[key])).compact.join(".") }
27
26
  end
27
+ private_class_method :deep_keys
28
28
  end
29
29
  end
30
30
  end
@@ -6,14 +6,15 @@ require "shiny_json_logic/operations/missing"
6
6
  module ShinyJsonLogic
7
7
  module Operations
8
8
  class MissingSome < Missing
9
- def call
10
- min_required = evaluate(rules[0])
11
- keys = wrap_nil(evaluate(rules[1])).map(&:to_s)
12
- return keys unless data.is_a?(Hash) && rules.is_a?(Array)
9
+ def self.execute(rules, scope_stack)
10
+ min_required = evaluate(rules[0], scope_stack)
11
+ keys = Utils::Array.wrap_nil(evaluate(rules[1], scope_stack)).map(&:to_s)
12
+ current_data = scope_stack.current
13
+ return keys unless current_data.is_a?(Hash) && rules.is_a?(Array)
13
14
 
14
- data_keys = data.keys.map(&:to_s)
15
+ data_keys = current_data.keys.map(&:to_s)
15
16
  present = keys & data_keys
16
- present.size >= min_required ? [] : Missing.new(keys, scope_stack).call
17
+ present.size >= min_required ? [] : Missing.execute(keys, scope_stack)
17
18
  end
18
19
  end
19
20
  end
@@ -7,18 +7,19 @@ require "shiny_json_logic/numericals/numerify"
7
7
  module ShinyJsonLogic
8
8
  module Operations
9
9
  class Modulo < Base
10
- include Numericals::WithErrorHandling
11
- include Numericals::Numerify
10
+ extend Numericals::WithErrorHandling
12
11
 
13
- def call
14
- operands = wrap_nil(rules)
12
+ def self.execute(rules, scope_stack)
13
+ operands = Utils::Array.wrap_nil(rules)
15
14
  return handle_invalid_args if operands.empty?
16
15
 
17
16
  safe_arithmetic do
18
17
  result = nil
19
18
  count = 0
20
19
 
21
- each_operand(operands) do |num|
20
+ operands.each do |rule|
21
+ evaluated = evaluate(rule, scope_stack)
22
+ num = Numericals::Numerify.numerify(evaluated)
22
23
  count += 1
23
24
  result = result.nil? ? num : result.remainder(num)
24
25
  end
@@ -28,15 +29,6 @@ module ShinyJsonLogic
28
29
  result
29
30
  end
30
31
  end
31
-
32
- private
33
-
34
- def each_operand(operands)
35
- operands.each do |rule|
36
- evaluated = evaluate(rule)
37
- yield numerify(evaluated)
38
- end
39
- end
40
32
  end
41
33
  end
42
34
  end
@@ -8,9 +8,7 @@ module ShinyJsonLogic
8
8
  class None < Iterable::Base
9
9
  raise_on_dynamic_args!
10
10
 
11
- private
12
-
13
- def on_after(results)
11
+ def self.on_after(results, _scope_stack)
14
12
  return true if results.empty?
15
13
 
16
14
  results.none? { |res| Truthy.call(res) }
@@ -6,9 +6,9 @@ require "shiny_json_logic/truthy"
6
6
  module ShinyJsonLogic
7
7
  module Operations
8
8
  class Not < Base
9
- def call
9
+ def self.execute(rules, scope_stack)
10
10
  value = rules.is_a?(Array) ? rules.first : rules
11
- !Truthy.call(evaluate(value))
11
+ !Truthy.call(evaluate(value, scope_stack))
12
12
  end
13
13
  end
14
14
  end
@@ -7,17 +7,16 @@ require "shiny_json_logic/numericals/with_error_handling"
7
7
  module ShinyJsonLogic
8
8
  module Operations
9
9
  class Or < Base
10
- include Numericals::WithErrorHandling
10
+ extend Numericals::WithErrorHandling
11
11
  raise_on_dynamic_args!
12
12
 
13
- def call
14
- return handle_invalid_args if dynamic_args?
13
+ def self.execute(rules, scope_stack)
15
14
  return handle_invalid_args unless rules.is_a?(Array)
16
15
  return false if rules.empty?
17
16
 
18
17
  result = nil
19
18
  rules.each do |rule|
20
- result = evaluate(rule)
19
+ result = evaluate(rule, scope_stack)
21
20
  return result if Truthy.call(result)
22
21
  end
23
22
  result
@@ -1,37 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shiny_json_logic/truthy"
4
+ require "shiny_json_logic/operations/iterable/base"
4
5
 
5
6
  module ShinyJsonLogic
6
7
  module Operations
7
8
  class Preserve < Iterable::Base
8
- def initialize(rules, scope_stack)
9
- @collection = wrap(rules) || []
10
- # Skip Iterable::Base initialization, go directly to Operations::Base
11
- # Preserve doesn't need the standard iterable setup (filter, collection from rules[0], etc.)
12
- @rules = rules
13
- @scope_stack = scope_stack
14
- end
15
-
16
- private
17
-
18
- def on_each(item)
19
- Engine.call(item, scope_stack)
20
- end
9
+ def self.call(rules, scope_stack)
10
+ # Preserve doesn't create new scopes - evaluates each item directly
11
+ collection = Utils::Array.wrap(rules)
21
12
 
22
- def on_after(results)
23
- return results.first if results.size == 1
24
-
25
- results
26
- end
27
-
28
- # Preserve doesn't create new scopes - it just evaluates expressions
29
- def on_before_each(_item, _index = 0)
30
- # Don't push to scope stack
31
- end
13
+ results = collection.each_with_object([]) do |item, acc|
14
+ acc << Engine.call(item, scope_stack)
15
+ end
32
16
 
33
- def on_after_each
34
- # Don't pop from scope stack
17
+ results.size == 1 ? results.first : results
35
18
  end
36
19
  end
37
20
  end
@@ -7,18 +7,20 @@ require "shiny_json_logic/numericals/numerify"
7
7
  module ShinyJsonLogic
8
8
  module Operations
9
9
  class Product < Base
10
- include Numericals::WithErrorHandling
11
- include Numericals::Numerify
10
+ extend Numericals::WithErrorHandling
12
11
 
13
- def call
14
- operands = wrap_nil(rules)
12
+ def self.execute(rules, scope_stack)
13
+ operands = Utils::Array.wrap_nil(rules)
15
14
  return 1 if operands.empty?
16
15
 
17
16
  safe_arithmetic do
18
17
  result = nil
19
18
  count = 0
20
19
 
21
- each_operand(operands) do |num|
20
+ operands.each do |rule|
21
+ evaluated = evaluate(rule, scope_stack)
22
+ num = Numericals::Numerify.numerify(evaluated)
23
+ num = 0 if num.nil?
22
24
  return handle_nan if num.nil?
23
25
  count += 1
24
26
  result = result.nil? ? num.to_f : result * num.to_f
@@ -29,21 +31,6 @@ module ShinyJsonLogic
29
31
  result
30
32
  end
31
33
  end
32
-
33
- private
34
-
35
- def each_operand(operands)
36
- operands.each do |rule|
37
- evaluated = evaluate(rule)
38
- yield numerify(evaluated)
39
- end
40
- end
41
-
42
- def numerify(value)
43
- val = super
44
- return 0 if val.nil?
45
- val
46
- end
47
34
  end
48
35
  end
49
36
  end
@@ -7,39 +7,33 @@ require "shiny_json_logic/numericals/with_error_handling"
7
7
  module ShinyJsonLogic
8
8
  module Operations
9
9
  class Reduce < Iterable::Base
10
- include Numericals::WithErrorHandling
10
+ extend Numericals::WithErrorHandling
11
11
  raise_on_dynamic_args!
12
12
 
13
- def initialize(rules, scope_stack)
14
- # Capture initial accumulator before super (which may pre-process rules)
15
- initial_accumulator_rule = rules.is_a?(Array) ? rules[2] : nil
16
- super
17
- # Evaluate the initial accumulator value (third argument)
18
- @accumulator = Engine.call(initial_accumulator_rule, scope_stack)
19
- end
20
-
21
- private
22
-
23
- attr_accessor :accumulator
24
-
25
- def on_before_each(item, index = 0)
26
- # For reduce, we need to create a special scope with current and accumulator
27
- # Push iterator context
28
- scope_stack.push({ "index" => index }, index: index)
29
-
30
- # Push item scope with current and accumulator
31
- reduce_scope = { "current" => item, "accumulator" => accumulator }
32
- scope_stack.push(reduce_scope, index: index)
33
- end
34
-
35
- def on_each(_item)
36
- self.accumulator = Engine.call(filter, scope_stack)
37
- end
38
-
39
- def on_after(_results)
40
- safe_arithmetic do
41
- self.accumulator
13
+ def self.call(rules, scope_stack)
14
+ rules = resolve_rules(rules, scope_stack)
15
+
16
+ collection, filter = setup_collection(rules, scope_stack)
17
+
18
+ # Evaluate initial accumulator (third argument)
19
+ accumulator = Engine.call(rules[2], scope_stack)
20
+
21
+ collection.each_with_index do |item, index|
22
+ scope_stack.push({ "index" => index }, index: index)
23
+ reduce_scope = { "current" => item, "accumulator" => accumulator }
24
+ scope_stack.push(reduce_scope, index: index)
25
+ begin
26
+ accumulator = Engine.call(filter, scope_stack)
27
+ scope_stack.pop
28
+ scope_stack.pop
29
+ rescue => e
30
+ scope_stack.pop
31
+ scope_stack.pop
32
+ raise e
33
+ end
42
34
  end
35
+
36
+ safe_arithmetic { accumulator }
43
37
  end
44
38
  end
45
39
  end
@@ -1,30 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shiny_json_logic/operations/base"
4
- require "shiny_json_logic/numericals/with_error_handling"
5
4
  require "shiny_json_logic/comparisons/comparable"
6
5
 
7
6
  module ShinyJsonLogic
8
7
  module Operations
9
8
  class Smaller < Base
10
- include Numericals::WithErrorHandling
11
- include Comparisons::Comparable
12
9
  raise_on_dynamic_args!
13
10
 
14
- def call
15
- return handle_invalid_args if dynamic_args?
16
- operands = wrap_nil(rules)
17
- return handle_invalid_args if operands.length < 2
18
-
19
- prev = evaluate(operands[0])
20
- operands[1..].each do |rule|
21
- curr = evaluate(rule)
22
- result = compare(prev, curr)
23
- return handle_nan if result == :nan
24
- return false unless result == -1
25
- prev = curr
26
- end
27
- true
11
+ def self.execute(rules, scope_stack)
12
+ Comparisons::Comparable.compare_chain(rules, scope_stack) { |r| r == -1 }
28
13
  end
29
14
  end
30
15
  end
@@ -1,30 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shiny_json_logic/operations/base"
4
- require "shiny_json_logic/numericals/with_error_handling"
5
4
  require "shiny_json_logic/comparisons/comparable"
6
5
 
7
6
  module ShinyJsonLogic
8
7
  module Operations
9
8
  class SmallerEqual < Base
10
- include Numericals::WithErrorHandling
11
- include Comparisons::Comparable
12
9
  raise_on_dynamic_args!
13
10
 
14
- def call
15
- return handle_invalid_args if dynamic_args?
16
- operands = wrap_nil(rules)
17
- return handle_invalid_args if operands.length < 2
18
-
19
- prev = evaluate(operands[0])
20
- operands[1..].each do |rule|
21
- curr = evaluate(rule)
22
- result = compare(prev, curr)
23
- return handle_nan if result == :nan
24
- return false unless result <= 0
25
- prev = curr
26
- end
27
- true
11
+ def self.execute(rules, scope_stack)
12
+ Comparisons::Comparable.compare_chain(rules, scope_stack) { |r| r <= 0 }
28
13
  end
29
14
  end
30
15
  end
@@ -8,9 +8,7 @@ module ShinyJsonLogic
8
8
  class Some < Iterable::Base
9
9
  raise_on_dynamic_args!
10
10
 
11
- private
12
-
13
- def on_after(results)
11
+ def self.on_after(results, _scope_stack)
14
12
  results.any? { |res| res == true }
15
13
  end
16
14
  end
@@ -1,33 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shiny_json_logic/operations/base"
4
+ require "shiny_json_logic/comparisons/comparable"
4
5
  require "shiny_json_logic/numericals/with_error_handling"
5
6
 
6
7
  module ShinyJsonLogic
7
8
  module Operations
8
9
  class StrictDifferent < Base
9
- include Numericals::WithErrorHandling
10
+ extend Numericals::WithErrorHandling
10
11
  raise_on_dynamic_args!
11
12
 
12
- def call
13
- return handle_invalid_args if dynamic_args?
14
- operands = wrap_nil(rules)
13
+ def self.execute(rules, scope_stack)
14
+ operands = Utils::Array.wrap_nil(rules)
15
15
  return handle_invalid_args if operands.length < 2
16
16
 
17
- prev = cast(evaluate(operands[0]))
17
+ prev = Comparisons::Comparable.cast(evaluate(operands[0], scope_stack))
18
18
  operands[1..].each do |rule|
19
- curr = cast(evaluate(rule))
19
+ curr = Comparisons::Comparable.cast(evaluate(rule, scope_stack))
20
20
  return false if curr == prev
21
21
  prev = curr
22
22
  end
23
23
  true
24
24
  end
25
-
26
- private
27
-
28
- def cast(value)
29
- value.is_a?(Numeric) ? value.to_f : value
30
- end
31
25
  end
32
26
  end
33
27
  end
@@ -1,31 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shiny_json_logic/operations/base"
4
+ require "shiny_json_logic/comparisons/comparable"
4
5
  require "shiny_json_logic/numericals/with_error_handling"
5
6
 
6
7
  module ShinyJsonLogic
7
8
  module Operations
8
9
  class StrictEqual < Base
9
- include Numericals::WithErrorHandling
10
+ extend Numericals::WithErrorHandling
10
11
  raise_on_dynamic_args!
11
12
 
12
- def call
13
- return handle_invalid_args if dynamic_args?
14
- operands = wrap_nil(rules)
13
+ def self.execute(rules, scope_stack)
14
+ operands = Utils::Array.wrap_nil(rules)
15
15
  return handle_invalid_args if operands.length < 2
16
16
 
17
- first = cast(evaluate(operands[0]))
17
+ first = Comparisons::Comparable.cast(evaluate(operands[0], scope_stack))
18
18
  operands[1..].each do |rule|
19
- return false unless cast(evaluate(rule)) == first
19
+ return false unless Comparisons::Comparable.cast(evaluate(rule, scope_stack)) == first
20
20
  end
21
21
  true
22
22
  end
23
-
24
- private
25
-
26
- def cast(value)
27
- value.is_a?(Numeric) ? value.to_f : value
28
- end
29
23
  end
30
24
  end
31
25
  end
@@ -5,10 +5,10 @@ require "shiny_json_logic/operations/base"
5
5
  module ShinyJsonLogic
6
6
  module Operations
7
7
  class Substring < Base
8
- def call
9
- str = evaluate(rules[0]).to_s
10
- start = evaluate(rules[1]).to_i
11
- length = rules[2] ? evaluate(rules[2]).to_i : str.length
8
+ def self.execute(rules, scope_stack)
9
+ str = evaluate(rules[0], scope_stack).to_s
10
+ start = evaluate(rules[1], scope_stack).to_i
11
+ length = rules[2] ? evaluate(rules[2], scope_stack).to_i : str.length
12
12
  start += str.length if start < 0
13
13
  start = 0 if start < 0 # clamp negative start to 0
14
14
  return "" if start >= str.length