1 最初の接触
rails
コマンドを使用してアプリケーションを作成すると、実際にはRailsジェネレータを使用しています。その後、bin/rails generate
を呼び出すことで、利用可能なすべてのジェネレータのリストを取得できます。
$ rails new myapp
$ cd myapp
$ bin/rails generate
注意:Railsアプリケーションを作成するには、gem install rails
を使用してインストールされたRailsのバージョンを使用するrails
グローバルコマンドを使用します。アプリケーションのディレクトリ内では、アプリケーションにバンドルされたRailsのバージョンを使用するbin/rails
コマンドを使用します。
Railsに付属しているすべてのジェネレータのリストが表示されます。特定のジェネレータの詳細な説明を表示するには、--help
オプションを指定してジェネレータを呼び出します。例:
$ bin/rails generate scaffold --help
2 最初のジェネレータの作成
ジェネレータは、Thorの上に構築されており、パーシングのための強力なオプションとファイルの操作のための優れたAPIを提供しています。
config/initializers
内にinitializer.rb
という名前の初期化ファイルを作成するジェネレータを作成しましょう。最初のステップは、lib/generators/initializer_generator.rb
というファイルを作成し、以下の内容を記述することです。
class InitializerGenerator < Rails::Generators::Base
def create_initializer_file
create_file "config/initializers/initializer.rb", <<~RUBY
# 初期化の内容をここに追加
RUBY
end
end
新しいジェネレータは非常にシンプルです。Rails::Generators::Base
を継承し、1つのメソッド定義を持っています。ジェネレータが呼び出されると、ジェネレータ内の各パブリックメソッドが定義された順序で順次実行されます。私たちのメソッドはcreate_file
を呼び出し、指定されたディレクトリに指定された内容でファイルを作成します。
新しいジェネレータを呼び出すには、次のコマンドを実行します。
$ bin/rails generate initializer
次に、新しいジェネレータの説明を確認しましょう。
$ bin/rails generate initializer --help
Railsは、ActiveRecord::Generators::ModelGenerator
のようなジェネレータが名前空間である場合には、通常、良い説明を導出できますが、この場合はできません。この問題を解決する方法は2つあります。1つ目の方法は、ジェネレータ内でdesc
を呼び出すことで説明を追加する方法です。
class InitializerGenerator < Rails::Generators::Base
desc "This generator creates an initializer file at config/initializers"
def create_initializer_file
create_file "config/initializers/initializer.rb", <<~RUBY
# 初期化の内容をここに追加
RUBY
end
end
これで、新しいジェネレータの説明を--help
で確認できるようになりました。
2つ目の方法は、ジェネレータと同じディレクトリにUSAGE
という名前のファイルを作成することで説明を追加する方法です。次のステップでそれを行います。
3 ジェネレータを使用してジェネレータを作成する
ジェネレータ自体もジェネレータを持っています。InitializerGenerator
を削除し、bin/rails generate generator
を使用して新しいジェネレータを生成しましょう。
$ rm lib/generators/initializer_generator.rb
$ bin/rails generate generator initializer
create lib/generators/initializer
create lib/generators/initializer/initializer_generator.rb
create lib/generators/initializer/USAGE
create lib/generators/initializer/templates
invoke test_unit
create test/lib/generators/initializer_generator_test.rb
これが生成されたジェネレータです。
class InitializerGenerator < Rails::Generators::NamedBase
source_root File.expand_path("templates", __dir__)
end
まず、ジェネレータがRails::Generators::NamedBase
を継承していることに注意してください。これは、ジェネレータが少なくとも1つの引数を期待しており、それが初期化ファイルの名前であり、name
を介してコードで利用できることを意味します。
新しいジェネレータの説明を確認することで、それを確認できます。
$ bin/rails generate initializer --help
Usage:
bin/rails generate initializer NAME [options]
また、ジェネレータにはsource_root
というクラスメソッドがあります。このメソッドは、テンプレートの場所を指定します(あれば)。デフォルトでは、lib/generators/initializer/templates
ディレクトリを指すようになっています。
ジェネレータのテンプレートの動作を理解するために、lib/generators/initializer/templates/initializer.rb
というファイルを作成し、次の内容を記述しましょう。
# 初期化の内容をここに追加
そして、ジェネレータが呼び出されたときにこのテンプレートをコピーするようにジェネレータを変更しましょう。 ```ruby class InitializerGenerator < Rails::Generators::NamedBase source_root File.expand_path("templates", dir)
def copy_initializer_file copy_file "initializer.rb", "config/initializers/#{file_name}.rb" end end ```
さあ、ジェネレータを実行しましょう:
$ bin/rails generate initializer core_extensions
create config/initializers/core_extensions.rb
$ cat config/initializers/core_extensions.rb
# 初期化の内容をここに追加してください
copy_file
がテンプレートの内容でconfig/initializers/core_extensions.rb
を作成したことがわかります(宛先パスで使用されるfile_name
メソッドはRails::Generators::NamedBase
から継承されます)。
4 ジェネレータのコマンドラインオプション
class_option
を使用して、ジェネレータはコマンドラインオプションをサポートできます。例えば:
class InitializerGenerator < Rails::Generators::NamedBase
class_option :scope, type: :string, default: "app"
end
これでジェネレータは--scope
オプションを使用して呼び出すことができます:
$ bin/rails generate initializer theme --scope dashboard
オプションの値は、ジェネレータのメソッド内でoptions
を介してアクセスできます:
def copy_initializer_file
@scope = options["scope"]
end
5 ジェネレータの解決
ジェネレータの名前を解決する際、Railsは複数のファイル名を使用してジェネレータを探します。例えば、bin/rails generate initializer core_extensions
を実行すると、Railsは以下のファイルを順番に読み込んでいき、最初に見つかったものを使用します:
rails/generators/initializer/initializer_generator.rb
generators/initializer/initializer_generator.rb
rails/generators/initializer_generator.rb
generators/initializer_generator.rb
これらのいずれも見つからない場合はエラーが発生します。
ジェネレータをアプリケーションのlib/
ディレクトリに配置したのは、そのディレクトリが$LOAD_PATH
に含まれているため、Railsがファイルを見つけて読み込むことができるからです。
6 Railsジェネレータのテンプレートのオーバーライド
Railsは、ジェネレータのテンプレートファイルを解決する際に複数の場所を探します。そのうちの1つがアプリケーションのlib/templates/
ディレクトリです。この動作により、Railsの組み込みジェネレータが使用するテンプレートをオーバーライドすることができます。例えば、scaffold controller templateやscaffold view templatesをオーバーライドすることができます。
これを実演するために、次の内容のlib/templates/erb/scaffold/index.html.erb.tt
ファイルを作成しましょう:
<%% @<%= plural_table_name %>.count %> <%= human_name.pluralize %>
テンプレートは、別のERBテンプレートをレンダリングするERBテンプレートです。したがって、結果としてのテンプレートに表示される<%
は、ジェネレータのテンプレートでは<%%
とエスケープする必要があります。
それでは、Railsの組み込みのscaffoldジェネレータを実行してみましょう:
$ bin/rails generate scaffold Post title:string
...
create app/views/posts/index.html.erb
...
app/views/posts/index.html.erb
の内容は次のとおりです:
<% @posts.count %> Posts
7 Railsジェネレータのオーバーライド
Railsの組み込みジェネレータは、config.generators
を使用して設定することができます。これには、一部のジェネレータを完全にオーバーライドすることも含まれます。
まず、scaffoldジェネレータの動作を詳しく見てみましょう。
$ bin/rails generate scaffold User name:string
invoke active_record
create db/migrate/20230518000000_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
invoke resource_route
route resources :users
invoke scaffold_controller
create app/controllers/users_controller.rb
invoke erb
create app/views/users
create app/views/users/index.html.erb
create app/views/users/edit.html.erb
create app/views/users/show.html.erb
create app/views/users/new.html.erb
create app/views/users/_form.html.erb
create app/views/users/_user.html.erb
invoke resource_route
invoke test_unit
create test/controllers/users_controller_test.rb
create test/system/users_test.rb
invoke helper
create app/helpers/users_helper.rb
invoke test_unit
invoke jbuilder
create app/views/users/index.json.jbuilder
create app/views/users/show.json.jbuilder
出力からわかるように、scaffoldジェネレータは他のジェネレータを呼び出しています。例えば、scaffold_controller
ジェネレータなどです。そして、それらのジェネレータの中にも他のジェネレータを呼び出すものがあります。特に、scaffold_controller
ジェネレータは、helper
ジェネレータを含むいくつかの他のジェネレータを呼び出します。
組み込みのhelper
ジェネレータを新しいジェネレータでオーバーライドしてみましょう。ジェネレータの名前をmy_helper
とします:
$ bin/rails generate generator rails/my_helper
create lib/generators/rails/my_helper
create lib/generators/rails/my_helper/my_helper_generator.rb
create lib/generators/rails/my_helper/USAGE
create lib/generators/rails/my_helper/templates
invoke test_unit
create test/lib/generators/rails/my_helper_generator_test.rb
そして、lib/generators/rails/my_helper/my_helper_generator.rb
に以下のようにジェネレータを定義します:
class Rails::MyHelperGenerator < Rails::Generators::NamedBase
def create_helper_file
create_file "app/helpers/#{file_name}_helper.rb", <<~RUBY
module #{class_name}Helper
# I'm helping!
end
RUBY
end
end
最後に、組み込みのhelper
ジェネレータの代わりにmy_helper
ジェネレータを使用するようにRailsに指示する必要があります。そのためには、config.generators
を使用します。config/application.rb
に以下を追加しましょう:
config.generators do |g|
g.helper :my_helper
end
これで、scaffoldジェネレータを再度実行すると、my_helper
ジェネレータが動作することがわかります:
$ bin/rails generate scaffold Article body:text
...
invoke scaffold_controller
...
invoke my_helper
create app/helpers/articles_helper.rb
...
注意:組み込みのhelper
ジェネレータの出力には「invoke test_unit」という記述が含まれていることに気付くかもしれませんが、my_helper
の出力には含まれていません。helper
ジェネレータはデフォルトではテストを生成しませんが、hook_for
を使用してテストを生成するフックを提供しています。同様に、MyHelperGenerator
クラスにhook_for :test_framework, as: :helper
を含めることで、同じことを行うことができます。詳細については、hook_for
のドキュメントを参照してください。
7.1 ジェネレータのフォールバック
特定のジェネレータをオーバーライドする別の方法として、フォールバックを使用する方法があります。フォールバックを使用すると、ジェネレータの名前空間が別のジェネレータの名前空間に委譲することができます。
例えば、test_unit:model
ジェネレータを my_test_unit:model
ジェネレータで上書きしたい場合を考えましょう。ただし、test_unit:controller
のような他の test_unit:*
ジェネレータはすべて置き換えたくありません。
まず、lib/generators/my_test_unit/model/model_generator.rb
に my_test_unit:model
ジェネレータを作成します。
module MyTestUnit
class ModelGenerator < Rails::Generators::NamedBase
source_root File.expand_path("templates", __dir__)
def do_different_stuff
say "Doing different stuff..."
end
end
end
次に、config.generators
を使用して test_framework
ジェネレータを my_test_unit
として設定しますが、my_test_unit:*
ジェネレータが見つからない場合は test_unit:*
にフォールバックするように設定します。
config.generators do |g|
g.test_framework :my_test_unit, fixture: false
g.fallbacks[:my_test_unit] = :test_unit
end
これで、スキャフォールドジェネレータを実行すると、my_test_unit
が test_unit
の代わりに使用されますが、モデルテストのみが影響を受けます。
$ bin/rails generate scaffold Comment body:text
invoke active_record
create db/migrate/20230518000000_create_comments.rb
create app/models/comment.rb
invoke my_test_unit
Doing different stuff...
invoke resource_route
route resources :comments
invoke scaffold_controller
create app/controllers/comments_controller.rb
invoke erb
create app/views/comments
create app/views/comments/index.html.erb
create app/views/comments/edit.html.erb
create app/views/comments/show.html.erb
create app/views/comments/new.html.erb
create app/views/comments/_form.html.erb
create app/views/comments/_comment.html.erb
invoke resource_route
invoke my_test_unit
create test/controllers/comments_controller_test.rb
create test/system/comments_test.rb
invoke helper
create app/helpers/comments_helper.rb
invoke my_test_unit
invoke jbuilder
create app/views/comments/index.json.jbuilder
create app/views/comments/show.json.jbuilder
8 アプリケーションテンプレート
アプリケーションテンプレートは特別な種類のジェネレータです。これらはすべてのジェネレータヘルパーメソッドを使用できますが、RubyクラスではなくRubyスクリプトとして書かれます。以下に例を示します。
# template.rb
if yes?("Would you like to install Devise?")
gem "devise"
devise_model = ask("What would you like the user model to be called?", default: "User")
end
after_bundle do
if devise_model
generate "devise:install"
generate "devise", devise_model
rails_command "db:migrate"
end
git add: ".", commit: %(-m 'Initial commit')
end
まず、テンプレートはユーザーに Devise をインストールするかどうか尋ねます。ユーザーが「はい」(または「y」)と回答すると、テンプレートは Gemfile
に Devise を追加し、Deviseユーザーモデルの名前をユーザーに尋ねます(デフォルトは User
)。その後、bundle install
が実行された後、テンプレートはDeviseジェネレータと rails db:migrate
を実行します。最後に、テンプレートはアプリケーションディレクトリ全体を git add
および git commit
します。
新しいRailsアプリケーションを生成する際に、rails new
コマンドに -m
オプションを渡すことで、テンプレートを実行できます。
$ rails new my_cool_app -m path/to/template.rb
または、既存のアプリケーション内で bin/rails app:template
を使用してテンプレートを実行できます。
$ bin/rails app:template LOCATION=path/to/template.rb
テンプレートはローカルに保存する必要はありません。パスの代わりにURLを指定することもできます。
$ rails new my_cool_app -m http://example.com/template.rb
$ bin/rails app:template LOCATION=http://example.com/template.rb
9 ジェネレータヘルパーメソッド
Thorは、Thor::Actions
を介して多くのジェネレータヘルパーメソッドを提供しています。以下にいくつかの例を示します。
さらに、RailsはRails::Generators::Actions
を介して多くのヘルパーメソッドを提供しています。
フィードバック
このガイドの品質向上にご協力ください。
タイポや事実の誤りを見つけた場合は、ぜひ貢献してください。 開始するには、ドキュメントへの貢献セクションを読んでください。
不完全なコンテンツや最新でない情報も見つかるかもしれません。 メインのドキュメントに不足しているドキュメントを追加してください。 修正済みかどうかは、まずEdge Guidesを確認してください。 スタイルと規約については、Ruby on Rails Guides Guidelinesを確認してください。
修正すべき点を見つけたが、自分で修正できない場合は、 問題を報告してください。
そして最後に、Ruby on Railsのドキュメントに関するあらゆる議論は、公式のRuby on Railsフォーラムで大歓迎です。