shiny_json_logic 0.3.0 → 0.3.2
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/CHANGELOG.md +12 -0
- data/Gemfile.lock +1 -1
- data/lib/shiny_json_logic/engine.rb +11 -5
- data/lib/shiny_json_logic/operations/val.rb +4 -3
- data/lib/shiny_json_logic/operations/var.rb +6 -2
- data/lib/shiny_json_logic/scope_stack.rb +4 -4
- data/lib/shiny_json_logic/truthy.rb +9 -7
- data/lib/shiny_json_logic/utils/data_hash.rb +18 -0
- data/lib/shiny_json_logic/version.rb +1 -1
- data/lib/shiny_json_logic.rb +3 -28
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 47eab10babd10e80ceeda0d5f5dc857c73a69ae7f36c5a8b09c71984e02c4243
|
|
4
|
+
data.tar.gz: 21adb41b87070e1ca2438d83fd351f3bf785ad60ce7842f889beea21d2fa7624
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 10f5c5bb5bca496e4f2f0957f304658181cec45277fea2bb91f9cd6c8d4901aaa937113a433821c2e728560e84fda5149d88ac5a27b18059452d8156e55b767b
|
|
7
|
+
data.tar.gz: a4b7d20d5fbcedd32fe347d418a5efddba8adb77fb11ac6caf9dcfb06ff4e7b3585035db905ccb1242c0b1581a2a5957d83b43d76f0fec89f01524d11351e704
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
|
+
## [0.3.2] - 2026-02-28
|
|
5
|
+
### Changed
|
|
6
|
+
- Refactors scope stack as an array of arrays in order to improve performance
|
|
7
|
+
|
|
8
|
+
## [0.3.1] - 2026-02-23
|
|
9
|
+
### Changed
|
|
10
|
+
- Removes operations validation pass for optimization purposes.
|
|
11
|
+
- Refactors Truthiness module for case/when in order to improve performance.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- Adds `Utils::DataHash` to differentiate context objects from rule objects allowing operation validity on the fly.
|
|
15
|
+
|
|
4
16
|
## [0.3.0] - 2026-02-21
|
|
5
17
|
### Changed
|
|
6
18
|
- Refactors internal architecture to improve performance by removing instantiation of operations.
|
data/Gemfile.lock
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "shiny_json_logic/operator_solver"
|
|
4
|
+
require "shiny_json_logic/utils/data_hash"
|
|
4
5
|
|
|
5
6
|
module ShinyJsonLogic
|
|
6
7
|
module Engine
|
|
7
8
|
OPERATIONS = OperatorSolver::SOLVERS
|
|
8
9
|
|
|
9
10
|
def self.call(rule, scope_stack)
|
|
10
|
-
if rule.is_a?(
|
|
11
|
+
if rule.is_a?(Utils::DataHash)
|
|
12
|
+
rule
|
|
13
|
+
elsif rule.is_a?(Hash)
|
|
11
14
|
return rule if rule.empty?
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
raise Errors::UnknownOperator if rule.size > 1
|
|
17
|
+
|
|
18
|
+
operation, args = rule.first
|
|
14
19
|
operation_key = operation.to_s
|
|
15
|
-
|
|
16
|
-
return rule unless OPERATIONS.key?(operation_key)
|
|
17
20
|
|
|
18
|
-
OPERATIONS.
|
|
21
|
+
raise Errors::UnknownOperator unless OPERATIONS.key?(operation_key)
|
|
22
|
+
|
|
23
|
+
OPERATIONS[operation_key].call(args, scope_stack)
|
|
19
24
|
elsif rule.is_a?(Array)
|
|
20
25
|
rule.map { |val| call(val, scope_stack) }
|
|
21
26
|
else
|
|
@@ -24,3 +29,4 @@ module ShinyJsonLogic
|
|
|
24
29
|
end
|
|
25
30
|
end
|
|
26
31
|
end
|
|
32
|
+
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "shiny_json_logic/operations/base"
|
|
4
|
+
require "shiny_json_logic/utils/data_hash"
|
|
4
5
|
|
|
5
6
|
module ShinyJsonLogic
|
|
6
7
|
module Operations
|
|
@@ -10,7 +11,7 @@ module ShinyJsonLogic
|
|
|
10
11
|
|
|
11
12
|
# {"val": []} or {"val": null} - return current scope
|
|
12
13
|
if raw_keys.empty? || raw_keys == [nil]
|
|
13
|
-
return scope_stack.current
|
|
14
|
+
return Utils::DataHash.wrap(scope_stack.current)
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
# Check if first element is an array (scope navigation syntax)
|
|
@@ -23,13 +24,13 @@ module ShinyJsonLogic
|
|
|
23
24
|
evaluated_keys = remaining_keys.map { |rule| evaluate(rule, scope_stack) }
|
|
24
25
|
|
|
25
26
|
levels = level_indicator.abs
|
|
26
|
-
return scope_stack.resolve(levels, *evaluated_keys)
|
|
27
|
+
return Utils::DataHash.wrap(scope_stack.resolve(levels, *evaluated_keys))
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
# Normal case: {"val": "key"} or {"val": ["key1", "key2"]}
|
|
30
31
|
keys = raw_keys.map { |rule| evaluate(rule, scope_stack) }
|
|
31
32
|
current_data = scope_stack.current
|
|
32
|
-
dig_value(current_data, keys)
|
|
33
|
+
Utils::DataHash.wrap(dig_value(current_data, keys))
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def self.dig_value(data, keys)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "shiny_json_logic/truthy"
|
|
4
4
|
require "shiny_json_logic/operations/base"
|
|
5
|
+
require "shiny_json_logic/utils/data_hash"
|
|
5
6
|
|
|
6
7
|
module ShinyJsonLogic
|
|
7
8
|
module Operations
|
|
@@ -12,10 +13,13 @@ module ShinyJsonLogic
|
|
|
12
13
|
default = items[1] ? evaluate(items[1], scope_stack) : nil
|
|
13
14
|
current_data = scope_stack.current
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
if key.nil? || key == ""
|
|
17
|
+
return Utils::DataHash.wrap(current_data)
|
|
18
|
+
end
|
|
16
19
|
|
|
17
20
|
result = fetch_value(current_data, key)
|
|
18
|
-
result.nil? ? default : result
|
|
21
|
+
result = result.nil? ? default : result
|
|
22
|
+
Utils::DataHash.wrap(result)
|
|
19
23
|
rescue
|
|
20
24
|
default || scope_stack.current
|
|
21
25
|
end
|
|
@@ -21,12 +21,12 @@ module ShinyJsonLogic
|
|
|
21
21
|
|
|
22
22
|
def initialize(root_data)
|
|
23
23
|
@root_data = root_data
|
|
24
|
-
@stack = [
|
|
24
|
+
@stack = [[@root_data, 0]]
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# Push a new scope onto the stack (when entering an iteration)
|
|
28
28
|
def push(data, index: 0)
|
|
29
|
-
stack.push(
|
|
29
|
+
stack.push([data, index])
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
# Pop the top scope (when exiting an iteration)
|
|
@@ -36,7 +36,7 @@ module ShinyJsonLogic
|
|
|
36
36
|
|
|
37
37
|
# Returns the current scope's data (top of stack)
|
|
38
38
|
def current
|
|
39
|
-
stack.last[
|
|
39
|
+
stack.last[0]
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# Resolve a value by going up n levels and then accessing keys
|
|
@@ -51,7 +51,7 @@ module ShinyJsonLogic
|
|
|
51
51
|
scope = stack[target_index]
|
|
52
52
|
return nil unless scope
|
|
53
53
|
|
|
54
|
-
data = scope[
|
|
54
|
+
data = scope[0]
|
|
55
55
|
|
|
56
56
|
if keys.empty?
|
|
57
57
|
data
|
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
module ShinyJsonLogic
|
|
6
6
|
module Truthy
|
|
7
7
|
def self.call(subject)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
case subject
|
|
9
|
+
when true, false then subject
|
|
10
|
+
when Numeric then !subject.zero?
|
|
11
|
+
when String then !subject.empty?
|
|
12
|
+
when Array then subject.any?
|
|
13
|
+
when Hash then !subject.empty?
|
|
14
|
+
when NilClass then false
|
|
15
|
+
else true
|
|
16
|
+
end
|
|
15
17
|
end
|
|
16
18
|
end
|
|
17
19
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShinyJsonLogic
|
|
4
|
+
module Utils
|
|
5
|
+
# A Hash subclass that marks its contents as user data (result of var/val).
|
|
6
|
+
# The Engine uses this to skip operator validation for these values.
|
|
7
|
+
class DataHash < Hash
|
|
8
|
+
def self.wrap(obj)
|
|
9
|
+
return obj unless obj.is_a?(Hash)
|
|
10
|
+
return obj if obj.is_a?(DataHash)
|
|
11
|
+
|
|
12
|
+
result = new
|
|
13
|
+
obj.each { |k, v| result[k] = v }
|
|
14
|
+
result
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/shiny_json_logic.rb
CHANGED
|
@@ -11,17 +11,17 @@ require "shiny_json_logic/scope_stack"
|
|
|
11
11
|
|
|
12
12
|
module ShinyJsonLogic
|
|
13
13
|
def self.apply(rule, data = {})
|
|
14
|
-
validate_operators!(rule)
|
|
15
|
-
|
|
16
14
|
normalized_data = deep_stringify_keys(data || {})
|
|
17
15
|
scope_stack = ScopeStack.new(normalized_data)
|
|
18
16
|
Engine.call(rule, scope_stack)
|
|
19
17
|
end
|
|
20
18
|
|
|
21
|
-
# Recursively converts all hash keys to strings
|
|
19
|
+
# Recursively converts all hash keys to strings.
|
|
20
|
+
# Fast path: if all keys are already strings, skip the copy.
|
|
22
21
|
def self.deep_stringify_keys(obj)
|
|
23
22
|
case obj
|
|
24
23
|
when Hash
|
|
24
|
+
return obj if obj.keys.all? { |k| k.is_a?(String) }
|
|
25
25
|
obj.each_with_object({}) do |(key, value), result|
|
|
26
26
|
result[key.to_s] = deep_stringify_keys(value)
|
|
27
27
|
end
|
|
@@ -31,31 +31,6 @@ module ShinyJsonLogic
|
|
|
31
31
|
obj
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
|
-
|
|
35
|
-
# Validates that all operations in the rule tree use known operators
|
|
36
|
-
def self.validate_operators!(rule)
|
|
37
|
-
case rule
|
|
38
|
-
when Hash
|
|
39
|
-
return if rule.empty?
|
|
40
|
-
|
|
41
|
-
# Multi-key hashes are invalid
|
|
42
|
-
if rule.size > 1
|
|
43
|
-
raise Errors::UnknownOperator
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
operation, args = rule.first
|
|
47
|
-
|
|
48
|
-
# Check if operation is known
|
|
49
|
-
unless OperatorSolver::SOLVERS.key?(operation.to_s)
|
|
50
|
-
raise Errors::UnknownOperator
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Recursively validate args
|
|
54
|
-
validate_operators!(args)
|
|
55
|
-
when Array
|
|
56
|
-
rule.each { |item| validate_operators!(item) }
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
34
|
end
|
|
60
35
|
|
|
61
36
|
JsonLogic = ShinyJsonLogic
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: shiny_json_logic
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Luis Moyano
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-02-
|
|
11
|
+
date: 2026-02-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -177,6 +177,7 @@ files:
|
|
|
177
177
|
- lib/shiny_json_logic/scope_stack.rb
|
|
178
178
|
- lib/shiny_json_logic/truthy.rb
|
|
179
179
|
- lib/shiny_json_logic/utils/array.rb
|
|
180
|
+
- lib/shiny_json_logic/utils/data_hash.rb
|
|
180
181
|
- lib/shiny_json_logic/version.rb
|
|
181
182
|
- results/ruby.json
|
|
182
183
|
- shiny_json_logic.gemspec
|