6123


`knife-
solo`の実行時に、適用先のユーザーのホームディレクトリに`.bashrc`が存在するとエラーに成ってしまうので、実際にどのような処理がそこでされているかを確認しようとしました。

まず `chef_version`メソッド内部で`run_command`が呼ばれていたので  
[lib/chef/knife/solo_cook.rb#L294](https://github.com/matschaffer/knife-
solo/blob/master/lib/chef/knife/solo_cook.rb#L294)

[code]

    run_command(cmd).stdout.strip
    
[/code]

`run_command`の宣言場所を探し、`prepare.run_command`が呼ばれているのを確認しました。  
[lib/knife-solo/bootstraps.rb#L31](https://github.com/matschaffer/knife-
solo/blob/f95195e5fc24ab12c540385caf087e50812ee42c/lib/knife-
solo/bootstraps.rb#L31)

[code]

    def run_command(cmd)
      prepare.run_command(cmd)
    end
    
[/code]

ただ、

[lib/knife-solo/bootstraps.rb#L43](https://github.com/matschaffer/knife-
solo/blob/f95195e5fc24ab12c540385caf087e50812ee42c/lib/knife-
solo/bootstraps.rb#L43)

[code]

    module Delegates
      ...
      def prepare
        @prepare
      end
    end #Delegates
    
[/code]

[lib/knife-solo/bootstraps.rb#L122](https://github.com/matschaffer/knife-
solo/blob/f95195e5fc24ab12c540385caf087e50812ee42c/lib/knife-
solo/bootstraps.rb#L122)

[code]

    class Base
      ...
      def initialize(prepare)
        @prepare = prepare
      end
      ...
    end
    
[/code]

の処理がメソッドがインスタンス変数を呼び、インスタンス変数がメソッドにより定義されてるように見えるのですが、これがどのような事を期待した処理なのか理解できませんでした。

Issueへの登録はしています、自分で解決できればと思い質問いたします。  
<https://github.com/matschaffer/knife-solo/issues/410>

この`prepare`からどのように外部コマンドの実行処理部分を探せるのでしょうか？


CC BY-SA 3.0
2015-02-07T09:42:15.430
6094
2015-02-07T14:39:00.007
2015-02-07T14:39:00.007
30
3271
question
ruby chef
knife-soloの内部メソッド`run_command`の実態の場所について


## prepare オブジェクトから直接 run_command メソッドの定義場所を取得する

Ruby >= 1.9 であれば、Method クラスの [`source_location` メソッド](http://ruby-
doc.org/core-1.9.3/Method.html#method-i-source_location)を利用できます。まず gem の場所を調べ:

[code]

    $ gem which knife-solo
    /Users/ento/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/knife-solo-0.4.2/lib/knife-solo.rb
    
[/code]

`prepare` オブジェクトが渡される箇所にデバッグ出力を仕込みます:

[code]

    def initialize(prepare)
      # instance of Chef::Knife::SoloPrepare
      @prepare = prepare
      puts prepare.method(:run_command).source_location
    end
    
[/code]

`knife solo prepare` を実行すると、 `run_command` メソッドが定義されているファイルの場所と行番号が出力されます。

[code]

    $ knife solo prepare vagrant@127.0.0.1 -p 2201 -i ~/.vagrant.d/insecure_private_key
    Bootstrapping Chef...
    /Users/ento/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/knife-solo-0.4.2/lib/knife-solo/ssh_command.rb
    236
    
[/code]

## クラス名から辿る

ソースに手を入れなくても、幸いコメントに`prepare`は何のインスタンスであるかが書いてあります。

[code]

    # instance of Chef::Knife::SoloPrepare
    @prepare = prepare
    
[/code]

`Chef::Knife::SoloPrepare`クラスには残念ながら`run_command`メソッドはありませんが、それらしき`include`があります:

[code]

    class SoloPrepare < Knife
      include KnifeSolo::SshCommand
      include KnifeSolo::NodeConfigCommand
    
[/code]

`KnifeSolo::SshCommand`
クラスに[まさに求めていたメソッド](https://github.com/matschaffer/knife-
solo/blob/v0.4.2/lib/knife-solo/ssh_command.rb#L236)があり、さらに
`KnifeSolo::SshConnection` クラスの [`run_command`
メソッド](https://github.com/matschaffer/knife-solo/blob/v0.4.2/lib/knife-
solo/ssh_connection.rb#L51)を呼び出しています。

[code]

    # KnifeSolo::SshCommand
    def run_command(command, options = {})
      ...
      @connection ||= ssh_connection
      @connection.run_command(command, output)
    end
    
[/code]

クラス名を書いてくれているコメントがなかった場合、クラス名を元に変数を命名している可能性に期待し、「prepare」でレポジトリ内を検索して当たりをつけるというアプローチが考えられます。

* * *

`prepare` オブジェクトの役割としては、 [OS
ごとに定義された初期化用クラス群](https://github.com/matschaffer/knife-
solo/tree/v0.4.2/lib/knife-solo/bootstraps)にコマンド実行やメッセージ表示の API
を提供するファサード的なものであるようです。 [`Chef::Knife::SoloPrepare#bootstrap`
メソッド](https://github.com/matschaffer/knife-
solo/blob/v0.4.2/lib/chef/knife/solo_prepare.rb#L59)内で初期化用クラスに自身を渡しています:

[code]

    KnifeSolo::Bootstraps.class_for_operating_system(operating_system).new(self)
    
[/code]

* * *

`prepare`から辿る方法ではありませんが、["def
run_command"でレポジトリ内を検索する](https://github.com/matschaffer/knife-
solo/search?utf8=%E2%9C%93&q=%22def+run_command%22&type=Code)ことでも実態を探すことができます。この方法は、別のライブラリ
/ レポジトリで定義されているメソッドだった場合には失敗します。


CC BY-SA 3.0
2015-02-07T14:34:18.987
6123
2015-02-07T14:34:18.987
30
6094
answer