読者です 読者をやめる 読者になる 読者になる

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が呼ばれる