ボクココ

個人開発に関するテックブログ

コードを記述するコード

引き続き「メタプログラミングRuby」の学習継続。
最近はクイズを答えを見ないで自分で書いた後に比較するようにしている。何気全く同じコードがかけていたときはかなりうれしいw

今日はクラス拡張ミックスインについて。
クラスの中でモジュールをインクルードし、そのクラスのクラスメソッドやインスタンスメソッドをモジュール内で定義しちゃおう。そしてさらに動的にメソッドを作ってより柔軟にメソッドを使えるようにするという話。

attr_checked というクラスメソッドを用意。これは引数で指定したパラメータをクラスのインスタンスメソッドとしてもっておいてそれをセッターとして代入するときに検証してくれるもの。


1 require 'test/unit'
2
3 module CheckedAttributes
4 def self.included(base)
5 base.extend(ClassMethods) 6 end
7 module ClassMethods
8 def attr_checked(attribute, &validate)
9 define_method "#{attribute}" do
10 instance_variable_get "@#{attribute}"
11 end
12 define_method "#{attribute}=" do |value|
13 raise 'Invalid attribute' unless validate.call(value)
14 instance_variable_set("@#{attribute}", value)
15 end
16 end
17 end
18 end
19
20 class Person
21 include CheckedAttributes
22 attr_checked :age do |v|
23 v >= 18
24 end
25 end
26
27 class TestCheckedAttribute < Test::Unit::TestCase
28 def setup
29 @bob = Person.new
30 end
31
32 def test_accepts_valid_values
33 @bob.age = 20
34 assert_equal 20, @bob.age
35 end
36
37 def test_refuses_nil_values
38 assert_raises RuntimeError, 'Invalid attribute' do
39 @bob.age = 17
40 end
41 end
42
43 end
44

※ちなみにソースは全てGithubに置きました。
https://github.com/honmadayo/other/tree/master/metaprogrammingruby

ふつくしい。。
evalで強引にメソッド動的定義したらclass_eval&define_methodでかっこよく安全にし、どのクラスでも使えるようにClass クラスに追加し、最後にinclude指定したクラス内のみ適用するコードを書いた。


メタプログラミングマジック!