2009-03-05 15 views
4

多くのRubyファイルがあり、それぞれがClassと宣言されていますが、それぞれがコマンドラインから実行される可能性があります。Rubyソースを別のファイルにインラインで含めるにはどうすればよいですか?

私は可能少なくとも重複して各ファイルの下部に次の機能を入れたいのですが:

if __FILE__ == $0 
    # instantiate the class and pass ARGV to instance.run 
end 

私の最初の本能は、これを実行することでした。

# /lib/scriptize.rb: 
Kernel.class_eval do 
    def scriptize(&block) 
    block.call(ARGV) if __FILE__ == $0 
    end 
end 

# /lib/some_other_file.rb: 
include 'scriptize' 
class Foo 
    # ... 
end 
scriptize { |args| Foo.new.run(args) } 

をしかし、それはdoesnの__FILE__scriptize.rbで評価されているので、それはではありません。 Fooです。

解決策は文字通りscriptize.rbの内容をインライン展開することですが、構文はわかりません。私はevalを使うことができましたが、これはまだかなり重複しています。それは実際にKernelに追加する方法には還元できません。

+0

を実行することができます。ファイルの末尾にある1行ではなぜそれを行えないのでしょうか?何か 'Foo.new.run(* ARGV)if __FILE__ == $ 0'のようなもの – kch

+0

Kernel#callerを使った解決策があるかもしれません。あなたが私にそれを詳しく説明してもらいたいかどうか教えてください。 – kch

答えて

4

使用callerあなたは、コールスタックの最上部にどれだけ近いかを判断するには:一例として、我々は2を使用することができます

---------------------------------------------------------- Kernel#caller 
    caller(start=1) => array 
------------------------------------------------------------------------ 
    Returns the current execution stack---an array containing strings 
    in the form ``_file:line_'' or ``_file:line: in `method'_''. The 
    optional _start_ parameter determines the number of initial stack 
    entries to omit from the result. 

     def a(skip) 
      caller(skip) 
     end 
     def b(skip) 
      a(skip) 
     end 
     def c(skip) 
      b(skip) 
     end 
     c(0) #=> ["prog:2:in `a'", "prog:5:in `b'", "prog:8:in `c'", "prog:10"] 
     c(1) #=> ["prog:5:in `b'", "prog:8:in `c'", "prog:11"] 
     c(2) #=> ["prog:8:in `c'", "prog:12"] 
     c(3) #=> ["prog:13"] 

これは、今scriptize

# scriptize.rb 
def scriptize 
    yield ARGV if caller.size == 1 
end 

ため、この定義を与えますお互いに必要なライブラリ/実行可能ファイル

# libexA.rb 
require 'scriptize' 
require 'libexB' 

puts "in A, caller = #{caller.inspect}" 
if __FILE__ == $0 
    puts "A is the main script file" 
end 

scriptize { |args| puts "A was called with #{args.inspect}" } 

# libexB.rb 
require 'scriptize' 
require 'libexA' 

puts "in B, caller = #{caller.inspect}" 
if __FILE__ == $0 
    puts "B is the main script file" 
end 

scriptize { |args| puts "B was called with #{args.inspect}" } 

だから我々ru nは、コマンドラインから:

% ruby libexA.rb 1 2 3 4 
in A, caller = ["./libexB.rb:2:in `require'", "./libexB.rb:2", "libexA.rb:2:in `require'", "libexA.rb:2"] 
in B, caller = ["libexA.rb:2:in `require'", "libexA.rb:2"] 
in A, caller = [] 
A is the main script file 
A was called with ["1", "2", "3", "4"] 
% ruby libexB.rb 4 3 2 1 
in B, caller = ["./libexA.rb:2:in `require'", "./libexA.rb:2", "libexB.rb:2:in `require'", "libexB.rb:2"] 
in A, caller = ["libexB.rb:2:in `require'", "libexB.rb:2"] 
in B, caller = [] 
B is the main script file 
B was called with ["4", "3", "2", "1"] 

だから、これはscriptizeを使用しての等価性を示し、if $0 == __FILE__

しかし、それを考慮してください。

  1. if $0 == __FILE__ ... endを簡単に読ん他人が認識し、標準ルビーイディオムですあなたのコード
  2. require 'scriptize'; scriptize { |args| ... }は、同じ効果のためのより多くのタイピングです。

これが本当に価値があるためには、スクリプト化の本文にいくつかのファイルの初期化、引数の解析などの共通性が必要です。十分に複雑になると、クラスをスクリプト化して、インスタンス化してメインのスクリプト本体を実行したり、名前が何であるかに応じてクラスの1つを動的に必要とするメインスクリプトを作成したりすることができます。

+0

Creative、徹底的に、長所と短所について説明します。私は+2を与えることができたらいいと思う。 –

10

評価してください。 config/environments内のファイルをロードするとき、それはRails::Initializer.runブロック内でそれらを評価する必要があるため、Railsの初期化子は、何をするかだ

eval(IO.read(rubyfile), binding) 

bindingは、現在のコンテキストを返すルビメソッドで、evalに渡されると呼び出し環境内のコードを評価します。


このお試しください:

# my_class.rb 
    class MyClass 
    def run 
     puts 'hi' 
    end 
    end 

    eval(IO.read('whereami.rb'), binding) 


    # whereami.rb 
    puts __FILE__ 


    $ ruby my_class.rb 
    my_class.rb 
+0

どのようにパラメータを渡しますか?おそらくARGVの前に貼っているのだろうか?そして私はどのようにブロックを通過するだろうか?あるいは、respond_toに保証されているインスタンスを渡す必要がありますか(:run)(または私が選ぶ他の方法)? –

+0

by "parameters"私は実行したいクラスの名前やインスタンスのようなものを意味しました。 –

+0

私はあなたがインライン化したいコードを評価することを提案しています。そのため、スクリプト化された定義をファイルに入れて、それを例示したように評価してください。 – kch

-1
load 'somefile' 
+0

いいえ、ロードされたファイルの中で__FILE__を評価するのは、質問に記載されている問題があります:eval's to "./somefile.rb" –

1

それとも、あなたは単に私もclass_eval事を廃止するために時間がかかった__FILE__

scriptizeから
# /lib/scriptize.rb: 
module Kernel 
    def scriptize(calling_file, &block) 
    block.call(ARGV) if calling_file == $0 
    end 
end 

# /lib/some_other_file.rb: 
... 
scriptize(__FILE__) { |args| Foo.new.run(args) } 

を渡すことができます。 (Kernelがデフォルトであなたのスコープになっているので、moduleのものをすべて削除することもできます。

+0

私はこれを私自身の解決策としてタイプしました。私はそれを好きではありませんが、_something_をスクリプト化する(__FILE__またはクラスの名前を渡す)必要があるようですので、__FILE__も同様に良い/悪いです。 –

+0

本当に、これは "Foo.new.run(ARGV)if __FILE__ == $ 0"よりも短くはないので、実際には自分自身をあまり買っていません。しかし、それだけでなく、1つのことについての質問に答える。 –

+0

さて、15分後にもう一度チェックして、楽しい解決策を見つけましょう。 – kch

1

もう1つの方法は、どうすればTest::Unitですか。テストケースファイルには、クラス定義のみ(およびrequire 'test/unit')しかありません。

'test/unit'ライブラリは、テストケースとスイートを自動的に実行するat_exitハンドラを設定します。あなたの最も一般的なケースがこれらのクラスファイルを実行していて、それらを時折ライブラリとして使用している場合は、同様のことを行うことができ、ライブラリとしてインクルードされたときに自動実行を無効にするようglobalを設定します。例えば

# tc_mytest.rb 
require 'test/unit' 

class TC_MyTest < Test::Unit::TestCase 
    def test_succeed 
    assert(true, 'Assertion was true.') 
    end 
    def test_fail 
    assert(false, 'Assertion was false.') 
    end 
end 

実行する必要がありませんboilerplater:

% ruby tc_mytest.rb 
Loaded suite tc_mytest 
Started 
F. 
Finished in 0.007241 seconds. 

    1) Failure: 
test_fail(TC_MyTest) [tc_mytest.rb:8]: 
Assertion was false. 
<false> is not true. 

2 tests, 2 assertions, 1 failures, 0 errors 
+0

これは良い例です。今私は掘り起こし、彼らがこれをどうやって行っているかを見ることに決めました。 –

+0

これは 'test/unit.rb'の最後です。 – rampion

1

我々はevalの(IO.read( 'filename.rb')、結合)を使用することができます

例: -

setup.rb

def setup 
    @driver = Selenium::WebDriver.for :chrome 
    @base_url = "http://stage.checkinforgood.com/" 
    @driver.manage.timeouts.implicit_wait = 30 
    @verification_errors = [] 
end 

def teardown 
    @driver.quit 
    assert_equal [], @verification_errors 
end 

c4g.rb

require "selenium-webdriver" 
require "test/unit" 

class C4g < Test::Unit::TestCase 

    eval(IO.read('setup.rb'), binding) 

    def test_login 
    @driver.get "http://stage.checkinforgood.com/" 
    @driver.find_element(:link, "Sign In").click 
    @driver.find_element(:id, "user_email").clear 
    @driver.find_element(:id, "user_email").send_keys "[email protected]" 
    @driver.find_element(:id, "user_password").clear 
    @driver.find_element(:id, "user_password").send_keys "test123" 
    @driver.find_element(:id, "user_submit").click 
    end 

    def element_present?(how, what) 
    @driver.find_element(how, what) 
    true 
    rescue Selenium::WebDriver::Error::NoSuchElementError 
    false 
    end 

    def verify(&blk) 
    yield 
    rescue Test::Unit::AssertionFailedError => ex 
    @verification_errors << ex 
    end 

end 

今、私たちは、私は私もそれのいずれかの必要性を見ているかわからない、

$ルビーc4g.rb

関連する問題