= README
release:: 0.1.2
copyright:: $Copyright: copyright(c) 2010 kuwata-lab.com all rights reserved $
license:: $License: MIT-License $
== About
'annotation.rb' is a very small but pretty good library to introduce Java's
annotation or Python's function decorator into Ruby.
Using 'annotatin.rb', you can write your code more declarative, like:
class MyController < Controller
GET('/')
def index; ...; end
GET('/:id')
def show(id); ...; end
PUT('/:id')
login_required
def update(id); ...; end
end
== Install
Install rubygems at first and:
$ gem install annotation
== Examples
ex1. my_controller.rb:
require 'annotation'
module ControllerAnnotation
extend Annotation
def GET(imethod, path)
(@__routes ||= []) << [path, :GET, imethod]
end
def POST(imethod, path)
(@__routes ||= []) << [path, :POST, imethod]
end
def login_required(imethod)
alias_method "__orig_#imethod", imethod
s = "def #imethod(*args)
raise '302 Found' unless @current_user
__orig_#imethod(*args)
end"
self.class_eval s # not 'eval(s)'
end
annotation :GET, :POST, :login_required # !!!!!!
end
class Controller
extend ControllerAnnotation
end
class MyController < Controller
GET('/')
def index
"index() called."
end
GET('/:id')
def show(id)
"show(#id) called."
end
POST('/:id')
login_required
def update(id)
"update(#id) called."
end
p @__routes #=> [["/", :GET, :index],
# ["/:id", :GET, :show],
# ["/:id", :POST, :update]]
end
p MyController.new.update(123) #=> 302 Found (RuntimeError)
ex2. memoize.rb
require 'annotation'
module Memoize
extend Annotation
def memoize(func_name)
aliased = "_orig_#func_name" # or "_#func_name}_#{rand().to_s[2.func_name}_#{rand().to_s[2..9]"
alias_method aliased, func_name
s = "def #func_name(*args)
@_memos ||= {}
hash = (@_memos[:#func_name] ||= {})
hash[args] = __send__(:#aliased, *args) unless hash.key?(args)
return hash[args]
end"
class_eval s
end
annotation :memoize # !!!!!!
end
class Fib
extend Memoize
def fib1(n)
n <= 2 ? 1 : fib1(n-1) + fib1(n-2)
end
memoize # !!!!
def fib2(n)
n <= 2 ? 1 : fib2(n-1) + fib2(n-2)
end
end
require 'benchmark'
fib = Fib.new
Benchmark.bm(20) do |x|
x.report('fib1(30)') { ret = fib.fib1(30) }
x.report('fib2(30)') { ret = fib.fib2(30) }
end
### Result:
# $ ruby memoize.rb
# user system total real
# fib1(30) 1.060000 0.000000 1.060000 ( 1.063110)
# fib2(30) 0.000000 0.000000 0.000000 ( 0.000327)
ex3. obsolete.rb
require 'annotation'
module Obsolete
extend Annotation
def obsolete(method)
aliased = "_orig_#method" # or "_#method}_#{rand().to_s[2.method}_#{rand().to_s[2..9]"
alias_method aliased, method
s = "def #method(*args)
warn %Q`*** warning: #method is obsolete.`
__send__(:#aliased, *args)
end"
class_eval s
end
annotation :obsolete # !!!!!!
end
class Hello
extend Obsolete
obsolete # !!!!!!
def hello(name)
puts "Hello #name!"
end
end
Hello.new.hello('World') #=> *** warning: hello is obsolete.
ex4. my_test.rb
require 'test/unit'
require 'annotation'
module DummyFiles
extend Annotation
def dummy_files(method_name, files={})
aliased = "__#method_name}_#{rand().to_s[2.method_name}_#{rand().to_s[2..10]"
alias_method aliased, method_name
define_method method_name do
begin
files.each do |filename, content|
next unless content
File.open(filename, 'w') {|f| f.write(content) }
end
__send__(aliased)
ensure
files.each do |filename, _|
File.unlink(filename) if File.exist?(filename)
end
end
end
end
annotation :dummy_files # !!!!!!
end
class MyTestCase < Test::Unit::TestCase
extend DummyFiles
dummy_files 'A.txt'=>'AAA', 'B.txt'=>'BBB' # !!!!!!
def test_something
assert_equal 'AAA', File.read('A.txt')
assert_equal 'BBB', File.read('B.txt')
end
end
== Known Issues
* Annotation and RDoc cannot be good friends.
## ...document...
GET('/') # this annotation prevent RDoc to generate document!
def index()
...
end
release:: 0.1.2
copyright:: $Copyright: copyright(c) 2010 kuwata-lab.com all rights reserved $
license:: $License: MIT-License $
== About
'annotation.rb' is a very small but pretty good library to introduce Java's
annotation or Python's function decorator into Ruby.
Using 'annotatin.rb', you can write your code more declarative, like:
class MyController < Controller
GET('/')
def index; ...; end
GET('/:id')
def show(id); ...; end
PUT('/:id')
login_required
def update(id); ...; end
end
== Install
Install rubygems at first and:
$ gem install annotation
== Examples
ex1. my_controller.rb:
require 'annotation'
module ControllerAnnotation
extend Annotation
def GET(imethod, path)
(@__routes ||= []) << [path, :GET, imethod]
end
def POST(imethod, path)
(@__routes ||= []) << [path, :POST, imethod]
end
def login_required(imethod)
alias_method "__orig_#imethod", imethod
s = "def #imethod(*args)
raise '302 Found' unless @current_user
__orig_#imethod(*args)
end"
self.class_eval s # not 'eval(s)'
end
annotation :GET, :POST, :login_required # !!!!!!
end
class Controller
extend ControllerAnnotation
end
class MyController < Controller
GET('/')
def index
"index() called."
end
GET('/:id')
def show(id)
"show(#id) called."
end
POST('/:id')
login_required
def update(id)
"update(#id) called."
end
p @__routes #=> [["/", :GET, :index],
# ["/:id", :GET, :show],
# ["/:id", :POST, :update]]
end
p MyController.new.update(123) #=> 302 Found (RuntimeError)
ex2. memoize.rb
require 'annotation'
module Memoize
extend Annotation
def memoize(func_name)
aliased = "_orig_#func_name" # or "_#func_name}_#{rand().to_s[2.func_name}_#{rand().to_s[2..9]"
alias_method aliased, func_name
s = "def #func_name(*args)
@_memos ||= {}
hash = (@_memos[:#func_name] ||= {})
hash[args] = __send__(:#aliased, *args) unless hash.key?(args)
return hash[args]
end"
class_eval s
end
annotation :memoize # !!!!!!
end
class Fib
extend Memoize
def fib1(n)
n <= 2 ? 1 : fib1(n-1) + fib1(n-2)
end
memoize # !!!!
def fib2(n)
n <= 2 ? 1 : fib2(n-1) + fib2(n-2)
end
end
require 'benchmark'
fib = Fib.new
Benchmark.bm(20) do |x|
x.report('fib1(30)') { ret = fib.fib1(30) }
x.report('fib2(30)') { ret = fib.fib2(30) }
end
### Result:
# $ ruby memoize.rb
# user system total real
# fib1(30) 1.060000 0.000000 1.060000 ( 1.063110)
# fib2(30) 0.000000 0.000000 0.000000 ( 0.000327)
ex3. obsolete.rb
require 'annotation'
module Obsolete
extend Annotation
def obsolete(method)
aliased = "_orig_#method" # or "_#method}_#{rand().to_s[2.method}_#{rand().to_s[2..9]"
alias_method aliased, method
s = "def #method(*args)
warn %Q`*** warning: #method is obsolete.`
__send__(:#aliased, *args)
end"
class_eval s
end
annotation :obsolete # !!!!!!
end
class Hello
extend Obsolete
obsolete # !!!!!!
def hello(name)
puts "Hello #name!"
end
end
Hello.new.hello('World') #=> *** warning: hello is obsolete.
ex4. my_test.rb
require 'test/unit'
require 'annotation'
module DummyFiles
extend Annotation
def dummy_files(method_name, files={})
aliased = "__#method_name}_#{rand().to_s[2.method_name}_#{rand().to_s[2..10]"
alias_method aliased, method_name
define_method method_name do
begin
files.each do |filename, content|
next unless content
File.open(filename, 'w') {|f| f.write(content) }
end
__send__(aliased)
ensure
files.each do |filename, _|
File.unlink(filename) if File.exist?(filename)
end
end
end
end
annotation :dummy_files # !!!!!!
end
class MyTestCase < Test::Unit::TestCase
extend DummyFiles
dummy_files 'A.txt'=>'AAA', 'B.txt'=>'BBB' # !!!!!!
def test_something
assert_equal 'AAA', File.read('A.txt')
assert_equal 'BBB', File.read('B.txt')
end
end
== Known Issues
* Annotation and RDoc cannot be good friends.
## ...document...
GET('/') # this annotation prevent RDoc to generate document!
def index()
...
end