Rubyフックメソッド使用例
フックメソッドを利用することで、Rubyのクラスやモジュールのライフサイクルに介入することができ、プログラミングの自由度を大きく向上させることができます。
ライフサイクルに関連するフックメソッドは以下の通りです。
クラス・モジュール関連
- クラス#inherited
- モジュール#include
- モジュール#prepended
- モジュール#extend_object
- モジュール#method_added
- モジュール#method_removed
- モジュール#method_undefined
シングルピース・クラス関連
- BasicObject#singleton_method_addedを指定します。
- BasicObject#singleton_method_removed
- BasicObject#singleton_method_undefined
サンプルコード
module M1
def self.included(othermod)
puts "M1 was included into #{othermod}"
end
end
module M2
def self.prepended(othermod)
puts "M2 was prepended to #{othermod}"
end
end
class C
include M1
include M2
end
# Output
M1 was included into C
M2 was prepended to C
module M
def self.method_added(method)
puts "New method: M##{method}"
end
def my_method; end
end
# Output
New method: M#my_method
上記のいくつかの方法に加えて、親クラスのメソッドをオーバーライドして何らかのフィルタリング処理を行い、スーパーメソッドを呼び出して元の関数の機能を完結させることで、フック的な効果を得ることも可能です。このように、エイリアスをラップすることもフックメソッドの代替実装として使用できます。
使用例
タスクの説明
属性の値をチェックするために使用する attr_accessor の attr_checked のような操作を行うクラスマクロを以下のように記述しなさい。
class Person
include CheckedAttributes
attr_checked :age do |v|
v >= 18
end
end
me = Person.new
me.age = 39 #ok
me.age = 12 #throw exception
実施計画:
eval メソッドを使って、指定されたクラスに単純なチェック付き属性を追加する add_checked_attribute というカーネルメソッドを書いてください。
add_checked_attribute メソッドをリファクタリングして eval メソッドを削除し、他の手段で実装します。
コードブロックチェック機能の追加
add_checked_attribute を attr_checked を必要とするように変更し、すべてのクラスで利用できるようにした。
モジュールを導入することで、機能モジュールを導入するクラスのみにattr_checkedメソッドを追加する
ステップ1
def add_checked_attribute(klass, attribute)
eval "
class #{klass}
def #{attribute}=(value)
raise 'Invalid attribute' unless value
@#{attribute} = value
end
def #{attribute}()
@#{attribute}
end
end
"
end
add_checked_attribute(String, :my_attr)
t = "hello,kitty"
t.my_attr = 100
puts t.my_attr
t.my_attr = false
puts t.my_attr
このステップでは eval メソッドを使い、class と def キーワードでそれぞれクラスを開き、指定された属性の get と set メソッドを定義します。set メソッドは単に値が null (nil または false) かどうかを判断し、そうであれば Invalid attribute 例外をスローします。
セットプ 2
def add_checked_attribute(klass, attribute)
klass.class_eval do
define_method "#{attribute}=" do |value|
raise "Invaild attribute" unless value
instance_variable_set("@#{attribute}", value)
end
define_method attribute do
instance_variable_get "@#{attribute}"
end
end
end
このステップでは、eval メソッドを置き換え、さらに class キーワードと def キーワードをそれぞれ class_eval メソッドと define_method メソッドに置き換えています。最初のステップとの違いはなく、いくつかの内部実装の違いがあるだけです。
ステップ3
def add_checked_attribute(klass, attribute, &validation)
klass.class_eval do
define_method "#{attribute}=" do |value|
raise "Invaild attribute" unless validation.call(value)
instance_variable_set("@#{attribute}", value)
end
define_method attribute do
instance_variable_get "@#{attribute}"
end
end
end
add_checked_attribute(String, :my_attr){|v| v >= 180 }
t = "hello,kitty"
t.my_attr = 100 #Invaild attribute (RuntimeError)
puts t.my_attr
t.my_attr = 200
puts t.my_attr #200
派手さはありませんが、コードブロックによる検証を追加したことで、チェックサムに柔軟性が加わり、もはやnilとfalseだけに制限されなくなりました。
ステップ4
class class
def attr_checked(attribute, &validation)
define_method "#{attribute}=" do |value|
raise "Invaild attribute" unless validation.call(value)
instance_variable_set("@#{attribute}", value)
end
define_method attribute do
instance_variable_get "@#{attribute}"
end
end
end
String.add_checked(:my_attr){|v| v >= 180 }
t = "hello,kitty"
t.my_attr = 100 #Invaild attribute (RuntimeError)
puts t.my_attr
t.my_attr = 200
puts t.my_attr #200
なぜなら、すべてのオブジェクトはClassのインスタンスであり、ここで定義されたインスタンスメソッドはRubyの他のすべてのクラスからアクセスすることができるからです。メソッドをattr_checkedに変更しています。
ステップ5
module CheckedAttributes
def self.included(base)
base.extend ClassMethods
end
end
module ClassMethods
def attr_checked(attribute, &validation)
define_method "#{attribute}=" do |value|
raise "Invaild attribute" unless validation.call(value)
instance_variable_set("@#{attribute}", value)
end
define_method attribute do
instance_variable_get "@#{attribute}"
end
end
end
class Person
include CheckedAttributes
attr_checked :age do |v|
v >= 18
end
end
最後のステップは、CheckedAttributesモジュールが導入された後、フックを介して導入されたモジュールで現在のクラスを拡張し、現在のクラスが導入されたメソッド呼び出し、すなわちここでいうgetとsetのメソッド群をサポートするようにすることである。
この時点で、attr_accessorと同様のattr_checkedというマクロを用意し、属性を任意にチェックできるようにしています。
関連
-
Rubyの二分探索(dichotomous search)アルゴリズムの簡単な例
-
PythonのFlaskフレームワークでSERVER_NAMEドメイン名を設定するためのチュートリアル
-
モンキーパッチのプログラミングスタイルとRubyでの利用について
-
Rubyのブロック機能をより深く理解するために
-
アプレットを置き換えるHTMLスクリプトを記述するRubyの例
-
Ruby on Railsで構築するアプリケーションの基本的なディレクトリ構造のまとめ
-
Rubyのデザインパターン開発におけるobserverパターンの一例
-
Ruby on Railsのメーラーの使い方を説明します。
-
Rubyの基本構文
-
Rubyのメソッドを検索して実行する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Rubyイテレータの知識まとめ
-
Rubyの乱数生成方法のまとめ
-
Mac OS XにRuby実行環境をインストールするための詳細な手順
-
Rubyのgemパッケージ管理およびgemソース構築のチュートリアル
-
RubyのXMLデータパースライブラリ「Nokogiri」の高度な使い方
-
CentOS7でruby on railsの開発環境を構築する。
-
Rubyにおけるコメントの使い方と中国語のエンコーディングについて解説します。
-
Rubyの基本的な環境変数の設定と一般的なインタープリタコマンド
-
Ruby on Railsのビューの書き方に関するいくつかのアドバイス
-
Ruby on Railsのマイグレーションに関するいくつかの考慮点