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.
@@ -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