RubyのActiveRecordでMySQLに接続できるRubyGemを公開しました(SSH経由でも接続できるよ)
この記事は、Finatextグループ Advent Calendar 2024の16日目の記事です。
はじめに
こんにちは、Finatextのインシュアテック事業でバックエンドエンジニアをしている岸と申します。
今回は、RubyのActiveRecordでMySQLにSSH接続できるRubyGemを作成しましたので、その紹介をさせていただきます。
TL;DR
こちらのRubyGemを作ったのでぜひ使ってみてください!
GitHubのREADMEにはサンプルのDBやコマンドのショーケースをたくさん用意しています👏🏻
作成の背景
弊社ではバックエンド開発にGo言語をメインで使っているため、多くのメンバーはDB操作をするのにGUIツールを使用したりコンソールでSQLを実行することがほとんどですが、私はRuby on Railsでの開発経験があるため、 Rails consoleのActiveRecordのようにプログラマティックにDBを操作をしたい と思っていました。
空き時間で今回のRubyGemの原型となったツールを自作して日常的に使用していましたが、社内ミーティングのちょっとした時間に「こんなツール使ってるんですよ〜」と軽い気持ちで簡単なデモをしたところ、 「普通に有益だろ」(原文ママ)というフィードバックを頂きました。
原型となっていたツールは「The 野良ツール」という感じだったので、せっかくなので本年のAdvent Calendarに向けてOSSとして開発しました。OSSということもあり、GitHub側は可能な限り英語で表記しています(Copilotさまさま🙏🏻)
作成したRubyGem
ActiveRecordMysqlReplというRubyGem(Rubyのライブラリやコマンドラインツール)を作成しました。
Active Record Mysql Repl という頭文字を取って、インストールすれば army
(あーみー)というコマンドで実行できるようになります。また、REPLという名前が付いている通り、Pry を使って対話型でDBを操作することができます。
余談ですが、Pry同様の対話型Rubyシェルである irb も最近はとても高機能化されているので、REPL機能として使えないかいつか試してみたいと思っています。
ActiveRecordとは??
「ActiveRecordって何?」という方もいるかもしれないので簡単に説明します。
ActiveRecordとは、Ruby on RailsのデファクトスタンダードなORMです。DBのテーブルをRubyのクラスとして扱うことができ、SQLを書かずにDBの操作ができるようになります。
テーブルから取得した値をRubyのオブジェクトとして扱うことができ、テーブル同士の関連性をメソッドチェーンで辿ることができたり、オブジェクトに変更を加えたのちそのオブジェクトの #save()
メソッドを呼んで再びDBに保存することもできる仕組みです。
例えば、以下のER図のようなテーブル構成のDBに対してログインしてインタラクティブに操作することができます。
army
の使い方(概観)
詳しくはGitHubリポジトリのREADMEにインストール方法や使用方法のドキュメンテーションや、その中の sample_config/
ディレクトリにあるサンプルの設定ファイルを充実させていますので、ここでは概要をお伝えしたいと思います。
設定ファイル
.army.yml # ルートの設定ファイル(デフォルトは ~/.army.yml)
- databases.yml # DBの接続情報
- association.yml # テーブルの関連性定義ファイル
- extensions/ # 拡張機能の設定ファイルディレクトリ
- *.rb
- pryrc # pryの設定ファイル(デフォルトは ~/.pryrc)
以下のように、それぞれの設定を .army.yml からの相対パスとして記載します。
各種設定ファイルのサンプルには、JSONスキーマによる説明や補完も効くようにしているので、設定の際にはぜひ参考にしてください。
DBへの接続(database.yml)
以下のようなDBごとの接続設定をyamlファイルに複数定義することができます。 prompt_color
はREPLのプロンプトに表示される文字色です。開発サーバーや本番サーバーで色を分けて注意を促すのに利用できます。
SSH Tunnelにも対応しており、bastion
や ssh-user
を設定することでSSH経由でDBに接続することができます。
AWS Session Managerを利用してローカルからリモートのDBに接続する場合は、すでにポートフォワーディングされているので、ローカル(127.0.0.1)のポートを指定するだけで接続できます。
ここで定義したDB名は、別途用意したzsh補完(後述)を設定してもらうと army
コマンドの引数として補完されるようになります。
アソシエーション(association.yml)
ActiveRecordでいうアソシエーションとは、よく「リレーション」と言われるもので、テーブル同士の関連性を定義するものです。
基本的にはActiveRecordMysqlReplはDBに存在するテーブルカラム名に基づいて自動的にテーブル同士の belongs_to
has_many
の関連付けるようにしていますが、以下のケースはテーブルから明示的に読み取れないので定義する必要があります。
- カラム名から明示的ではない関連性(例:
profile_id
がuser_profiles
テーブルのidを参照している場合 ) -
xxxx_id
が他テーブルを参照しない
拡張機能(extensions/*.rb)
テーブルによってはアソシエーションだけでは実現できない振る舞いを定義したい場合もあるかもしれないので、利用者が自由に拡張機能を定義できるようにしています。
例えば以下の例では UserProfile
オブジェクトに upcase_name
というメソッドを追加しています。
その他にも、Rubyのメタプログラミングを使用してグローバルメソッドを定義したり拡張スクリプト側でアソシエーションやhookを定義したりと、とにかく自由に拡張できるようにしています。利用方法は自由ですが、あくまでDB閲覧や操作の補助ツールなので、ここにアプリケーションとは異なるビジネスロジックが混入しないように注意してください。
pryrc
REPLに使用しているPryですが、多くのカスタマイズ項目が存在します。
army
を使用する際に限り行うカスタマイズを指定することができます。例えば、以下ではコマンドの戻り値のオブジェクトを awesome_print で綺麗に表示し、ページャーで表示するようにしています。
ERD出力
RailsERD を使用してER図を出力することができます。
$ army [-c /path/to/.army.yml] -d <database> -e erd
以下のようなER図が出力されます。
zsh補完
以下のスクリプトでコマンド補完できるようにしています。都度実行するか、 ~/.zshrc に追記してください👍
$ eval "$(army --zsh-completion)"
その他、ビルトイン拡張
.XXX
はクラスメソッド、#xxx
はインスタンスメソッドです。
- テーブル定義系:
.d
.ddl
- 文字列やArrayに動的メソッド生やしてselectできる系:
"value".{table名}
"value".{table名}_by_{カラム名}
- JSON系
#j
#jp
- クリップボード系:
#cp
- 表示系:
#tabulate
#csv
- トランザクションのショートハンド:
transaction { ... }
-
ActiveRecord::Base.connection.select_all
→exec_sql
-
- 生SQL実行のショートハンド:
exec_sql
-
ActiveRecord::Base.transaction
→transaction
-
ActiveRecordネイティブの機能
とにかく便利な機能がめちゃくちゃあり、全てはここでは網羅できないので、以下のリンクを参考にしてください。
まとめ
今回は、RubyのActiveRecordでMySQLにSSH接続してREPL操作できるRubyGemをご紹介しました。ぜひ、便利に使って頂ければ幸いです。
問題や改善のご要望があればGitHubにてレポートしていただいたり、Pull Requestを送っていただけると何よりです!
Appendix
RubyGemを作成するのに使用した参考文献
- https://guides.rubygems.org/make-your-own-gem/
- https://guides.rubygems.org/publishing/
- https://qiita.com/okakam/items/ad55b7f239da3fab816d
- https://qiita.com/YumaInaura/items/f6a1dcbde2fa288bd86d
- https://zsh.sourceforge.io/Doc/Release/Completion-System.html
- https://blog.freedom-man.com/zsh-completions
Discussion