StringBuilder

Ruby method chains → any string format. 113 lines. Zero dependencies.

gem install string_builder

Contents


Git

Git.commit.m("fix: null pointer in parser")
Git.push.origin.main.force(true)
Git.log.oneline(true).graph(true).n(20)
Git.rebase.i(true).("HEAD~5")
git commit -m fix: null pointer in parser
git push origin main --force
git log --oneline --graph -n 20
git rebase -i HEAD~5

See how this works →


Docker

Docker.run.d(true).name("web").p("8080:80").("nginx:latest")
Docker.build.t("myapp:latest").no_cache(true).(".")
Docker.compose.up.d(true).build(true).remove_orphans(true)
docker run -d --name web -p 8080:80 nginx:latest
docker build -t myapp:latest --no-cache .
docker compose up -d --build --remove-orphans

See how this works →


Terraform

Terraform.plan.var("region=us-east-1").var_file("prod.tfvars").out("plan.out")
Terraform.apply.auto_approve(true).("plan.out")
Terraform.destroy.auto_approve(true).target("aws_instance.web")
Terraform.state.mv.("aws_instance.old").("aws_instance.new")
Terraform.import.("aws_instance.web").("i-1234567890abcdef0")
terraform plan --var region=us-east-1 --var-file prod.tfvars --out plan.out
terraform apply --auto-approve plan.out
terraform destroy --auto-approve --target aws_instance.web
terraform state mv aws_instance.old aws_instance.new
terraform import aws_instance.web i-1234567890abcdef0

See how this works →


AWS

AWS.s3.cp.("s3://my-bucket/data.csv").(".").recursive(true)
AWS.lambda.invoke.function_name("my-function").payload('{"key":"value"}').("output.json")
AWS.cloudformation.deploy.template_file("template.yaml").stack_name("my-stack")
aws s3 cp s3://my-bucket/data.csv . --recursive
aws lambda invoke --function-name my-function --payload {"key":"value"} output.json
aws cloudformation deploy --template-file template.yaml --stack-name my-stack

See how this works →


SQL

SQL.query { columns(:name, :email).from("users").where(active: true) }
SQL.query { delete.from("sessions").where(expired: true) }
SQL.query { insert.into("users").values("alice", "alice@example.com", 28) }
SQL.query {
  columns(:id, :name)
    .from("products")
    .where(category: "electronics")
    .order_by(:price)
    .limit(10)
}
SELECT name, email FROM 'users' WHERE active = TRUE
DELETE FROM 'sessions' WHERE expired = TRUE
INSERT INTO 'users' VALUES 'alice', 'alice@example.com', 28
SELECT id, name FROM 'products' WHERE category = 'electronics' ORDER BY price LIMIT 10

See how this works →


HTML

HTML.tag.h1("Hello, World!")
HTML.tag.a("Click here", href: "/about", class: "link")
HTML.build { div(class: "container") / h1("Welcome back.") }
HTML.build { nav(class: "sidebar") / ul / li("Dashboard") }
<h1>Hello, World!</h1>
<a href="/about" class="link">Click here</a>
<div class="container">
  <h1>Welcome back.</h1>
</div>
<nav class="sidebar">
  <ul>
    <li>Dashboard</li>
  </ul>
</nav>

See how this works →


Same chain, different output

One buffer. Swap the handler.

sb = StringBuilder.new.get.users.page(1).limit(25)
Handler Output
Default get users page(1) limit(25)
URL /get/users?page=1&limit=25
JSONPath $.get.users.page[1].limit[25]

See how this works →


.env files

Env.database_url("postgres://localhost:5432/myapp")
   .redis_url("redis://localhost:6379")
   .secret_key_base("a1b2c3d4e5f6")
   .rails_env("production")
   .port(3000)
DATABASE_URL=postgres://localhost:5432/myapp
REDIS_URL=redis://localhost:6379
SECRET_KEY_BASE=a1b2c3d4e5f6
RAILS_ENV=production
PORT=3000

See how this works →


CSS selectors

CSS.wrap { div(:container) / ul(:list) / li(:active) / a }
CSS.wrap { body / main("content") / section(:hero) / h1 }
div.container > ul.list > li.active > a
body > main#content > section.hero > h1

See how this works →


JSONPath

JP.data.users(0).name
JP.store.book(2).author
JP.response.items(0)..labels
$.data.users[0].name
$.store.book[2].author
$.response.items[0].metadata.labels

See how this works →


URLs

URL.api.v2.users
URL.api.v1.search.page(1).limit(25)
URL.api.v3.repos.("octocat/hello-world").commits.per_page(10)
/api/v2/users
/api/v1/search?page=1&limit=25
/api/v3/repos/octocat/hello-world/commits?per_page=10

See how this works →


Makefile targets

Make.build(:clean, :deps).go.build("./...")
Make.test.go.test("./...", "-v", "-race")
build: clean deps
    go build ./...

test:
    go test ./... -v -race

See how this works →



Built with StringBuilder

kube_ctl -- kubectl & helm as Ruby DSLs

Kube.ctl { create.namespace.my-app }
Kube.ctl { apply.f './k8s/deployment.yaml' }
Kube.ctl { get.pods.o(:wide) }
Kube.ctl { logs.f(true).deployment/web.c('my-app') }
Kube.ctl { scale.deployment/web.replicas(5) }
Kube.ctl { set.image.deployment/web.('my-app=registry.example.com/my-app:v2') }
Kube.ctl { rollout.undo.deployment/web.to_revision(3) }
Kube.ctl { exec.i(true).t(true).web.c('my-app').('-- /bin/sh') }
kubectl create namespace my-app
kubectl apply -f ./k8s/deployment.yaml
kubectl get pods -o wide
kubectl logs -f deployment/web -c my-app
kubectl scale deployment/web --replicas=5
kubectl set image deployment/web my-app=registry.example.com/my-app:v2
kubectl rollout undo deployment/web --to-revision=3
kubectl exec -i -t web -c my-app -- /bin/sh
Kube.helm { repo.add.("bitnami").("https://charts.bitnami.com/bitnami") }
Kube.helm {
  install.my_nginx.("bitnami/nginx")
    .f("values.yaml")
    .set("image.tag=1.25.0")
    .namespace("web")
    .create_namespace(true)
    .wait(true)
    .timeout("5m0s")
}
Kube.helm {
  upgrade.install(true).my_nginx.("bitnami/nginx")
    .reuse_values(true)
    .set("image.tag=1.26.0")
    .namespace("web")
}
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-nginx bitnami/nginx -f values.yaml --set image.tag=1.25.0 --namespace=web --create-namespace --wait --timeout=5m0s
helm upgrade --install my-nginx bitnami/nginx --reuse-values --set image.tag=1.26.0 --namespace=web

A full 31-step deployment walkthrough and 10-step helm workflow. gem install kube_kubectl

kubectl examples → · helm examples →


The library is 113 lines. Every example above is a different concat handler -- a single .call(buffer) method that decides how tokens become strings. The chain is data. The handler is interpretation.

Start with the basics →