annotation 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES.txt +6 -0
- data/MIT-LICENSE +19 -0
- data/README.txt +242 -0
- data/annotation.gemspec +49 -0
- data/lib/annotation.rb +119 -0
- data/setup.rb +1585 -0
- data/test/annotation_test.rb +199 -0
- data/test/examples_test.rb +243 -0
- metadata +74 -0
@@ -0,0 +1,199 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
###
|
4
|
+
### $Release: 0.1.0 $
|
5
|
+
### $Copyright: copyright(c) 2010 kuwata-lab.com all rights reserved $
|
6
|
+
### $License: MIT-License $
|
7
|
+
###
|
8
|
+
|
9
|
+
$: << 'lib' if File.file?('lib/annotation.rb')
|
10
|
+
$: << '../lib' if File.file?('../lib/annotation.rb')
|
11
|
+
$: << 'test' if File.file?('test/annotation_test.rb')
|
12
|
+
|
13
|
+
|
14
|
+
require 'oktest'
|
15
|
+
require 'annotation'
|
16
|
+
|
17
|
+
HAVE_INSTANCE_EXEC = RUBY_VERSION >= '1.8.7' unless defined?(HAVE_INSTANCE_EXEC)
|
18
|
+
|
19
|
+
|
20
|
+
module DummyMod1
|
21
|
+
extend Annotation
|
22
|
+
def GET(method_name, path)
|
23
|
+
(@__actions ||= []) << [method_name, :GET, path]
|
24
|
+
end
|
25
|
+
annotation :GET
|
26
|
+
[:POST, :PUT, :DELETE].each do |req_meth|
|
27
|
+
define_method req_meth do |method_name, path|
|
28
|
+
(@__actions ||= []) << [method_name, req_meth, path]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
annotation :POST, :PUT, :DELETE
|
32
|
+
end
|
33
|
+
|
34
|
+
class Dummy1
|
35
|
+
extend DummyMod1
|
36
|
+
|
37
|
+
GET('/')
|
38
|
+
def index
|
39
|
+
"index() called."
|
40
|
+
end
|
41
|
+
GET('/:id')
|
42
|
+
def show(id)
|
43
|
+
"show(#{id.inspect}) called."
|
44
|
+
end
|
45
|
+
PUT('/:id')
|
46
|
+
def update(id)
|
47
|
+
"update(#{id.inspect}) called."
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
module DummyMod2
|
54
|
+
extend Annotation
|
55
|
+
def login_required(method_name)
|
56
|
+
orig_method = "_orig_#{method_name}"
|
57
|
+
class_eval do
|
58
|
+
alias_method orig_method, method_name
|
59
|
+
eval "def #{method_name}(*args)
|
60
|
+
raise '302 Found' unless @_current_user
|
61
|
+
#{orig_method}(*args)
|
62
|
+
end"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
annotation :login_required
|
66
|
+
end
|
67
|
+
|
68
|
+
class Dummy2
|
69
|
+
extend DummyMod2
|
70
|
+
login_required
|
71
|
+
def do_update(*args)
|
72
|
+
return "updated: args=#{args.inspect}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
class AnnotationTest
|
79
|
+
include Oktest::TestCase
|
80
|
+
|
81
|
+
|
82
|
+
def self.spec_of(target, &block)
|
83
|
+
define_method("test_#{target}", &block) if block
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
spec_of "#annotation"
|
88
|
+
def test_annotation
|
89
|
+
|
90
|
+
spec "define annotation method." do
|
91
|
+
ok_(Dummy1.respond_to?(:GET)) == true
|
92
|
+
ok_(Dummy1.respond_to?(:POST)) == true
|
93
|
+
ok_(Dummy1.respond_to?(:PUT)) == true
|
94
|
+
ok_(Dummy1.respond_to?(:DELETE)) == true
|
95
|
+
end
|
96
|
+
|
97
|
+
spec "annotation method should be protected." do
|
98
|
+
msg = "protected method `GET' called for Dummy1:Class"
|
99
|
+
ok_(proc { Dummy1.GET('/') }).raise?(NoMethodError, msg)
|
100
|
+
end
|
101
|
+
|
102
|
+
spec "aliased method should be private." do
|
103
|
+
ok_(Dummy1.methods.grep(/^GET$/).length) == 1
|
104
|
+
ok_(Dummy1.private_methods.grep(/^__anno_GET/).length) == 1
|
105
|
+
end
|
106
|
+
|
107
|
+
spec "callback is called when instance method is defined." do
|
108
|
+
#falldown
|
109
|
+
expected = [ [:index, :GET, '/'],
|
110
|
+
[:show, :GET, '/:id'],
|
111
|
+
[:update, :PUT, '/:id'],
|
112
|
+
]
|
113
|
+
ok_(Dummy1.instance_variable_get('@__actions')) == expected
|
114
|
+
end
|
115
|
+
|
116
|
+
spec "if annotation method is not called then do nothing on method_add." do
|
117
|
+
#falldown
|
118
|
+
Dummy1.instance_variable_get('@__actions').clear
|
119
|
+
Dummy1.class_eval do
|
120
|
+
GET('/new')
|
121
|
+
def new
|
122
|
+
"new() called."
|
123
|
+
end
|
124
|
+
def create
|
125
|
+
"create() called."
|
126
|
+
end
|
127
|
+
end
|
128
|
+
ok_(Dummy1.instance_variable_get('@__actions')) == [ [:new, :GET, '/new'] ]
|
129
|
+
end
|
130
|
+
|
131
|
+
spec "callback is called only when method is defined." do
|
132
|
+
called = false
|
133
|
+
DummyMod1.module_eval do
|
134
|
+
define_method :ann1 do |method_name|
|
135
|
+
called = true
|
136
|
+
end
|
137
|
+
annotation :ann1
|
138
|
+
end
|
139
|
+
ok_(called) == false
|
140
|
+
Dummy1.class_eval do
|
141
|
+
ann1
|
142
|
+
def meth1; end
|
143
|
+
end
|
144
|
+
ok_(called) == true
|
145
|
+
end
|
146
|
+
|
147
|
+
spec "self in annotation callback is class object." do
|
148
|
+
$__self = false
|
149
|
+
DummyMod1.class_eval do
|
150
|
+
def ann2(method_name)
|
151
|
+
$__self = self
|
152
|
+
end
|
153
|
+
annotation :ann2
|
154
|
+
end
|
155
|
+
Dummy1.class_eval do
|
156
|
+
ann2
|
157
|
+
def meth2; end
|
158
|
+
end
|
159
|
+
#ok_(Dummy1.__send__(:class_variable_get, '@@__self__')) == Dummy1
|
160
|
+
ok_($__self) == Dummy1
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
spec_of "#method_added"
|
167
|
+
def test_method_added
|
168
|
+
|
169
|
+
spec "if annotation is specified then call callbacks." do
|
170
|
+
annotated = []
|
171
|
+
DummyMod2.module_eval do
|
172
|
+
@@_annotated_ = annotated
|
173
|
+
extend Annotation
|
174
|
+
def anno3(method_name)
|
175
|
+
@@_annotated_ << method_name
|
176
|
+
end
|
177
|
+
annotation :anno3
|
178
|
+
end
|
179
|
+
ok_(annotated) == []
|
180
|
+
Dummy2.class_eval do
|
181
|
+
def foo; end
|
182
|
+
anno3
|
183
|
+
def bar; end
|
184
|
+
anno3
|
185
|
+
def baz; end
|
186
|
+
end
|
187
|
+
ok_(annotated) == [:bar, :baz]
|
188
|
+
end
|
189
|
+
|
190
|
+
spec "it is possible to define new method in annotation callback." do
|
191
|
+
obj = Dummy2.new
|
192
|
+
ok_(obj.respond_to?(:_orig_do_update)) == true
|
193
|
+
ok_(proc { obj.do_update(123) }).raise?(RuntimeError, '302 Found')
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
###
|
4
|
+
### $Release: 0.1.0 $
|
5
|
+
### $Copyright: copyright(c) 2010 kuwata-lab.com all rights reserved $
|
6
|
+
### $License: MIT-License $
|
7
|
+
###
|
8
|
+
|
9
|
+
$: << 'lib' if File.file?('lib/annotation.rb')
|
10
|
+
$: << '../lib' if File.file?('../lib/annotation.rb')
|
11
|
+
$: << 'test' if File.file?('test/annotation_test.rb')
|
12
|
+
|
13
|
+
|
14
|
+
require 'oktest'
|
15
|
+
require 'annotation'
|
16
|
+
|
17
|
+
|
18
|
+
class ExamplesTest
|
19
|
+
include Oktest::TestCase
|
20
|
+
|
21
|
+
|
22
|
+
if "ex1".to_s
|
23
|
+
|
24
|
+
module ControllerAnnotation
|
25
|
+
extend Annotation
|
26
|
+
|
27
|
+
def GET(imethod, path)
|
28
|
+
(@__routes ||= []) << [path, :GET, imethod]
|
29
|
+
end
|
30
|
+
|
31
|
+
def POST(imethod, path)
|
32
|
+
(@__routes ||= []) << [path, :POST, imethod]
|
33
|
+
end
|
34
|
+
|
35
|
+
def login_required(imethod)
|
36
|
+
alias_method "__orig_#{imethod}", imethod
|
37
|
+
s = "def #{imethod}(*args)
|
38
|
+
raise '302 Found' unless @current_user
|
39
|
+
__orig_#{imethod}(*args)
|
40
|
+
end"
|
41
|
+
self.class_eval s # not 'eval(s)'
|
42
|
+
end
|
43
|
+
|
44
|
+
annotation :GET, :POST, :login_required # !!!!!!
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
class Controller
|
50
|
+
extend ControllerAnnotation
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
class MyController < Controller
|
55
|
+
|
56
|
+
GET('/')
|
57
|
+
def index
|
58
|
+
"index() called."
|
59
|
+
end
|
60
|
+
|
61
|
+
GET('/:id')
|
62
|
+
def show(id)
|
63
|
+
"show(#{id}) called."
|
64
|
+
end
|
65
|
+
|
66
|
+
POST('/:id')
|
67
|
+
login_required
|
68
|
+
def update(id)
|
69
|
+
"update(#{id}) called."
|
70
|
+
end
|
71
|
+
|
72
|
+
#p @__routes #=> [["/", :GET, :index],
|
73
|
+
# # ["/:id", :GET, :show],
|
74
|
+
# # ["/:id", :POST, :update]]
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
#p MyController.new.update(123) #=> 302 Found (RuntimeError)
|
79
|
+
|
80
|
+
|
81
|
+
def test_FUNC_ex1_class_instance_variable
|
82
|
+
actual = MyController.instance_variable_get('@__routes')
|
83
|
+
expected = [["/", :GET, :index], ["/:id", :GET, :show], ["/:id", :POST, :update]]
|
84
|
+
ok_(actual) == expected
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_FUNC_ex1_method_override
|
88
|
+
ok_(proc { MyController.new.update(123) }).raise?(RuntimeError, '302 Found')
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
if "ex2".to_s
|
96
|
+
|
97
|
+
module Memoize
|
98
|
+
extend Annotation
|
99
|
+
|
100
|
+
def memoize(func_name)
|
101
|
+
aliased = "_orig_#{func_name}" # or "_#{func_name}_#{rand().to_s[2..9]}"
|
102
|
+
alias_method aliased, func_name
|
103
|
+
s = "def #{func_name}(*args)
|
104
|
+
@_memos ||= {}
|
105
|
+
hash = (@_memos[:#{func_name}] ||= {})
|
106
|
+
hash[args] = __send__(:#{aliased}, *args) unless hash.key?(args)
|
107
|
+
return hash[args]
|
108
|
+
end"
|
109
|
+
class_eval s
|
110
|
+
end
|
111
|
+
annotation :memoize # !!!!!!
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
class Fib
|
116
|
+
extend Memoize
|
117
|
+
|
118
|
+
def fib1(n)
|
119
|
+
n <= 2 ? 1 : fib1(n-1) + fib1(n-2)
|
120
|
+
end
|
121
|
+
|
122
|
+
memoize # !!!!
|
123
|
+
def fib2(n)
|
124
|
+
n <= 2 ? 1 : fib2(n-1) + fib2(n-2)
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
#require 'benchmark'
|
130
|
+
#fib = Fib.new
|
131
|
+
#Benchmark.bm(20) do |x|
|
132
|
+
# x.report('fib1(30)') { ret = fib.fib1(30) }
|
133
|
+
# x.report('fib2(30)') { ret = fib.fib2(30) }
|
134
|
+
#end
|
135
|
+
|
136
|
+
### Result:
|
137
|
+
# $ ruby memoize.rb
|
138
|
+
# user system total real
|
139
|
+
# fib1(30) 1.060000 0.000000 1.060000 ( 1.063110)
|
140
|
+
# fib2(30) 0.000000 0.000000 0.000000 ( 0.000327)
|
141
|
+
|
142
|
+
|
143
|
+
def test_FUNC_ex2_memoized_fib
|
144
|
+
fib = Fib.new
|
145
|
+
ok_(fib.fib1(30)) == 832040
|
146
|
+
ok_(fib.fib2(30)) == 832040
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
if "ex3. obsolete.rb".to_s
|
153
|
+
|
154
|
+
module Obsolete
|
155
|
+
extend Annotation
|
156
|
+
|
157
|
+
def obsolete(method)
|
158
|
+
aliased = "_orig_#{method}" # or "_#{method}_#{rand().to_s[2..9]}"
|
159
|
+
alias_method aliased, method
|
160
|
+
s = "def #{method}(*args)
|
161
|
+
warn %Q`*** warning: #{method} is obsolete.`
|
162
|
+
__send__(:#{aliased}, *args)
|
163
|
+
end"
|
164
|
+
class_eval s
|
165
|
+
end
|
166
|
+
annotation :obsolete # !!!!!!
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
class Hello
|
171
|
+
extend Obsolete
|
172
|
+
|
173
|
+
obsolete # !!!!!!
|
174
|
+
def hello(name)
|
175
|
+
puts "Hello #{name}!"
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
def test_FUNC_ex3_obsolete_message
|
182
|
+
stdout, stderr = Oktest::Helper::capture_io do
|
183
|
+
Hello.new.hello('World')
|
184
|
+
end
|
185
|
+
ok_(stdout) == "Hello World!\n"
|
186
|
+
ok_(stderr) == "*** warning: hello is obsolete.\n"
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
if "ex4. my_test.rb".to_s
|
193
|
+
|
194
|
+
module DummyFiles
|
195
|
+
extend Annotation
|
196
|
+
|
197
|
+
def dummy_files(method_name, files={})
|
198
|
+
aliased = "__#{method_name}_#{rand().to_s[2..10]}"
|
199
|
+
alias_method aliased, method_name
|
200
|
+
method_undefined method_name
|
201
|
+
define_method method_name do
|
202
|
+
begin
|
203
|
+
files.each do |filename, content|
|
204
|
+
next unless content
|
205
|
+
File.open(filename, 'w') {|f| f.write(content) }
|
206
|
+
end
|
207
|
+
__send__(aliased)
|
208
|
+
ensure
|
209
|
+
files.each do |filename, _|
|
210
|
+
File.unlink(filename) if File.exist?(filename)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
annotation :dummy_files # !!!!!!
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
#class MyTestCase < Test::Unit::TestCase
|
221
|
+
# extend DummyFiles
|
222
|
+
#
|
223
|
+
# dummy_files 'A.txt'=>'AAA', 'B.txt'=>'BBB' # !!!!!!
|
224
|
+
# def test_something
|
225
|
+
# assert_equal 'AAA', File.read('A.txt')
|
226
|
+
# assert_equal 'BBB', File.read('B.txt')
|
227
|
+
# end
|
228
|
+
#
|
229
|
+
#end
|
230
|
+
|
231
|
+
extend DummyFiles
|
232
|
+
dummy_files 'A.txt'=>'AAA', 'B.txt'=>'BBB'
|
233
|
+
def test_FUNC_ex4_dummy_files
|
234
|
+
ok_(File.read('A.txt')) == 'AAA'
|
235
|
+
ok_(File.read('B.txt')) == 'BBB'
|
236
|
+
end
|
237
|
+
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
|
243
|
+
end
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: annotation
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- makoto kuwata
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-10-10 00:00:00 +09:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: " Annotation.rb is a very small but pretty good library which introduces\n Java's annotation or Python's functhon decorator into Ruby.\n"
|
23
|
+
email: kwa(at)kuwata-lab.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- lib/annotation.rb
|
32
|
+
- test/annotation_test.rb
|
33
|
+
- test/examples_test.rb
|
34
|
+
- README.txt
|
35
|
+
- CHANGES.txt
|
36
|
+
- MIT-LICENSE
|
37
|
+
- setup.rb
|
38
|
+
- annotation.gemspec
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://www.kuwata-lab.com/annotation/
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
hash: 3
|
54
|
+
segments:
|
55
|
+
- 0
|
56
|
+
version: "0"
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 3
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
requirements: []
|
67
|
+
|
68
|
+
rubyforge_project: annotation
|
69
|
+
rubygems_version: 1.3.7
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: annotation library similar to Java or Python
|
73
|
+
test_files:
|
74
|
+
- test/annotation_test.rb
|