shiny_json_logic 0.2.8 → 0.2.11

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +15 -0
  4. data/Gemfile.lock +1 -1
  5. data/bin/test.sh +57 -0
  6. data/lib/shiny_json_logic/comparisons/comparable.rb +36 -0
  7. data/lib/shiny_json_logic/engine.rb +6 -27
  8. data/lib/shiny_json_logic/errors/base.rb +1 -5
  9. data/lib/shiny_json_logic/errors/invalid_arguments.rb +11 -0
  10. data/lib/shiny_json_logic/errors/not_a_number.rb +11 -0
  11. data/lib/shiny_json_logic/errors/unknown_operator.rb +11 -0
  12. data/lib/shiny_json_logic/numericals/min_max_collection.rb +31 -0
  13. data/lib/shiny_json_logic/numericals/with_error_handling.rb +4 -10
  14. data/lib/shiny_json_logic/operations/addition.rb +2 -4
  15. data/lib/shiny_json_logic/operations/and.rb +1 -3
  16. data/lib/shiny_json_logic/operations/base.rb +15 -25
  17. data/lib/shiny_json_logic/operations/coalesce.rb +1 -3
  18. data/lib/shiny_json_logic/operations/concatenation.rb +3 -5
  19. data/lib/shiny_json_logic/operations/different.rb +7 -37
  20. data/lib/shiny_json_logic/operations/division.rb +2 -4
  21. data/lib/shiny_json_logic/operations/double_not.rb +2 -4
  22. data/lib/shiny_json_logic/operations/equal.rb +5 -35
  23. data/lib/shiny_json_logic/operations/exists.rb +2 -4
  24. data/lib/shiny_json_logic/operations/filter.rb +1 -2
  25. data/lib/shiny_json_logic/operations/greater.rb +5 -35
  26. data/lib/shiny_json_logic/operations/greater_equal.rb +5 -35
  27. data/lib/shiny_json_logic/operations/if.rb +6 -18
  28. data/lib/shiny_json_logic/operations/inclusion.rb +1 -3
  29. data/lib/shiny_json_logic/operations/iterable/base.rb +17 -33
  30. data/lib/shiny_json_logic/operations/max.rb +4 -9
  31. data/lib/shiny_json_logic/operations/merge.rb +3 -5
  32. data/lib/shiny_json_logic/operations/min.rb +4 -9
  33. data/lib/shiny_json_logic/operations/missing.rb +4 -6
  34. data/lib/shiny_json_logic/operations/missing_some.rb +5 -7
  35. data/lib/shiny_json_logic/operations/modulo.rb +4 -6
  36. data/lib/shiny_json_logic/operations/not.rb +1 -3
  37. data/lib/shiny_json_logic/operations/or.rb +1 -3
  38. data/lib/shiny_json_logic/operations/preserve.rb +8 -7
  39. data/lib/shiny_json_logic/operations/product.rb +2 -4
  40. data/lib/shiny_json_logic/operations/reduce.rb +5 -7
  41. data/lib/shiny_json_logic/operations/smaller.rb +5 -35
  42. data/lib/shiny_json_logic/operations/smaller_equal.rb +5 -35
  43. data/lib/shiny_json_logic/operations/strict_different.rb +4 -6
  44. data/lib/shiny_json_logic/operations/strict_equal.rb +2 -4
  45. data/lib/shiny_json_logic/operations/substring.rb +1 -3
  46. data/lib/shiny_json_logic/operations/subtraction.rb +4 -6
  47. data/lib/shiny_json_logic/operations/throw.rb +3 -5
  48. data/lib/shiny_json_logic/operations/try.rb +20 -27
  49. data/lib/shiny_json_logic/operations/val.rb +7 -7
  50. data/lib/shiny_json_logic/operations/var.rb +27 -5
  51. data/lib/shiny_json_logic/operator_solver.rb +1 -3
  52. data/lib/shiny_json_logic/scope_stack.rb +15 -13
  53. data/lib/shiny_json_logic/utils/array.rb +22 -0
  54. data/lib/shiny_json_logic/utils/indifferent_hash.rb +72 -0
  55. data/lib/shiny_json_logic/version.rb +1 -1
  56. data/lib/shiny_json_logic.rb +34 -4
  57. data/shiny_json_logic.gemspec +2 -2
  58. metadata +12 -6
  59. data/lib/core_ext/array.rb +0 -19
  60. data/lib/core_ext/hash.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e05288f3f43187927c102d03ce0cd653bf8762dd7e0adc40b29d27e0c4f7984f
4
- data.tar.gz: 4c6e781dec321b4f928b9b1d786c708ffe39662cc22676cc5ab7d8228dff8aa8
3
+ metadata.gz: f6c51d139183162260ba3b85195e9e8854da441bedad9ca5753785d2a781b9d4
4
+ data.tar.gz: 499f7e445cb8aadebb2397781c36b09722b688ee955994002963e38a0ce64b78
5
5
  SHA512:
6
- metadata.gz: '0459cef93e4ce203e8ea1022c7bf7305aef736551e56bf3e8c47326ddcf95481232c788f5b3d5c96f25137df730b6cb5180ac3b7aadd696a1c4a0d9bea7e0aa3'
7
- data.tar.gz: d3801f6f5cc0a0df664d829e352c934345305ce264a57bd39a7dd3f7e637963d402a0c7676e8c4f9fffa76fea7dffe6cca04d655bc38b79dfc781877faa616d5
6
+ metadata.gz: 5723ddd51acda1014b43b9f84e4ce2054a7b9fc1ea6e86fecc7d30742bd8b6f51309d7d220f15a7546713a151be503977cc7452bac1953a08c21fd6efbd9d285
7
+ data.tar.gz: bd2182abf594716ee39c4b892dae1079969f5d49402c258ad136d8b935460f9cd88d924dc6ffb8aa9336328578157f34eddbd04dc4ce8b7310c91cd4fbd18f48
data/.gitignore CHANGED
@@ -21,3 +21,4 @@
21
21
 
22
22
  # Development context
23
23
  AGENTS_DEVELOPMENT.md
24
+ DOCUMENTATION.md
data/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.2.11] - 2026-02-09
6
+ ### Changed
7
+ - Removes monkey patches in favour of isolated modules.
8
+
9
+ ### Added
10
+ - Supports hashes with string or symbol access indifferently (e.g. `{"a" => 1}` can be accessed with `:a` or `"a"`).
11
+
12
+ ## [0.2.9] - 2026-02-08
13
+ ### Added
14
+ - New specific error classes: `Errors::InvalidArguments`, `Errors::NotANumber`, `Errors::UnknownOperator`
15
+ - All inherit from `Errors::Base`, so existing `rescue Errors::Base` will continue to work
16
+ ### Changed
17
+ - Refactors max/min operators to use shared MinMaxCollection module
18
+ - Eliminates code duplication between max.rb and min.rb
19
+
5
20
  ## [0.2.8] - 2026-02-07
6
21
  ### Changed
7
22
  - Improves error handling in try/throw/reduce operators
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shiny_json_logic (0.2.8)
4
+ shiny_json_logic (0.2.11)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/bin/test.sh CHANGED
@@ -7,8 +7,11 @@ cd "$ROOT_DIR"
7
7
  # Config
8
8
  COMPAT_REF="${COMPAT_REF:-main}"
9
9
  COMPAT_REPO="${COMPAT_REPO:-json-logic/compat-tables}"
10
+ OFFICIAL_REF="${OFFICIAL_REF:-main}"
11
+ OFFICIAL_REPO="${OFFICIAL_REPO:-json-logic/.github}"
10
12
  TMP_DIR="${TMP_DIR:-$ROOT_DIR/tmp}"
11
13
  SUITES_DIR="$TMP_DIR/compat-suites"
14
+ OFFICIAL_DIR="$TMP_DIR/official-tests"
12
15
 
13
16
  # Helpers
14
17
  log() { printf "\n\033[1m%s\033[0m\n" "$*"; }
@@ -37,6 +40,22 @@ curl -fsSL "$ARCHIVE_URL" \
37
40
 
38
41
  log "Compat suites ready at: $SUITES_DIR"
39
42
 
43
+ log "Fetching official tests from GitHub: $OFFICIAL_REPO@$OFFICIAL_REF"
44
+
45
+ rm -rf "$OFFICIAL_DIR"
46
+ mkdir -p "$OFFICIAL_DIR"
47
+
48
+ OFFICIAL_URL="https://codeload.github.com/${OFFICIAL_REPO}/tar.gz/${OFFICIAL_REF}"
49
+
50
+ # Extract only: .github-<ref>/tests -> tmp/official-tests
51
+ curl -fsSL "$OFFICIAL_URL" \
52
+ | tar -xz \
53
+ --strip-components=2 \
54
+ -C "$OFFICIAL_DIR" \
55
+ ".github-${OFFICIAL_REF}/tests" 2>/dev/null \
56
+
57
+ log "Official tests ready at: $OFFICIAL_DIR"
58
+
40
59
  log "Running compatibility suite"
41
60
  COMPAT_OUT="$TMP_DIR/compat-rspec.out"
42
61
 
@@ -75,3 +94,41 @@ cat > results/ruby.json <<EOF
75
94
  EOF
76
95
 
77
96
  echo "Compat: ${PASSED}/${TOTAL} (failures: ${FAILURES}, exit: ${COMPAT_EXIT})"
97
+
98
+ # ---- Official tests (informational only, does not affect badge) ----
99
+ log "Running official tests (informational)"
100
+ OFFICIAL_OUT="$TMP_DIR/official-rspec.out"
101
+
102
+ set +e
103
+ OFFICIAL_TESTS_DIR="$OFFICIAL_DIR" bundle exec rspec spec/official_spec.rb | tee "$OFFICIAL_OUT"
104
+ OFFICIAL_EXIT=${PIPESTATUS[0]}
105
+ set -e
106
+
107
+ # Parse totals from RSpec summary
108
+ OFFICIAL_TOTAL=$(ruby -e '
109
+ s = STDIN.read
110
+ m = s.match(/(\d+)\s+examples?,\s+(\d+)\s+failures?/)
111
+ if m
112
+ puts m[1]
113
+ else
114
+ puts "0"
115
+ end
116
+ ' < "$OFFICIAL_OUT")
117
+
118
+ OFFICIAL_FAILURES=$(ruby -e '
119
+ s = STDIN.read
120
+ m = s.match(/(\d+)\s+examples?,\s+(\d+)\s+failures?/)
121
+ if m
122
+ puts m[2]
123
+ else
124
+ puts "0"
125
+ end
126
+ ' < "$OFFICIAL_OUT")
127
+
128
+ OFFICIAL_PASSED=$((OFFICIAL_TOTAL - OFFICIAL_FAILURES))
129
+
130
+ echo "Official: ${OFFICIAL_PASSED}/${OFFICIAL_TOTAL} (failures: ${OFFICIAL_FAILURES}, exit: ${OFFICIAL_EXIT})"
131
+ echo ""
132
+ echo "=== Summary ==="
133
+ echo "Compat (badge): ${PASSED}/${TOTAL}"
134
+ echo "Official (info): ${OFFICIAL_PASSED}/${OFFICIAL_TOTAL}"
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shiny_json_logic/numericals/numerify"
4
+
5
+ module ShinyJsonLogic
6
+ module Comparisons
7
+ module Comparable
8
+ include Numericals::Numerify
9
+
10
+ private
11
+
12
+ def compare(a, b)
13
+ return :nan if a.is_a?(Array) || a.is_a?(Hash) || b.is_a?(Array) || b.is_a?(Hash)
14
+
15
+ if a.is_a?(String) && b.is_a?(String)
16
+ return a <=> b
17
+ end
18
+
19
+ num_a = numerify_for_comparison(a)
20
+ num_b = numerify_for_comparison(b)
21
+ return :nan if num_a.nil? || num_b.nil?
22
+
23
+ num_a <=> num_b
24
+ end
25
+
26
+ def numerify_for_comparison(value)
27
+ return value.to_f if value.is_a?(Numeric)
28
+ return 0.0 if value == false
29
+ return 1.0 if value == true
30
+ return 0.0 if value.nil?
31
+ return value.to_f if value.is_a?(String) && numeric_string?(value)
32
+ nil
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,19 +1,10 @@
1
- require "core_ext/array"
2
- require "core_ext/hash"
3
1
  require "shiny_json_logic/operator_solver"
4
- require "shiny_json_logic/scope_stack"
5
2
 
6
3
  module ShinyJsonLogic
7
4
  class Engine
8
- attr_reader :errors
9
-
10
- # Initialize with either:
11
- # - Engine.new(rule, data) - creates a new scope_stack with data as root
12
- # - Engine.new(rule, scope_stack: existing_stack) - uses existing scope_stack
13
- def initialize(rule, data = nil, scope_stack: nil)
5
+ def initialize(rule, scope_stack)
14
6
  @rule = rule
15
- @errors = []
16
- @scope_stack = scope_stack || ScopeStack.new(data || {})
7
+ @scope_stack = scope_stack
17
8
  end
18
9
 
19
10
  def call(rule = self.rule)
@@ -21,9 +12,10 @@ module ShinyJsonLogic
21
12
  return rule if rule.empty?
22
13
 
23
14
  operation, args = rule.to_a.first
24
- return rule unless operations.solvers.key?(operation)
15
+ operation_key = operation.to_s
16
+ return rule unless operations.key?(operation_key)
25
17
 
26
- solve(operation, args)
18
+ operations.fetch(operation_key).new(args, scope_stack).call
27
19
  elsif rule.is_a?(Array)
28
20
  rule.map { |val| call(val) }
29
21
  else
@@ -34,22 +26,9 @@ module ShinyJsonLogic
34
26
  private
35
27
 
36
28
  attr_reader :rule, :scope_stack
37
- attr_writer :errors
38
-
39
- def solve(operation, args)
40
- context = {
41
- "rules" => args,
42
- "errors" => errors,
43
- "scope_stack" => scope_stack
44
- }
45
- result, errors = operations.solvers.fetch(operation).new(context).call.values_at("result", "errors")
46
- self.errors = [*self.errors, *errors].uniq
47
-
48
- result
49
- end
50
29
 
51
30
  def operations
52
- @operations ||= OperatorSolver.new
31
+ @operations ||= OperatorSolver.new.solvers
53
32
  end
54
33
  end
55
34
  end
@@ -1,15 +1,11 @@
1
- require "securerandom"
2
-
3
1
  module ShinyJsonLogic
4
2
  module Errors
5
3
  class Base < StandardError
6
- attr_reader :type, :id
7
- attr_accessor :panic
4
+ attr_reader :type
8
5
 
9
6
  def initialize(type: nil)
10
7
  super(type)
11
8
  @type = type
12
- @id = "shiny_error_#{SecureRandom.uuid}"
13
9
  end
14
10
 
15
11
  def payload
@@ -0,0 +1,11 @@
1
+ require "shiny_json_logic/errors/base"
2
+
3
+ module ShinyJsonLogic
4
+ module Errors
5
+ class InvalidArguments < Base
6
+ def initialize
7
+ super(type: "Invalid Arguments")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require "shiny_json_logic/errors/base"
2
+
3
+ module ShinyJsonLogic
4
+ module Errors
5
+ class NotANumber < Base
6
+ def initialize
7
+ super(type: "NaN")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require "shiny_json_logic/errors/base"
2
+
3
+ module ShinyJsonLogic
4
+ module Errors
5
+ class UnknownOperator < Base
6
+ def initialize
7
+ super(type: "Unknown Operator")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ require "shiny_json_logic/errors/invalid_arguments"
2
+
3
+ module ShinyJsonLogic
4
+ module Numericals
5
+ module MinMaxCollection
6
+ private
7
+
8
+ def collect_numeric_values
9
+ values = collect_values
10
+ raise Errors::InvalidArguments if values.empty?
11
+ raise Errors::InvalidArguments unless values.all? { |v| v.is_a?(Numeric) }
12
+ values
13
+ end
14
+
15
+ def collect_values
16
+ result = []
17
+ wrap_nil(rules).each do |rule|
18
+ evaluated = evaluate(rule)
19
+ # If rule was an operation (Hash), expand the result array
20
+ # If rule was a literal array, it's invalid (will fail numeric check)
21
+ if operation?(rule)
22
+ wrap_nil(evaluated).each { |val| result << val }
23
+ else
24
+ result << evaluated
25
+ end
26
+ end
27
+ result
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,4 +1,6 @@
1
1
  require "shiny_json_logic/errors/base"
2
+ require "shiny_json_logic/errors/not_a_number"
3
+ require "shiny_json_logic/errors/invalid_arguments"
2
4
 
3
5
  module ShinyJsonLogic
4
6
  module Numericals
@@ -14,20 +16,12 @@ module ShinyJsonLogic
14
16
  end
15
17
 
16
18
  def handle_nan
17
- error = ShinyJsonLogic::Errors::Base.new(type: "NaN")
18
- self.errors << error
19
- error.id
19
+ raise Errors::NotANumber
20
20
  end
21
21
 
22
22
  def handle_invalid_args
23
- error = ShinyJsonLogic::Errors::Base.new(type: "Invalid Arguments")
24
- self.errors << error
25
- error.id
23
+ raise Errors::InvalidArguments
26
24
  end
27
-
28
- # Alias for backward compatibility
29
- alias_method :handle_invalid_operand, :handle_nan
30
- alias_method :handle_no_operators, :handle_invalid_args
31
25
  end
32
26
  end
33
27
  end
@@ -8,9 +8,7 @@ module ShinyJsonLogic
8
8
  include Numericals::WithErrorHandling
9
9
  include Numericals::Numerify
10
10
 
11
- protected
12
-
13
- def run
11
+ def call
14
12
  safe_arithmetic do
15
13
  result = 0.0
16
14
  count = 0
@@ -27,7 +25,7 @@ module ShinyJsonLogic
27
25
  private
28
26
 
29
27
  def each_operand
30
- Array.wrap_nil(rules).each do |rule|
28
+ wrap_nil(rules).each do |rule|
31
29
  yield numerify(evaluate(rule))
32
30
  end
33
31
  end
@@ -8,9 +8,7 @@ module ShinyJsonLogic
8
8
  include Numericals::WithErrorHandling
9
9
  raise_on_dynamic_args!
10
10
 
11
- protected
12
-
13
- def run
11
+ def call
14
12
  return handle_invalid_args if dynamic_args?
15
13
  return handle_invalid_args unless rules.is_a?(Array)
16
14
  return false if rules.empty?
@@ -1,42 +1,33 @@
1
1
  require "shiny_json_logic/truthy"
2
+ require "shiny_json_logic/utils/array"
2
3
 
3
4
  module ShinyJsonLogic
4
5
  module Operations
5
6
  class Base
6
- def initialize(context)
7
- @context = context
8
- @rules, @errors, @scope_stack = @context.values_at("rules", "errors", "scope_stack")
9
- @dynamic_args = operation?(@rules)
10
- @rules = pre_process(@rules)
7
+ include Utils::Array
8
+
9
+ def initialize(rules, scope_stack)
10
+ @scope_stack = scope_stack
11
+ @dynamic_args = operation?(rules)
12
+ @rules = pre_process(rules)
11
13
  end
12
14
 
13
15
  def call
14
- deliver run
16
+ raise NotImplementedError
15
17
  end
16
18
 
17
19
  protected
18
20
 
19
- attr_reader :context, :scope_stack
20
- attr_accessor :rules, :errors
21
+ attr_reader :scope_stack
22
+ attr_accessor :rules
21
23
 
22
24
  # Access current data through scope_stack
23
25
  def data
24
26
  scope_stack.current
25
27
  end
26
28
 
27
- def run
28
- raise NotImplementedError
29
- end
30
-
31
- def deliver(result = nil)
32
- {"result" => result, "errors" => self.errors}
33
- end
34
-
35
29
  def evaluate(rule)
36
- engine = Engine.new(rule, scope_stack: scope_stack)
37
- result = engine.call
38
- self.errors = [*errors, *engine.errors].uniq
39
- result
30
+ Engine.new(rule, scope_stack).call
40
31
  end
41
32
 
42
33
  def dynamic_args?
@@ -54,15 +45,14 @@ module ShinyJsonLogic
54
45
  private
55
46
 
56
47
  def pre_process(rules)
57
- if operation?(rules)
58
- evaluate(rules)
59
- else
60
- rules
61
- end
48
+ return evaluate(rules) if operation?(rules)
49
+
50
+ rules
62
51
  end
63
52
 
64
53
  def operation?(value)
65
54
  return false unless value.is_a?(Hash) && !value.empty?
55
+
66
56
  OperatorSolver.new.operation?(value)
67
57
  end
68
58
  end
@@ -3,9 +3,7 @@ require "shiny_json_logic/operations/base"
3
3
  module ShinyJsonLogic
4
4
  module Operations
5
5
  class Coalesce < Base
6
- protected
7
-
8
- def run
6
+ def call
9
7
  rules.each do |rule|
10
8
  result = evaluate(rule)
11
9
  return result unless result.nil?
@@ -3,13 +3,11 @@ require "shiny_json_logic/operations/base"
3
3
  module ShinyJsonLogic
4
4
  module Operations
5
5
  class Concatenation < Base
6
- protected
7
-
8
- def run
6
+ def call
9
7
  result = []
10
- Array.wrap_nil(rules).each do |rule|
8
+ wrap_nil(rules).each do |rule|
11
9
  evaluated = evaluate(rule)
12
- Array.wrap_nil(evaluated).each { |v| result << v.to_s }
10
+ wrap_nil(evaluated).each { |v| result << v.to_s }
13
11
  end
14
12
  result.join
15
13
  end
@@ -1,19 +1,17 @@
1
1
  require "shiny_json_logic/operations/base"
2
2
  require "shiny_json_logic/numericals/with_error_handling"
3
- require "shiny_json_logic/numericals/numerify"
3
+ require "shiny_json_logic/comparisons/comparable"
4
4
 
5
5
  module ShinyJsonLogic
6
6
  module Operations
7
7
  class Different < Base
8
8
  include Numericals::WithErrorHandling
9
- include Numericals::Numerify
9
+ include Comparisons::Comparable
10
10
  raise_on_dynamic_args!
11
11
 
12
- protected
13
-
14
- def run
12
+ def call
15
13
  return handle_invalid_args if dynamic_args?
16
- operands = Array.wrap_nil(rules)
14
+ operands = wrap_nil(rules)
17
15
  return handle_invalid_args if operands.length < 2
18
16
 
19
17
  prev = evaluate(operands[0])
@@ -21,39 +19,11 @@ module ShinyJsonLogic
21
19
  curr = evaluate(rule)
22
20
  result = compare(prev, curr)
23
21
  return handle_nan if result == :nan
24
- return false if result == 0 # Si son iguales, != es false
22
+ return false if result == 0
25
23
  prev = curr
26
24
  end
27
- true # Todos los pares consecutivos son diferentes
28
- end
29
-
30
- private
31
-
32
- def compare(a, b)
33
- # Arrays u objetos → NaN
34
- return :nan if a.is_a?(Array) || a.is_a?(Hash) || b.is_a?(Array) || b.is_a?(Hash)
35
-
36
- # Ambos strings → comparación directa
37
- if a.is_a?(String) && b.is_a?(String)
38
- return a <=> b
39
- end
40
-
41
- # Convertir a números para comparar
42
- num_a = numerify_for_compare(a)
43
- num_b = numerify_for_compare(b)
44
- return :nan if num_a.nil? || num_b.nil?
45
-
46
- num_a <=> num_b
47
- end
48
-
49
- def numerify_for_compare(value)
50
- return value.to_f if value.is_a?(Numeric)
51
- return 0.0 if value == false
52
- return 1.0 if value == true
53
- return 0.0 if value.nil?
54
- return value.to_f if value.is_a?(String) && numeric_string?(value)
55
- nil # String no numérica
25
+ true
56
26
  end
57
27
  end
58
28
  end
59
- end
29
+ end
@@ -8,10 +8,8 @@ module ShinyJsonLogic
8
8
  include Numericals::WithErrorHandling
9
9
  include Numericals::Numerify
10
10
 
11
- protected
12
-
13
- def run
14
- operands = Array.wrap_nil(rules)
11
+ def call
12
+ operands = wrap_nil(rules)
15
13
  return handle_invalid_args if operands.empty?
16
14
 
17
15
  result = nil
@@ -4,10 +4,8 @@ require "shiny_json_logic/truthy"
4
4
  module ShinyJsonLogic
5
5
  module Operations
6
6
  class DoubleNot < Base
7
- protected
8
-
9
- def run
10
- value = Array.wrap_nil(rules).first
7
+ def call
8
+ value = wrap_nil(rules).first
11
9
  !!Truthy.call(evaluate(value))
12
10
  end
13
11
  end
@@ -1,19 +1,17 @@
1
1
  require "shiny_json_logic/operations/base"
2
2
  require "shiny_json_logic/numericals/with_error_handling"
3
- require "shiny_json_logic/numericals/numerify"
3
+ require "shiny_json_logic/comparisons/comparable"
4
4
 
5
5
  module ShinyJsonLogic
6
6
  module Operations
7
7
  class Equal < Base
8
8
  include Numericals::WithErrorHandling
9
- include Numericals::Numerify
9
+ include Comparisons::Comparable
10
10
  raise_on_dynamic_args!
11
11
 
12
- protected
13
-
14
- def run
12
+ def call
15
13
  return handle_invalid_args if dynamic_args?
16
- operands = Array.wrap_nil(rules)
14
+ operands = wrap_nil(rules)
17
15
  return handle_invalid_args if operands.length < 2
18
16
 
19
17
  first = evaluate(operands[0])
@@ -25,34 +23,6 @@ module ShinyJsonLogic
25
23
  end
26
24
  true
27
25
  end
28
-
29
- private
30
-
31
- def compare(a, b)
32
- # Arrays u objetos → NaN
33
- return :nan if a.is_a?(Array) || a.is_a?(Hash) || b.is_a?(Array) || b.is_a?(Hash)
34
-
35
- # Ambos strings → comparación directa
36
- if a.is_a?(String) && b.is_a?(String)
37
- return a <=> b
38
- end
39
-
40
- # Convertir a números para comparar
41
- num_a = numerify_for_compare(a)
42
- num_b = numerify_for_compare(b)
43
- return :nan if num_a.nil? || num_b.nil?
44
-
45
- num_a <=> num_b
46
- end
47
-
48
- def numerify_for_compare(value)
49
- return value.to_f if value.is_a?(Numeric)
50
- return 0.0 if value == false
51
- return 1.0 if value == true
52
- return 0.0 if value.nil?
53
- return value.to_f if value.is_a?(String) && numeric_string?(value)
54
- nil # String no numérica
55
- end
56
26
  end
57
27
  end
58
- end
28
+ end
@@ -3,12 +3,10 @@ require "shiny_json_logic/operations/base"
3
3
  module ShinyJsonLogic
4
4
  module Operations
5
5
  class Exists < Base
6
- protected
7
-
8
- def run
6
+ def call
9
7
  current = data
10
8
 
11
- Array.wrap_nil(rules).each do |rule|
9
+ wrap_nil(rules).each do |rule|
12
10
  segment = evaluate(rule)
13
11
  return false unless current.key?(segment)
14
12
  current = current[segment]
@@ -10,8 +10,7 @@ module ShinyJsonLogic
10
10
  private
11
11
 
12
12
  def on_each(item)
13
- engine = Engine.new(filter, scope_stack: scope_stack)
14
- [Truthy.call(engine.call) ? item : nil, engine]
13
+ Truthy.call(Engine.new(filter, scope_stack).call) ? item : nil
15
14
  end
16
15
 
17
16
  def on_after(results)