私はgem install chef
を行っています。今私はrun_with_pretty_exceptions
だけを交換する別の解決策を試みるが、require
がスクリプトに入れることを知らない。私はこれでした:
require 'chef'
$:.unshift('Users/b/.rvm/gems/ruby-2.3.3/gems/chef-13-6-4/lib')
require 'chef/knife'
しかし、その後:
$ ruby chef_knife.rb
WARNING: No knife configuration file found
ERROR: Error connecting to https://supermarket.chef.io/api/v1/cookbooks/xyz, retry 1/5
...
ので、全体のインフラなしで、私は以下のソリューションをテストすることはできません。 Rubyでは、既存のクラスを再度開き、別の場所で定義されたメソッドを置き換えることができます。私はそれをチェックし、あなたを残している:
# necessary require of chef and knife ...
class Chef::Knife # reopen the Knife class and replace this method
def run_with_pretty_exceptions(raise_exception = false)
unless respond_to?(:run)
ui.error "You need to add a #run method to your knife command before you can use it"
end
enforce_path_sanity
maybe_setup_fips
Chef::LocalMode.with_server_connectivity do
run
end
rescue Exception => e
raise if e.class == Interrupt # <---------- added ********************
raise if raise_exception || Chef::Config[:verbosity] == 2
humanize_exception(e)
exit 100
end
end
name = cookbook_name(File.join(path, 'Metadata.rb'))
error = 0
begin
::Chef::Knife.run(['cookbook', 'site', 'show', "#{name}"])
rescue SystemExit => e
puts "in rescue SystemExit e=#{e.inspect}"
error = 1
rescue Interrupt
puts 'in rescue Interrupt'
end
raise if e.class == Interrupt
それが1である場合Interrupt
再発生させます。
は、通常、私はこのようになり、診断を表示するruby -w
を実行します。
$ ruby -w ck.rb
ck.rb:9: warning: method redefined; discarding old run_with_pretty_exceptions
ck.rb:4: warning: previous definition of run_with_pretty_exceptions was here
は残念ながら非常に多くの初期化されていない変数と円形があります。このオプションは、未手に負えない出力を生成することを、この宝石で警告を必要としています。
この解決法の欠点は、この変更に関する文書の追跡が必要であり、シェフのリリース変更の場合、誰かがrun_with_pretty_exceptions
のコードが変更されたかどうかを確認する必要があることです。
フィードバックをお願いします。
===== UPDATE =====
Chef::Knife
にexit
方法を規定することからなる以下侵入溶液があります。
exit 100
、つまり受信者のないメッセージの場合、暗黙の受信者はself
です。これはself.exit 100
に相当します。私たちの場合、self
はinstance = subcommand_class.new(args)
によって作成されたオブジェクトで、受信者はinstance.run_with_pretty_exceptions
です。
メッセージがオブジェクトに送信されると、メッセージ検索メカニズムがこのオブジェクトのクラスの検索を開始します。クラスにこの名前のメソッドがない場合、検索機構は含まれているモジュールを検索し、スーパークラスなどをObjectに達するまで検索します。デフォルトのスーパークラスはChef::Knife
です。ここではObject#exit
が見つかり、実行します。それはChef::Knife
として暗黙レシーバのインスタンスとexit 100
に遭遇したとき
Chef::Knife
にexit
方法を定義した後、メッセージ検索機構は、最初にこのローカル方法を見つけ、それを実行します。以前に元のObject#exit
にエイリアスを付けることによって、Rubyスクリプトの終了を開始するオリジナルのRubyメソッドを呼び出すことは可能です。このようにローカルのexit
メソッドは、元のObject#exit
を呼び出すか、他のアクションをとるかを決めることができます。
以下は、どのように動作するかを示す完全な例です。
# ***** Emulation of the gem *****
class Chef end
class Chef::Knife
def self.run(x)
puts 'in original run'
self.new.run_with_pretty_exceptions
end
def run_with_pretty_exceptions
print 'Press Ctrl_C > '
gets
rescue Exception => e
puts
puts "in run_with_pretty...'s Exception e=#{e.inspect} #{e.class}"
raise if false # if raise_exception || Chef::Config[:verbosity] == 2
# humanize_exception(e)
puts "now $!=#{$!.inspect}"
puts "about to exit, self=#{self}"
exit 100
end
end
# ***** End of gem emulation *****
#----------------------------------------------------------------------
# ***** This is what you put into your script. *****
class Chef::Knife # reopen the Knife class and define one's own exit
alias_method :object_exit, :exit
def exit(p)
puts "in my own exit with parameter #{p}, self=#{self}"
puts "$!=#{$!.inspect}"
if Interrupt === $!
puts 'then about to raise Interrupt'
raise # re-raise Interrupt
else
puts 'else about to call Object#exit'
object_exit(p)
end
end
end
begin
::Chef::Knife.run([])
rescue SystemExit => e
puts "in script's rescue SystemExit e=#{e.inspect}"
rescue Interrupt
puts "in script's rescue Interrupt"
end
実行。 Ctrl-Cによる最初のテスト:
$ ruby -w simul_chef.rb
in original run
Press Ctrl_C > ^C
in run_with_pretty...'s Exception e=Interrupt Interrupt
now $!=Interrupt
about to exit, self=#<Chef::Knife:0x007fb2361c7038>
in my own exit with parameter 100, self=#<Chef::Knife:0x007fb2361c7038>
$!=Interrupt
then about to raise Interrupt
in script's rescue Interrupt
ハード割り込みによる2番目のテスト。別のターミナルウィンドウで
$ ruby -w simul_chef.rb
in original run
Press Ctrl_C >
:戻る最初の端末で
$ ps -ef
UID PID PPID C STIME TTY TIME CMD
0 1 0 0 Fri01PM ?? 0:52.65 /sbin/launchd
...
0 363 282 0 Fri01PM ttys000 0:00.02 login -pfl b /bin/bash -c exec -la bash /bin/bash
501 364 363 0 Fri01PM ttys000 0:00.95 -bash
501 3175 364 0 9:51PM ttys000 0:00.06 ruby -w simul_chef.rb
...
$ kill 3175
:
1つのターミナルウィンドウで
in run_with_pretty...'s Exception e=#<SignalException: SIGTERM> SignalException
now $!=#<SignalException: SIGTERM>
about to exit, self=#<Chef::Knife:0x007fc5a79d70a0>
in my own exit with parameter 100, self=#<Chef::Knife:0x007fc5a79d70a0>
$!=#<SignalException: SIGTERM>
else about to call Object#exit
in script's rescue SystemExit e=#<SystemExit: exit>
は、すべてのあなたを、あなたがもともと投稿コードを考慮すると最初に挿入する必要がありますが、必要な後にrequire
:
class Chef::Knife # reopen the Knife class and define one's own exit
alias_method :object_exit, :exit
def exit(p)
if Interrupt === $!
raise # re-raise Interrupt
else
object_exit(p)
end
end
end
オリジナルの宝石に触れる必要はありません。
あなたの質問は私には分かりません。あなたは 'レスキューSystemExit'がSTRG + CがRubyを終了するのを防ぎ、あなたは回避策を探していることに気付きましたか?最初に 'レスキューSystemExit'が必要なのはなぜですか? – spickermann
私のMacでは、プログラムを待たせるために 'gets'ステートメントを使ってCtrl-Cを実行すると、レスキューされた場合は' Interrupt'、レスキューされている場合は 'Exception'が生成され、そうでない場合は' gets ':Interrupt'が表示されます。 – BernardK
私のLinuxシステムでは、Crtl-Cは 'Interrupt'を生成し、' Intercept'が 'Exception'から継承するので、' Exception'と 'Interrupt'の両方でキャッチされます。しかし、 'SystemExit'を救済することは、私の理解の中でCtrl-Cを捕らえるべきではありません。 @spickermann 'SystemExit'をキャッチする必要があります。なぜなら' :: Chef :: Knife.run'はエラーで 'SystemExit'で終了するからです。 –