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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: df6cbb1873a1d4237a634ab972f9da4f2e1b56012f0e9353e34c723746f75655
4
- data.tar.gz: b62ce7480cdbab13df690836b3c837b96b360d08fb6cd0c86ee8e743222234bc
3
+ metadata.gz: 47eab10babd10e80ceeda0d5f5dc857c73a69ae7f36c5a8b09c71984e02c4243
4
+ data.tar.gz: 21adb41b87070e1ca2438d83fd351f3bf785ad60ce7842f889beea21d2fa7624
5
5
  SHA512:
6
- metadata.gz: cb846733714d95672c98d615c1474582e5516d13e02becbe13a9839d7571d7f2792235595777464b53c5e4173ea8af49d5d721d09283bfa72ade52f231bb74f7
7
- data.tar.gz: 121d271417ace9b145dfc92861e7857ff8c22eb859a7a8d9f2d8ff6b37708a1ee3c55a22f60cf925c1826c41622012848a2a54dd3f4afc9b8084b4ae6424a8e1
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,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shiny_json_logic (0.3.0)
4
+ shiny_json_logic (0.3.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -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?(Hash)
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
- operation, args = rule.to_a.first
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.fetch(operation_key).call(args, scope_stack)
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
- return current_data if key.nil? || key == ""
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 = [{ data: @root_data, index: 0 }]
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({ data: data, index: index })
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[:data]
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[:data]
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
- return subject if [true, false].include? subject
9
- return !subject.zero? if subject.is_a? Numeric
10
- return subject.any? if subject.is_a? Array
11
- return !subject.empty? if subject.is_a? String
12
- return subject.keys.any? if subject.is_a? Hash
13
-
14
- !subject.nil?
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShinyJsonLogic
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.2"
5
5
  end
@@ -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.0
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-21 00:00:00.000000000 Z
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