wanabe-definition 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ Fri Feb 13 14:25:30 (JST) 2009 wanabe <matz@ruby-lang.org>
2
+
3
+ * initial release.
data/README ADDED
@@ -0,0 +1,38 @@
1
+ definition
2
+ ==========
3
+
4
+ Type striction for Ruby.
5
+
6
+ WARNING
7
+ -------
8
+ This library is very experimental.
9
+ Everything is under construction and fluid.
10
+
11
+ How to use
12
+ ----------
13
+
14
+ like this:
15
+
16
+ module IFoo
17
+ as_interface
18
+ define::foo(Integer, String, Rest, Block)
19
+ end
20
+ class Foo
21
+ implement IFoo
22
+ end
23
+ Foo.new #=> NotImplementedError
24
+
25
+ Licence
26
+ -------
27
+ Modified-BSD Licence.
28
+
29
+ Copyright (c) 2009, wanabe
30
+ All rights reserved.
31
+
32
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
33
+
34
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
35
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
36
+ * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
37
+
38
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,12 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "definition"
3
+ s.version = "0.0.1"
4
+ s.date = "2009-02-13"
5
+ s.summary = ""
6
+ s.email = "s.wanabe@gmail.com"
7
+ s.homepage = "http://github.com/wanabe/definition"
8
+ s.description = ""
9
+ s.has_rdoc = true
10
+ s.authors = [""]
11
+ s.files = ["Changelog", "README", "definition.gemspec", "lib/definition.rb", "test/tc_definition.rb"]
12
+ end
@@ -0,0 +1,239 @@
1
+ #! /usr/local/bin/ruby
2
+ # -*- coding: UTF-8 -*-
3
+
4
+ class DefinitionError < StandardError
5
+ end
6
+ class Definition
7
+ module Const
8
+ module Block
9
+ end
10
+ module Rest
11
+ end
12
+ module Any
13
+ def self.===(other)
14
+ true
15
+ end
16
+ end
17
+ end
18
+ class Enroller
19
+ def initialize(defn, table)
20
+ @defn = defn
21
+ @table = table
22
+ end
23
+ def method_missing(name, *args)
24
+ @defn.set(args)
25
+ @table[name] ||= []
26
+ @table[name] << @defn
27
+ end
28
+ end
29
+
30
+ attr_reader :arity
31
+ def initialize(ret_type, root)
32
+ @ret_type = ret_type
33
+ @root = root
34
+ end
35
+ def to_enroll(table)
36
+ Enroller.new(self, table)
37
+ end
38
+ def assert_match(name, meth, implement)
39
+ begin
40
+ if meth.respond_to?(:parameters)
41
+ if meth.parameters.length != @arg_types.length
42
+ message = "%s: arity mismatch (%i for %i)"
43
+ raise DefinitionError, message, name, meth.parameters.length,
44
+ @arg_types.length
45
+ end
46
+ meth.parameters.each_with_index do |param, i|
47
+ type, var = param
48
+ arg_type = @arg_types[i]
49
+ case type
50
+ when :req, :opt
51
+ next if arg_type != Const::Rest && arg_type != Const::Block
52
+ when :rest
53
+ next if arg_type == Const::Rest
54
+ when :block
55
+ next if arg_type == Const::Block
56
+ end
57
+ raise DefinitionError, "%s: arg %i: definition mismatch", name, i
58
+ end
59
+ elsif meth.arity != @arity
60
+ message = "%s: arity mismatch (%i for %i)"
61
+ raise DefinitionError, message, name, meth.arity, @arity
62
+ end
63
+ rescue DefinitionError
64
+ implement.module_eval { remove_method(name) }
65
+ Kernel.raise
66
+ end
67
+ end
68
+ def set(arg_types)
69
+ @arg_types = arg_types
70
+ @arity = arg_types.length
71
+ if arg_types.last == Const::Block
72
+ @arity -= 1
73
+ @block = true
74
+ end
75
+ @arity = -@arity if arg_types.include?(Const::Rest)
76
+ end
77
+ def assert_args(args)
78
+ start_index = 0
79
+ length = args.length
80
+ args.each_with_index do |arg, i|
81
+ next if i < start_index
82
+ break if i >= length
83
+ arg_type = @arg_types[i]
84
+ if arg_type == Definition::Const::Rest
85
+ start_index = @arity + args.length + i + 1
86
+ elsif !(arg_type === arg)
87
+ raise ArgumentError, "arg %i: not %p === %p", i, arg_type, arg
88
+ end
89
+ end
90
+ end
91
+ def assert_ret(ret)
92
+ ret_type = @ret_type
93
+ unless ret_type === ret
94
+ raise TypeError, "return value: not %p === %p", ret_type, ret
95
+ end
96
+ end
97
+ def raise(exclass, message, *args)
98
+ message = sprintf(message, *args)
99
+ if @root.ancestors.include? Interface
100
+ message << " (interface #{@root})"
101
+ else
102
+ message << " (implementation #{@root})"
103
+ end
104
+ super exclass, message
105
+ end
106
+ end
107
+ module Interface
108
+ include Definition::Const
109
+ module InterfaceMethods
110
+ def included(mod)
111
+ mod.extend InterfaceMethods
112
+ mod.init_table @define_table
113
+ end
114
+ def init_table(table)
115
+ @define_table ||= {}
116
+ if table
117
+ table.each do |name, defs|
118
+ @define_table[name] = defs.dup
119
+ end
120
+ end
121
+ end
122
+ def each(&block)
123
+ @define_table.each(&block)
124
+ end
125
+ def define(ret=Definition::Const::Any)
126
+ defn = Definition.new(ret, self)
127
+ return defn.to_enroll(@define_table)
128
+ end
129
+ def [](name)
130
+ return @define_table[name]
131
+ end
132
+ end
133
+ extend InterfaceMethods
134
+ end
135
+ module Implementation
136
+ include Definition::Const
137
+ def self.included(klass)
138
+ klass.module_eval do
139
+ extend Implementation::ImplMethods
140
+ impl_initialize
141
+ end
142
+ end
143
+ module ImplMethods
144
+ NEW = Class.instance_method(:new)
145
+ def new
146
+ assert_implemented unless @skip_assertion
147
+ NEW.bind(self).call
148
+ end
149
+ def define(ret=Definition::Const::Any)
150
+ defn = Definition.new(ret, self)
151
+ return defn.to_enroll(@define_table)
152
+ end
153
+ def implement(interface)
154
+ interface.each do |name, defs|
155
+ @define_table[name] ||= []
156
+ @define_table[name] += defs
157
+ end
158
+ end
159
+ def assert_implemented(once = false)
160
+ nodefs = @define_table.keys - instance_methods(true).map{|m|m.to_sym}
161
+ unless nodefs.empty?
162
+ raise NotImplementedError, "not implemented #{nodefs.join(', ')}"
163
+ end
164
+ @skip_assertion ||= once
165
+ end
166
+ def assert_args(name, args)
167
+ defns = @define_table[name]
168
+ defns.each {|defn| defn.assert_args(args)}
169
+ end
170
+ def assert_ret(name, ret)
171
+ defns = @define_table[name]
172
+ defns.each {|defn| defn.assert_ret(ret)}
173
+ return ret
174
+ end
175
+ def impl_initialize(table = {})
176
+ @define_table = table
177
+ end
178
+ private
179
+ def included(klass)
180
+ klass.extend Implementation::ImplMethods
181
+ inherited(klass)
182
+ end
183
+ def inherited(klass)
184
+ klass.impl_initialize(@define_table)
185
+ assert_implemented
186
+ end
187
+ def method_added(name)
188
+ return if @in_addition_check
189
+ begin
190
+ @in_addition_check = true
191
+ check_definition(name)
192
+ add_assertion(name)
193
+ ensure
194
+ @in_addition_check = false
195
+ end
196
+ end
197
+ def check_definition(name)
198
+ @define_table[name].each do |defn|
199
+ defn.assert_match name, instance_method(name), self
200
+ end
201
+ end
202
+ def add_assertion(name)
203
+ num = 0
204
+ new_name = name
205
+ new_name = "#{name}_#{num += 1}" while method_defined?(new_name)
206
+ alias_method new_name, name
207
+ module_eval <<-EOC, __FILE__, __LINE__ + 1
208
+ def #{name}(*args, &block)
209
+ self.class.assert_args(:#{name}, args)
210
+ self.class.assert_ret(:#{name}, #{new_name}(*args, &block))
211
+ end
212
+ EOC
213
+ end
214
+ end
215
+ end
216
+ class Module
217
+ def implement(*interfaces)
218
+ as_implementation
219
+ interfaces.each do |interface|
220
+ implement(interface)
221
+ end
222
+ end
223
+ def define(ret=Definition::Const::Any)
224
+ as_implementation
225
+ define(ret)
226
+ end
227
+ private
228
+ def as_implementation
229
+ include Implementation unless include?(Implementation)
230
+ end
231
+ def as_interface
232
+ include Interface unless include?(Interface)
233
+ end
234
+ end
235
+
236
+ if __FILE__ == $0
237
+ $" << "definition.rb"
238
+ load "../test/tc_definition.rb"
239
+ end
@@ -0,0 +1,192 @@
1
+ # -*- coding: UTF-8 -*-
2
+ require 'test/unit'
3
+ require 'rubygems'
4
+ require 'definition'
5
+
6
+ class TC_Foo < Test::Unit::TestCase
7
+ module IFoo
8
+ as_interface
9
+ define::foo(Integer, String, Rest, Block)
10
+ end
11
+ module IBar
12
+ as_interface
13
+ define(Any).bar(Integer)
14
+ end
15
+ module IFooBar
16
+ include IFoo, IBar
17
+ define.foo(10..20, Any, Rest, Block)
18
+ end
19
+ module IBar2
20
+ include IBar
21
+ define(Any).bar(0..10)
22
+ end
23
+
24
+ def test_arity
25
+ mismatch_arity_class = Class.new
26
+ mismatch_arity_class.implement IFoo
27
+
28
+ assert_raise(DefinitionError) do
29
+ mismatch_arity_class.module_eval do
30
+ implement IFoo
31
+ def foo(a, b, c, *d, &e)
32
+ end
33
+ end
34
+ end
35
+ assert_raise(DefinitionError) do
36
+ mismatch_arity_class.module_eval do
37
+ def foo(a, *b, &c)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ def test_after_rescue
43
+ defective_module = Module.new
44
+ defective_class = Class.new
45
+ defective_module.implement IFoo
46
+
47
+ assert_raise(NotImplementedError) do
48
+ defective_class.module_eval do
49
+ include defective_module
50
+ end
51
+ end
52
+ assert_raise(NotImplementedError) do
53
+ defective_class.new
54
+ end
55
+ end
56
+ def test_rest
57
+ mismatch_rest_class1 = Class.new
58
+ mismatch_rest_class1.implement IFoo
59
+ mismatch_rest_class2 = Class.new
60
+ mismatch_rest_class2.implement IBar
61
+
62
+ assert_raise(DefinitionError) do
63
+ mismatch_rest_class1.module_eval do
64
+ def foo(a, b, c, &d)
65
+ end
66
+ end
67
+ end
68
+ assert_raise(DefinitionError) do
69
+ mismatch_rest_class1.module_eval do
70
+ def foo(a, b, &d)
71
+ end
72
+ end
73
+ end
74
+ assert_raise(DefinitionError) do
75
+ mismatch_rest_class2.module_eval do
76
+ def bar(a, *b)
77
+ end
78
+ end
79
+ end
80
+ assert_raise(DefinitionError) do
81
+ mismatch_rest_class2.module_eval do
82
+ def bar(*a)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ def test_correct
88
+ correct_class = Class.new
89
+ correct_class.implement IFoo
90
+
91
+ assert_nothing_raised do
92
+ correct_class.module_eval do
93
+ def foo(a, b, *c, &d)
94
+ end
95
+ end
96
+ correct_class.new.foo(1, "a")
97
+ end
98
+ end
99
+ def test_defective
100
+ defective_class = Class.new
101
+ defective_class.module_eval do
102
+ define(String).baz
103
+ end
104
+
105
+ assert_raise(NotImplementedError) do
106
+ Class.new(defective_class)
107
+ end
108
+ assert_raise(NotImplementedError) do
109
+ defective_class.new
110
+ end
111
+ end
112
+ def test_retval
113
+ baz_class = Class.new
114
+ baz_class.module_eval do
115
+ define(String).baz
116
+ def baz
117
+ :ng
118
+ end
119
+ end
120
+
121
+ assert_raise(TypeError) do
122
+ baz_class.new.baz
123
+ end
124
+ baz_class.module_eval do
125
+ def baz
126
+ "ok"
127
+ end
128
+ end
129
+ assert_equal(baz_class.new.baz, "ok")
130
+ end
131
+ def test_argument
132
+ foobar_class = Class.new
133
+ foobar_class.module_eval do
134
+ implement IFooBar, IBar2
135
+ def foo(a, b, *c, &d)
136
+ end
137
+ def bar(a)
138
+ end
139
+ end
140
+ o = foobar_class.new
141
+ assert_raise(ArgumentError) do
142
+ o.foo(1,"a")
143
+ end
144
+ assert_raise(ArgumentError) do
145
+ o.foo(10,2)
146
+ end
147
+ assert_raise(ArgumentError) do
148
+ o.bar(:ng)
149
+ end
150
+ assert_raise(ArgumentError) do
151
+ o.bar(-1)
152
+ end
153
+ assert_nothing_raised do
154
+ o.foo(10,"a")
155
+ o.bar(1)
156
+ end
157
+ end
158
+ if RUBY_VERSION >= "1.9"
159
+ def test_arg_after_rest
160
+ class_arg_after_rest = Class.new
161
+ class_arg_after_rest.module_eval do
162
+ define.foo(Any, Rest, Range)
163
+ end
164
+ assert_raise(DefinitionError) do
165
+ class_arg_after_rest.module_eval do
166
+ def foo(a, *b)
167
+ end
168
+ end
169
+ end
170
+ assert_raise(DefinitionError) do
171
+ class_arg_after_rest.module_eval do
172
+ def foo(a, b, *c)
173
+ end
174
+ end
175
+ end
176
+ assert_nothing_raised do
177
+ class_arg_after_rest.module_eval do
178
+ eval "def foo(a, *b, c);end"
179
+ end
180
+ end
181
+ end
182
+ def test_block
183
+ assert_raise(DefinitionError) do
184
+ Class.new.module_eval do
185
+ define.foo
186
+ def foo(&a)
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wanabe-definition
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - ""
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-13 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: ""
17
+ email: s.wanabe@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - Changelog
26
+ - README
27
+ - definition.gemspec
28
+ - lib/definition.rb
29
+ - test/tc_definition.rb
30
+ has_rdoc: true
31
+ homepage: http://github.com/wanabe/definition
32
+ post_install_message:
33
+ rdoc_options: []
34
+
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: "0"
42
+ version:
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ requirements: []
50
+
51
+ rubyforge_project:
52
+ rubygems_version: 1.2.0
53
+ signing_key:
54
+ specification_version: 2
55
+ summary: ""
56
+ test_files: []
57
+