annotation 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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