Rails ジェネレータとテンプレート入門

Railsの各種ジェネレータとアプリケーションテンプレートは、定型コードを自動的に生成してワークフローを改善するツールとして非常に有用です。

このガイドの内容:

  • アプリケーションで利用可能なジェネレータを確認する方法
  • テンプレートを利用してカスタムジェネレータを作成する方法
  • Railsがジェネレータを呼び出すときにジェネレータを探索するしくみ
  • ジェネレータやテンプレートをオーバーライドしてRailsのscaffoldをカスタマイズする方法
  • 特定のジェネレータをオーバーライドするフォールバックの設定方法
  • テンプレートでRailsアプリケーションを作成・カスタマイズする方法
  • RailsテンプレートAPIを利用して再利用可能なアプリケーションテンプレートを作成する方法

1 ジェネレータとは

rails newコマンドでアプリケーションを作成すると、Railsのジェネレータが使われます。ジェネレータ(generator)は、アプリケーションの特定のファイルを作成し、定型コードの自動化を可能にします。

Railsアプリケーション内で以下のようにbin/rails generateコマンドを呼び出すと、利用可能なすべてのジェネレータのリストを取得できます。

$ rails new myapp
$ cd myapp
$ bin/rails generate
Usage:
  bin/rails generate GENERATOR [args] [options]

General options:
  -h, [--help]     # Print generator's options and usage
  -p, [--pretend]  # Run but do not make any changes
  -f, [--force]    # Overwrite files that already exist
  -s, [--skip]     # Skip files that already exist
  -q, [--quiet]    # Suppress status output

Please choose a generator below.

Rails:
  application_record
  authentication
  benchmark
  channel
  controller
  generator
  ...
SolidQueue:
  solid_queue:install

Stimulus:
  stimulus

TestUnit:
  test_unit:authentication
  test_unit:channel
  ...

Railsアプリケーションを新しく作成するときは、gem install railsでインストールしたバージョンのRailsを使うグローバルなrailsコマンドが使われますが、作成したアプリケーションのディレクトリ内では、アプリケーションにバンドルされているバージョンのRailsを使うbin/railsコマンドが使われる点が異なります。

上のコマンドによって、Railsで利用可能なすべてのジェネレータのリストと利用方法を表示できます。

ジェネレータを実行するときに、以下のように--pretend(または-p)オプションを付けて実行すると、ジェネレータがどのような処理を行うかを、ファイルを変更せずに確認できます。

$ bin/rails generate model product name:string --pretend
      invoke  active_record
      create    db/migrate/20260407190300_create_products.rb
      create    app/models/product.rb
      invoke    test_unit
      create      test/models/product_test.rb
      create      test/fixtures/products.yml

上記のファイルは、--pretendオプションを付けて実行した場合、実際には作成されません。

--pretendオプションは、関連するジェネレータが生成するファイルの差分を、実際に実行する前に確認するのに便利です。たとえば、modelとresourceジェネレータの違いを確認できます。

特定のジェネレータの詳しい説明を表示するには、以下のように--helpオプションを付けてジェネレータを呼び出します。

$ bin/rails generate scaffold --help
Usage:
  bin/rails generate scaffold NAME [field[:type][:index] field[:type][:index]] [options]
...
Description:
    Scaffolds an entire resource, from model and migration to controller and
    views, along with a full test suite. The resource is ready to use as a
    starting point for your RESTful, resource-oriented application.
...
Examples:
    `bin/rails generate scaffold post`
    `bin/rails generate scaffold post title:string body:text published:boolean`
    `bin/rails generate scaffold purchase amount:decimal tracking_id:integer:uniq`
    `bin/rails generate scaffold user email:uniq password:digest`
...

--helpオプションを付けると詳しい利用方法や実行例が出力されるので、特定のジェネレータについて詳しく知るための良い情報源となります。

2 最初のジェネレータを作成する

Railsでは、ジェネレータに加えて、カスタムジェネレータを構築する機能も提供されています。ここでは、config/initializers/フォルダ内にhello_generator.rbという名前のイニシャライザファイルを作成するジェネレータを作成してみましょう。最初は手動でジェネレータを作成し、次にgeneratorコマンドでジェネレータを作成する方法も見ていきます。

ジェネレータはThorをベースとして構築されています。Thorは解析機能などの便利なオプションやファイル操作用のAPIを提供するライブラリです。

ジェネレータを手書きするときの最初のステップとして、lib/generators/ディレクトリの下にinitializer_generator.rbという名前のファイルを以下の内容で作成します。

class InitializerGenerator < Rails::Generators::Base
  def create_initializer_file
    create_file "config/initializers/hello_generator.rb", <<~RUBY
      # hello_generator.rbファイルの内容をここに追加する
    RUBY
  end
end

このジェネレータの名前は、ファイル名とRubyクラス名に基づいてinitializerとし、Rails::Generators::Baseクラスを継承しています。ジェネレータが呼び出されると、ジェネレータ内の各パブリックメソッドが定義された順序で順番に実行されます。

この新しいジェネレータは意図的にシンプルなものにしてあり、メソッド定義は1個しかありません。このメソッドはcreate_fileを呼び出し、指定された場所に指定の内容でファイルを作成します。

新しいジェネレータを呼び出すには、以下を実行します。

$ bin/rails generate initializer

これで、config/initializersフォルダ内にhello_generator.rbという空のファイルが作成されます。

次に進む前に、今作成したばかりのジェネレータの説明を表示してみましょう。

$ bin/rails generate initializer --help

通常は、ジェネレータがActiveRecord::Generators::ModelGeneratorのように名前空間化されていれば、実用的な説明文を生成できますが、今作成したジェネレータはそうなっていません。

この問題は2通りの方法で解決できます。1つ目の方法は、ジェネレータ内でdescメソッドを呼び出すことです。

class InitializerGenerator < Rails::Generators::Base
  desc "このジェネレータはconfig/initializersにイニシャライザファイルを作成します"
  def create_initializer_file
    create_file "config/initializers/hello_generator.rb", <<~RUBY
      # hello_generator.rbファイルの内容をここに追加する
    RUBY
  end
end

これで、--helpを付けて新しいジェネレータを呼び出すと新しい説明文が表示されるようになりました。

説明文を追加する2つ目の方法は、ジェネレータと同じディレクトリにUSAGEという名前のファイルを作成することです。次に、この方法で実際に説明文を追加してみましょう。

2.1 ジェネレータでジェネレータを生成する

Railsには、ジェネレータを生成するためのジェネレータもあります。

今作った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

これで、以下のようなジェネレータが作成されます。

# lib/generators/initializer/initializer_generator.rb
class InitializerGenerator < Rails::Generators::NamedBase
  source_root File.expand_path("templates", __dir__)
end

上のジェネレータを見て最初に気付く点は、Rails::Generators::Baseではなく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ファイルを以下の内容で作成しましょう。

# 初期化用のコンテンツをここに追加する

次に、ジェネレータを以下のように変更して、ジェネレータが呼び出されたときにこのテンプレートをコピーするようにします。

# lib/generators/initializer/initializer_generator.rb
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から継承されています)。

2.2 ジェネレータのコマンドラインオプション

ジェネレータでコマンドラインオプションをサポートするには、以下のようにclass_optionメソッドを使います。

class InitializerGenerator < Rails::Generators::NamedBase
  class_option :scope, type: :string, default: "app"
end

これで、--scopeオプションを指定してジェネレータを呼び出せるようになります。

$ bin/rails generate initializer theme --scope dashboard

これにより、デフォルト値の"app"が"dashboard"で上書きされます。

オプションの値は、ジェネレータ内のメソッドからoptionsでアクセスできます。

def copy_initializer_file
  @scope = options["scope"]
  copy_file "initializer.rb", "config/initializers/#{@scope}/#{file_name}.rb"
end

これで、ジェネレータで--scopeオプションを設定すると、theme.rbファイルがconfig/initializers/dashboard/に生成されるようになりました。

3 ジェネレータ名の解決

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

ジェネレータがどのファイルにも見つからない場合は、エラーがraiseされます。

上の例でジェネレータのファイルをアプリケーションのlib/ディレクトリの下に置いた理由は、このディレクトリが$LOAD_PATH(Rubyがファイルを読み込むときに探索するディレクトリのリスト)に含まれているからです。これにより、Railsがこのファイルを検索して読み込めるようになります。

$LOAD_PATHはRubyによって初期化され、起動時にBundlerとRailsによって拡張されます。Bundlerは各gemに含まれているlib/ディレクトリを追加し、Railsはアプリケーションのlib/ディレクトリを追加します。これにより、そこに配置されたジェネレータが見つかるようになります。bin/rails runner 'puts $LOAD_PATH'を実行すると、完全な読み込みパスを確認できます。読み込みパスは、application.rbファイルのconfig.autoload_pathsで変更することも可能です。

4 Railsジェネレータとテンプレートをオーバーライドする

config.generatorsを設定することで、Rails組み込みのジェネレータをオーバーライドできます。Railsアプリケーションの成長に応じて、生成されるコントローラに独自のメソッドを追加したり、生成されるビューのフォーマットを変更したりしたくなることもあるでしょう。

組み込みジェネレータをオーバーライドする方法の例として、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にします。

generatorコマンドを使って、ジェネレータをlib/generators/railsディレクトリの下に作成します。

$ 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ファイルを開いて以下のジェネレータを定義します。

# 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
        # 私はヘルパー
      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のドキュメントを参照してください。

4.1 ジェネレータをフォールバックでオーバーライドする

特定のジェネレータをオーバーライドする別の方法は、フォールバックを使う方法です。フォールバックを使うと、マッチするジェネレータが見つからない場合に、ジェネレータの名前空間を別のジェネレータの名前空間に委譲できます。

たとえば、my_test_unit:modelジェネレータを作成してtest_unit:modelジェネレータをオーバーライドしたいとします。しかし、test_unit:controllerジェネレータなどの他のtest_unit:*ジェネレータはオーバーライドしたくないとします。

このような場合、すべてのジェネレータをmy_test_unit名前空間に実装する代わりに、明示的に定義していないジェネレータについてはtest_unitにフォールバックするようにmy_test_unitを設定できます。

最初に、my_test_unit:modelジェネレータをlib/generators/my_test_unit/model/model_generator.rbファイルに作成します。

module MyTestUnit
  class ModelGenerator < Rails::Generators::NamedBase
    source_root File.expand_path("templates", __dir__)

    def do_different_stuff
      say "別の作業を実行中..."
    end
  end
end

my_test_unitは、Rails組み込みのジェネレータをオーバーライドするのではなく、カスタム名前空間であるため、ここではlib/generators/rails/ディレクトリではなくlib/generators/my_test_unit/ディレクトリに配置しています。Railsは通常の読み込みパスの探索でこのジェネレータを見つけます。次のステップで、config.generatorsを使ってtest_frameworkとして登録する必要があります。

次に、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

これで、scaffoldジェネレータを実行すると、test_unitがmy_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
    別の作業を実行中...
      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

モデルでのmy_test_unitジェネレータの呼び出しは、"別の作業を実行中..."と表示されるだけで、テストファイルは作成されません。これは、カスタムジェネレータがテストを作成していないためです。コントローラとヘルパーでのmy_test_unitの呼び出しはtest_unitにフォールバックするため、test/controllers/comments_controller_test.rbは通常通り生成されます。

4.2 ジェネレータのテンプレートをオーバーライドする

Railsは、ジェネレータのテンプレートファイルを解決するときに、最初にアプリケーションのlib/templates/ディレクトリを探索し、それからジェネレータ自身のsource_rootディレクトリを探索します。つまり、lib/templates/ディレクトリに自分のバージョンのテンプレートを置くことで、Rails組み込みのジェネレータで使われるテンプレートをオーバーライドできるということです。

たとえば、コントローラのscaffoldテンプレートやビューのscaffoldテンプレートをオーバーライドできます。

これを実際に見るために、lib/templates/erb/scaffold/index.html.erb.ttファイルを作成して以下のコンテンツを追加してみましょう。なお、.ttという拡張子が追加されているのは、このファイルがThorによって最初に処理される必要があるジェネレータテンプレートであることをRailsに伝えるためです(.ttは"thor template"の略です)。

<%%= @<%= 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

5 アプリケーションテンプレート

アプリケーションテンプレートは、ジェネレータと若干異なる点があります。

ジェネレータは、既存のRailsアプリケーションにモデルやビューなどのファイルを追加しますが、アプリケーションテンプレートは、rails newコマンドで生成する新規Railsアプリケーションをその場で自動セットアップするのに使われます。アプリケーションテンプレートは、新しいRailsアプリケーションを生成した直後にカスタマイズを実行するRubyスクリプトであり、通常はtemplate.rbという名前です。

Railsアプリケーションをアプリケーションテンプレートで作成する方法を見てみましょう。

5.1 テンプレートを作成して利用する

最初は、サンプルのRubyスクリプトテンプレートを作成してみましょう。 以下のテンプレートは、ユーザーに確認した後、GemfileにDeviseを追加し、Deviseユーザーモデル名を入力できるようにします。bundle installの実行後、テンプレートはDeviseジェネレータとマイグレーションを実行します。最後に、git addとgit commitを実行します。

# template.rb
if yes?("Deviseをインストールしますか?")
  gem "devise"
  devise_model = ask("ユーザーモデル名は何にしますか?", 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

rails newコマンドでこのテンプレートを使って新しいRailsアプリケーションを作成するには、以下のように-mオプションでテンプレートの場所を指定します。

$ rails new blog -m ~/template.rb

これで、新規Railsアプリケーションをblogという名前で作成するときに、Devise gemも設定されるようになります。

app:templateコマンドを使えば、既存のRailsアプリケーションにテンプレートを適用することも可能です。 この場合、テンプレートファイルの場所をLOCATION環境変数で指定する必要があります。

$ bin/rails app:template LOCATION=~/template.rb

テンプレートは必ずしもローカルに保存する必要はありません。ファイルパスの代わりに外部URLも指定できます。

$ rails new blog -m https://example.com/template.rb
$ bin/rails app:template LOCATION=https://example.com/template.rb

第三者が提供するリモートスクリプトを実行するときは注意が必要です。テンプレートは単なるRubyスクリプトなので、ローカルコンピュータを危険にさらすコード(ウイルスのダウンロード、ファイルの削除、個人ファイルのサーバーへのアップロードなど)を簡単に仕込めてしまいます。

上述のtemplate.rbファイルでは、after_bundleやrails_commandなどのヘルパーメソッドを使い、yes?のようなユーザーインタラクティビティも追加しています。これらのメソッドはすべてRailsテンプレートAPIの一部です。これらのメソッドの利用例を以後のセクションで示します。

6 RailsジェネレータAPI

ジェネレータやテンプレートのRubyスクリプトは、DSL(ドメイン固有言語)を使ってさまざまなヘルパーメソッドにアクセスできます。これらのメソッドはRailsジェネレータAPIの一部であり、詳しくはThor::ActionsやRails::Generators::ActionsのAPIドキュメントで確認できます。

もう一つの典型的なRailsテンプレートの例を見てみましょう。このテンプレートはモデルをscaffoldで生成してからマイグレーションを実行し、変更をgitでコミットします。

# template.rb
generate(:scaffold, "person name:string")
route "root to: 'people#index'"
rails_command("db:migrate")

after_bundle do
  git :init
  git add: "."
  git commit: %Q{ -m 'Initial commit' }
end

以下の例で使われているコードスニペットは、すべて上記のtemplate.rbファイルなどのテンプレートファイルで利用可能です。

6.1 add_source

add_sourceメソッドは、指定したソース(gemの取得元)を、生成されるアプリケーションのGemfileに追加します。

add_source "https://rubygems.org"

このメソッドにブロックを渡すと、ブロック内のgemエントリがソースグループにラップされます。 たとえば、gemを"http://gems.github.com"から取得する必要がある場合は以下のようにします。

add_source "http://gems.github.com/" do
  gem "rspec-rails"
end

6.2 after_bundle

after_bundleメソッドは、gemのバンドルが完了した後に実行されるコールバックを登録します。 たとえば、tailwindcss-railsとdeviseのインストールコマンドは、それらのgemがバンドルされた後に実行するのが合理的です。

# gemをインストールする
after_bundle do
  # TailwindCSSをインストールする
  rails_command "tailwindcss:install"

  # Deviseをインストールする
  generate "devise:install"
end

このコールバックは、rails newコマンドで--skip-bundleオプションを指定した場合でも実行される点にご注意ください。

6.3 environment

environmentメソッドは、config/application.rbのApplicationクラス内に行を追加します。options[:env]が指定されている場合、その行はconfig/environments/ディレクトリ内の対応するファイルに追加されます。

environment 'config.action_mailer.default_url_options = {host: "http://yourwebsite.example.com"}', env: "production"

上のコードは、config/environments/production.rbに設定行を追加します。

6.4 gem

gemメソッドは、指定のgemエントリを、生成されるアプリケーションのGemfileに追加します。

たとえば、アプリケーションをdevise gemとtailwindcss-rails gemに依存させる場合は、以下のようにします。

gem "devise"
gem "tailwindcss-rails"

このメソッドは、gemをGemfileに追加するだけで、gemのインストールは行わない点にご注意ください。

gemのバージョンも指定できます。

gem "devise", "~> 4.9.4"

Gemfileにコメント付きでgemを追加することも可能です。

gem "devise", comment: "Add devise for authentication."

6.5 gem_group

gem_groupメソッドは、gemエントリをグループにラップします。 たとえば、rspec-railsをdevelopmentグループとtestグループでのみ読み込むには、以下のようにします。

gem_group :development, :test do
  gem "rspec-rails"
end

6.6 generate

generateメソッドを使うと、template.rbファイル内でRailsジェネレータを呼び出せます。 たとえば、scaffoldジェネレータを呼び出してPersonモデルを生成するには、以下のようにします。

generate(:scaffold, "person", "name:string", "address:text", "age:number")

6.7 git

gitヘルパーメソッドを使うと、Railsテンプレート内で任意のgitコマンドを実行できます。

git :init
git add: "."
git commit: "-a -m 'Initial commit'"

6.8 initializer、vendor、lib、file

initializerヘルパーメソッドは、生成されるアプリケーションのconfig/initializers/ディレクトリにイニシャライザファイルを追加します。

template.rbファイルに以下のコードを追加すると、アプリケーションでObject#not_nil?とObject#not_blank?を使えるようになります。

initializer "not_methods.rb", <<-CODE
  class Object
    def not_nil?
      !nil?
    end

    def not_blank?
      !blank?
    end
  end
CODE

同様に、libメソッドはファイルをlib/ディレクトリに作成し、vendorメソッドはファイルをvendor/ディレクトリに作成します。

fileメソッドはcreate_fileのエイリアスです。これはRails.rootからの相対パスを受け取って、必要なディレクトリとファイルをすべて作成します。

file "app/components/foo.rb", <<-CODE
  class Foo
  end
CODE

上のコードはapp/components/ディレクトリを作成し、その中にfoo.rbを配置します。

6.9 rakefile

rakefileメソッドは、指定のタスクを含む新しいRakeファイルをlib/tasks/ディレクトリに作成します。

rakefile("bootstrap.rake") do
  <<-TASK
    namespace :boot do
      task :strap do
        puts "I like boots!"
      end
    end
  TASK
end

上のコードは、lib/tasks/bootstrap.rakeファイルを作成し、boot:strap rakeタスクを定義します。

6.10 run

runメソッドは、任意のコマンドを実行します。 たとえば、README.rdocファイルを削除したい場合は、以下のようにします。

run "rm README.rdoc"

6.11 rails_command

rails_commandメソッドを使うと、生成されるアプリケーションでRailsコマンドを実行できます。 たとえば、テンプレートのRubyスクリプト内でデータベースをマイグレーションしたい場合は、以下のようにします。

rails_command "db:migrate"

Railsの環境を指定してコマンドを実行することも可能です。

rails_command "db:migrate", env: "production"

abort_on_failureオプションを指定することで、コマンド実行に失敗した場合はアプリケーションの生成を中止することも可能です。

rails_command "db:migrate", abort_on_failure: true

6.12 route

routeメソッドは、config/routes.rbファイルにエントリを追加します。 アプリケーションのデフォルトページをPeopleController#indexにするには、以下を追加します。

route "root to: 'people#index'"

この他にも、copy_file、create_file、insert_into_file、insideなどのローカルファイルシステムを操作するヘルパーメソッドが多数用意されています。詳しくはThorのAPIドキュメントを参照してください。

以下にそのようなメソッドの例を示します。

6.13 inside

insideメソッドは、コマンドを指定のディレクトリ内から実行できるようにします。 たとえば、新しいアプリケーションからedge railsのコピーへのシンボリックリンクを作成したい場合は、以下のようにします。

inside("vendor") do
  run "ln -s ~/my-forks/rails rails"
end

この他に、ask、yes?、no?など、Rubyテンプレートからユーザーと対話できるメソッドも利用できます。すべてのユーザー対話メソッドについては、Thorのシェルドキュメントで確認できます。 以下にask、yes?、no?の例を示します。

6.14 ask

askメソッドを使うと、ユーザーからの入力を受け取ってテンプレートで利用できます。 たとえば、新しいライブラリの名前をユーザーに尋ねたい場合は、以下のようにします。

lib_name = ask("新しいライブラリの名前を入力してください:")
lib_name << ".rb" unless lib_name.index(".rb")

lib lib_name, <<-CODE
  class Shiny
  end
CODE

6.15 yes?とno?

yes?メソッドやno?メソッドを使って、yes/noで答えられる質問を手軽にユーザーに表示して、ユーザーの回答に応じて処理の流れを決められます。 たとえば、ユーザーにマイグレーションを実行するかどうか尋ねたい場合は、以下のようにします。

rails_command("db:migrate") if yes?("マイグレーションを実行しますか?")
# no?メソッドはyes?メソッドの逆の動作

7 ジェネレータをテストする

Railsは、Rails::Generators::Testing::Behaviorで以下のようなテストヘルパーメソッドを提供しています。

ジェネレータをテストする場合、デバッグツールが機能するために以下のようにコマンドでRAILS_LOG_TO_STDOUT=trueを指定する必要があります。

RAILS_LOG_TO_STDOUT=true ./bin/test test/generators/actions_test.rb

Railsではその他にも、Rails::Generators::Testing::Assertionsで追加のアサーションを提供しています。

フィードバックについて

Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。

原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨

本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。

Railsガイド運営チーム (@RailsGuidesJP)

支援・協賛

Railsガイド協賛プラン - バナー画像

Railsガイドは下記の協賛企業から継続的な支援を受けています。もしご興味あれば、協賛プランから気軽にお問い合わせいただけると嬉しいです。

  1. Star
  2. このエントリーをはてなブックマークに追加