json-logic-rb 0.1.1 → 0.1.3
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/README.md +42 -49
- data/lib/json_logic/operation.rb +1 -1
- data/lib/json_logic/operations/add.rb +1 -1
- data/lib/json_logic/operations/all.rb +1 -1
- data/lib/json_logic/operations/and.rb +1 -1
- data/lib/json_logic/operations/bool_cast.rb +1 -1
- data/lib/json_logic/operations/cat.rb +1 -1
- data/lib/json_logic/operations/div.rb +1 -1
- data/lib/json_logic/operations/equal.rb +1 -1
- data/lib/json_logic/operations/filter.rb +1 -1
- data/lib/json_logic/operations/gt.rb +1 -1
- data/lib/json_logic/operations/gte.rb +1 -1
- data/lib/json_logic/operations/if.rb +1 -1
- data/lib/json_logic/operations/in.rb +1 -1
- data/lib/json_logic/operations/lt.rb +1 -1
- data/lib/json_logic/operations/lte.rb +1 -1
- data/lib/json_logic/operations/map.rb +1 -1
- data/lib/json_logic/operations/max.rb +1 -1
- data/lib/json_logic/operations/merge.rb +1 -1
- data/lib/json_logic/operations/min.rb +1 -1
- data/lib/json_logic/operations/missing.rb +1 -1
- data/lib/json_logic/operations/missing_some.rb +1 -1
- data/lib/json_logic/operations/mod.rb +1 -1
- data/lib/json_logic/operations/mul.rb +1 -1
- data/lib/json_logic/operations/none.rb +1 -1
- data/lib/json_logic/operations/not.rb +1 -1
- data/lib/json_logic/operations/not_equal.rb +1 -1
- data/lib/json_logic/operations/or.rb +1 -1
- data/lib/json_logic/operations/reduce.rb +1 -1
- data/lib/json_logic/operations/some.rb +1 -1
- data/lib/json_logic/operations/strict_equal.rb +3 -3
- data/lib/json_logic/operations/strict_not_equal.rb +1 -1
- data/lib/json_logic/operations/sub.rb +1 -1
- data/lib/json_logic/operations/substr.rb +1 -1
- data/lib/json_logic/operations/ternary.rb +1 -1
- data/lib/json_logic/operations/var.rb +1 -1
- data/lib/json_logic/registry.rb +1 -1
- data/lib/json_logic/semantics.rb +1 -7
- data/lib/json_logic/version.rb +1 -1
- data/lib/json_logic.rb +13 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 975d191c041cd7afd3e3ab977d01944e00693ec7320e6418ae312d71afa67d7f
|
|
4
|
+
data.tar.gz: 9b871c8f3b53fbc5cf3e2dbd180a556e5815940c536da6e2c248bf8b66c9be8d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eef9bc4ce561c092cc5bd7885b9f69051e4aa0712dd15e93c92f4dd32c177570472a401e7bdce65565cee4ee8b80e3ebc3afaa46a0fab91cb1f730c83f0e1a83
|
|
7
|
+
data.tar.gz: fce35e13727e2d30e827c787c5543eb13005c38a59e44abcb8a340b486d1c814d4367d297dc20826b845dd7da98c193b2fa1bf0a521d6aa70bff2847da51eb6c
|
data/README.md
CHANGED
|
@@ -55,7 +55,7 @@ There are **two types of operations** in this implementation: Default Operations
|
|
|
55
55
|
|
|
56
56
|
### 1. Default Operations
|
|
57
57
|
|
|
58
|
-
For **Default Operations**, the engine **evaluates all arguments first** and then calls the operator with the **resulting Ruby values**.
|
|
58
|
+
For **Default Operations**, the engine **evaluates all arguments first** and then calls the operator with the **resulting Ruby values**.
|
|
59
59
|
This matches the reference behavior for arithmetic, comparisons, string operations, and other pure operations that do not control evaluation order.
|
|
60
60
|
|
|
61
61
|
**Groups and references:**
|
|
@@ -70,16 +70,16 @@ Some operations must control **whether** and **when** their arguments are evalua
|
|
|
70
70
|
|
|
71
71
|
**Groups and references:**
|
|
72
72
|
|
|
73
|
-
- **Branching / boolean control** — `if`, `?:`, `and`, `or`, `var`
|
|
73
|
+
- **Branching / boolean control** — `if`, `?:`, `and`, `or`, `var`
|
|
74
74
|
[Logic & boolean operations](https://jsonlogic.com/operations.html#logic-and-boolean-operations) • [Truthiness](https://jsonlogic.com/truthy.html)
|
|
75
75
|
|
|
76
|
-
- **Enumerable operators** — `map`, `filter`, `reduce`, `all`, `none`, `some`
|
|
76
|
+
- **Enumerable operators** — `map`, `filter`, `reduce`, `all`, `none`, `some`
|
|
77
77
|
[Array operations](https://jsonlogic.com/operations.html#array-operations)
|
|
78
78
|
|
|
79
79
|
**How enumerable per-item evaluation works:**
|
|
80
80
|
|
|
81
|
-
1. The first argument is a rule that returns the list of items — evaluated **once** to a Ruby array.
|
|
82
|
-
2. The second argument is the per-item rule — evaluated **for each item** with that item as the **current root**.
|
|
81
|
+
1. The first argument is a rule that returns the list of items — evaluated **once** to a Ruby array.
|
|
82
|
+
2. The second argument is the per-item rule — evaluated **for each item** with that item as the **current root**.
|
|
83
83
|
3. For `reduce`, the current item is also available as `"current"`, and the running total as `"accumulator"`.
|
|
84
84
|
|
|
85
85
|
|
|
@@ -131,7 +131,7 @@ Below is a list that mirrors the sections on [jsonlogic.com/operations.html](htt
|
|
|
131
131
|
| `var` | ✅ |
|
|
132
132
|
| `missing` | ✅ |
|
|
133
133
|
| `missing_some` | ✅ |
|
|
134
|
-
|[Logic and Boolean Operations](https://jsonlogic.com/operations.html#logic-and-boolean-operations])
|
|
134
|
+
|[Logic and Boolean Operations](https://jsonlogic.com/operations.html#logic-and-boolean-operations])
|
|
135
135
|
| `if` | ✅ |
|
|
136
136
|
| `==` | ✅ |
|
|
137
137
|
| `===` | ✅ |
|
|
@@ -171,16 +171,43 @@ Below is a list that mirrors the sections on [jsonlogic.com/operations.html](htt
|
|
|
171
171
|
|
|
172
172
|
Need a custom operation? It’s straightforward.
|
|
173
173
|
|
|
174
|
-
###
|
|
174
|
+
### Quick — register a Proc or Lambda
|
|
175
|
+
|
|
176
|
+
Register little anonymous functions, by passing a Proc or Lambda.
|
|
177
|
+
|
|
178
|
+
```ruby
|
|
179
|
+
JsonLogic.add_operation("times2") { |(value), _| value.to_i * 2 }
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Once the function added, you can use it in your logic.
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
JsonLogic.apply({ "times2" => [21] })
|
|
186
|
+
# => 42
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Is useful for rapid prototyping with minimal boilerplate;
|
|
190
|
+
Later you can “promote” it into a full class or use additional features.
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
### 1) Pick the Operation type
|
|
175
194
|
Choose one of:
|
|
176
195
|
- **Default**
|
|
177
196
|
```ruby
|
|
178
197
|
class JsonLogic::Operations::StartsWith < JsonLogic::Operation; end
|
|
179
198
|
```
|
|
199
|
+
For anonymous functions:
|
|
200
|
+
```ruby
|
|
201
|
+
JsonLogic.add_operation("starts_with", lazy: false) do; end
|
|
202
|
+
```
|
|
180
203
|
- **Lazy**
|
|
181
204
|
```ruby
|
|
182
205
|
class JsonLogic::Operations::StartsWith < JsonLogic::LazyOperation; end
|
|
183
206
|
```
|
|
207
|
+
For anonymous functions:
|
|
208
|
+
```ruby
|
|
209
|
+
JsonLogic.add_operation("starts_with", lazy: true) do; end
|
|
210
|
+
```
|
|
184
211
|
|
|
185
212
|
See [§How](#how) for details.
|
|
186
213
|
|
|
@@ -197,17 +224,17 @@ See [§JsonLogic Semantic](#jsonlogic-semantic) for details.
|
|
|
197
224
|
### 3) Create an Operation and provide a machine name
|
|
198
225
|
|
|
199
226
|
Operation methods use a consistent call shape.
|
|
200
|
-
|
|
227
|
+
|
|
201
228
|
- The first parameter is the **array of operator arguments**.
|
|
202
229
|
- The second is the current **data**.
|
|
203
230
|
|
|
204
|
-
|
|
231
|
+
|
|
205
232
|
|
|
206
233
|
Thanks to Ruby’s destructuring, you can unpack the argument array right in the method signature.
|
|
207
234
|
|
|
208
235
|
```ruby
|
|
209
236
|
class JsonLogic::Operations::StartsWith < JsonLogic::Operation
|
|
210
|
-
def self.
|
|
237
|
+
def self.name = "starts_with"
|
|
211
238
|
def call((str, prefix), _data)
|
|
212
239
|
# str, prefix are ALREADY evaluated to Ruby values
|
|
213
240
|
str.to_s.start_with?(prefix.to_s)
|
|
@@ -227,45 +254,11 @@ After registration, use it in rules:
|
|
|
227
254
|
{ "starts_with": [ { "var": "email" }, "admin@" ] }
|
|
228
255
|
```
|
|
229
256
|
|
|
230
|
-
### Alternative — register a Proc/Lambda
|
|
231
257
|
|
|
232
|
-
The public API is class‑oriented, but **technically** you can express an Operations as a `Proc`/`Lambda` and register it through a little anonymous functions.
|
|
233
|
-
|
|
234
|
-
DSL to register callables:
|
|
235
|
-
|
|
236
|
-
```ruby
|
|
237
|
-
module JsonLogic
|
|
238
|
-
module DSL
|
|
239
|
-
def self.register_proc(name, lazy: false, &block)
|
|
240
|
-
base = lazy ? JsonLogic::LazyOperation : JsonLogic::Operation
|
|
241
|
-
klass = Class.new(base) do
|
|
242
|
-
define_singleton_method(:op_name) { name.to_s }
|
|
243
|
-
define_method(:call) { |args, data| block.call(args, data) }
|
|
244
|
-
end
|
|
245
|
-
JsonLogic::Engine.default.registry.register(klass)
|
|
246
|
-
klass
|
|
247
|
-
end
|
|
248
|
-
end
|
|
249
|
-
end
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
Is useful for rapid prototyping with minimal boilerplate;
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
```ruby
|
|
259
|
-
JsonLogic::DSL.register_proc("starts_with") do |(str, prefix), _data|
|
|
260
|
-
str.to_s.start_with?(prefix.to_s)
|
|
261
|
-
end
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
Later you can “promote” the it into a full class.
|
|
265
258
|
|
|
266
259
|
## JsonLogic Semantic
|
|
267
260
|
|
|
268
|
-
All supported Operations follow JsonLogic semantics.
|
|
261
|
+
All supported Operations follow JsonLogic semantics.
|
|
269
262
|
|
|
270
263
|
### Comparisons
|
|
271
264
|
As JsonLogic primary developed in JavaScript it inherits JavaScript's type coercion in build-in Operations. JsonLogic (JS‑style) comparisons coerce types; Ruby does not.
|
|
@@ -293,7 +286,7 @@ using JsonLogic::Semantics
|
|
|
293
286
|
|
|
294
287
|
### Truthiness
|
|
295
288
|
|
|
296
|
-
JsonLogic’s truthiness differs from Ruby’s (see <https://jsonlogic.com/truthy.html>).
|
|
289
|
+
JsonLogic’s truthiness differs from Ruby’s (see <https://jsonlogic.com/truthy.html>).
|
|
297
290
|
In Ruby, only `false` and `nil` are falsey. In JsonLogic empty strings and empty arrays are also falsey.
|
|
298
291
|
|
|
299
292
|
**In Ruby:**
|
|
@@ -318,18 +311,18 @@ truthy?([])
|
|
|
318
311
|
|
|
319
312
|
Optional: quick self-test
|
|
320
313
|
|
|
321
|
-
|
|
314
|
+
|
|
322
315
|
|
|
323
316
|
```bash
|
|
324
317
|
ruby test/selftest.rb
|
|
325
318
|
```
|
|
326
319
|
|
|
327
|
-
|
|
320
|
+
|
|
328
321
|
Official test suite
|
|
329
322
|
|
|
330
323
|
1. Fetch the official suite
|
|
331
324
|
|
|
332
|
-
|
|
325
|
+
|
|
333
326
|
|
|
334
327
|
```bash
|
|
335
328
|
mkdir -p spec/tmp
|
data/lib/json_logic/operation.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class JsonLogic::Operations::Reduce < JsonLogic::EnumerableOperation
|
|
4
|
-
def self.
|
|
4
|
+
def self.name = "reduce"
|
|
5
5
|
|
|
6
6
|
def call(args, data)
|
|
7
7
|
rule_that_returns_items, step_rule_applied_per_item, rule_that_returns_initial_accumulator = args
|
data/lib/json_logic/registry.rb
CHANGED
data/lib/json_logic/semantics.rb
CHANGED
|
@@ -86,7 +86,7 @@ module JsonLogic
|
|
|
86
86
|
end
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
-
[String, Integer, Float].each do |klass|
|
|
89
|
+
[String, Integer, Float, NilClass, Array, TrueClass, FalseClass].each do |klass|
|
|
90
90
|
refine klass do
|
|
91
91
|
def ==(other) = JsonLogic::Semantics.eq(self, other)
|
|
92
92
|
def >(other) = (c = JsonLogic::Semantics.cmp(self, other)) && c == 1
|
|
@@ -95,11 +95,5 @@ module JsonLogic
|
|
|
95
95
|
def <=(other) = (c = JsonLogic::Semantics.cmp(self, other)) && (c == -1 || c == 0)
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
|
-
|
|
99
|
-
[Array, TrueClass, FalseClass, NilClass].each do |klass|
|
|
100
|
-
refine klass do
|
|
101
|
-
def ==(other) = JsonLogic::Semantics.eq(self, other)
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
98
|
end
|
|
105
99
|
end
|
data/lib/json_logic/version.rb
CHANGED
data/lib/json_logic.rb
CHANGED
|
@@ -14,10 +14,10 @@ module JsonLogic
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
# Load operation classes (each file defines one class with .
|
|
17
|
+
# Load operation classes (each file defines one class with .name)
|
|
18
18
|
Dir[File.join(__dir__, 'json_logic', 'operations', '*.rb')].sort.each { |f| require f }
|
|
19
19
|
|
|
20
|
-
# Auto-register all operation classes with .
|
|
20
|
+
# Auto-register all operation classes with .name
|
|
21
21
|
module JsonLogic
|
|
22
22
|
module Loader
|
|
23
23
|
module_function
|
|
@@ -25,7 +25,7 @@ module JsonLogic
|
|
|
25
25
|
def register_all!(registry)
|
|
26
26
|
ObjectSpace.each_object(Class) do |klass|
|
|
27
27
|
next unless klass < JsonLogic::Operation
|
|
28
|
-
next unless klass.respond_to?(:
|
|
28
|
+
next unless klass.respond_to?(:name) && klass.name && !klass.name.to_s.empty?
|
|
29
29
|
|
|
30
30
|
registry.register(klass)
|
|
31
31
|
end
|
|
@@ -36,6 +36,16 @@ module JsonLogic
|
|
|
36
36
|
def apply(rule, data = nil)
|
|
37
37
|
Engine.default.evaluate(rule, data)
|
|
38
38
|
end
|
|
39
|
+
|
|
40
|
+
def add_operation(name, lazy: false, &block)
|
|
41
|
+
base = lazy ? JsonLogic::LazyOperation : JsonLogic::Operation
|
|
42
|
+
klass = Class.new(base) do
|
|
43
|
+
define_singleton_method(:name) { name.to_s }
|
|
44
|
+
define_method(:call) { |args, data| block.call(args, data) }
|
|
45
|
+
end
|
|
46
|
+
JsonLogic::Engine.default.registry.register(klass)
|
|
47
|
+
klass
|
|
48
|
+
end
|
|
39
49
|
end
|
|
40
50
|
end
|
|
41
51
|
|