Posted at

Rubyでアノテーションを実装してみた

More than 3 years have passed since last update.


Rubyでアノテーション

RubyでJava的なアノテーションが使いたくなったので実装してみました。

参考にしたのは以下の質問に対する回答。


How to simulate Java-like annotations in Ruby?

http://stackoverflow.com/questions/3157426/how-to-simulate-java-like-annotations-in-ruby


そのまんまだとインスタンスメソッドにしか対応できなかったので、クラスメソッド含めアノテーションを付与できるようにしてみました。


使い方

メソッドに対して_で始まるアノテーションを記述する。


sample.rb

require './annotations'

class Sample
annotate!

_label_only
def instance_method1; end

_with_args arg1: 'val1', arg2: 1
def instance_method2; end

_label_only
def self.class_method1; end

_with_args arg1: 'val2', arg2: 2
def self.class_method2; end
end


クラス名.annotations()でメソッド名をシンボルで指定することで、メソッドに追加されたアノテーションを取得できる。引数も指定可能。

クラスメソッドとインスタンスメソッドで同名が使われるとアノテーションが混在してしまうので、インスタンスメソッドのシンボルはメソッド名の頭に_を付与することにした。


test.rb

require './sample'

p Sample.annotations(:_instance_method1) # {:label_only=>[]}
p Sample.annotations(:_instance_method2) # {:with_args=>{:arg1=>"val1", :arg2=>1}}
p Sample.annotations(:class_method1) # {:label_only=>[]}
p Sample.annotations(:class_method2) # {:with_args=>{:arg1=>"val2", :arg2=>2}}



annotations.rbの実装


annotations.rb

module Annotations

@@__last_annotation__ = nil

def annotations(meth=nil)
return @@__annotations__[meth] if meth
@@__annotations__
end

private

def singleton_method_added(m)
(@@__annotations__ ||= {})[m] = @@__last_annotation__ if @@__last_annotation__
@@__last_annotation__ = nil
super
end

def method_added(m)
(@@__annotations__ ||= {})["_#{m.to_s}".to_sym] = @@__last_annotation__ if @@__last_annotation__
@@__last_annotation__ = nil
super
end

def method_missing(meth, *args)
return super unless /^\_/ =~ meth
@@__last_annotation__ ||= {}
@@__last_annotation__[meth[1..-1].to_sym] = args.size == 1 ? args.first : args
end
end

class Module
private

def annotate!
extend Annotations
end
end


Gistにも投稿しました。

https://gist.github.com/matsukaz/06bd42aca25cba9dc569bc5aaaf3374f