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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile.lock +1 -1
- data/bin/test.sh +57 -0
- data/lib/shiny_json_logic/comparisons/comparable.rb +36 -0
- data/lib/shiny_json_logic/engine.rb +6 -27
- data/lib/shiny_json_logic/errors/base.rb +1 -5
- data/lib/shiny_json_logic/errors/invalid_arguments.rb +11 -0
- data/lib/shiny_json_logic/errors/not_a_number.rb +11 -0
- data/lib/shiny_json_logic/errors/unknown_operator.rb +11 -0
- data/lib/shiny_json_logic/numericals/min_max_collection.rb +31 -0
- data/lib/shiny_json_logic/numericals/with_error_handling.rb +4 -10
- data/lib/shiny_json_logic/operations/addition.rb +2 -4
- data/lib/shiny_json_logic/operations/and.rb +1 -3
- data/lib/shiny_json_logic/operations/base.rb +15 -25
- data/lib/shiny_json_logic/operations/coalesce.rb +1 -3
- data/lib/shiny_json_logic/operations/concatenation.rb +3 -5
- data/lib/shiny_json_logic/operations/different.rb +7 -37
- data/lib/shiny_json_logic/operations/division.rb +2 -4
- data/lib/shiny_json_logic/operations/double_not.rb +2 -4
- data/lib/shiny_json_logic/operations/equal.rb +5 -35
- data/lib/shiny_json_logic/operations/exists.rb +2 -4
- data/lib/shiny_json_logic/operations/filter.rb +1 -2
- data/lib/shiny_json_logic/operations/greater.rb +5 -35
- data/lib/shiny_json_logic/operations/greater_equal.rb +5 -35
- data/lib/shiny_json_logic/operations/if.rb +6 -18
- data/lib/shiny_json_logic/operations/inclusion.rb +1 -3
- data/lib/shiny_json_logic/operations/iterable/base.rb +17 -33
- data/lib/shiny_json_logic/operations/max.rb +4 -9
- data/lib/shiny_json_logic/operations/merge.rb +3 -5
- data/lib/shiny_json_logic/operations/min.rb +4 -9
- data/lib/shiny_json_logic/operations/missing.rb +4 -6
- data/lib/shiny_json_logic/operations/missing_some.rb +5 -7
- data/lib/shiny_json_logic/operations/modulo.rb +4 -6
- data/lib/shiny_json_logic/operations/not.rb +1 -3
- data/lib/shiny_json_logic/operations/or.rb +1 -3
- data/lib/shiny_json_logic/operations/preserve.rb +8 -7
- data/lib/shiny_json_logic/operations/product.rb +2 -4
- data/lib/shiny_json_logic/operations/reduce.rb +5 -7
- data/lib/shiny_json_logic/operations/smaller.rb +5 -35
- data/lib/shiny_json_logic/operations/smaller_equal.rb +5 -35
- data/lib/shiny_json_logic/operations/strict_different.rb +4 -6
- data/lib/shiny_json_logic/operations/strict_equal.rb +2 -4
- data/lib/shiny_json_logic/operations/substring.rb +1 -3
- data/lib/shiny_json_logic/operations/subtraction.rb +4 -6
- data/lib/shiny_json_logic/operations/throw.rb +3 -5
- data/lib/shiny_json_logic/operations/try.rb +20 -27
- data/lib/shiny_json_logic/operations/val.rb +7 -7
- data/lib/shiny_json_logic/operations/var.rb +27 -5
- data/lib/shiny_json_logic/operator_solver.rb +1 -3
- data/lib/shiny_json_logic/scope_stack.rb +15 -13
- data/lib/shiny_json_logic/utils/array.rb +22 -0
- data/lib/shiny_json_logic/utils/indifferent_hash.rb +72 -0
- data/lib/shiny_json_logic/version.rb +1 -1
- data/lib/shiny_json_logic.rb +34 -4
- data/shiny_json_logic.gemspec +2 -2
- metadata +12 -6
- data/lib/core_ext/array.rb +0 -19
- data/lib/core_ext/hash.rb +0 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f6c51d139183162260ba3b85195e9e8854da441bedad9ca5753785d2a781b9d4
|
|
4
|
+
data.tar.gz: 499f7e445cb8aadebb2397781c36b09722b688ee955994002963e38a0ce64b78
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5723ddd51acda1014b43b9f84e4ce2054a7b9fc1ea6e86fecc7d30742bd8b6f51309d7d220f15a7546713a151be503977cc7452bac1953a08c21fd6efbd9d285
|
|
7
|
+
data.tar.gz: bd2182abf594716ee39c4b892dae1079969f5d49402c258ad136d8b935460f9cd88d924dc6ffb8aa9336328578157f34eddbd04dc4ce8b7310c91cd4fbd18f48
|
data/.gitignore
CHANGED
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
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
|
-
|
|
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
|
-
@
|
|
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
|
-
|
|
15
|
+
operation_key = operation.to_s
|
|
16
|
+
return rule unless operations.key?(operation_key)
|
|
25
17
|
|
|
26
|
-
|
|
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
|
|
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,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
|
-
|
|
18
|
-
self.errors << error
|
|
19
|
-
error.id
|
|
19
|
+
raise Errors::NotANumber
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def handle_invalid_args
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@
|
|
10
|
-
@
|
|
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
|
-
|
|
16
|
+
raise NotImplementedError
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
protected
|
|
18
20
|
|
|
19
|
-
attr_reader :
|
|
20
|
-
attr_accessor :rules
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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,13 +3,11 @@ require "shiny_json_logic/operations/base"
|
|
|
3
3
|
module ShinyJsonLogic
|
|
4
4
|
module Operations
|
|
5
5
|
class Concatenation < Base
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def run
|
|
6
|
+
def call
|
|
9
7
|
result = []
|
|
10
|
-
|
|
8
|
+
wrap_nil(rules).each do |rule|
|
|
11
9
|
evaluated = evaluate(rule)
|
|
12
|
-
|
|
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/
|
|
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
|
|
9
|
+
include Comparisons::Comparable
|
|
10
10
|
raise_on_dynamic_args!
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def run
|
|
12
|
+
def call
|
|
15
13
|
return handle_invalid_args if dynamic_args?
|
|
16
|
-
operands =
|
|
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
|
|
22
|
+
return false if result == 0
|
|
25
23
|
prev = curr
|
|
26
24
|
end
|
|
27
|
-
true
|
|
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
|
-
|
|
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
|
|
@@ -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/
|
|
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
|
|
9
|
+
include Comparisons::Comparable
|
|
10
10
|
raise_on_dynamic_args!
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def run
|
|
12
|
+
def call
|
|
15
13
|
return handle_invalid_args if dynamic_args?
|
|
16
|
-
operands =
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
def run
|
|
6
|
+
def call
|
|
9
7
|
current = data
|
|
10
8
|
|
|
11
|
-
|
|
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
|
-
|
|
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)
|