From 8b4b17855bd14a801a35d62aaa2c8fa2558248ee Mon Sep 17 00:00:00 2001 From: harehare Date: Sat, 4 Jul 2026 22:53:39 +0900 Subject: [PATCH] feat(query): support builtins added in mq 0.6.4 Add uuid/uuid_v4/uuid_v7/rand/rand_int as instance and class-level generators, plus shuffle, sample, to_boolean, url_decode, scan, ascii_downcase, and ascii_upcase to the Query DSL and docs. --- README.md | 16 ++++++++++++++++ lib/mq/query.rb | 33 +++++++++++++++++++++++++++++++++ spec/mq_spec.rb | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/README.md b/README.md index 0e35955..6ae812b 100644 --- a/README.md +++ b/README.md @@ -254,6 +254,7 @@ MQ::Query.list.map { contains("important") } .to_html # to_html() — HTML string .to_string # to_string() — string coercion .to_number # to_number() — numeric coercion +.to_boolean # to_boolean() — boolean coercion .to_array # to_array() .to_bytes # to_bytes() .to_markdown_string # to_markdown_string() @@ -267,11 +268,14 @@ MQ::Query.list.map { contains("important") } .rtrim # rtrim() .downcase # downcase() .upcase # upcase() +.ascii_downcase # ascii_downcase() +.ascii_upcase # ascii_upcase() .len # len() .utf8bytelen # utf8bytelen() .explode # explode() — string to codepoints .implode # implode() — codepoints to string .url_encode # url_encode() +.url_decode # url_decode() .intern # intern() .split(",") # split(",") @@ -279,6 +283,7 @@ MQ::Query.list.map { contains("important") } .replace("a", "b") # replace("a", "b") — literal replace .test("\\d+") # test("\\d+") — regex test → bool .capture("(\\w+)") # capture("(\\w+)") — regex capture +.scan("(\\w+)") # scan("(\\w+)") — all regex matches .slice(0, 5) # slice(0, 5) .index("sub") # index("sub") — position of substring .rindex("sub") # rindex("sub") — last position @@ -309,6 +314,8 @@ MQ::Query.list.map { contains("important") } .range(3) # range(3) .del("key") # del("key") .insert(0, "val") # insert(0, "val") +.shuffle # shuffle() — random order +.sample(3) # sample(3) — n random elements, no replacement ``` #### Math Operations @@ -350,8 +357,17 @@ MQ::Query.list.map { contains("important") } .sha512 # sha512() .from_hex # from_hex() .to_hex # to_hex() +.uuid # uuid() — random (v4) UUID +.uuid_v4 # uuid_v4() — alias of uuid +.uuid_v7 # uuid_v7() — time-ordered (v7) UUID +.rand # rand() — pseudo-random number in [0, 1) +.rand_int(1, 10) # rand_int(1, 10) — pseudo-random integer in [min, max] ``` +`MQ::Query.uuid`, `.uuid_v4`, `.uuid_v7`, `.rand`, and `.rand_int(min, max)` are +also available as class-level generators to start a query without a preceding +selector, e.g. `MQ::Query.uuid.to_query # => "uuid()"`. + #### Path Operations ```ruby diff --git a/lib/mq/query.rb b/lib/mq/query.rb index 0dc8db2..c1ed2bb 100644 --- a/lib/mq/query.rb +++ b/lib/mq/query.rb @@ -66,6 +66,7 @@ def to_mdx = pipe_with("to_mdx()") def to_html = pipe_with("to_html()") def to_string = pipe_with("to_string()") def to_number = pipe_with("to_number()") + def to_boolean = pipe_with("to_boolean()") def to_array = pipe_with("to_array()") def to_bytes = pipe_with("to_bytes()") def to_markdown_string = pipe_with("to_markdown_string()") @@ -135,14 +136,23 @@ def repeat(n) pipe_with("repeat(#{n})") end + def shuffle = pipe_with("shuffle()") + + def sample(n) + pipe_with("sample(#{n})") + end + def trim = pipe_with("trim()") def ltrim = pipe_with("ltrim()") def rtrim = pipe_with("rtrim()") def downcase = pipe_with("downcase()") def upcase = pipe_with("upcase()") + def ascii_downcase = pipe_with("ascii_downcase()") + def ascii_upcase = pipe_with("ascii_upcase()") def explode = pipe_with("explode()") def implode = pipe_with("implode()") def url_encode = pipe_with("url_encode()") + def url_decode = pipe_with("url_decode()") def intern = pipe_with("intern()") def gsub(pattern, replacement) @@ -161,6 +171,10 @@ def capture(pattern) pipe_with("capture(#{pattern.inspect})") end + def scan(pattern) + pipe_with("scan(#{pattern.inspect})") + end + def abs = pipe_with("abs()") def ceil = pipe_with("ceil()") def floor = pipe_with("floor()") @@ -205,6 +219,15 @@ def from_hex = pipe_with("from_hex()") def to_hex = pipe_with("to_hex()") def to_hex_str = pipe_with("to_hex()") + def uuid = pipe_with("uuid()") + def uuid_v4 = pipe_with("uuid_v4()") + def uuid_v7 = pipe_with("uuid_v7()") + def rand = pipe_with("rand()") + + def rand_int(min, max) + pipe_with("rand_int(#{min}, #{max})") + end + def basename = pipe_with("basename()") def dirname = pipe_with("dirname()") def extname = pipe_with("extname()") @@ -453,6 +476,16 @@ def select(filter = nil, &block) def to_text = new("to_text()") def to_markdown = new("to_markdown()") + + # --- Value generators (usable as query starting points) --- + def uuid = new("uuid()") + def uuid_v4 = new("uuid_v4()") + def uuid_v7 = new("uuid_v7()") + def rand = new("rand()") + + def rand_int(min, max) + new("rand_int(#{min}, #{max})") + end end private diff --git a/spec/mq_spec.rb b/spec/mq_spec.rb index 6935a7d..c378934 100644 --- a/spec/mq_spec.rb +++ b/spec/mq_spec.rb @@ -370,6 +370,7 @@ it "chains to_html" do expect(MQ::Query.text.to_html.to_query).to eq(".text | to_html()") end it "chains to_string" do expect(MQ::Query.text.to_string.to_query).to eq(".text | to_string()") end it "chains to_number" do expect(MQ::Query.text.to_number.to_query).to eq(".text | to_number()") end + it "chains to_boolean" do expect(MQ::Query.text.to_boolean.to_query).to eq(".text | to_boolean()") end it "chains to_array" do expect(MQ::Query.text.to_array.to_query).to eq(".text | to_array()") end it "chains to_markdown_string" do expect(MQ::Query.text.to_markdown_string.to_query).to eq(".text | to_markdown_string()") end end @@ -380,9 +381,12 @@ it "chains rtrim" do expect(MQ::Query.text.rtrim.to_query).to eq(".text | rtrim()") end it "chains downcase" do expect(MQ::Query.text.downcase.to_query).to eq(".text | downcase()") end it "chains upcase" do expect(MQ::Query.text.upcase.to_query).to eq(".text | upcase()") end + it "chains ascii_downcase" do expect(MQ::Query.text.ascii_downcase.to_query).to eq(".text | ascii_downcase()") end + it "chains ascii_upcase" do expect(MQ::Query.text.ascii_upcase.to_query).to eq(".text | ascii_upcase()") end it "chains explode" do expect(MQ::Query.text.explode.to_query).to eq(".text | explode()") end it "chains implode" do expect(MQ::Query.text.implode.to_query).to eq(".text | implode()") end it "chains url_encode" do expect(MQ::Query.text.url_encode.to_query).to eq(".text | url_encode()") end + it "chains url_decode" do expect(MQ::Query.text.url_decode.to_query).to eq(".text | url_decode()") end it "chains intern" do expect(MQ::Query.text.intern.to_query).to eq(".text | intern()") end it "chains len" do expect(MQ::Query.text.len.to_query).to eq(".text | len()") end it "chains utf8bytelen" do expect(MQ::Query.text.utf8bytelen.to_query).to eq(".text | utf8bytelen()") end @@ -403,6 +407,10 @@ expect(MQ::Query.text.capture("(\\w+)").to_query).to eq('.text | capture("(\\\\w+)")') end + it "chains scan with regex pattern" do + expect(MQ::Query.text.scan("(\\w+)").to_query).to eq('.text | scan("(\\\\w+)")') + end + it "chains split" do expect(MQ::Query.text.split(",").to_query).to eq('.text | split(",")') end @@ -455,6 +463,12 @@ it "chains insert" do expect(MQ::Query.list.insert(0, "new").to_query).to eq('.[] | insert(0, "new")') end + + it "chains shuffle" do expect(MQ::Query.list.shuffle.to_query).to eq(".[] | shuffle()") end + + it "chains sample" do + expect(MQ::Query.list.sample(3).to_query).to eq(".[] | sample(3)") + end end describe "math methods" do @@ -502,6 +516,34 @@ it "chains sha512" do expect(MQ::Query.text.sha512.to_query).to eq(".text | sha512()") end it "chains from_hex" do expect(MQ::Query.text.from_hex.to_query).to eq(".text | from_hex()") end it "chains to_hex" do expect(MQ::Query.text.to_hex.to_query).to eq(".text | to_hex()") end + it "chains uuid" do expect(MQ::Query.text.uuid.to_query).to eq(".text | uuid()") end + it "chains uuid_v4" do expect(MQ::Query.text.uuid_v4.to_query).to eq(".text | uuid_v4()") end + it "chains uuid_v7" do expect(MQ::Query.text.uuid_v7.to_query).to eq(".text | uuid_v7()") end + it "chains rand" do expect(MQ::Query.text.rand.to_query).to eq(".text | rand()") end + + it "chains rand_int" do + expect(MQ::Query.text.rand_int(1, 10).to_query).to eq(".text | rand_int(1, 10)") + end + + it "builds uuid as a class-level generator" do + expect(MQ::Query.uuid.to_query).to eq("uuid()") + end + + it "builds uuid_v4 as a class-level generator" do + expect(MQ::Query.uuid_v4.to_query).to eq("uuid_v4()") + end + + it "builds uuid_v7 as a class-level generator" do + expect(MQ::Query.uuid_v7.to_query).to eq("uuid_v7()") + end + + it "builds rand as a class-level generator" do + expect(MQ::Query.rand.to_query).to eq("rand()") + end + + it "builds rand_int as a class-level generator" do + expect(MQ::Query.rand_int(1, 10).to_query).to eq("rand_int(1, 10)") + end end describe "path methods" do