mq-ruby 0.1.28 → 0.1.30

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.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +1977 -0
  3. data/Cargo.toml +7 -3
  4. data/README.md +44 -3
  5. data/lib/mq/query.rb +97 -0
  6. metadata +2 -1
data/Cargo.toml CHANGED
@@ -10,7 +10,7 @@ name = "mq-ruby"
10
10
  publish = false
11
11
  readme = "README.md"
12
12
  repository = "https://github.com/harehare/mq"
13
- version = "0.1.28"
13
+ version = "0.1.30"
14
14
 
15
15
  [lib]
16
16
  crate-type = ["cdylib"]
@@ -18,6 +18,10 @@ name = "mq_ruby"
18
18
 
19
19
  [dependencies]
20
20
  magnus = {version = "0.8"}
21
- mq-lang = "0.6.4"
22
- mq-markdown = "0.6.4"
21
+ mq-lang = {version = "0.7.0", features = ["css-selector"]}
22
+ mq-markdown = "0.7.0"
23
+ # Pinned below the rb-sys releases (0.9.120+) that added Ruby 4.x-specific
24
+ # ABI handling, which is buggy on Ruby 4.0 under magnus 0.8.2 and breaks
25
+ # TypedData class conversion (see MQ::Result CI failures on Ruby 4.0).
26
+ rb-sys = "=0.9.117"
23
27
 
data/README.md CHANGED
@@ -254,6 +254,7 @@ MQ::Query.list.map { contains("important") }
254
254
  .to_html # to_html() — HTML string
255
255
  .to_string # to_string() — string coercion
256
256
  .to_number # to_number() — numeric coercion
257
+ .to_boolean # to_boolean() — boolean coercion
257
258
  .to_array # to_array()
258
259
  .to_bytes # to_bytes()
259
260
  .to_markdown_string # to_markdown_string()
@@ -267,22 +268,34 @@ MQ::Query.list.map { contains("important") }
267
268
  .rtrim # rtrim()
268
269
  .downcase # downcase()
269
270
  .upcase # upcase()
271
+ .ascii_downcase # ascii_downcase()
272
+ .ascii_upcase # ascii_upcase()
270
273
  .len # len()
271
274
  .utf8bytelen # utf8bytelen()
272
275
  .explode # explode() — string to codepoints
273
276
  .implode # implode() — codepoints to string
274
277
  .url_encode # url_encode()
278
+ .url_decode # url_decode()
275
279
  .intern # intern()
280
+ .html_escape # html_escape()
281
+ .html_unescape # html_unescape()
282
+ .sanitize_html # sanitize_html() — strip dangerous tags/attrs
283
+ .strip_tags # strip_tags() — remove all HTML tags
276
284
 
277
285
  .split(",") # split(",")
278
286
  .gsub("pat", "r") # gsub("pat", "r") — regex replace all
279
287
  .replace("a", "b") # replace("a", "b") — literal replace
280
288
  .test("\\d+") # test("\\d+") — regex test → bool
281
289
  .capture("(\\w+)") # capture("(\\w+)") — regex capture
290
+ .scan("(\\w+)") # scan("(\\w+)") — all regex matches
282
291
  .slice(0, 5) # slice(0, 5)
283
292
  .index("sub") # index("sub") — position of substring
284
293
  .rindex("sub") # rindex("sub") — last position
285
294
  .repeat(3) # repeat(3)
295
+ .word_wrap(20) # word_wrap(20) — wrap text to column width
296
+ .truncate(10, "…") # truncate(10, "…") — shorten with an ellipsis
297
+ .token_count # token_count() — estimate LLM token count
298
+ .token_count("gpt-4") # token_count("gpt-4")
286
299
  ```
287
300
 
288
301
  #### Collection Operations
@@ -309,6 +322,8 @@ MQ::Query.list.map { contains("important") }
309
322
  .range(3) # range(3)
310
323
  .del("key") # del("key")
311
324
  .insert(0, "val") # insert(0, "val")
325
+ .shuffle # shuffle() — random order
326
+ .sample(3) # sample(3) — n random elements, no replacement
312
327
  ```
313
328
 
314
329
  #### Math Operations
@@ -350,8 +365,18 @@ MQ::Query.list.map { contains("important") }
350
365
  .sha512 # sha512()
351
366
  .from_hex # from_hex()
352
367
  .to_hex # to_hex()
368
+ .uuid # uuid() — random (v4) UUID
369
+ .uuid_v4 # uuid_v4() — alias of uuid
370
+ .uuid_v7 # uuid_v7() — time-ordered (v7) UUID
371
+ .rand # rand() — pseudo-random number in [0, 1)
372
+ .rand_int(1, 10) # rand_int(1, 10) — pseudo-random integer in [min, max]
353
373
  ```
354
374
 
375
+ `MQ::Query.uuid`, `.uuid_v4`, `.uuid_v7`, `.rand`, `.rand_int(min, max)`, and
376
+ `.random_string(len, charset)` are also available as class-level generators to
377
+ start a query without a preceding selector, e.g. `MQ::Query.uuid.to_query # =>
378
+ "uuid()"`.
379
+
355
380
  #### Path Operations
356
381
 
357
382
  ```ruby
@@ -360,14 +385,30 @@ MQ::Query.list.map { contains("important") }
360
385
  .extname # extname()
361
386
  .stem # stem()
362
387
  .path_join("sub") # path_join("sub")
388
+ .glob_match("docs/readme.md") # glob_match("docs/readme.md") — self is the glob pattern
389
+ ```
390
+
391
+ #### HTML / CSS Selector Operations
392
+
393
+ Query a raw HTML string directly with a CSS selector, bypassing Markdown
394
+ conversion — useful for tags/classes/ids/`data-*` attributes that would
395
+ otherwise be lost.
396
+
397
+ ```ruby
398
+ .css("p.intro") # css("p.intro") — outer HTML of matches
399
+ .css_text("p.intro") # css_text("p.intro") — text content of matches
400
+ .css_attr("a", "href") # css_attr("a", "href") — attribute value of matches
363
401
  ```
364
402
 
365
403
  #### Dict Operations
366
404
 
367
405
  ```ruby
368
- .get("key") # get("key")
369
- .set("key", "val") # set("key", "val")
370
- .property("key") # ."key"
406
+ .get("key") # get("key")
407
+ .set("key", "val") # set("key", "val")
408
+ .property("key") # ."key"
409
+ .get_path(["a", "b"]) # get_path(["a", "b"])
410
+ .set_path(["a", "b"], "val") # set_path(["a", "b"], "val")
411
+ .paths # paths() — array of leaf-path arrays
371
412
  ```
372
413
 
373
414
  #### Markdown Attribute Operations
data/lib/mq/query.rb CHANGED
@@ -66,6 +66,7 @@ module MQ
66
66
  def to_html = pipe_with("to_html()")
67
67
  def to_string = pipe_with("to_string()")
68
68
  def to_number = pipe_with("to_number()")
69
+ def to_boolean = pipe_with("to_boolean()")
69
70
  def to_array = pipe_with("to_array()")
70
71
  def to_bytes = pipe_with("to_bytes()")
71
72
  def to_markdown_string = pipe_with("to_markdown_string()")
@@ -135,15 +136,42 @@ module MQ
135
136
  pipe_with("repeat(#{n})")
136
137
  end
137
138
 
139
+ def shuffle = pipe_with("shuffle()")
140
+
141
+ def sample(n)
142
+ pipe_with("sample(#{n})")
143
+ end
144
+
138
145
  def trim = pipe_with("trim()")
139
146
  def ltrim = pipe_with("ltrim()")
140
147
  def rtrim = pipe_with("rtrim()")
141
148
  def downcase = pipe_with("downcase()")
142
149
  def upcase = pipe_with("upcase()")
150
+ def ascii_downcase = pipe_with("ascii_downcase()")
151
+ def ascii_upcase = pipe_with("ascii_upcase()")
143
152
  def explode = pipe_with("explode()")
144
153
  def implode = pipe_with("implode()")
145
154
  def url_encode = pipe_with("url_encode()")
155
+ def url_decode = pipe_with("url_decode()")
146
156
  def intern = pipe_with("intern()")
157
+ def html_escape = pipe_with("html_escape()")
158
+ def html_unescape = pipe_with("html_unescape()")
159
+ def sanitize_html = pipe_with("sanitize_html()")
160
+ def strip_tags = pipe_with("strip_tags()")
161
+
162
+ def word_wrap(width)
163
+ pipe_with("word_wrap(#{width})")
164
+ end
165
+
166
+ def truncate(len, ellipsis)
167
+ pipe_with("truncate(#{len}, #{ellipsis.inspect})")
168
+ end
169
+
170
+ # Estimates (or, when mq was built with tiktoken support, exactly counts) the
171
+ # LLM tokens the current string would consume. +model+ is optional.
172
+ def token_count(model = nil)
173
+ model ? pipe_with("token_count(#{model.inspect})") : pipe_with("token_count()")
174
+ end
147
175
 
148
176
  def gsub(pattern, replacement)
149
177
  pipe_with("gsub(#{pattern.inspect}, #{replacement.inspect})")
@@ -161,6 +189,10 @@ module MQ
161
189
  pipe_with("capture(#{pattern.inspect})")
162
190
  end
163
191
 
192
+ def scan(pattern)
193
+ pipe_with("scan(#{pattern.inspect})")
194
+ end
195
+
164
196
  def abs = pipe_with("abs()")
165
197
  def ceil = pipe_with("ceil()")
166
198
  def floor = pipe_with("floor()")
@@ -205,6 +237,15 @@ module MQ
205
237
  def to_hex = pipe_with("to_hex()")
206
238
  def to_hex_str = pipe_with("to_hex()")
207
239
 
240
+ def uuid = pipe_with("uuid()")
241
+ def uuid_v4 = pipe_with("uuid_v4()")
242
+ def uuid_v7 = pipe_with("uuid_v7()")
243
+ def rand = pipe_with("rand()")
244
+
245
+ def rand_int(min, max)
246
+ pipe_with("rand_int(#{min}, #{max})")
247
+ end
248
+
208
249
  def basename = pipe_with("basename()")
209
250
  def dirname = pipe_with("dirname()")
210
251
  def extname = pipe_with("extname()")
@@ -214,6 +255,30 @@ module MQ
214
255
  pipe_with("path_join(#{other.inspect})")
215
256
  end
216
257
 
258
+ # Treats the current string as a glob pattern (e.g. "*.md", "docs/**/*.rs")
259
+ # and returns whether it matches +path+.
260
+ def glob_match(path)
261
+ pipe_with("glob_match(#{path.inspect})")
262
+ end
263
+
264
+ # Returns the outer HTML of every element in the current (raw) HTML string
265
+ # matching the CSS +selector+, as an array of strings.
266
+ def css(selector)
267
+ pipe_with("css(#{selector.inspect})")
268
+ end
269
+
270
+ # Returns the text content of every element in the current (raw) HTML
271
+ # string matching the CSS +selector+, as an array of strings.
272
+ def css_text(selector)
273
+ pipe_with("css_text(#{selector.inspect})")
274
+ end
275
+
276
+ # Returns the value of attribute +name+ for every element in the current
277
+ # (raw) HTML string matching the CSS +selector+, as an array.
278
+ def css_attr(selector, name)
279
+ pipe_with("css_attr(#{selector.inspect}, #{name.inspect})")
280
+ end
281
+
217
282
  def get(key)
218
283
  pipe_with("get(#{key.inspect})")
219
284
  end
@@ -222,6 +287,22 @@ module MQ
222
287
  pipe_with("set(#{key.inspect}, #{val.inspect})")
223
288
  end
224
289
 
290
+ # Retrieves a nested value by following an array of keys/indices.
291
+ def get_path(path)
292
+ pipe_with("get_path(#{path.inspect})")
293
+ end
294
+
295
+ # Sets a nested value by following an array of keys/indices, creating
296
+ # missing intermediate dicts/arrays automatically.
297
+ def set_path(path, new_value)
298
+ pipe_with("set_path(#{path.inspect}, #{new_value.inspect})")
299
+ end
300
+
301
+ # Returns an array of leaf-path arrays for the current value.
302
+ def paths
303
+ pipe_with("paths()")
304
+ end
305
+
225
306
  # Access a dict property by key (generates ."key" selector)
226
307
  def property(key)
227
308
  pipe_with(".\"#{key}\"")
@@ -453,6 +534,22 @@ module MQ
453
534
 
454
535
  def to_text = new("to_text()")
455
536
  def to_markdown = new("to_markdown()")
537
+
538
+ # --- Value generators (usable as query starting points) ---
539
+ def uuid = new("uuid()")
540
+ def uuid_v4 = new("uuid_v4()")
541
+ def uuid_v7 = new("uuid_v7()")
542
+ def rand = new("rand()")
543
+
544
+ def rand_int(min, max)
545
+ new("rand_int(#{min}, #{max})")
546
+ end
547
+
548
+ # Returns a random string of +len+ characters drawn (with replacement)
549
+ # from +charset+.
550
+ def random_string(len, charset)
551
+ new("random_string(#{len}, #{charset.inspect})")
552
+ end
456
553
  end
457
554
 
458
555
  private
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mq-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.28
4
+ version: 0.1.30
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takahiro Sato
@@ -72,6 +72,7 @@ extensions:
72
72
  - extconf.rb
73
73
  extra_rdoc_files: []
74
74
  files:
75
+ - Cargo.lock
75
76
  - Cargo.toml
76
77
  - LICENSE
77
78
  - README.md