QA@IT

Rails 用の gem の Railtie で rake タスクを追加するかどうかをユーザが設定できるようにするには?

3111 PV

rails 用の gem で rake タスクを追加する、以下のコードの XXXXXXX の部分がうまくかけません。
rake タスクを追加するかどうかをユーザが設定できるようにしたいです。

require 'foo'
require 'rails'

module Foo

  class Railtie < ::Rails::Railtie

    if XXXXXXX
      rake_tasks do
        load "path/to/foobar.rake"
      end
    end
  end
end

config.foo = ActiveSupport::OrderedOptions.new などとして、config/application.rb で設定しても、rake_tasks が有効なのは、config.before_configuration みたいで設定を効かせられません。

別の案として、gem の方で

module Foo
  def self.enable=(enable)
    @enable = enable
  end

  def self.enable?
    @enable == true
  end
end

とし、config/initializer/foo.rb で

if defined? Foo
  Foo.enable = true
end

とやっても、rake -T では config/initializer/foo.rb を見ていないように思えます(config/initializer/foo.rb 内で raise "foo" しても rake -T でエラーになりませんでした)。
rake タスクを追加するかをユーザが設定できるようにする方法はないでしょうか。

回答

まず、Rails アプリケーションのルートディレクトリで rake を実行した場合、処理の流れは下記のようになります。

  1. Rakefile をロード
  2. Rakefile から config/application.rb をロード
  3. config/application.rb 内の Bundler.require で Gemfile に書かれた gem をロード
  4. (config/application.rb で config.* など設定)
  5. Rakefile から アプリケーション名::Application.load_tasks が呼ばれ lib/tasks/*.rake をロード

提示された例ではおそらく 3 の時点で rake_tasks を含むコードがロードされているため、config/application.rb (の大部分) より前にタスクの追加が行われています (config/initializers/* は最後までロードされません)。

上記の流れを考慮すると、下記のいづれかのファイル中に 1. ユーザによる、表示・非表示を制御するコード2. rake タスクを追加するコード をこの順番で記述する必要があります。

  • Rakefile
  • config/application.rb
  • lib/tasks/*.rake

個人的には下記のような実装がいいかなと思います。

gem 内に lib/foo/tasks.rb を作成し、下記の様に記述します。

# #{Gem のディレクトリ}/lib/foo/tasks.rb
if Foo.enable?
  desc "Foo task"
  task "foo" do
    p "Foo"
  end
end

gem を使うときは Rails アプリケーション中に lib/tasks/foo.rake を作成し、下記のコードを記述します。

# #{Rails.root}/lib/tasks/foo.rake
Foo.enable = true # タスクの追加制御フラグ設定
require "foo/tasks" # タスクの追加

上記の lib/taks/foo.rake を generator で生成すると親切かもしれません。

編集 履歴 (1)
  • わかりました。読み込み順や rake_tasks() の定義を調べるのをサボって質問してしまい、なんだか悪いことしてしまったな、という気分になってしまいました。。仕組みと対処例をありがとうございました。 -
  • いや、私もこれを機に調べてわかったことばかりで、勉強になりました。ご参考になれば幸いです。 -
ウォッチ

この質問への回答やコメントをメールでお知らせします。