mq-ruby 0.1.29 → 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 +31 -6
  5. data/lib/mq/query.rb +64 -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.29"
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.5"
22
- mq-markdown = "0.6.5"
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
@@ -277,6 +277,10 @@ MQ::Query.list.map { contains("important") }
277
277
  .url_encode # url_encode()
278
278
  .url_decode # url_decode()
279
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
280
284
 
281
285
  .split(",") # split(",")
282
286
  .gsub("pat", "r") # gsub("pat", "r") — regex replace all
@@ -288,6 +292,10 @@ MQ::Query.list.map { contains("important") }
288
292
  .index("sub") # index("sub") — position of substring
289
293
  .rindex("sub") # rindex("sub") — last position
290
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")
291
299
  ```
292
300
 
293
301
  #### Collection Operations
@@ -364,9 +372,10 @@ MQ::Query.list.map { contains("important") }
364
372
  .rand_int(1, 10) # rand_int(1, 10) — pseudo-random integer in [min, max]
365
373
  ```
366
374
 
367
- `MQ::Query.uuid`, `.uuid_v4`, `.uuid_v7`, `.rand`, and `.rand_int(min, max)` are
368
- also available as class-level generators to start a query without a preceding
369
- selector, e.g. `MQ::Query.uuid.to_query # => "uuid()"`.
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()"`.
370
379
 
371
380
  #### Path Operations
372
381
 
@@ -376,14 +385,30 @@ selector, e.g. `MQ::Query.uuid.to_query # => "uuid()"`.
376
385
  .extname # extname()
377
386
  .stem # stem()
378
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
379
401
  ```
380
402
 
381
403
  #### Dict Operations
382
404
 
383
405
  ```ruby
384
- .get("key") # get("key")
385
- .set("key", "val") # set("key", "val")
386
- .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
387
412
  ```
388
413
 
389
414
  #### Markdown Attribute Operations
data/lib/mq/query.rb CHANGED
@@ -154,6 +154,24 @@ module MQ
154
154
  def url_encode = pipe_with("url_encode()")
155
155
  def url_decode = pipe_with("url_decode()")
156
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
157
175
 
158
176
  def gsub(pattern, replacement)
159
177
  pipe_with("gsub(#{pattern.inspect}, #{replacement.inspect})")
@@ -237,6 +255,30 @@ module MQ
237
255
  pipe_with("path_join(#{other.inspect})")
238
256
  end
239
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
+
240
282
  def get(key)
241
283
  pipe_with("get(#{key.inspect})")
242
284
  end
@@ -245,6 +287,22 @@ module MQ
245
287
  pipe_with("set(#{key.inspect}, #{val.inspect})")
246
288
  end
247
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
+
248
306
  # Access a dict property by key (generates ."key" selector)
249
307
  def property(key)
250
308
  pipe_with(".\"#{key}\"")
@@ -486,6 +544,12 @@ module MQ
486
544
  def rand_int(min, max)
487
545
  new("rand_int(#{min}, #{max})")
488
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
489
553
  end
490
554
 
491
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.29
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