json-logic-rb 0.1.1.beta2 → 0.1.1

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: c6eafce983f8ec5d721d323a038d0e2c739d9e616069ec703847cd9a010bae2a
4
- data.tar.gz: 1a57bc686fab6a2cee116c072b62eb07a6856faa19f04b1ee1f3eb8b66ceaa8e
3
+ metadata.gz: bc614e1da0fa7f6a40304c0e988508dc71f67b2f65ded67e3ecdbf5be6b4c2ce
4
+ data.tar.gz: 2e80d861339668970a908e3ffbc6f3ea7562daa51850ce9ce90a1eb35b6ca598
5
5
  SHA512:
6
- metadata.gz: a4afb877d45472c538008605048a0ba09800cb14f2c85336ffb4ee31093a7e8a08a5df7770091b58913009eb77bd88abf8a127621654e841b2eb3af47883333b
7
- data.tar.gz: 16650f3a7ec8cbcce5dc64e7fb634c269b4c95361ead60dfaa07efda47332704bf0ac0a660b6f7d4acfe2a0078955dff18367cb1958173a9643c6043bb14b063
6
+ metadata.gz: b28f05aeb82f7163634d49b7a1eec0e3988b68bd6981111f8ee94de43fc000293730740c116dcb3d757500e58c22672ef1b913199b4d1a901fd50c18fee0963f
7
+ data.tar.gz: f724dda9f6f0237144fac1255c9b0b20a848decb6981d6a3c19606375a5611c8367bed214e987ecb88b38c9cdaed1e73478ff17052a055b4b0249dfd1ff76bf4
data/README.md CHANGED
@@ -1,39 +1,30 @@
1
- # json-logic-rb
2
1
 
3
- Ruby implementation of [JsonLogic](https://jsonlogic.com/) — simple and extensible. Ships with a compliance runner for the official test suite.
2
+ # json-logic-rb
4
3
 
5
- <a href="#"><img alt="build" src="https://img.shields.io/github/actions/workflow/status/your-org/json-logic-rb/ci.yml?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>
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) Operators (default)](#1-operators-default)
14
- - [2) Lazy operators](#2-lazy--operators)
15
- - [Supported Built-in Operations](#supported-built-in-operations)
16
- - [Accessing Data](#accessing-data)
17
- - [Logic and Boolean Operations](#logic-and-boolean-operations)
18
- - [Numeric Operations](#numeric-operations)
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 (Builtin)](#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
- - [Contributing](#contributing)
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 kinds of operators** in this implementation. This mapping follows the official behavior described on [jsonlogic.com](https://jsonlogic.com).
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
- **Official docs:**
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) Lazy operators
67
+ ### 2. Lazy Operations
83
68
 
84
- Some operators must control **whether** and **when** their arguments are evaluated.
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
- - **Branching / boolean control** — `if`, `?:`, `and`, `or`, `var`
92
- Docs: Logic and Boolean Operations — [https://jsonlogic.com/operations.html#logic-and-boolean-operations](https://jsonlogic.com/operations.html#logic-and-boolean-operations)
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. The first argument is a rule that returns the list of items — evaluated **once** to a Ruby array.
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
- 3. For `reduce`, the current item is also available as `"current"`, and the running total as `"accumulator"` (this mirrors the reference usage in docs and tests).
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,253 @@ JsonLogic.apply(
127
110
 
128
111
  ### Why laziness matters?
129
112
 
130
- Lazy operators **prevent evaluation** of branches you do not need.
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
138
-
139
- # "if" evaluates only the 'then' branch when condition is true.
140
- # The 'else' branch with 1/0 is never evaluated.
141
- JsonLogic.apply({ "if" => [true, 42, { "/" => [1, 0] }] })
142
- # => 42
143
120
  ```
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
-
146
121
 
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
+ ### 1) Pick the operation type
175
+ Choose one of:
176
+ - **Default**
177
+ ```ruby
178
+ class JsonLogic::Operations::StartsWith < JsonLogic::Operation; end
179
+ ```
180
+ - **Lazy**
181
+ ```ruby
182
+ class JsonLogic::Operations::StartsWith < JsonLogic::LazyOperation; end
183
+ ```
147
184
 
148
- ---
185
+ See [§How](#how) for details.
149
186
 
150
- ## Supported Built-in Operations
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.)
187
+ ### 2) Enable JsonLogic Semantics (optional)
188
+ Enable semantics to mirror JsonLogic’s comparison/truthiness in Ruby:
216
189
 
217
- ---
190
+ ```ruby
191
+ using JsonLogic::Semantics
192
+ ```
218
193
 
219
- ## Extending (add your own operator)
220
194
 
221
- ### Operation Type
195
+ See [§JsonLogic Semantic](#jsonlogic-semantic) for details.
222
196
 
223
- Each operator is a class.
224
- - **Value operations** inherit `JsonLogic::Operation` (engine passes values).
197
+ ### 3) Create an Operation and provide a machine name
225
198
 
226
- - **Lazy operations** inherit `JsonLogic::LazyOperation` (engine passes raw sub‑rules).
199
+ Operation methods use a consistent call shape.
200
+
201
+ - The first parameter is the **array of operator arguments**.
202
+ - The second is the current **data**.
227
203
 
228
- - **Enumerable operations** inherit `JsonLogic::EnumerableOperation` (standardized data binding for per‑item rules).
204
+
229
205
 
230
- ### Guide
206
+ Thanks to Ruby’s destructuring, you can unpack the argument array right in the method signature.
231
207
 
232
- First, create the Class for you Operation based on it's type:
233
208
  ```ruby
234
209
  class JsonLogic::Operations::StartsWith < JsonLogic::Operation
235
- def self.op_name = "starts_with" # {"starts_with": [string, prefix]}
236
-
210
+ def self.op_name = "starts_with"
237
211
  def call((str, prefix), _data)
212
+ # str, prefix are ALREADY evaluated to Ruby values
238
213
  str.to_s.start_with?(prefix.to_s)
239
214
  end
240
215
  end
241
216
  ```
242
217
 
243
- Second, register your operation:
218
+ ### 4) Register the new operation
219
+
244
220
  ```ruby
245
221
  JsonLogic::Engine.default.registry.register(JsonLogic::Operations::StartsWith)
246
222
  ```
247
223
 
248
- Use it!
224
+ After registration, use it in rules:
225
+
226
+ ```json
227
+ { "starts_with": [ { "var": "email" }, "admin@" ] }
228
+ ```
229
+
230
+ ### Alternative — register a Proc/Lambda
231
+
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
+
249
258
  ```ruby
250
- rule = {
251
- "if" => [
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"
259
+ JsonLogic::DSL.register_proc("starts_with") do |(str, prefix), _data|
260
+ str.to_s.start_with?(prefix.to_s)
261
+ end
262
262
  ```
263
263
 
264
- ---
264
+ Later you can “promote” the it into a full class.
265
+
266
+ ## JsonLogic Semantic
265
267
 
266
- ## Public Interface
267
- Use the high-level facade to evaluate a JsonLogic rule against input and get a plain Ruby value back.
268
- If you need more control (e.g., custom operator sets or multiple engines), use `JsonLogic::Engine`
268
+ All supported Operations follow JsonLogic semantics.
269
269
 
270
+ ### Comparisons
271
+ 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.
272
+
273
+ **JavaScript:**
274
+
275
+ ```js
276
+ 1 >= "1.0" // true
277
+ ```
278
+
279
+ **Ruby:**
270
280
 
271
281
  ```ruby
272
- # Main facade
273
- JsonLogic.apply(rule, data = nil) # => value
282
+ 1 >= "1.0"
283
+ # ArgumentError: comparison of Integer with String failed
284
+ ```
274
285
 
275
- # Engine/Registry (advanced)
276
- engine = JsonLogic::Engine.default
277
- engine.evaluate(rule, data)
286
+ **Ruby (with JsonLogic semantics enabled):**
278
287
 
279
- # Register a custom op class (auto‑registration is also supported)
280
- engine.registry.register(JsonLogic::Operations::StartsWith)
288
+ ```ruby
289
+ using JsonLogic::Semantics
290
+
291
+ 1 >= "1.0" # => true
292
+ ```
293
+
294
+ ### Truthiness
295
+
296
+ JsonLogic’s truthiness differs from Ruby’s (see <https://jsonlogic.com/truthy.html>).
297
+ In Ruby, only `false` and `nil` are falsey. In JsonLogic empty strings and empty arrays are also falsey.
298
+
299
+ **In Ruby:**
300
+ ```ruby
301
+ !![]
302
+ # => true
303
+ ```
304
+
305
+ While JsonLogic as was mentioned before has it's own truthiness:
306
+
307
+ **In Ruby (with JsonLogic Semantic):**
308
+
309
+ ```ruby
310
+ include JsonLogic::Semantics
311
+
312
+ truthy?([])
313
+ # => false
281
314
  ```
282
315
 
283
- ---
284
316
 
285
317
  ## Compliance and tests
318
+
286
319
  Optional: quick self-test
287
320
 
321
+
322
+
288
323
  ```bash
289
324
  ruby test/selftest.rb
290
325
  ```
291
326
 
327
+
292
328
  Official test suite
329
+
293
330
  1. Fetch the official suite
294
331
 
332
+
333
+
295
334
  ```bash
296
335
  mkdir -p spec/tmp
297
336
  curl -fsSL https://jsonlogic.com/tests.json -o spec/tmp/tests.json
298
337
  ```
338
+
299
339
  2. Run it
340
+
300
341
  ```bash
301
342
  ruby script/compliance.rb spec/tmp/tests.json
302
343
  ```
303
344
 
304
- Expected output
345
+ Expected output
346
+
305
347
  ```bash
306
348
  # => Compliance: X/X passed
307
349
  ```
308
350
 
309
- ---
310
-
311
351
  ## Security
312
352
 
313
353
  - Rules are **data**, not code; no Ruby eval.
314
- - When evaluating untrusted rules, consider adding a timeout and error handling at the call site.
315
- - Custom operations should be **pure** (no IO, no network, no shell).
354
+ - Operations are **pure** (no IO, no network, no shell).
355
+ - Rules have **no write** access to anything.
356
+
357
+ ## License
316
358
 
359
+ MIT — see [LICENSE](LICENSE).
317
360
 
318
361
  ## Authors
319
362
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using JsonLogic::Semantics
4
+
3
5
  class JsonLogic::Operations::All < JsonLogic::EnumerableOperation
4
6
  def self.op_name = "all"
5
7
 
@@ -8,7 +10,7 @@ class JsonLogic::Operations::All < JsonLogic::EnumerableOperation
8
10
  return false if items.empty?
9
11
 
10
12
  items.all? do |item|
11
- truthy?(JsonLogic.apply(rule_applied_to_each_item, item))
13
+ !!JsonLogic.apply(rule_applied_to_each_item, item)
12
14
  end
13
15
  end
14
16
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using JsonLogic::Semantics
4
+
3
5
  class JsonLogic::Operations::And < JsonLogic::LazyOperation
4
6
  def self.op_name = "and"
5
7
 
@@ -7,7 +9,7 @@ class JsonLogic::Operations::And < JsonLogic::LazyOperation
7
9
  last = nil
8
10
  args.each do |a|
9
11
  last = JsonLogic.apply(a, data)
10
- return last unless truthy?(last)
12
+ return last unless !!last
11
13
  end
12
14
  last
13
15
  end
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using JsonLogic::Semantics
3
4
  class JsonLogic::Operations::BoolCast < JsonLogic::Operation
4
5
  def self.op_name = "!!"
5
6
 
6
7
  def call((a), _data)
7
- truthy?(a)
8
+ !!a
8
9
  end
9
10
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using JsonLogic::Semantics
4
+
3
5
  class JsonLogic::Operations::Filter < JsonLogic::EnumerableOperation
4
6
  def self.op_name = "filter"
5
7
 
@@ -7,7 +9,7 @@ class JsonLogic::Operations::Filter < JsonLogic::EnumerableOperation
7
9
  items, rule_applied_to_each_item = resolve_items_and_per_item_rule(args, data)
8
10
 
9
11
  items.filter do |item|
10
- truthy?(JsonLogic.apply(rule_applied_to_each_item, item))
12
+ !!JsonLogic.apply(rule_applied_to_each_item, item)
11
13
  end
12
14
  end
13
15
  end
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using JsonLogic::Semantics
4
+
3
5
  class JsonLogic::Operations::If < JsonLogic::LazyOperation
4
6
  def self.op_name = "if"
5
7
 
6
8
  def call(args, data)
7
9
  i = 0
8
10
  while i < args.size - 1
9
- return JsonLogic.apply(args[i + 1], data) if truthy?(JsonLogic.apply(args[i], data))
11
+ return JsonLogic.apply(args[i + 1], data) if !!(JsonLogic.apply(args[i], data))
10
12
  i += 2
11
13
  end
12
14
  return JsonLogic.apply(args[-1], data) if args.size.odd?
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using JsonLogic::Semantics
4
+
3
5
  class JsonLogic::Operations::None < JsonLogic::EnumerableOperation
4
6
  def self.op_name = "none"
5
7
 
6
8
  def call(args, data)
7
9
  items, rule_applied_to_each_item = resolve_items_and_per_item_rule(args, data)
8
10
  items.none? do |item|
9
- truthy?(JsonLogic.apply(rule_applied_to_each_item, item))
11
+ !!(JsonLogic.apply(rule_applied_to_each_item, item))
10
12
  end
11
13
  end
12
14
  end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using JsonLogic::Semantics
4
+
3
5
  class JsonLogic::Operations::Not < JsonLogic::Operation
4
6
  def self.op_name = "!";
5
7
 
6
- def call((a), _data) = !truthy?(a)
8
+ def call((a), _data) = !a
7
9
  end
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
+
3
+ using JsonLogic::Semantics
2
4
  class JsonLogic::Operations::Or < JsonLogic::LazyOperation
3
5
  def self.op_name = "or"
4
6
 
5
7
  def call(args, data)
6
8
  args.each do |a|
7
9
  v = JsonLogic.apply(a, data)
8
- return v if truthy?(v)
10
+ return v if !!v
9
11
  end
10
12
 
11
13
  args.empty? ? nil : JsonLogic.apply(args.last, data)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: tru
2
2
 
3
+ using JsonLogic::Semantics
4
+
3
5
  class JsonLogic::Operations::Some < JsonLogic::EnumerableOperation
4
6
  def self.op_name = "some"
5
7
 
@@ -8,7 +10,7 @@ class JsonLogic::Operations::Some < JsonLogic::EnumerableOperation
8
10
  return false if items.empty?
9
11
 
10
12
  items.any? do |item|
11
- truthy?(JsonLogic.apply(rule_applied_to_each_item, item))
13
+ !!JsonLogic.apply(rule_applied_to_each_item, item)
12
14
  end
13
15
  end
14
16
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using JsonLogic::Semantics
4
+
3
5
  class JsonLogic::Operations::StrictNotEqual < JsonLogic::Operation
4
6
  def self.op_name = "!=="
5
7
 
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using JsonLogic::Semantics
4
+
3
5
  class JsonLogic::Operations::Ternary < JsonLogic::LazyOperation
4
6
  def self.op_name = "?:"
5
7
 
6
8
  def call((cond_rule, then_rule, else_rule), data)
7
- if truthy?(JsonLogic.apply(cond_rule, data))
9
+ if !!JsonLogic.apply(cond_rule, data)
8
10
  JsonLogic.apply(then_rule, data)
9
11
  else
10
12
  JsonLogic.apply(else_rule, data)
@@ -6,11 +6,18 @@ module JsonLogic
6
6
 
7
7
  def truthy?(v)
8
8
  case v
9
- when nil then false
10
- when TrueClass, FalseClass then v
11
- when Numeric then !v.zero?
12
- when String, Array then !v.empty?
13
- else true
9
+ when nil
10
+ false
11
+ when TrueClass, FalseClass
12
+ v
13
+ when Numeric
14
+ v.zero? ? false : true
15
+ when String
16
+ v.empty? ? false : true
17
+ when Array
18
+ v.empty? ? false : true
19
+ else
20
+ true
14
21
  end
15
22
  end
16
23
 
@@ -24,7 +31,11 @@ module JsonLogic
24
31
  when String
25
32
  s = v.strip
26
33
  return 0.0 if s.empty?
27
- Float(s) rescue Float::NAN
34
+ begin
35
+ Float(s)
36
+ rescue ArgumentError
37
+ Float::NAN
38
+ end
28
39
  else
29
40
  Float::NAN
30
41
  end
@@ -32,11 +43,15 @@ module JsonLogic
32
43
 
33
44
  def eq(a, b)
34
45
  if a.class == b.class
35
- return false if a.is_a?(Numeric) && (a.to_f.nan? || b.to_f.nan?)
36
- return a.is_a?(Numeric) ? (a.to_f == b.to_f) : a.eql?(b)
46
+ if a.is_a?(Numeric)
47
+ return false if a.to_f.nan? || b.to_f.nan?
48
+ return a.to_f == b.to_f
49
+ else
50
+ return a.eql?(b)
51
+ end
37
52
  end
38
53
 
39
- if a == true || a == false || b == true || b == false
54
+ if a.is_a?(TrueClass) || a.is_a?(FalseClass) || b.is_a?(TrueClass) || b.is_a?(FalseClass)
40
55
  na = num(a); nb = num(b)
41
56
  return false if na.nan? || nb.nan?
42
57
  return na == nb
@@ -61,29 +76,29 @@ module JsonLogic
61
76
  end
62
77
  end
63
78
 
64
- [String, Integer, TrueClass, FalseClass, NilClass, Array].each do |klass|
65
- refine klass do
66
- def ==(other) = JsonLogic::Semantics.eq(self, other)
67
-
68
- def >(other)
69
- c = JsonLogic::Semantics.cmp(self, other)
70
- c && c == 1
71
- end
79
+ refine Object do
80
+ def !@
81
+ JsonLogic::Semantics.truthy?(self) ? false : true
82
+ end
72
83
 
73
- def >=(other)
74
- c = JsonLogic::Semantics.cmp(self, other)
75
- c && (c == 1 || c == 0)
76
- end
84
+ def to_bool
85
+ JsonLogic::Semantics.truthy?(self)
86
+ end
87
+ end
77
88
 
78
- def <(other)
79
- c = JsonLogic::Semantics.cmp(self, other)
80
- c && c == -1
81
- end
89
+ [String, Integer, Float].each do |klass|
90
+ refine klass do
91
+ def ==(other) = JsonLogic::Semantics.eq(self, other)
92
+ def >(other) = (c = JsonLogic::Semantics.cmp(self, other)) && c == 1
93
+ def >=(other) = (c = JsonLogic::Semantics.cmp(self, other)) && (c == 1 || c == 0)
94
+ def <(other) = (c = JsonLogic::Semantics.cmp(self, other)) && c == -1
95
+ def <=(other) = (c = JsonLogic::Semantics.cmp(self, other)) && (c == -1 || c == 0)
96
+ end
97
+ end
82
98
 
83
- def <=(other)
84
- c = JsonLogic::Semantics.cmp(self, other)
85
- c && (c == -1 || c == 0)
86
- end
99
+ [Array, TrueClass, FalseClass, NilClass].each do |klass|
100
+ refine klass do
101
+ def ==(other) = JsonLogic::Semantics.eq(self, other)
87
102
  end
88
103
  end
89
104
  end
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module JsonLogic; VERSION = '0.1.1.beta2'; end
3
+ module JsonLogic; VERSION = '0.1.1'; end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json-logic-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1.beta2
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tavrel Kate