| name | gandy |
| description | DB 조회/분석 지원. gandy 쿼리, .gandy 설정, 데이터 수집/분석 워크플로우. Use when querying databases, writing gandy queries, setting up .gandy project config, collecting or analyzing DB data, exploring table structure, or any situation involving gandy, pj:db. Also use when user needs data from postgres or sqlite for investigation, reporting, or debugging. Do NOT use for SQL migration, schema design, or ORM framework setup (use devops instead). |
gandy Query & Analysis Assistant
gandy — Interactive Ruby Database Console. Sequel 기반이지만 AR 스타일 API를 제공하여 ActiveRecord처럼 사용 가능. PostgreSQL, SQLite 지원.
핵심 철학:
- 스크립트(run_script) 전에 interactive/
-e로 해결 시도
- 반복 패턴은
.gandy에 헬퍼로 승격
- Raw SQL 금지.
DB["..."]는 차단됨
- Sandbox: 모든 변경은 트랜잭션 안에서 실행, 명시적 commit! 전까지 미확정
When to Use
- 사용자가 DB 데이터 조회/분석 요청 시
-e 모드 명령 구성 시
.gandy 프로젝트 설정 작성 시
- 데이터 수집/분석 워크플로우 설계 시
Instructions
워크플로우 1: 쿼리 생성
사용자가 "~~ 조회해줘", "~~ 데이터 뽑아줘" 요청 시:
-
대상 파악: 어떤 테이블/모델, 어떤 조건
-
쿼리 구성 (우선순위):
- (1) Model API:
Model.where(col: val).all
- (2) Association:
record.assoc, Model.joins(:assoc)
- (3) Auto-join where:
Model.where(other_table: { col: val })
- (4) Raw Dataset:
DB[:table].join(:other, fk: :id).select_all(:table)
-
출력 형식 제안:
- 확인용: interactive (직접
.all)
- 파이프:
dataset >> :csv, dataset >> :json, dataset >> "file.csv"
- 클립보드:
dataset >> :clip
- 보고서:
to_report hash, "path"
워크플로우 2: .gandy 설정 생성
프로젝트의 .gandy 작성 요청 시:
-
DB 스키마 파악 (사용자에게 실행 요청):
gandy <url> -e 'tables'
gandy <url> -e 'schema :table_name'
-
설정 구성:
fk_alias creator: :member, singer: :member
enable_soft_delete
enable_soft_delete Match, Song
Users.description "사용자 마스터"
Users.enum :role, %w[admin member guest]
Users.enum :role, admin: '관리자', member: '회원'
Users.comment :email, "로그인 이메일"
Users.label_column :nickname, :phone
Users.normalizes :email, with: ->(v) { v&.strip&.downcase }
Orders.ref_label user: :name
Orders.ref_label user: [:name, :email]
Orders.ref_label user: ->(u) { "#{u.name} (#{u.email})" }
pipe_to(:slack) { |data| ... }
def active_members
Member.where(active: true).where { last_login > Date.today - 30 }
end
-
헬퍼 승격 기준: 동일 패턴 2회+ 반복 시 헬퍼로 추출 제안
워크플로우 3: 데이터 수집/분석
복잡한 다단계 데이터 수집 요청 시:
-
interactive로 가능한지 먼저 판단:
- 단일 쿼리 →
-e 모드
- 2-3단계 → interactive 세션 (변수 재사용)
- 4단계+ 또는 재현성 필요 →
.gandy 헬퍼 또는 run_script
-
수집 전략:
- 메타데이터 → 상세 데이터 순서 (좁은 → 넓은)
- 중간 결과는 변수에, 최종만 export
- subquery 활용:
where(col: Model.where(...).select(:id))
-
스크립트가 필요한 경우에도:
.gandy 헬퍼 함수로 작성 (재사용 가능)
- run_script은 일회성 분석에만
Quick Reference
CLI 사용법
gandy <db-url>
gandy <ssh-host> <db-url>
gandy - <db-url>
gandy <db-url> -e '<code>'
gandy <db-url> --write
Model 규칙
테이블 → 클래스: split('_').map(&:capitalize).join
order_items → OrderItems (NOT OrderItem)
※ Rails inflector 아님 (statuses → Statuses)
Select & Filter
User.all
User.all!
User.first / User[1] / User.last(5)
User.where(active: true).all
User.where.not(role: "admin")
User.pluck(:name)
User.exclude(active: false)
User.where(name: 'foo').or(name: 'bar')
User.select(:id, :name).all
User.distinct.select(:role).all
User.where(age: 20..)
User.where(age: 20..30)
User.where { created_at > 7.days.ago }
User.where(deleted_at: nil)
User.exclude(deleted_at: nil)
User.find_by(email: "x@y.com")
User.exists?(email: "x@y.com")
User.find_or_create_by(email: "x")
Association
user.orders
user.orders.where(status: "paid")
order.user
User.includes(:orders).all
fk_alias creator: :member
song.creator
Join
User.joins(:orders).distinct.select_all(:users).all
User.joins(:orders, :posts)
User.left_joins(:orders)
User.where(orders: { status: "paid" }).all
Group & Aggregate
Order.group_and_count(:status).all
Order.group(:user_id).select_append { sum(amount).as(total) }.all
User.sum(:age) / .avg(:age) / .min(:age) / .max(:age)
Users.enum_stats(:role)
Order & Pagination
User.order(:created_at).reverse.all
User.order(:created_at).limit(10).offset(20).all
sample :users
sample User.where(...), 5
User.all
_.next / _.prev / _.page(3)
User.all!
CRUD & Dirty Tracking
User.create(name: "x")
user.update(name: "y")
user.delete
user.touch
user.touch(:verified_at)
user.changed?
user.changes
user.diff
Sandbox (트랜잭션)
user.update(name: "x")
commit!
rollback!
>> Pipe (Export)
User.all >> :csv
User.all >> :json
User.all >> :yaml
User.all >> :jq
User.all >> :tbl
User.all >> :vim
User.all >> :clip
User.all >> "out.csv"
User.all >> "out.json"
User.all >> "out.md"
User.all >> :json >> :vim
User.all >> :csv >> :clip
User.all >> :json >> "a.json"
Time 헬퍼
1.days.ago / 3.hours.from_now / 2.weeks.ago
yesterday / tomorrow / today
2.days.before(some_time) / 1.hour.after(some_time)
this_week / this_month
Date.today.all_day / Date.today.all_week / Date.today.all_month
Time.now.beginning_of_day / Time.now.end_of_day
Date.today.beginning_of_week / Date.today.beginning_of_month
User.where(created_at: 7.days.ago..)
User.where(created_at: ..30.days.ago)
User.where(created_at: this_week)
탐색 워크플로우
tables
tables(/keyword/)
overview :users
User
relations :users
tree User
sample :users
User.associations
schema :users
fk :orders
User.explain
ERD
print_erd
print_erd_d2
print_erd_d2 User, depth: 2
open_erd_d2 User
Analysis 헬퍼
load_json "~/logs.json"
index_by users, :id
merge_by db_data, logs, :user_id
to_report({ meta: {}, summary: {}, details: data }, "~/report.json")
run_script "~/analysis.rb"
Import
from_csv :users, "~/a.csv"
from_csv :users, "~/a.csv", run: true
from_json :users, "~/a.json"
디버그
verbose!
quiet!
hist
-e 모드
gandy <url> -e 'User.count'
gandy <url> -e 'User.where(active: true).limit(5)'
Soft Delete
enable_soft_delete
enable_soft_delete Match, Song
Model.unfiltered.where(...)
Model DSL (.gandy)
Users.description "사용자 마스터"
Users.enum :role, %w[admin member guest]
Users.enum :role, admin: '관리자', member: '회원'
Users.comment :email, "로그인 이메일"
Users.label_column :nickname
Users.ref_label org: :name
Users.normalizes :email, with: ->(v) { v&.strip&.downcase }
Users.enum_stats(:role)
user.role_label
user.role_admin?
Users.role_admin
중요 원칙
- Raw SQL 금지:
DB["..."]는 차단됨. gandy API 사용
- 헬퍼 승격: 2회 이상 반복되는 쿼리 패턴은
.gandy 헬퍼로 추출 제안
- 스크립트 최소화: run_script은 최후 수단. interactive +
.gandy 헬퍼 조합으로 대부분 해결 가능
- fk_alias 활용: 비표준 FK명은 fk_alias로 해결. raw join 강제 방지
- prod 안전: prod 환경은 기본 read-only.
--write 없이 변경 불가
- Sandbox 활용: 변경 작업은 commit!/rollback! 활용. exit 시 자동 rollback
- Pipe 우선: export는
>> :format 또는 >> "file.ext" 사용. 체인 가능
Examples
단순 조회
User: "활성 레코드 수 알려줘"
→ -e 모드: gandy <url> -e 'Model.where(active: true).count'
관계 데이터 수집
User: "특정 주문의 전체 결제 데이터 뽑아줘"
→ .gandy에 fk_alias + enable_soft_delete 설정 후 interactive:
order = Order.first(external_id: "xxx")
items = OrderItem.joins(:product).left_joins(:discount).where(order_id: order.id)
items >> "~/data/order_items.json"
.gandy 생성
User: "프로젝트 gandy 설정 만들어줘"
→ 스키마 확인 → fk_alias/soft_delete/enum/comment/label_column/도메인 헬퍼 구성
Technical Details
- 도구 내장 help:
gandy --help, 세션 내 help / help :topic
- 프로젝트 설정:
$PWD/.gandy (자동 로드, reload!로 재로드)
- 히스토리:
$PWD/.gandy_history (프로젝트별 분리)
- 환경 감지: SSH host 기반 PROD/STAG/DEV/LOCAL 자동 표시
- 지원 DB: PostgreSQL (
postgres://), SQLite (sqlite://)
- 요구사항: Ruby 3.1+