Thorの起動の流れを追う
Thorはまってきました。これすごい手になじむというか。。。
基本はRuby 1.9.1 + Thor 0.13.4でなんでもできちゃいそうな錯覚すら。
まずは、thorコマンドから。
Thor::Runner.startを呼んでるだけです。
#!/usr/bin/env ruby # -*- mode: ruby -*- require 'thor/runner' $thor_runner = true Thor::Runner.start
Thor::RunnerはThorを継承しているので呼ばれているのはThor.startですね。
ちなみにThot::Runnerにはデフォルトのメソッドが用意されています。
help install version uninstall update installed list
Thor.startは以下のコード。
def start(original_args=ARGV, config={}) super do |given_args| meth = normalize_task_name(given_args.shift) task = all_tasks[meth] if task args, opts = Thor::Options.split(given_args) config.merge!(:task_options => task.options) else args, opts = given_args, {} end task ||= Thor::Task::Dynamic.new(meth) trailing = args[Range.new(arguments.size, -1)] new(args, opts, config).invoke(task, trailing || []) end end
0. Thor::Base.startからデフォルトのオプション値とconfig[:shell]を設定 1. 名前変換("-" => "_"に変換される) 2. Thor::TaskをいれるためのOrderedHashを生成(1.9ではdefaultではそうだが、1.8系だとライブラリ独自実装してるものを利用) 3. OrderedHashからThor::Taskがないか探す 4. 引数名からThor::Task::Dynamic.newする(Structを作る) 5. 引数からタスク名を除いたものを取り出す 6. 自身を生成してinvokeを呼ぶ(thorコマンドで呼んだときなのでnewするのはThor::Runner)
ここでThor::Runnerインスタンスが生成されます。(newは継承先のThor::Baseが呼ばれる)
def initialize(args=[], options={}, config={}) args = Thor::Arguments.parse(self.class.arguments, args) args.each { |key, value| send("#{key}=", value) } parse_options = self.class.class_options if options.is_a?(Array) task_options = config.delete(:task_options) # hook for start parse_options = parse_options.merge(task_options) if task_options array_options, hash_options = options, {} else array_options, hash_options = [], options end opts = Thor::Options.new(parse_options, hash_options) self.options = opts.parse(array_options) opts.check_unknown! if self.class.check_unknown_options? end
1. Thor::Arguments.parseは以下のことをする 1-1. thorコマンドラインオプションを取る 1-2. それ以外の引数を渡してThor::Argumentsインスタンスを生成し、そのインスタンスに対してparseをthorコマンドラインオプションとともに呼ぶ 1-3. parseした結果をHashで返す 2. オプション解析
なんか何回よんでる気もするけどここでThor::Invocation::.invokeが呼ばれる