はじめに
Railsエンジニアの皆さん、管理画面作るときはどうしていますか?
ActiveAdminなどの管理画面系Gemを使うのも手ですが、
- DSLでカスタマイズするのが辛い
- 欲しい機能が実現できないことがある
- デザインがあまり好みでない
私個人はこんな理由で、既存のGemは使わず、イチから開発していました。しかし、都度イチから開発するのは非効率ですし、管理画面は共通化できる要素が多いことも特徴です。
そこで、自作フレームワークを作って開発を効率化してきました。この仕組みが私自身の開発を大きく改善できたので、是非みなさんにも共有したいと思い、Gemにして公開しました。
今回はこのGem Infold の使い方をご紹介いたします。
どんなツール?
まずは こちらのデモサイト をご参照ください。
このアプリは、 Gemから自動生成した機能だけで構築 しています。
ツールの特徴
以下をテーマとして開発をしています。
- コードを自動生成する
- DSLは使わない
- モダンで使い勝手の良いUI/UX
1. コードを自動生成する
といってもGithub Copilotのような代物ではなく、RailsのScaffoldを拡張しています。Scaffoldはリソースの定義をするだけで、controller, model, viewなどのファイルが自動で生成されてCRUDできる基本機能が自動で出来上がります。この仕組みを応用して、管理画面に特化したジェネレータを開発しました。
また、生成されるコードは、可読性を重視しています。ただ動けばよいのではなく、生成されたコードをエンジニア自身が容易に可読でき、追加のカスタマイズがしやすい仕組みを目的としています。
2. DSLは使わない
設定はDSLではなくYAMLに記載します。このYAMLを上記のジェネレータに読み込ませると、設定に応じたCRUDアプリのコードを自動生成できます。
YAMLなのでDSLのようにロジックをゴリゴリ書くことはできませんが、シンプルでわかりやすいことを重視しています。全ての機能をGemとDSLで実現するのではなく、無理なく共通化できるレベルまでを自動生成し、追加のカスタマイズは通常のRails開発と同様にできたほうがよいと考えています(なので、コードを自動生成する仕組みを採用しています)
とはいえ、YAMLだけでもそれなりの機能性を実現しています。デモサイトでも実装していますが、例えば以下のような機能性があります。
- has_many なデータの一括登録・参照
- Validation, Enum, Decoratorなどの仕組み
- 画像ファイルの登録や、CSVファイルの出力、などなど
3. モダンでユーザビリティの高いUI/UX
Hotwireを利用して、SPAライクなUI/UXを実現しています。また、UIのテンプレートにはBootstrap5をベースにした tabler を採用しています。これにより、モダンでユーザビリティの高い管理画面を実現しています。
管理画面の開発には、デザイナーの方は参画せず、エンジニアだけで画面のUIを含めて構築することが多いかと思います。画面周りが苦手なエンジニアの方だけでも、ユーザビリティに優れた管理画面を実現できることを目指しています。
対応するRailsのバージョン
- Hotwireでの動作を想定していることから、Railsは7.0以上で動作します
- また、Railsはesbuild等でJSのビルド環境が必要となります
実際の使い方
前置きが長くなりましたが、ここからはインストールから実際のカスタマイズまでをご説明します。
Infoldのインストール
Gemfileに[infold]と、infoldで利用している各種Gemを追記し、 bundle install
します。
gem 'infold', require: false
gem 'devise' # 認証
gem 'haml-rails' # HAML
gem 'kaminari' # ページネーション
gem 'view_component' # View Component
gem 'active_decorator' # Decoration
gem 'enum_help' # Enum Helpers
次に、infoldのジェネレータからインストールをします。
$ rails generate infold:install
このタイミングで、view_componentやstimulusなどで利用する共通的なファイルが生成されます。
また、Deviseのマイグレーションも生成されますので、テーブルの作成と、rails consoleからテスト用アカウントの登録をしておきます
$ rails db:migrate
$ rails console
AdminUser.create(email: 'user@example.com', password: 'password')
以上でインストールは完了です。このタイミングで bin/dev
からRailsを起動後、ブラウザで http://localhost:3000/admin
にアクセスすると、ログイン画面が表示されるので、先程作成したユーザー情報でログインします。
これでログイン機能が作れました。続けて、実際にアプリを構築していきます。
顧客管理アプリの作成
まずは顧客(Customer)モデルを作成します。ここでは通常のRailsのgenerate model
を使います。
$ bin/rails g model Customer name:string address:string gender:integer
$ bin/rails db:migrate
infoldのジェネレータを使って、このモデルに対するアプリを作成します。
$ bin/rails g infold Customer
これだけで顧客管理アプリが生成されています。この段階でブラウザからlocalhost:3000/admin/customers
にアクセスすると、CUSTOMERSアプリが表示されます。
画面右上のオレンジ色のボタン「Create New」をクリックするとフォームが表示されます。適当に入力して画面右下の青色のボタン「Create」から登録ができます。
レコードの左端「ノートアイコン」から参照画面、右端「鉛筆アイコン」から編集画面が表示されます。
ほんの数ステップで、CRUDをベースとした管理画面アプリが生成することができました。
このタイミングでControllerやViewなどのファイルが自動で生成されています。例えばControllerは app/controllers/admin/customer_controller.rb
が以下の通り生成されています。
このように、InfoldはActiveAdminのようにアプリそのものの生成ではなく、ソースコードを生成し、それを動作させる仕組みになっています。また、コードは一般的なRailsで書かれているので、Railsエンジニアの方であれば編集が容易にできます。ツールだけで実現できないカスタマイズは、このコードを直接編集することで、柔軟な対応が可能になります。
顧客管理アプリのカスタマイズ
上記で生成したアプリは、YAMLで設定をすることでカスタマイズができます。試しに以下の機能を追加してみます。
- Nameを必須項目にする
- Genderは male, female, other のEnumとし、ラジオボタンから選択する
前述の rails g infold Customer
のタイミングで、プロジェクトの config/infold/customer.yml
が生成されています。このファイルを以下の内容に変更してください。
model:
validate:
name: presence
enum:
gender:
male: 1
female: 2
other: 3
app:
form:
fields:
- name
- address
- gender:
kind: radio
YAMLの編集後、改めて g infold
を実行します。
$ bin/rails g infold Customer
これで、YAMLの設定に応じたコードが再生成されます。ブラウザから動作確認をしてご確認ください。バリデーションとラジオボタンのフォームが有効化されます。
このように、InfoldではYAMLを編集してカスタマイズを行います。
商品管理アプリの作成
続けて、商品管理を作成してみます。
Railsのモデル生成
$ bin/rails g model Product title:string price:integer
$ bin/rails db:migrate
infoldでアプリの生成
$ bin/rails g infold Product
ブラウザでlocalhost:3000/admin/products
にアクセスすると、商品管理アプリが表示されます。ヘッダーメニューも「PRODUCTS」が追加されています。
商品画像の表示
InfoldはActiveStorageをサポートします。そこで、例えば商品画像を管理したい場合、config/infold/product.yml
を以下の設定にします。
model:
active_storage:
photo:
kind: image
app:
index:
list:
fields:
- id
- title
- photo
- price
form:
fields:
- title
- photo:
kind: file
- price
なお、プロジェクトでActiveStorageがインストールされていることが前提となります。
$ bin/rails active_storage:install
$ bin/rails db:migrate
infoldでアプリを生成します。
$ bin/rails g infold Product
上記設定で、フォームから画像が登録でき、一覧に表示されます。
注文管理アプリの作成
上記の「顧客」「商品」管理は、単一のテーブルをCRUDとしていましたが、次に作る「注文管理」は、顧客と商品のリレーションを設定していきます。
RailsからOrderモデルを作成します。
$ bin/rails g model Order customer:references product:references amount:integer
$ bin/rails db:migrate
以下のリレーションになります。
注文管理アプリをinfoldから生成します。g infold:resource
とすることで、YAMLファイルのみの生成ができます。
$ bin/rails g infold:resource Order
Orderと他モデルにアソシエーションを設定するために、config/infold/order.yml
を以下のように編集します。
なお、以下の ! 部分にある name_field
は、レコードの名称を表すフィールドを指定します。その結果、関連先のレコードをそのフィールドで表示できます。例えば、orderモデルはcustomer_id
を介してcustomerモデルとの関連を持ちますが、画面上では customer_id
を表示する代わりに、customer.name
を表示します。
model:
association:
customer:
kind: belongs_to
name_field: name # !
product:
kind: belongs_to
name_field: title # !
app:
form:
fields:
- customer:
kind: select
- product:
kind: select
- amount:
kind: number
YAML編集後、Infoldのジェネレータでコードを生成します。
$ bin/rails g infold Order
ブラウザから確認すると、例えば登録フォームからは、CustomerやProductがリストから選択可能になっています。
また、Orderを登録後、Customer, Productはリンクで表示され、それをクリックすると詳細を参照できるようになります。
応用編
has_manyの一括登録
現状の注文管理は、OrderモデルがProductモデルに直接関連しているため( order belongs_to product
)、一度の注文に一つの商品しか購入できません。複数商品をまとめて購入できるよう、OrderDetail
を追加し、以下のモデリングに変更します( order has_many order_details
, order_detail belongs_to product
)。
Rails側でモデルの変更・追加をしていきます。まず、OrderとProductの関連を除去するために、以下のマイグレーションを生成します。
$ bin/rails g migration RemoveProductFromOrders product:references amount:integer
また、Orderモデルにも belongs_to :product
の記述が残っているので、削除します。
class Order < ApplicationRecord
belongs_to :customer
- belongs_to :product
end
次に、OrderDetailモデルを作成し、マイグレーションを実行します。
$ bin/rails g model OrderDetail order:references product:references amount:integer
$ bin/rails db:migrate
Infoldで注文管理 (config/infold/order.yml
) を変更していきます。
model:
...
association:
customer:
kind: belongs_to
name_field: name
- # 以下は削除
- product:
- kind: belongs_to
- name_field: title
+ # 以下を追加
+ order_details:
+ kind: has_many
+ dependent: destroy
+ model: # 関連先モデル(order_details)の設定も可能
+ validate:
+ product: presence
+ association: # order_detailsの更に関連先(product)
+ product:
+ kind: belongs_to
+ name_field: title
app:
# 参照画面にも一括表示するために以下を追加
+ show:
+ fields:
+ - id
+ - customer
+ - order_details:
+ fields:
+ - product
+ - amount
form:
fields:
- customer:
kind: select
- # 以下は削除
- - product
- kind: select
- - amount
- kind: number
+ # 以下を追加
+ - order_details:
+ kind: association
+ fields:
+ - product:
+ kind: select
+ - amount:
+ kind: number
この状態でInfoldで注文管理アプリを再生成します。
$ bin/rails g infold Order
フォームで一括登録が可能になります。[ADD]ボタンをクリックすると行が追加され、行の右端「ゴミ箱アイコン」をクリックで削除できます。
子画面からの検索・指定
現状の注文管理(orders)の登録フォームでは、顧客(customer)がリストになっています。顧客データが多くなると、リストでは該当のレコードを指定することが困難になります(例えば顧客データが100件あったら、リストの選択肢も100件になり、使い物にならない)
Infoldでは、belongs_toによる関連モデルを、子画面から検索・指定できます。注文管理から顧客を子画面で指定するように変更してみます。
まず、顧客の子画面検索を有効化するために、顧客管理側のYAML (customer.yml) を編集していきます。
model:
...
app:
form:
...
+ # 以下を追加
+ association_search:
+ conditions:
+ - id:
+ sign: eq
+ - name:
+ sign: full_like
+ list:
+ fields:
+ - id
+ - name
次に、注文管理側のYAML (order.yml) を編集します(kindをselectからassociation_searchに変更)。
model:
...
app:
form:
fields:
- customer:
- kind: select
+ kind: association_search
- order_details:
...
顧客管理、注文管理、それぞれのコードをInfoldから再生成します。
$ bin/rails g infold Customer
$ bin/rails g infold Order
ブラウザで 注文管理の登録フォームを表示すると、顧客のフォームが変わっていることが確認できます。このフォームの青い虫眼鏡ボタンをクリックすると、子画面で顧客の検索画面が表示されます。
子画面上で検索をし、一覧の右端列「チェックアイコン」をクリックすると、該当の顧客が注文管理フォームに引き継がれます。
日本語化
Infoldはi18nに対応しています。日本語表示をする場合、Railsのdefault_localeを ja
に変更します。詳細はRailsガイドなどを参照ください。
一例として、config/initializers/locale.rb
を以下の通り作成します。
I18n.config.available_locales = [:ja, :en]
I18n.default_locale = :ja
I18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
また、rails-i18n から ja.yml をダウンロードして、config/locales
へ設置してください。
その後、config/locales
ディレクトリに、models
ディレクトリを作成し、その中にcustomer.ja.yml
を以下の内容で作成します。
ja:
activerecord:
attributes:
customer:
name: 氏名
address: 住所
gender: 性別
顧客管理の画面が日本語化されます(ボタンやメッセージ内容も日本語化されています)
なお、Enumの区分値を日本語化する場合は、以下の通りです。
ja:
activerecord:
attributes:
customer:
name: 氏名
address: 住所
gender: 性別
+ enums:
+ admin: # adminを挟む必要があります
+ customer:
+ gender:
+ male: 男性
+ female: 女性
+ other: その他
まとめ
長くなりましたが、Infoldの一連の使い方をご説明いたしました。
YAMLの設定だけでも、それなりの機能を生成できることが確認できたかと思います。
あとは、自動生成後のソースコードをカスタマイズする流れになります。
イチから実装する場合と比較して、管理画面の実装スピードをかなり高速化できると思いますので、是非ご利用を検討頂けたら幸いです!
今回の説明で使ったコード
上記で生成したアプリは、Githubにて公開しています。