nexus-standard 1.0.1 → 1.1.0
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 +33 -0
- data/nxs.rb +179 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 715afe41048d9af9f236d0d07833c900181aa85ca9572b0c739ce1bfe701c5df
|
|
4
|
+
data.tar.gz: 976755cd67a6a2d74660d090be9408fdab3e0f04c5ed3065e67d2733ce7aa519
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e1cf8eba02ffa4db83adfdfbbbc0f96c27e4b70872e9bedfca288ae4d560346c3b4853fd11c0dc6bb908c97e043202bbe680a8d8e0686f2419bc3cc7648a5c1e
|
|
7
|
+
data.tar.gz: 7e49fe80a28de4ab2cc4829e3877c8b8b1e1e40b45e93e0b2a68ca554226643eb07daf546221b63ef0fec59f0d1acd71beb13197ef8f5e2488646804b2468737
|
data/README.md
CHANGED
|
@@ -96,6 +96,39 @@ ruby bench_c.rb ../js/fixtures # C extension vs JSON
|
|
|
96
96
|
| `ext/nxs/extconf.rb` | Extension build configuration |
|
|
97
97
|
| `ext/build.sh` | Compiles the C extension |
|
|
98
98
|
|
|
99
|
+
## Query engine
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
require_relative 'nxs'
|
|
103
|
+
|
|
104
|
+
data = File.binread("data.nxb")
|
|
105
|
+
reader = Nxs::Reader.new(data)
|
|
106
|
+
|
|
107
|
+
# Count matching records
|
|
108
|
+
n = reader.where(Nxs::Eq.new("active", true) & Nxs::Gt.new("score", 80.0)).count
|
|
109
|
+
|
|
110
|
+
# Iterate — yields Nxs::Object
|
|
111
|
+
reader.where(Nxs::Eq.new("active", true)).each do |obj|
|
|
112
|
+
puts obj.get_str("username")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# First match or nil
|
|
116
|
+
first = reader.where(Nxs::Gt.new("score", 99.0)).first
|
|
117
|
+
|
|
118
|
+
# All records
|
|
119
|
+
reader.all.each { |obj| ... }
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Predicates
|
|
123
|
+
|
|
124
|
+
| Class | Matches |
|
|
125
|
+
|-------|---------|
|
|
126
|
+
| `Eq.new(key, value)` | equality — String, Integer, Float, boolean |
|
|
127
|
+
| `Gt.new(key, v)` / `Lt.new(key, v)` | numeric comparison |
|
|
128
|
+
| `p1 & p2` / `p1 \| p2` / `~p` | And / Or / Not via operator overloads |
|
|
129
|
+
|
|
130
|
+
`Query` includes `Enumerable` — all `map`, `select`, `reject` etc. are available.
|
|
131
|
+
|
|
99
132
|
---
|
|
100
133
|
|
|
101
134
|
For the format specification see [`SPEC.md`](../SPEC.md). For cross-language examples see [`GETTING_STARTED.md`](../GETTING_STARTED.md).
|
data/nxs.rb
CHANGED
|
@@ -251,6 +251,185 @@ module Nxs
|
|
|
251
251
|
end
|
|
252
252
|
end
|
|
253
253
|
|
|
254
|
+
# ── Query engine ─────────────────────────────────────────────────────────────
|
|
255
|
+
|
|
256
|
+
# Base predicate — supports & | ~ operator overloading.
|
|
257
|
+
class Predicate
|
|
258
|
+
def &(other) = And.new(self, other)
|
|
259
|
+
def |(other) = Or.new(self, other)
|
|
260
|
+
def ~@ = Not.new(self)
|
|
261
|
+
def call(_record) = raise NotImplementedError, "#{self.class}#call not implemented"
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Eq(key, value) — equality for String, Integer, Float, or boolean.
|
|
265
|
+
class Eq < Predicate
|
|
266
|
+
def initialize(key, value)
|
|
267
|
+
super()
|
|
268
|
+
@key = key
|
|
269
|
+
@value = value
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def call(record) = record[@key] == @value
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Gt(key, number) — numeric greater-than.
|
|
276
|
+
class Gt < Predicate
|
|
277
|
+
def initialize(key, value)
|
|
278
|
+
super()
|
|
279
|
+
@key = key
|
|
280
|
+
@value = value
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def call(record)
|
|
284
|
+
v = record[@key]
|
|
285
|
+
v.is_a?(Numeric) && v > @value
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Lt(key, number) — numeric less-than.
|
|
290
|
+
class Lt < Predicate
|
|
291
|
+
def initialize(key, value)
|
|
292
|
+
super()
|
|
293
|
+
@key = key
|
|
294
|
+
@value = value
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def call(record)
|
|
298
|
+
v = record[@key]
|
|
299
|
+
v.is_a?(Numeric) && v < @value
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# And(p1, p2) — conjunction.
|
|
304
|
+
class And < Predicate
|
|
305
|
+
def initialize(a, b)
|
|
306
|
+
super()
|
|
307
|
+
@a = a
|
|
308
|
+
@b = b
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def call(record) = @a.call(record) && @b.call(record)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Or(p1, p2) — disjunction.
|
|
315
|
+
class Or < Predicate
|
|
316
|
+
def initialize(a, b)
|
|
317
|
+
super()
|
|
318
|
+
@a = a
|
|
319
|
+
@b = b
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def call(record) = @a.call(record) || @b.call(record)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Not(p) — negation.
|
|
326
|
+
class Not < Predicate
|
|
327
|
+
def initialize(inner)
|
|
328
|
+
super()
|
|
329
|
+
@inner = inner
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def call(record) = !@inner.call(record)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# ── Record proxy ─────────────────────────────────────────────────────────────
|
|
336
|
+
|
|
337
|
+
# Thin hash-like wrapper around Nxs::Object so predicates can use record[key].
|
|
338
|
+
# Values are fetched lazily and memoised per field access.
|
|
339
|
+
class RecordProxy
|
|
340
|
+
def initialize(obj, reader)
|
|
341
|
+
@obj = obj
|
|
342
|
+
@reader = reader
|
|
343
|
+
@cache = {}
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# Reset to a new underlying object, clearing the field cache.
|
|
347
|
+
# Used by Query#each to reuse a single RecordProxy instance across iterations.
|
|
348
|
+
def reset(obj)
|
|
349
|
+
@obj = obj
|
|
350
|
+
@cache = {}
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def [](key)
|
|
354
|
+
return @cache[key] if @cache.key?(key)
|
|
355
|
+
|
|
356
|
+
slot = @reader.key_index[key]
|
|
357
|
+
unless slot
|
|
358
|
+
@cache[key] = nil
|
|
359
|
+
return nil
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
sigil = @reader.key_sigils[slot]
|
|
363
|
+
val = case sigil
|
|
364
|
+
when 0x22 then @obj.get_str(key) # '"' string
|
|
365
|
+
when 0x3D then @obj.get_i64(key) # '=' i64
|
|
366
|
+
when 0x7E then @obj.get_f64(key) # '~' f64
|
|
367
|
+
when 0x3F then @obj.get_bool(key) # '?' bool
|
|
368
|
+
end
|
|
369
|
+
@cache[key] = val
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# ── Query ─────────────────────────────────────────────────────────────────────
|
|
374
|
+
|
|
375
|
+
# Lazy filtered view over a Reader. Created via reader.where(pred) or reader.all.
|
|
376
|
+
#
|
|
377
|
+
# Includes Enumerable, so map/select/min/max etc. all work automatically.
|
|
378
|
+
# count and first are overridden for clarity (Enumerable would work too).
|
|
379
|
+
class Query
|
|
380
|
+
include Enumerable
|
|
381
|
+
|
|
382
|
+
def initialize(reader, pred = nil)
|
|
383
|
+
@reader = reader
|
|
384
|
+
@pred = pred
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Yield each matching Nxs::Object to the block.
|
|
388
|
+
def each
|
|
389
|
+
n = @reader.record_count
|
|
390
|
+
pred = @pred
|
|
391
|
+
proxy = RecordProxy.new(nil, @reader)
|
|
392
|
+
i = 0
|
|
393
|
+
while i < n
|
|
394
|
+
obj = @reader.record(i)
|
|
395
|
+
if pred.nil?
|
|
396
|
+
yield obj
|
|
397
|
+
else
|
|
398
|
+
proxy.reset(obj)
|
|
399
|
+
yield obj if pred.call(proxy)
|
|
400
|
+
end
|
|
401
|
+
i += 1
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# Number of matching records (no block form; delegates to Enumerable when block given).
|
|
406
|
+
def count(&blk)
|
|
407
|
+
return super if blk
|
|
408
|
+
|
|
409
|
+
n = 0
|
|
410
|
+
each { n += 1 }
|
|
411
|
+
n
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# First matching record, or nil.
|
|
415
|
+
def first
|
|
416
|
+
find { true }
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# ── Reader extensions ────────────────────────────────────────────────────────
|
|
421
|
+
|
|
422
|
+
class Reader
|
|
423
|
+
# Returns a Query filtered by pred.
|
|
424
|
+
def where(pred) = Query.new(self, pred)
|
|
425
|
+
|
|
426
|
+
# Returns a Query over all records.
|
|
427
|
+
def all = Query.new(self)
|
|
428
|
+
|
|
429
|
+
# Expose key_sigils for RecordProxy
|
|
430
|
+
attr_reader :key_sigils
|
|
431
|
+
end
|
|
432
|
+
|
|
254
433
|
# ── Object ───────────────────────────────────────────────────────────────────
|
|
255
434
|
|
|
256
435
|
class Object
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nexus-standard
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Micael Malta
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: |
|
|
14
14
|
Pure-Ruby reader for NXB files produced by the NXS compiler. Provides
|