json-logic-rb 0.1.1.beta3 → 0.1.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/README.md +202 -166
- 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/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: d726f7f3bf1bd6cb21653d3764b60bb78d1d1e098f0f0f42cc821dbf3f5da9b1
|
|
4
|
+
data.tar.gz: 3a3816024e8d4ae47bced0a6a04abd92bdab15bd8f134c616d2f8333759f8f27
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9a83a929d74d39baf96a5721206e1d4935c5b432876d734a3b6d83e912224b60a5e1e2051242522cdfa25014c11138f3b7dbff9c13c7c01e26d6be11127da679
|
|
7
|
+
data.tar.gz: 4dd0db70c83bf78025d2c44c96f4e1cdbb1a3f9884b7f3e8f5031e1393d7cc3b2575979894393a37e15f804dfdec0e6dc5b3c1e7822d94681de61bbdedba88c2
|
data/README.md
CHANGED
|
@@ -1,39 +1,30 @@
|
|
|
1
|
-
# json-logic-rb
|
|
2
1
|
|
|
3
|
-
|
|
2
|
+
# json-logic-rb
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
Ruby implementation of [JsonLogic](https://jsonlogic.com/) — simple and extensible. Ships with a compliance runner for the official test suite.
|
|
6
5
|
|
|
6
|
+
<a href="#"><img alt="build" src="https://img.shields.io/github/actions/workflow/status/your-org/json-logic-rb/ci-complience?branch=main"> <a href="https://rubygems.org/gems/json-logic-rb"><img alt="rubygems" src="https://img.shields.io/gem/v/json-logic-rb"></a> <a href="LICENSE"><img alt="license" src="https://img.shields.io/badge/license-MIT-informational"></a>
|
|
7
7
|
|
|
8
8
|
## Table of Contents
|
|
9
9
|
- [What](#what)
|
|
10
10
|
- [Install](#install)
|
|
11
11
|
- [Quick start](#quick-start)
|
|
12
12
|
- [How](#how)
|
|
13
|
-
- [1
|
|
14
|
-
- [2
|
|
15
|
-
- [Supported Built
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- [Array Operations](#array-operations)
|
|
20
|
-
- [String Operations](#string-operations)
|
|
21
|
-
- [Miscellaneous](#miscellaneous)
|
|
22
|
-
- [Extending (add your own operator)](#extending-add-your-own-operator)
|
|
23
|
-
- [Public Interface](#public-interface)
|
|
24
|
-
- [Compliance & tests](#compliance-and-tests)
|
|
13
|
+
- [1. Default Operations](#1-default-operations)
|
|
14
|
+
- [2. Lazy Operations](#2-lazy-operations)
|
|
15
|
+
- [Supported Operations (Built‑in)](#supported-operations-built-in)
|
|
16
|
+
- [Adding Operations](#adding-operations)
|
|
17
|
+
- [JsonLogic Semantic](#jsonlogic-semantic)
|
|
18
|
+
- [Compliance and tests](#compliance-and-tests)
|
|
25
19
|
- [Security](#security)
|
|
26
20
|
- [License](#license)
|
|
27
|
-
- [
|
|
28
|
-
|
|
21
|
+
- [Authors](#authors)
|
|
29
22
|
|
|
23
|
+
---
|
|
30
24
|
|
|
31
25
|
## What
|
|
32
26
|
JsonLogic rules are JSON trees. The engine walks that tree and returns a Ruby value.
|
|
33
27
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
28
|
## Install
|
|
38
29
|
|
|
39
30
|
```bash
|
|
@@ -58,54 +49,42 @@ JsonLogic.apply({ "var" => "user.age" }, { "user" => { "age" => 42 } })
|
|
|
58
49
|
# => 42
|
|
59
50
|
```
|
|
60
51
|
|
|
61
|
-
|
|
62
52
|
## How
|
|
63
53
|
|
|
64
|
-
There are **two
|
|
65
|
-
|
|
66
|
-
### 1) Operators (default)
|
|
67
|
-
|
|
68
|
-
For **operators**, the engine **evaluates all arguments first** and then calls the operator with the **resulting Ruby values**.
|
|
69
|
-
This matches the reference behavior for arithmetic, comparisons, string operations, and other pure operators that do not control evaluation order.
|
|
54
|
+
There are **two types of operations** in this implementation: Default Operations and Lazy Operations.
|
|
70
55
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
- Numeric Operations — [https://jsonlogic.com/operations.html#numeric-operations](https://jsonlogic.com/operations.html#numeric-operations)
|
|
74
|
-
|
|
75
|
-
- String Operations — [https://jsonlogic.com/operations.html#string-operations](https://jsonlogic.com/operations.html#string-operations)
|
|
76
|
-
|
|
77
|
-
- Array Operations (simple transforms like `merge`, membership `in`) — [https://jsonlogic.com/operations.html#array-operations](https://jsonlogic.com/operations.html#array-operations)
|
|
56
|
+
### 1. Default Operations
|
|
78
57
|
|
|
58
|
+
For **Default Operations**, the engine **evaluates all arguments first** and then calls the operator with the **resulting Ruby values**.
|
|
59
|
+
This matches the reference behavior for arithmetic, comparisons, string operations, and other pure operations that do not control evaluation order.
|
|
79
60
|
|
|
61
|
+
**Groups and references:**
|
|
80
62
|
|
|
63
|
+
- [Numeric operations](https://jsonlogic.com/operations.html#numeric-operations)
|
|
64
|
+
- [String operations](https://jsonlogic.com/operations.html#string-operations)
|
|
65
|
+
- [Array operations](https://jsonlogic.com/operations.html#array-operations) — simple transforms like `merge`, membership `in`.
|
|
81
66
|
|
|
82
|
-
### 2
|
|
67
|
+
### 2. Lazy Operations
|
|
83
68
|
|
|
84
|
-
Some
|
|
85
|
-
They implement branching, short-circuiting, or “apply a rule per item” semantics.
|
|
86
|
-
For these **lazy operators**, the engine passes **raw sub-rules** and current `data`.
|
|
87
|
-
The operator then evaluates only the sub-rules it actually needs.
|
|
69
|
+
Some operations must control **whether** and **when** their arguments are evaluated. They implement branching, short-circuiting, or “apply a rule per item” semantics. For these **Lazy Operations**, the engine passes **raw sub-rules** and current data. The operator then evaluates only the sub-rules it actually needs.
|
|
88
70
|
|
|
89
71
|
**Groups and references:**
|
|
90
72
|
|
|
91
|
-
-
|
|
92
|
-
|
|
93
|
-
Truthiness table used by these operators — [https://jsonlogic.com/truthy.html](https://jsonlogic.com/truthy.html)
|
|
94
|
-
|
|
95
|
-
- **Enumerable operators** — `map`, `filter`, `reduce`, `all`, `none`, `some`
|
|
96
|
-
Docs: Array Operations — [https://jsonlogic.com/operations.html#array-operations](https://jsonlogic.com/operations.html#array-operations)
|
|
73
|
+
- **Branching / boolean control** — `if`, `?:`, `and`, `or`, `var`
|
|
74
|
+
[Logic & boolean operations](https://jsonlogic.com/operations.html#logic-and-boolean-operations) • [Truthiness](https://jsonlogic.com/truthy.html)
|
|
97
75
|
|
|
76
|
+
- **Enumerable operators** — `map`, `filter`, `reduce`, `all`, `none`, `some`
|
|
77
|
+
[Array operations](https://jsonlogic.com/operations.html#array-operations)
|
|
98
78
|
|
|
99
|
-
**How per-item evaluation works:**
|
|
79
|
+
**How enumerable per-item evaluation works:**
|
|
100
80
|
|
|
101
|
-
1.
|
|
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
|
+
3. For `reduce`, the current item is also available as `"current"`, and the running total as `"accumulator"`.
|
|
102
84
|
|
|
103
|
-
2. The second argument is the per-item rule — evaluated **for each item** with that item as the **current root**.
|
|
104
85
|
|
|
105
|
-
|
|
86
|
+
**Example #1**
|
|
106
87
|
|
|
107
|
-
|
|
108
|
-
**Examples**
|
|
109
88
|
```ruby
|
|
110
89
|
# filter: keep numbers >= 2
|
|
111
90
|
JsonLogic.apply(
|
|
@@ -113,7 +92,11 @@ JsonLogic.apply(
|
|
|
113
92
|
{ "ints" => [1,2,3] }
|
|
114
93
|
)
|
|
115
94
|
# => [2, 3]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Example #2**
|
|
116
98
|
|
|
99
|
+
```ruby
|
|
117
100
|
# reduce: sum using "current" and "accumulator"
|
|
118
101
|
JsonLogic.apply(
|
|
119
102
|
{ "reduce" => [
|
|
@@ -127,193 +110,246 @@ JsonLogic.apply(
|
|
|
127
110
|
|
|
128
111
|
### Why laziness matters?
|
|
129
112
|
|
|
130
|
-
Lazy
|
|
131
|
-
If division by zero raised an error (hypothetically), lazy control would avoid it:
|
|
113
|
+
Lazy operations **prevent evaluation** of branches you do not need. If division by zero raised an error (hypothetically), lazy control would avoid it:
|
|
132
114
|
|
|
133
115
|
```ruby
|
|
134
116
|
# "or" short-circuits: 1 is truthy, so the right side is NOT evaluated.
|
|
135
117
|
# If the right side were evaluated eagerly, it would attempt 1/0 (error).
|
|
136
118
|
JsonLogic.apply({ "or" => [1, { "/" => [1, 0] }] })
|
|
137
119
|
# => 1
|
|
120
|
+
```
|
|
138
121
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
122
|
+
> In this gem `/` returns `nil` on divide‑by‑zero, but these examples show **why** lazy evaluation is required by the spec: branching and boolean operators must **not** evaluate unused branches.
|
|
123
|
+
|
|
124
|
+
## Supported Operations (Built‑in)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
Below is a list that mirrors the sections on [jsonlogic.com/operations.html](https://jsonlogic.com/operations.html) and shows what this gem (library) implements. From the reference page’s list, everything except `log` is implemented.
|
|
128
|
+
|
|
129
|
+
| Operator | Supported |
|
|
130
|
+
|---|---:|
|
|
131
|
+
| `var` | ✅ |
|
|
132
|
+
| `missing` | ✅ |
|
|
133
|
+
| `missing_some` | ✅ |
|
|
134
|
+
|[Logic and Boolean Operations](https://jsonlogic.com/operations.html#logic-and-boolean-operations])
|
|
135
|
+
| `if` | ✅ |
|
|
136
|
+
| `==` | ✅ |
|
|
137
|
+
| `===` | ✅ |
|
|
138
|
+
| `!=` | ✅ |
|
|
139
|
+
| `!==` | ✅ |
|
|
140
|
+
| `!` | ✅ |
|
|
141
|
+
| `!!` | ✅ |
|
|
142
|
+
| `or` | ✅ |
|
|
143
|
+
| `and` | ✅ |
|
|
144
|
+
| `?:` | ✅ |
|
|
145
|
+
|[Numeric Operations](https://jsonlogic.com/operations.html#numeric-operations)|
|
|
146
|
+
| `map` | ✅ |
|
|
147
|
+
| `reduce` | ✅ |
|
|
148
|
+
| `filter` | ✅ |
|
|
149
|
+
| `all` | ✅ |
|
|
150
|
+
| `none` | ✅ |
|
|
151
|
+
| `some` | ✅ |
|
|
152
|
+
| `merge` | ✅ |
|
|
153
|
+
| `in` | ✅ |
|
|
154
|
+
|[Array Operations](https://jsonlogic.com/operations.html#array-operations)|
|
|
155
|
+
| `map` | ✅ |
|
|
156
|
+
| `reduce` | ✅ |
|
|
157
|
+
| `filter` | ✅ |
|
|
158
|
+
| `all` | ✅ |
|
|
159
|
+
| `none` | ✅ |
|
|
160
|
+
| `some` | ✅ |
|
|
161
|
+
| `merge` | ✅ |
|
|
162
|
+
| `in` | ✅ |
|
|
163
|
+
|[String Operations](https://jsonlogic.com/operations.html#string-operations)|
|
|
164
|
+
| `in` | ✅ |
|
|
165
|
+
| `cat` | ✅ |
|
|
166
|
+
| `substr` | ✅ |
|
|
167
|
+
|Miscellaneous|
|
|
168
|
+
| `log` | 🚫 |
|
|
169
|
+
|
|
170
|
+
## Adding Operations
|
|
171
|
+
|
|
172
|
+
Need a custom operation? It’s straightforward.
|
|
173
|
+
|
|
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] })
|
|
142
186
|
# => 42
|
|
143
187
|
```
|
|
144
|
-
>In this gem (library) `/` returns `nil` on divide-by-zero, but these examples show **why** lazy evaluation is required by the spec: branching and boolean operators must **not** evaluate unused branches.
|
|
145
188
|
|
|
189
|
+
Is useful for rapid prototyping with minimal boilerplate;
|
|
190
|
+
Later you can “promote” it into a full class or use additional features.
|
|
146
191
|
|
|
147
192
|
|
|
148
|
-
|
|
193
|
+
### 1) Pick the Operation type
|
|
194
|
+
Choose one of:
|
|
195
|
+
- **Default**
|
|
196
|
+
```ruby
|
|
197
|
+
class JsonLogic::Operations::StartsWith < JsonLogic::Operation; end
|
|
198
|
+
```
|
|
199
|
+
For anonymous functions:
|
|
200
|
+
```ruby
|
|
201
|
+
JsonLogic.add_operation("starts_with", lazy: false) do; end
|
|
202
|
+
```
|
|
203
|
+
- **Lazy**
|
|
204
|
+
```ruby
|
|
205
|
+
class JsonLogic::Operations::StartsWith < JsonLogic::LazyOperation; end
|
|
206
|
+
```
|
|
207
|
+
For anonymous functions:
|
|
208
|
+
```ruby
|
|
209
|
+
JsonLogic.add_operation("starts_with", lazy: true) do; end
|
|
210
|
+
```
|
|
149
211
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
Below is a checklist that mirrors the sections on [**jsonlogic.com/operations.html**](https://jsonlogic.com/operations.html) and shows what this gem (library) implements.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
### Accessing Data
|
|
156
|
-
| Operator | Supported | Notes |
|
|
157
|
-
|---|---:|---|
|
|
158
|
-
| `var` | ✅ | |
|
|
159
|
-
| `missing` | ✅ | Returns array of missing keys; works with complex sources (e.g. `merge` result). |
|
|
160
|
-
| `missing_some` | ✅ | Returns `[]` when the minimum is met, otherwise missing keys. |
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
### [Logic and Boolean Operations](https://jsonlogic.com/operations.html#logic-and-boolean-operations])
|
|
164
|
-
| Operator | Supported | Notes |
|
|
165
|
-
|---|---:|---|
|
|
166
|
-
| `if` | ✅ | |
|
|
167
|
-
| `==` | ✅ | |
|
|
168
|
-
| `===` | ✅ | Strict equality (same type and value). |
|
|
169
|
-
| `!=` | ✅ | |
|
|
170
|
-
| `!==` | ✅ | |
|
|
171
|
-
| `!` | ✅ | Follows JsonLogic truthiness. |
|
|
172
|
-
| `!!` | ✅ | Follows JsonLogic truthiness. |
|
|
173
|
-
| `or` | ✅ | Returns first truthy / last value; |
|
|
174
|
-
| `and` | ✅ | Returns first falsy / last value; |
|
|
175
|
-
| `?:` | ✅ | Returns the truth. |
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
### [Numeric Operations](https://jsonlogic.com/operations.html#numeric-operations)
|
|
179
|
-
| Operator / Topic | Supported | Notes |
|
|
180
|
-
|---|---:|---|
|
|
181
|
-
| `>` `>=` `<` `<=` | ✅ | |
|
|
182
|
-
| Between (pattern) | ✅ | Use `<`/`<=` with 3 args (not a separate op). |
|
|
183
|
-
| `max` / `min` | ✅ | |
|
|
184
|
-
| `+` `-` `*` `/` | ✅ | Unary `-` negates; unary `+` casts to number; `/` returns `nil` on divide‑by‑zero. |
|
|
185
|
-
| `%` | ✅ | |
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
### [Array Operations](https://jsonlogic.com/operations.html#array-operations)
|
|
189
|
-
| Operator | Supported | Notes |
|
|
190
|
-
|---|---:|---|
|
|
191
|
-
| `map` | ✅ | |
|
|
192
|
-
| `reduce` | ✅ | Per‑item rule sees `"current"` and `"accumulator"`. |
|
|
193
|
-
| `filter` | ✅ | Keeps items where per‑item rule is truthy (follows JsonLogic truthiness). |
|
|
194
|
-
| `all` | ✅ | `false` on empty; all per‑item are truthy (follows JsonLogic truthiness). |
|
|
195
|
-
| `none` | ✅ | `true` on empty; none per‑item are truthy (follows JsonLogic truthiness). |
|
|
196
|
-
| `some` | ✅ | `false` on empty; any per‑item is truthy (follows JsonLogic truthiness). |
|
|
197
|
-
| `merge` | ✅ | Flattens arguments into a single array; non‑arrays cast to singleton arrays. |
|
|
198
|
-
| `in` | ✅ | If second arg is an array: membership test. |
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
### [String Operations](https://jsonlogic.com/operations.html#string-operations)
|
|
202
|
-
| Operator | Supported | Notes |
|
|
203
|
-
|---|---:|---|
|
|
204
|
-
| `in` | ✅ | If second arg is a string: substring test. |
|
|
205
|
-
| `cat` | ✅ | Concatenate arguments as strings (no delimiter). |
|
|
206
|
-
| `substr` | ✅ | Start index can be negative; length can be omitted or negative (mirrors the official behavior). |
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
### Miscellaneous
|
|
210
|
-
| Operator | Supported | Notes |
|
|
211
|
-
|---|---:|---|
|
|
212
|
-
| `log` | 🚫 | Not implemented by default (and not part of the compliance tests). |
|
|
213
|
-
|
|
214
|
-
**Summary:** From the reference page’s list, everything except `log` is implemented.
|
|
215
|
-
(“Between” is not a standalone operator, but the `<`/`<=` 3‑argument form is supported.)
|
|
212
|
+
See [§How](#how) for details.
|
|
216
213
|
|
|
217
|
-
|
|
214
|
+
### 2) Enable JsonLogic Semantics (optional)
|
|
215
|
+
Enable semantics to mirror JsonLogic’s comparison/truthiness in Ruby:
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
using JsonLogic::Semantics
|
|
219
|
+
```
|
|
218
220
|
|
|
219
|
-
## Extending (add your own operator)
|
|
220
221
|
|
|
221
|
-
|
|
222
|
+
See [§JsonLogic Semantic](#jsonlogic-semantic) for details.
|
|
222
223
|
|
|
223
|
-
|
|
224
|
-
- **Value operations** inherit `JsonLogic::Operation` (engine passes values).
|
|
224
|
+
### 3) Create an Operation and provide a machine name
|
|
225
225
|
|
|
226
|
-
|
|
226
|
+
Operation methods use a consistent call shape.
|
|
227
227
|
|
|
228
|
-
-
|
|
228
|
+
- The first parameter is the **array of operator arguments**.
|
|
229
|
+
- The second is the current **data**.
|
|
229
230
|
|
|
230
|
-
### Guide
|
|
231
231
|
|
|
232
|
-
|
|
232
|
+
|
|
233
|
+
Thanks to Ruby’s destructuring, you can unpack the argument array right in the method signature.
|
|
234
|
+
|
|
233
235
|
```ruby
|
|
234
236
|
class JsonLogic::Operations::StartsWith < JsonLogic::Operation
|
|
235
|
-
def self.
|
|
236
|
-
|
|
237
|
+
def self.name = "starts_with"
|
|
237
238
|
def call((str, prefix), _data)
|
|
239
|
+
# str, prefix are ALREADY evaluated to Ruby values
|
|
238
240
|
str.to_s.start_with?(prefix.to_s)
|
|
239
241
|
end
|
|
240
242
|
end
|
|
241
243
|
```
|
|
242
244
|
|
|
243
|
-
|
|
245
|
+
### 4) Register the new operation
|
|
246
|
+
|
|
244
247
|
```ruby
|
|
245
248
|
JsonLogic::Engine.default.registry.register(JsonLogic::Operations::StartsWith)
|
|
246
249
|
```
|
|
247
250
|
|
|
248
|
-
|
|
251
|
+
After registration, use it in rules:
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{ "starts_with": [ { "var": "email" }, "admin@" ] }
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
## JsonLogic Semantic
|
|
260
|
+
|
|
261
|
+
All supported Operations follow JsonLogic semantics.
|
|
262
|
+
|
|
263
|
+
### Comparisons
|
|
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.
|
|
265
|
+
|
|
266
|
+
**JavaScript:**
|
|
267
|
+
|
|
268
|
+
```js
|
|
269
|
+
1 >= "1.0" // true
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Ruby:**
|
|
273
|
+
|
|
249
274
|
```ruby
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
{ "starts_with" => [ { "var" => "email" }, "admin@" ] },
|
|
253
|
-
"is_admin",
|
|
254
|
-
"regular_user"
|
|
255
|
-
]
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
p JsonLogic.apply(rule, { "email" => "admin@example.com" })
|
|
259
|
-
# => "is_admin"
|
|
260
|
-
p JsonLogic.apply(rule, { "email" => "user@example.com" })
|
|
261
|
-
# => "regular_user"
|
|
275
|
+
1 >= "1.0"
|
|
276
|
+
# ArgumentError: comparison of Integer with String failed
|
|
262
277
|
```
|
|
263
278
|
|
|
264
|
-
|
|
279
|
+
**Ruby (with JsonLogic semantics enabled):**
|
|
265
280
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
281
|
+
```ruby
|
|
282
|
+
using JsonLogic::Semantics
|
|
283
|
+
|
|
284
|
+
1 >= "1.0" # => true
|
|
285
|
+
```
|
|
269
286
|
|
|
287
|
+
### Truthiness
|
|
270
288
|
|
|
289
|
+
JsonLogic’s truthiness differs from Ruby’s (see <https://jsonlogic.com/truthy.html>).
|
|
290
|
+
In Ruby, only `false` and `nil` are falsey. In JsonLogic empty strings and empty arrays are also falsey.
|
|
291
|
+
|
|
292
|
+
**In Ruby:**
|
|
271
293
|
```ruby
|
|
272
|
-
|
|
273
|
-
|
|
294
|
+
!![]
|
|
295
|
+
# => true
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
While JsonLogic as was mentioned before has it's own truthiness:
|
|
274
299
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
300
|
+
**In Ruby (with JsonLogic Semantic):**
|
|
301
|
+
|
|
302
|
+
```ruby
|
|
303
|
+
include JsonLogic::Semantics
|
|
278
304
|
|
|
279
|
-
|
|
280
|
-
|
|
305
|
+
truthy?([])
|
|
306
|
+
# => false
|
|
281
307
|
```
|
|
282
308
|
|
|
283
|
-
---
|
|
284
309
|
|
|
285
310
|
## Compliance and tests
|
|
311
|
+
|
|
286
312
|
Optional: quick self-test
|
|
287
313
|
|
|
314
|
+
|
|
315
|
+
|
|
288
316
|
```bash
|
|
289
317
|
ruby test/selftest.rb
|
|
290
318
|
```
|
|
291
319
|
|
|
320
|
+
|
|
292
321
|
Official test suite
|
|
322
|
+
|
|
293
323
|
1. Fetch the official suite
|
|
294
324
|
|
|
325
|
+
|
|
326
|
+
|
|
295
327
|
```bash
|
|
296
328
|
mkdir -p spec/tmp
|
|
297
329
|
curl -fsSL https://jsonlogic.com/tests.json -o spec/tmp/tests.json
|
|
298
330
|
```
|
|
331
|
+
|
|
299
332
|
2. Run it
|
|
333
|
+
|
|
300
334
|
```bash
|
|
301
335
|
ruby script/compliance.rb spec/tmp/tests.json
|
|
302
336
|
```
|
|
303
337
|
|
|
304
|
-
Expected output
|
|
338
|
+
Expected output
|
|
339
|
+
|
|
305
340
|
```bash
|
|
306
341
|
# => Compliance: X/X passed
|
|
307
342
|
```
|
|
308
343
|
|
|
309
|
-
---
|
|
310
|
-
|
|
311
344
|
## Security
|
|
312
345
|
|
|
313
346
|
- Rules are **data**, not code; no Ruby eval.
|
|
314
|
-
-
|
|
315
|
-
-
|
|
347
|
+
- Operations are **pure** (no IO, no network, no shell).
|
|
348
|
+
- Rules have **no write** access to anything.
|
|
349
|
+
|
|
350
|
+
## License
|
|
316
351
|
|
|
352
|
+
MIT — see [LICENSE](LICENSE).
|
|
317
353
|
|
|
318
354
|
## Authors
|
|
319
355
|
|
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/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
|
|