💎

Rake Task の定義記法がよくわからなかったので調べた

2021/07/28に公開

Ruby も勉強しつつ Railsを触っています。Rake タスクの記述方法でよくわからない書き方に遭遇しました。

task products: :environment do
    calculate_products
end

do~end でタスク定義のブロックを渡していることはわかりますが、products: :environment が理解できず。 products というタスク名が定義され、ブロックが実行される前に :environment という事前タスクを実行しておく という流れになるのはわかるのですが、なぜそういう処理になるのか、引数の渡し方がわかっていません。せっかくなので調べてみることにしました。

前提知識もとい TL;DR

  • task メソッドは引数としてタスク名、パラメータ、依存タスク名を受け取れる
  • Hash リテラルの {:name => "value"}{name: "value"} は同じである
  • メソッド呼び出しで、引数リストの最後が Hash リテラルの場合は {} を省略できる

特に最後のルールがわかっていなくて苦労しました。こちらの記事に助けられました、ありがとうございます。

https://qiita.com/raccy/items/1168c7e8849dedf70fa4

task メソッド深堀り

よくわからなくて task メソッドの定義を見たら、

   task(task_name)
   task(task_name: dependencies)
   task(task_name, arguments => dependencies)

Declare a basic task. The task_name is always the first argument. If the task name contains a “:” it is defined in that namespace. The dependencies may be a single task name or an Array of task names. The argument (a single name) or arguments (an Array of names) define the arguments provided to the task. The task, argument and dependency names may be either symbols or strings. A task with a single dependency:

task clobber: %w[clean] do
    rm_rf "html"   
end

A task with an argument and a dependency:

task :package, [:version] => :test do |t, args|
    # ...
end

先頭の引数にタスク名がくることはわかりましたが、:=> の存在がどういう意味なのかがわかっていません。Hash リテラルっぽいですが、Hash リテラルは

{name: "value"}
// であったり
{:name => "value"}

のように、{}が必要なはずです。コンソールで打ってもエラーになるし。

irb(main):006:0> task_name: "abc"
Traceback (most recent call last):
SyntaxError ((irb):6: syntax error, unexpected ':', expecting end-of-input)
task_name: "abc"

よくわからないということで、task の実装まで見ていくことにしました。どうやら、末尾の依存関係があるかないかで引数をパースするメソッドを変えている模様。

def resolve_args_with_dependencies(args, hash) # :nodoc:

やはり引数として hash を受けているようです。ということは、特殊な受け取り方をして task 内部で Hash に変換しているか、メソッドにパラメータを渡すときに何かしらのルールがあるのかな?と考えました。調べているとドンピシャな記事が。

https://qiita.com/raccy/items/1168c7e8849dedf70fa4

引数リストの最後がHashリテラルの場合は{}を省略できる。

これか…。Ruby文法は勉強中ですが、このあたりの糖衣構文は全然カバーできていません。助かりました。

仕掛けがわかればいろいろな書き方ができる

というわけで、仕組みわからなかった以下のtask呼び出しは、

task products: :environment do
    calculate_products
end
  • key が :products シンボル、 value が :environment の Hash リテラルを渡している
  • これは task メソッド側で task :t => [:d] と認識される
  • :products がタスク名、 :environment が依存する事前タスクと解釈される

のような動きになっていることがわかりました。仕掛けがわかればこちらのものです。以下はすべて同じ解釈のされかたをされるはずです。

  task products: :environment do
    calculate_products
  end
  
  task :products => :environment do
    calculate_products
  end

  task products: [:environment] do
    calculate_products
  end


勉強になりました。

Discussion