Rails 5、Module#prepend、および`Alias_method_chain`の終わり
Rails 4.2の発表には、次のRails5に関する興味深いニュースがいくつかありました。おそらくRuby2.2が必要になるでしょう。 これにより、Ruby2のすべての優れた機能を利用する最初のRailsバージョンになります。
投稿では、ガベージコレクションされたシンボルとキーワード引数について言及しました。しかし、私にとって、最も興味深いRuby 2の機能の1つは、Module#prependです。
alias_method_chain
の良い点(および悪い点)
Railsを最初に学んだとき、alias_method_chain
完全に私の心を吹き飛ばしました。それは、Rubyがいかに柔軟であるかを実際に示しました。
1行のコードで、メソッドの動作方法を完全に変更できます。 必要なコードを追加するためにライブラリをハックする必要はもうありません。その場で追加するだけです。 alias_method_chain
宝石への最初のパッチにつながり、それが私の最初のプルリクエストにつながり、それが私の最初のオープンソースの貢献につながりました。
ただし、モンキーパッチのように、alias_method_chain
使いすぎて、その問題が明らかになり始めました:
- 生成されるメソッド名はわかりにくいため、エラーを見つけてデバッグするのが難しくなります。 例:
class Person
def greeting
"Hello"
end
end
module GreetingWithExcitement
def self.included(base)
base.class_eval do
alias_method_chain :greeting, :excitement
end
end
def greeting_with_excitement
"#{greeting_without_excitement}!!!!!"
end
end
Person.send(:include, GreetingWithExcitement)
Person#greeting
でエラーが発生した場合 、バックトレースは、エラーが実際にPerson#greeting_without_excitement
で発生したことを示します。 。しかし、そのメソッドはどこで定義されていますか?どこにも見えません。 どのgreeting
をどのように知っていますか メソッドはバグのあるメソッドですか? また、メソッド名は、連鎖するほどさらに混乱します。
-
alias_method_chain
を呼び出す場合 同じクラスで同じパラメータを2回使用すると、スタックオーバーフローが発生する可能性があります。 (理由がわかりますか?)require
である限り、これは通常は発生しません。 ステートメントは、使用するパスについて一貫しています。ただし、Railsコンソールにコードを頻繁に貼り付けると非常に煩わしくなります。 -
そして、イェフダカッツのブログ投稿で説明されている残りのこと。 この投稿により、多くのRails開発者が
alias_method_chain
を放棄し始めるようになりました。 モジュールの継承を支持します。
それで、なぜそれがまだ使用されているのですか?
ほとんどのalias_method_chain
を置き換えることができます ■モジュール内のこれらのメソッドをオーバーライドし、それらのモジュールを子クラスに含めることによって。ただし、これは、クラス自体ではなく、スーパークラスをオーバーライドする場合にのみ機能します。つまり:
class ParentClass
def log
puts "In parent"
end
end
class ChildClass < ParentClass
def log
puts "In child"
super
end
def log_with_extra_message
puts "In child, with extra message"
log_without_extra_message
end
alias_method_chain :log, :extra_message
end
ChildClass.new.log
を実行した場合 、次のように表示されます:
In child, with extra message
In child
In parent
alias_method_chain
の代わりにモジュールを使用しようとした場合 、次のように出力を取得できます:
In child
In child, with extra message
In parent
しかし、できません log
を変更せずに元の出力と一致させます ChildClass
のメソッド 。 Rubyの継承はそのようには機能しません。そうではありませんでした。
Ruby 2.0で何が変更されましたか?
Ruby 2.0までは、以下にコードを追加する方法はありませんでした。 その上だけのクラス。 ただし、prepend
、モジュールのメソッドを使用してクラスのメソッドをオーバーライドし、super
を使用してクラスの実装にアクセスできます。 。 したがって、最後の例を使用すると、次のようにして元の出力を取得できます。
class ParentClass
def log
puts "In parent"
end
end
module ExtraMessageLogging
def log
puts "In child, with extra message"
super
end
end
class ChildClass < ParentClass
prepend ExtraMessageLogging
def log
puts "In child"
super
end
end
In child, with extra message
In child
In parent
完璧です。
prepend
の場合 まだ頭を包み込むのは難しいので、次のようなことをしていると考えてください:
class NewChildClass < ChildClass
include ExtraMessageLogging
end
ChildClass = NewChildClass
クラス名を混乱させず、既存のオブジェクトに影響を与えることを除いて。
(はい、Rubyでクラス名を再割り当てできます。いいえ、それはおそらく良い考えではありません。)
これはRailsにとって何を意味しますか?
したがって、alias_method_chain
を使用するための最後の言い訳 Ruby2.0ではなくなりました。 alias_method_chain
の残りの数少ない例の1つを取ることができます Railsで:
require 'active_support/core_ext/module/aliasing'
class Range #:nodoc:
def each_with_time_with_zone(&block)
ensure_iteration_allowed
each_without_time_with_zone(&block)
end
alias_method_chain :each, :time_with_zone
def step_with_time_with_zone(n = 1, &block)
ensure_iteration_allowed
step_without_time_with_zone(n, &block)
end
alias_method_chain :step, :time_with_zone
private
def ensure_iteration_allowed
if first.is_a?(Time)
raise TypeError, "can't iterate from #{first.class}"
end
end
end
代わりに、モジュールと交換してください:
require 'active_support/core_ext/module/aliasing'
module RangeWithTimeWithZoneSupport #:nodoc:
def each(&block)
ensure_iteration_allowed
super(&block)
end
def step(n = 1, &block)
ensure_iteration_allowed
super(n, &block)
end
private
def ensure_iteration_allowed
if first.is_a?(Time)
raise TypeError, "can't iterate from #{first.class}"
end
end
end
Range.send(:prepend, RangeSupportingTimeWithZone)
よりクリーンなRange#each
名前が変更されず、ensure_iteration_allowed
モンキーパッチは適用されません。
Rubyはあなたにたくさんの柔軟性を与えます、そしてそれが私がそれを愛する理由の1つです。しかし、強力なオブジェクトモデルもあります。 したがって、独自のコードを挿入する場合は、ハッキングする前にモジュールと継承に頼ってみてください。 コードの理解とデバッグがはるかに簡単になり、alias_method_chain
などの検出が難しい副作用の一部を回避できます。 。
alias_method_chain
Railsで紹介された最もクールな方法の1つでした。しかし、その日は数えられています。私たちはそれを超えました。そして、それがなくなっても見逃すことはありません。
-
WindowsVistaサポートを終了するために知っておくべきこと
Windows Vistaを実行するコンピューターを所有または使用していますか?かなり古いオペレーティングシステムになり始めていますが、それでも日常生活の要求に対しては十分に機能します。そのため、仕事や遊びをVistaOSに依存している人もいます。 残念ながら、2017年4月11日に、MicrosoftはWindowsVistaのサポートを完全に停止します。これは、Vistaの「メインストリームサポート」と呼ばれるものの提供を停止してから5年後のことです。これには、ユーザーへの無料のインシデントサポートが含まれます。ただし、主流のサポートが終了した後も、MicrosoftがVistaをサポー
-
PythonとMatplotlibを使用して行の終わりに注釈を付ける方法は?
PythonとMatplotlibを使用して行の終わりに注釈を付けるには、次の手順を実行できます- 図のサイズを設定し、サブプロット間およびサブプロットの周囲のパディングを調整します。 変数、行を初期化します 、行数データを取得します。 長方形の表形式のデータでPandasデータフレームを取得します。 cumsum(累積合計)を計算します データフレームの。 plot()を使用してデータフレームをプロットします メソッド。 行を繰り返す および名前 行の終わりに注釈を付けます。 annotate()を使用する 列の名前、xy座標、線の色、サイズなどを使用したメソッド 図に凡例を配