strongtyping 2.0.6 → 2.0.7
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 +58 -0
- data/LGPL +515 -515
- data/MANIFEST +11 -9
- data/README +330 -0
- data/Rakefile +48 -0
- data/{extconf.rb → ext/extconf.rb} +3 -3
- data/ext/strongtyping.c +283 -0
- data/{strongtyping.h → ext/strongtyping.h} +34 -34
- data/{t/timetest.rb → samples/sample_strongtyping.rb} +28 -28
- data/strongtyping.gemspec +22 -24
- data/{t/test.rb → test/test_strongtyping.rb} +104 -100
- metadata +24 -21
- data/README.en +0 -420
- data/strongtyping.c +0 -247
- data/strongtyping.gemspec~ +0 -24
data/MANIFEST
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
CHANGES
|
2
|
+
LGPL
|
3
|
+
MANIFEST
|
4
|
+
README
|
5
|
+
Rakefile
|
6
|
+
strongtyping.gemspec
|
7
|
+
ext/extconf.rb
|
8
|
+
ext/strongtyping.c
|
9
|
+
ext/strongtyping.h
|
10
|
+
test/functest.rb
|
11
|
+
test/timetest.rb
|
data/README
ADDED
@@ -0,0 +1,330 @@
|
|
1
|
+
= Description
|
2
|
+
The strongtyping library is a provides a convenient way for Ruby methods
|
3
|
+
to check parameter types, and also dynamically query them. In addition to
|
4
|
+
merely checking a single set of types, it allows easy overloading based on
|
5
|
+
a number of different templates.
|
6
|
+
|
7
|
+
= Installation
|
8
|
+
gem install strongtyping
|
9
|
+
|
10
|
+
= Synopsis
|
11
|
+
Let's say you have the following function:
|
12
|
+
|
13
|
+
def foo(a, b)
|
14
|
+
...
|
15
|
+
end
|
16
|
+
|
17
|
+
Now let's say this function wants 'a' to always be a String, and 'b'
|
18
|
+
should be Numeric:
|
19
|
+
|
20
|
+
require 'strongtyping'
|
21
|
+
include strongtyping
|
22
|
+
|
23
|
+
def foo(a, b)
|
24
|
+
expect(a, String, b, Numeric)
|
25
|
+
...
|
26
|
+
end
|
27
|
+
|
28
|
+
If 'a' or 'b' is of the wrong type, an ArgumentTypeError will be raised.
|
29
|
+
|
30
|
+
Overloading is just as easy:
|
31
|
+
|
32
|
+
require 'strongtyping'
|
33
|
+
include strongtyping
|
34
|
+
|
35
|
+
def bar(*args)
|
36
|
+
overload(args, String, String){ |s1,s2|
|
37
|
+
...
|
38
|
+
return
|
39
|
+
}
|
40
|
+
|
41
|
+
overload(args, String, Integer){ |s,i|
|
42
|
+
...
|
43
|
+
return
|
44
|
+
}
|
45
|
+
|
46
|
+
overload_default args
|
47
|
+
end
|
48
|
+
|
49
|
+
If someone calls 'bar' with two Strings, or a String and an Integer,
|
50
|
+
the appropriate block will be called. Otherwise, an OverloadError
|
51
|
+
is raised.
|
52
|
+
|
53
|
+
How about default parameters? Say we have the following function:
|
54
|
+
|
55
|
+
def baz(a, b = nil)
|
56
|
+
action = "You baz #{a}";
|
57
|
+
action += " with a #{b}" if b
|
58
|
+
|
59
|
+
print action, "\n"
|
60
|
+
end
|
61
|
+
|
62
|
+
Now, b can either be nil or a String. We don't want to have two
|
63
|
+
full overload cases... that would duplicate code. So, expect()
|
64
|
+
allows an array of types:
|
65
|
+
|
66
|
+
expect(a, String, b, [String, NilClass]);
|
67
|
+
|
68
|
+
This takes care of the above case nicely.
|
69
|
+
|
70
|
+
What if your code is curious about which types are allowed? The
|
71
|
+
get_arg_types function is provided for just this purpose. Given the
|
72
|
+
above definitions for 'foo' and 'bar', consider the following code:
|
73
|
+
|
74
|
+
p get_arg_types(method(:foo)) # => [[String, Numeric]]
|
75
|
+
p get_arg_types(method(:bar)) # => [[String, String], [String, Integer]]
|
76
|
+
p get_arg_types(method(:baz)) # => [[String, [String, NilClass]]]
|
77
|
+
|
78
|
+
This is useful if you're converting user input into a form that the
|
79
|
+
method expects. (If you get "1234", should you convert it to an
|
80
|
+
integer, or is it best left a string? Now you know.)
|
81
|
+
|
82
|
+
What if you have an array of arguments, 'arr', and you're worried
|
83
|
+
that the method 'bar' won't accept them? You can check ahead of
|
84
|
+
time:
|
85
|
+
|
86
|
+
if not verify_args_for(method(:bar), arr)
|
87
|
+
print "I can't let you do that, Dave\n"
|
88
|
+
end
|
89
|
+
|
90
|
+
= Reference
|
91
|
+
Module: strongtyping
|
92
|
+
|
93
|
+
Methods:
|
94
|
+
|
95
|
+
expect(obj0, Module0[, obj1, Module1[,...objN, ModuleN]])
|
96
|
+
|
97
|
+
Verify the parameters obj0..objN are of the given class (or
|
98
|
+
module) Module0..ModuleN
|
99
|
+
|
100
|
+
overload(args, [Module0[, Module1[,...ModuleN]]]) { | o0, o1,..oN | }
|
101
|
+
|
102
|
+
Call the block with 'args' if they match the pattern
|
103
|
+
Module0..ModuleN. The block should _always_ call return at the
|
104
|
+
end.
|
105
|
+
|
106
|
+
overload_exception(args, [Module0[,...ModuleN]]]) { | o0, o1,..oN | }
|
107
|
+
|
108
|
+
This acts identically to overload(), except the case specified
|
109
|
+
is considered invalid, and thus not returned by get_arg_types().
|
110
|
+
It is expected that the specified block will throw an exception.
|
111
|
+
|
112
|
+
overload_default(args)
|
113
|
+
overload_error(args)
|
114
|
+
|
115
|
+
Raise OverloadError. This should _always_ be called after the
|
116
|
+
last overload() block. In addition to raising the exception,
|
117
|
+
it aids in checking parameters. As of 2.0, the overload_error
|
118
|
+
name is deprecated; use overload_default.
|
119
|
+
|
120
|
+
get_arg_types(Method)
|
121
|
+
|
122
|
+
Return an array of parameter templates. This is an array of
|
123
|
+
arrays, and will have multiple indices for functions using
|
124
|
+
multiple overload() blocks.
|
125
|
+
|
126
|
+
verify_args_for(method, args)
|
127
|
+
|
128
|
+
Verify the method 'method' will accept the arguments in array
|
129
|
+
'args', returning a boolean result.
|
130
|
+
|
131
|
+
Exceptions:
|
132
|
+
|
133
|
+
ArgumentTypeError < ArgumentError
|
134
|
+
|
135
|
+
This exception is raised by expect() if the arguments do not
|
136
|
+
match the expected types.
|
137
|
+
|
138
|
+
OverloadError < ArgumentTypeError
|
139
|
+
|
140
|
+
This exception is raised by overload_default() if no overload()
|
141
|
+
template matches the given arguments.
|
142
|
+
|
143
|
+
= Q & A
|
144
|
+
This section written by Ryan Pavlik.
|
145
|
+
|
146
|
+
Q: Why?
|
147
|
+
A: Because it was originally needed for the Mephle library.
|
148
|
+
|
149
|
+
Q: No really, why bother with static typing? Isn't ruby dynamic?
|
150
|
+
A: This is not 'static typing'. This is 'strong typing'. Static
|
151
|
+
typing is what you get when a variable can only be of a certain
|
152
|
+
type, as in C or C++. Strong typing is enforcing types. These may
|
153
|
+
seem similar, but they are actually not directly related.
|
154
|
+
|
155
|
+
Some other languages, such as Common Lisp, allow for dynamic,
|
156
|
+
strong typing. Strong typing and dynamic typing are not mutually
|
157
|
+
exclusive.
|
158
|
+
|
159
|
+
Q: Yeah, but really, why bother? Why not just let ruby sort out the
|
160
|
+
errors as they occur?
|
161
|
+
A: This is incorrect thinking. Allowing errors to just occur when
|
162
|
+
they happen is naive programming. Consider the following:
|
163
|
+
|
164
|
+
# Wait N seconds, then open the bridge for M seconds
|
165
|
+
def sendMsg(bridge, n, m)
|
166
|
+
sleep(n)
|
167
|
+
bridge.open
|
168
|
+
sleep(m)
|
169
|
+
bridge.close
|
170
|
+
end
|
171
|
+
|
172
|
+
Now say 'm' is pased in as a string. Oops! A TypeError is
|
173
|
+
raised. Now the bridge is open, and somewhere (hopefully!) someone
|
174
|
+
caught the exception so the program didn't crash, but the bridge
|
175
|
+
opening wasn't reversed, so it's going to stay open and back up
|
176
|
+
traffic until someone fixes the problem.
|
177
|
+
|
178
|
+
This is an academic example, but there are many cases when just
|
179
|
+
letting an error happen will lead to an inconsistent system state.
|
180
|
+
Ruby (and most systems) are not transactional, and inconsistent
|
181
|
+
states are unacceptable.
|
182
|
+
|
183
|
+
In addition, it is desireable to know _programmatically_ why
|
184
|
+
something failed, as specific action can be taken if desired.
|
185
|
+
|
186
|
+
"Wait," someone in the audience says, "you could just check to see
|
187
|
+
if 'm' and 'n' are of the correct type!"
|
188
|
+
|
189
|
+
Yes, yes you could.
|
190
|
+
|
191
|
+
That's what this module is for. ;-)
|
192
|
+
|
193
|
+
Q: Isn't it up to the caller to call my function correctly?
|
194
|
+
A: The caller cannot know and deal with errors that may occur in your
|
195
|
+
code. That's your job. Checking for errors ahead of time and
|
196
|
+
informing the caller about problems is also your job. This module
|
197
|
+
just makes it easy.
|
198
|
+
|
199
|
+
In addition, it's nice for the caller to be able to ask and check
|
200
|
+
what your method expects ahead of time to guard against error.
|
201
|
+
The strongtyping module also provides functionality for this.
|
202
|
+
|
203
|
+
Q: OK, but strong typing is baaad. What if I want to pass something
|
204
|
+
that acts like something else, or responds to a given symbol?
|
205
|
+
Doesn't ruby have "duck" typing?
|
206
|
+
A: First, what you're suggesting is evil. If you want that, go
|
207
|
+
write C++. :-)
|
208
|
+
|
209
|
+
Second, you should never depend on a function's implementation. If
|
210
|
+
the documentation says "pass me a hash" and you pass it anything
|
211
|
+
that responds to :[], your code may break when the next version
|
212
|
+
comes out.
|
213
|
+
|
214
|
+
Third, if you pass something that responds accurately to the
|
215
|
+
_interface_ (methods provided by class or module) specified, then
|
216
|
+
that should be _of_ that class or module. This may not be the case
|
217
|
+
with all ruby objects yet; for instance, anything responding to :[]
|
218
|
+
being something like a Mappable. You can make this the case in
|
219
|
+
your code, or urge developers to create a standard set of interface
|
220
|
+
mixins for just this purpose.
|
221
|
+
|
222
|
+
"Duck" typing just a term for this sort of "maybe" behavior, much
|
223
|
+
like what C++ STL templates use. However, the problem is that even
|
224
|
+
if an object responds to a method, there is no guarantee that the
|
225
|
+
method acts in an expected manner---and the interface may still
|
226
|
+
change without notice. "Duck" typing sounds much like 'duct
|
227
|
+
taping' depending on your accent, and I think duct-taping is a good
|
228
|
+
description of this is in practice. :-)
|
229
|
+
|
230
|
+
Another argument is that ruby allows one to change the behavior of
|
231
|
+
methods at any time:
|
232
|
+
|
233
|
+
a = String.new;
|
234
|
+
def a.split
|
235
|
+
print "hello world\n"
|
236
|
+
end
|
237
|
+
|
238
|
+
For this, I have two responses: first, if a method is deprecated
|
239
|
+
or changed dramatically, strongtyping can aid in letting the code
|
240
|
+
know:
|
241
|
+
|
242
|
+
a = String.new;
|
243
|
+
def a.split(*args)
|
244
|
+
overload(args) { print "hello world\n" }
|
245
|
+
overload_default args
|
246
|
+
end
|
247
|
+
|
248
|
+
This case will drop any normal calls through to overload_default,
|
249
|
+
raising an exception, which can be caught and analyzed. You can
|
250
|
+
even provide another case that calls the superclass.
|
251
|
+
|
252
|
+
Second, either you're changing the method in a subtle manner (it
|
253
|
+
does what it used to, with added effect), or an outrageous manner
|
254
|
+
(it acts nothing like it did before). In the former case, code
|
255
|
+
should work fine anyway. In the latter case, as in the above
|
256
|
+
example, you should ask yourself why you're changing it. The
|
257
|
+
function no longer splits, why is it called split? This is not
|
258
|
+
good design; the strongtyping module is here to aid in good design,
|
259
|
+
not prevent poor design.
|
260
|
+
|
261
|
+
A more realistic example would be the academic "Shape" class
|
262
|
+
example of inheritance, with "Ellipse" and "Circle". Ruby properly
|
263
|
+
allows one to make Circle a subclass of Ellipse, and redefine
|
264
|
+
"setSize" to the constrained definition of a circle. This change
|
265
|
+
is visible to code---an ArgumentError will be raised (2 arguments
|
266
|
+
for 1), or setSize can throw a ConstraintError. strongtyping
|
267
|
+
provides a useful function, overload_exception, for just this case:
|
268
|
+
|
269
|
+
class Circle < Ellipse
|
270
|
+
:
|
271
|
+
def setSize(*args)
|
272
|
+
overload(args, Integer) {
|
273
|
+
| r |
|
274
|
+
@radius = r
|
275
|
+
return
|
276
|
+
}
|
277
|
+
|
278
|
+
overload_exception(args, Integer, Integer) {
|
279
|
+
| a, b |
|
280
|
+
raise ConstraintError
|
281
|
+
}
|
282
|
+
|
283
|
+
overload_default args
|
284
|
+
end
|
285
|
+
:
|
286
|
+
end
|
287
|
+
|
288
|
+
Of course, there are a number of good choices for handling
|
289
|
+
this... you may still allow #setSize(a, b) if a == b. The
|
290
|
+
important part is that the change in behavior can now be determined
|
291
|
+
by code.
|
292
|
+
|
293
|
+
Q: But I always write perfect code. I know what my functions do, and
|
294
|
+
what they take, and what I'm passing them.
|
295
|
+
A: No one writes perfect code. Additionally, not all environments are
|
296
|
+
as controlled as yours may be. Especially in a networked
|
297
|
+
environment when someone may be invoking a method remotely, you
|
298
|
+
can't depend on calling code not to be malicious.
|
299
|
+
|
300
|
+
Q: OK, OK. But, uh... what is Mephle?
|
301
|
+
A: Mephle is a soon-to-be-released network-transparent persistant
|
302
|
+
object system written in ruby. It uses many of the Unity concepts
|
303
|
+
(http://unity-project.sf.net/). It will be on the RAA when
|
304
|
+
released.
|
305
|
+
|
306
|
+
= License
|
307
|
+
strongtyping - Method parameter checking for Ruby
|
308
|
+
Copyright (C) 2003-2011 Ryan Pavlik
|
309
|
+
|
310
|
+
This library is free software; you can redistribute it and/or
|
311
|
+
modify it under the terms of the GNU Lesser General Public
|
312
|
+
License as published by the Free Software Foundation; either
|
313
|
+
version 2.1 of the License, or (at your option) any later version.
|
314
|
+
|
315
|
+
This library is distributed in the hope that it will be useful,
|
316
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
317
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
318
|
+
Lesser General Public License for more details.
|
319
|
+
|
320
|
+
You should have received a copy of the GNU Lesser General Public
|
321
|
+
License along with this library; if not, write to the Free Software
|
322
|
+
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
323
|
+
|
324
|
+
= Future Plans
|
325
|
+
There's some odd C code that is causing a couple of warnings, but it
|
326
|
+
does not appear to be harmful. I want to clean this code up eventually.
|
327
|
+
|
328
|
+
= Authors
|
329
|
+
* Ryan Pavlik (original author)
|
330
|
+
* Daniel Berger (maintenance)
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rbconfig'
|
5
|
+
include Config
|
6
|
+
|
7
|
+
CLEAN.include(
|
8
|
+
'**/*.gem', # Gem files
|
9
|
+
'**/*.rbc', # Rubinius
|
10
|
+
'**/*.o', # C object file
|
11
|
+
'**/*.log', # Ruby extension build log
|
12
|
+
'**/Makefile', # C Makefile
|
13
|
+
'**/conftest.dSYM', # OS X build directory
|
14
|
+
"**/*.#{CONFIG['DLEXT']}" # C shared object
|
15
|
+
)
|
16
|
+
|
17
|
+
desc "Build the strongtyping source"
|
18
|
+
task :build => [:clean] do
|
19
|
+
make = File::ALT_SEPARATOR ? "nmake" : "make"
|
20
|
+
Dir.chdir('ext') do
|
21
|
+
ruby 'extconf.rb'
|
22
|
+
sh "#{make}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
namespace 'gem' do
|
27
|
+
desc 'Create the strongtyping gem'
|
28
|
+
task :create => [:clean] do
|
29
|
+
Dir["*.gem"].each{ |f| File.delete(f) } # Clean first
|
30
|
+
spec = eval(IO.read('strongtyping.gemspec'))
|
31
|
+
Gem::Builder.new(spec).build
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'Install the strongtyping gem'
|
35
|
+
task :install => [:create] do
|
36
|
+
file = Dir["*.gem"].first
|
37
|
+
sh "gem install #{file}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Rake::TestTask.new do |t|
|
42
|
+
task :test => :build
|
43
|
+
t.libs << 'ext'
|
44
|
+
t.verbose = true
|
45
|
+
t.warning = true
|
46
|
+
end
|
47
|
+
|
48
|
+
task :default => :test
|
@@ -1,3 +1,3 @@
|
|
1
|
-
require 'mkmf'
|
2
|
-
|
3
|
-
create_makefile "strongtyping"
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
create_makefile "strongtyping"
|
data/ext/strongtyping.c
ADDED
@@ -0,0 +1,283 @@
|
|
1
|
+
/*
|
2
|
+
StrongTyping - Method parameter checking for Ruby
|
3
|
+
Copyright (C) 2003 Ryan Pavlik
|
4
|
+
|
5
|
+
This library is free software; you can redistribute it and/or
|
6
|
+
modify it under the terms of the GNU Lesser General Public
|
7
|
+
License as published by the Free Software Foundation; either
|
8
|
+
version 2.1 of the License, or (at your option) any later version.
|
9
|
+
|
10
|
+
This library is distributed in the hope that it will be useful,
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
Lesser General Public License for more details.
|
14
|
+
|
15
|
+
You should have received a copy of the GNU Lesser General Public
|
16
|
+
License along with this library; if not, write to the Free Software
|
17
|
+
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
18
|
+
*/
|
19
|
+
|
20
|
+
#include "ruby.h"
|
21
|
+
#include "strongtyping.h"
|
22
|
+
|
23
|
+
/* Ruby 1.9.x */
|
24
|
+
#ifndef RSTRING_PTR
|
25
|
+
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
26
|
+
#endif
|
27
|
+
#ifndef RSTRING_LEN
|
28
|
+
#define RSTRING_LEN(s) (RSTRING(s)->len)
|
29
|
+
#endif
|
30
|
+
|
31
|
+
#ifndef RARRAY_PTR
|
32
|
+
#define RARRAY_PTR(a) (RARRAY(a)->ptr)
|
33
|
+
#endif
|
34
|
+
#ifndef RARRAY_LEN
|
35
|
+
#define RARRAY_LEN(a) (RARRAY(a)->len)
|
36
|
+
#endif
|
37
|
+
|
38
|
+
static int check_args(int argc, VALUE *obj, VALUE *mod);
|
39
|
+
|
40
|
+
static VALUE strongtyping_expect(int argc, VALUE *argv, VALUE self UNUSED) {
|
41
|
+
int i = 0;
|
42
|
+
VALUE obj[MAXARGS], mod[MAXARGS];
|
43
|
+
VALUE typestr;
|
44
|
+
|
45
|
+
if(!argc)
|
46
|
+
return Qnil;
|
47
|
+
|
48
|
+
if(argc % 2)
|
49
|
+
rb_raise(rb_eSyntaxError, "expect() requires argument pairs");
|
50
|
+
|
51
|
+
#ifndef __GNUC__
|
52
|
+
if(argc*2 > MAXARGS*2)
|
53
|
+
rb_raise(rb_eSyntaxError, "too many arguments to expect()");
|
54
|
+
#endif
|
55
|
+
|
56
|
+
for(i = 0; i < argc; i += 2) {
|
57
|
+
obj[i/2] = argv[i];
|
58
|
+
mod[(i+1)/2] = argv[i+1];
|
59
|
+
}
|
60
|
+
|
61
|
+
if(rb_funcall(obj[0], id_isa, 1, cQueryParams)) {
|
62
|
+
rb_funcall(obj[0], rb_intern("<<"), 1, rb_ary_new4(argc/2, mod));
|
63
|
+
rb_raise(eArgList, ""); // TODO: Why an empty string? Causes a warning in 1.9.x.
|
64
|
+
}
|
65
|
+
|
66
|
+
i = check_args(argc / 2, obj, mod);
|
67
|
+
|
68
|
+
if(i < 0)
|
69
|
+
return Qnil;
|
70
|
+
|
71
|
+
typestr = rb_funcall(mod[i], id_inspect, 0);
|
72
|
+
|
73
|
+
rb_raise(
|
74
|
+
eArgumentTypeError,
|
75
|
+
"Expecting %s as argument %d, got %s",
|
76
|
+
RSTRING_PTR(typestr), i + 1,
|
77
|
+
rb_class2name(rb_funcall(obj[i], id_class, 0))
|
78
|
+
);
|
79
|
+
}
|
80
|
+
|
81
|
+
static VALUE strongtyping_overload(int argc, VALUE *argv, VALUE self UNUSED) {
|
82
|
+
struct RArray *q;
|
83
|
+
|
84
|
+
if(argc < 1)
|
85
|
+
rb_raise(rb_eArgError, "At least one parameter required");
|
86
|
+
|
87
|
+
Check_Type(argv[0], T_ARRAY);
|
88
|
+
q = RARRAY(argv[0]);
|
89
|
+
|
90
|
+
if(RARRAY_LEN(q) && rb_funcall(RARRAY_PTR(q)[0], id_isa, 1, cQueryParams)) {
|
91
|
+
rb_funcall(RARRAY_PTR(q)[0], rb_intern("<<"), 1, rb_ary_new4(argc - 1, argv + 1));
|
92
|
+
return Qnil;
|
93
|
+
}
|
94
|
+
|
95
|
+
if(RARRAY_LEN(q) != (argc - 1))
|
96
|
+
return Qnil;
|
97
|
+
|
98
|
+
if(check_args(argc - 1, RARRAY_PTR(q), argv + 1) < 0){
|
99
|
+
if(argc == 2)
|
100
|
+
rb_yield(*RARRAY_PTR(*argv));
|
101
|
+
else
|
102
|
+
rb_yield(*argv);
|
103
|
+
}
|
104
|
+
|
105
|
+
return Qnil;
|
106
|
+
}
|
107
|
+
|
108
|
+
static VALUE strongtyping_overload_exception(int argc, VALUE *argv, VALUE self UNUSED) {
|
109
|
+
struct RArray *q;
|
110
|
+
|
111
|
+
if(argc < 1)
|
112
|
+
rb_raise(rb_eArgError, "At least one parameters required");
|
113
|
+
|
114
|
+
Check_Type(argv[0], T_ARRAY);
|
115
|
+
q = RARRAY(argv[0]);
|
116
|
+
|
117
|
+
if(RARRAY_LEN(q) && (argc - 1) == 0)
|
118
|
+
return Qnil;
|
119
|
+
|
120
|
+
if(check_args(argc - 1, RARRAY_PTR(q), argv + 1) < 0)
|
121
|
+
rb_yield(argv[0]);
|
122
|
+
|
123
|
+
return Qnil;
|
124
|
+
}
|
125
|
+
|
126
|
+
static VALUE strongtyping_overload_error(VALUE self UNUSED, VALUE args) {
|
127
|
+
struct RArray *q;
|
128
|
+
VALUE classlist;
|
129
|
+
const char *name = 0;
|
130
|
+
int i = 0;
|
131
|
+
|
132
|
+
Check_Type(args, T_ARRAY);
|
133
|
+
q = RARRAY(args);
|
134
|
+
|
135
|
+
if(RARRAY_LEN(q) && rb_funcall(RARRAY_PTR(q)[0], id_isa, 1, cQueryParams))
|
136
|
+
rb_raise(eArgList, "");
|
137
|
+
|
138
|
+
classlist = rb_str_new2("");
|
139
|
+
|
140
|
+
for(i = 0; i < RARRAY_LEN(q); i++) {
|
141
|
+
if(i > 0)
|
142
|
+
rb_str_cat(classlist, ", ", 2);
|
143
|
+
|
144
|
+
name = rb_class2name(rb_funcall(RARRAY_PTR(q)[i], id_class, 0));
|
145
|
+
rb_str_cat(classlist, name, strlen(name));
|
146
|
+
}
|
147
|
+
|
148
|
+
rb_raise(
|
149
|
+
eOverloadError,
|
150
|
+
"No matching template for arguments: [%s]",
|
151
|
+
RSTRING_PTR(classlist)
|
152
|
+
);
|
153
|
+
}
|
154
|
+
|
155
|
+
static int check_args(int argc, VALUE *obj, VALUE *mod) {
|
156
|
+
int i = 0;
|
157
|
+
VALUE ret;
|
158
|
+
|
159
|
+
for(i = 0; i < argc; i++){
|
160
|
+
if(TYPE(mod[i]) == T_ARRAY){
|
161
|
+
int j = 0, ok = 0;
|
162
|
+
|
163
|
+
for(j = 0; j < RARRAY_LEN(mod[i]); j++){
|
164
|
+
if(rb_funcall(obj[i], id_isa, 1, RARRAY_PTR(mod[i])[j]) == Qtrue)
|
165
|
+
ok = 1;
|
166
|
+
}
|
167
|
+
|
168
|
+
if(ok)
|
169
|
+
continue;
|
170
|
+
else
|
171
|
+
return i;
|
172
|
+
}
|
173
|
+
else{
|
174
|
+
ret = rb_funcall(obj[i], id_isa, 1, mod[i]);
|
175
|
+
if(ret == Qfalse) return i;
|
176
|
+
}
|
177
|
+
}
|
178
|
+
|
179
|
+
return -1;
|
180
|
+
}
|
181
|
+
|
182
|
+
static VALUE call_method(VALUE ary) {
|
183
|
+
VALUE method = RARRAY_PTR(ary)[0],
|
184
|
+
query = RARRAY_PTR(ary)[1];
|
185
|
+
VALUE *argv = NULL;
|
186
|
+
VALUE ret;
|
187
|
+
int argc = 0,
|
188
|
+
i = 0;
|
189
|
+
|
190
|
+
argc = FIX2INT(rb_funcall(method, rb_intern("arity"), 0));
|
191
|
+
|
192
|
+
if(argc == 0) {
|
193
|
+
rb_funcall(query, rb_intern("<<"), 1, rb_ary_new());
|
194
|
+
rb_raise(eArgList, "");
|
195
|
+
}
|
196
|
+
else if(argc < 0)
|
197
|
+
argc = -argc;
|
198
|
+
|
199
|
+
argv = malloc(sizeof(VALUE) * argc);
|
200
|
+
argv[0] = query;
|
201
|
+
|
202
|
+
for(i = 1; i < argc - 1; i++)
|
203
|
+
argv[i] = Qnil;
|
204
|
+
|
205
|
+
ret = rb_funcall2(method, rb_intern("call"), argc, argv);
|
206
|
+
free(argv);
|
207
|
+
|
208
|
+
return ret;
|
209
|
+
}
|
210
|
+
|
211
|
+
static VALUE grab_types(VALUE query) {
|
212
|
+
return query;
|
213
|
+
}
|
214
|
+
|
215
|
+
static VALUE strongtyping_get_arg_types(VALUE obj UNUSED, VALUE method) {
|
216
|
+
VALUE query, ary;
|
217
|
+
query = rb_funcall(cQueryParams, rb_intern("new"), 0);
|
218
|
+
ary = rb_ary_new3(2, method, query);
|
219
|
+
|
220
|
+
return rb_rescue2(call_method, ary, grab_types, query, eArgList, 0);
|
221
|
+
}
|
222
|
+
|
223
|
+
static VALUE strongtyping_verify_args_for(VALUE self, VALUE method, VALUE args) {
|
224
|
+
struct RArray *list = NULL;
|
225
|
+
struct RArray *t = NULL;
|
226
|
+
struct RArray *a = NULL;
|
227
|
+
int i = 0;
|
228
|
+
VALUE template = strongtyping_get_arg_types(self, method);
|
229
|
+
|
230
|
+
list = RARRAY(template);
|
231
|
+
a = RARRAY(args);
|
232
|
+
|
233
|
+
for(i = 0; i < RARRAY_LEN(list); i++){
|
234
|
+
t = RARRAY_PTR(list)[i];
|
235
|
+
|
236
|
+
if(RARRAY_LEN(a) != RARRAY_LEN(t))
|
237
|
+
continue;
|
238
|
+
|
239
|
+
if(check_args(RARRAY_LEN(a), RARRAY_PTR(a), RARRAY_PTR(t)) < 0)
|
240
|
+
return Qtrue;
|
241
|
+
}
|
242
|
+
|
243
|
+
return Qfalse;
|
244
|
+
}
|
245
|
+
|
246
|
+
void Init_strongtyping() {
|
247
|
+
mStrongTyping = rb_define_module("StrongTyping");
|
248
|
+
id_isa = rb_intern("is_a?");
|
249
|
+
id_class = rb_intern("class");
|
250
|
+
id_inspect = rb_intern("inspect");
|
251
|
+
|
252
|
+
/* 2.0.7: The version of the strongtyping library */
|
253
|
+
rb_define_const(mStrongTyping, "VERSION", rb_str_new2("2.0.7"));
|
254
|
+
|
255
|
+
cQueryParams = rb_define_class_under(mStrongTyping,
|
256
|
+
"%QueryParams",
|
257
|
+
rb_cArray);
|
258
|
+
|
259
|
+
eArgumentTypeError = rb_define_class_under(mStrongTyping,
|
260
|
+
"ArgumentTypeError",
|
261
|
+
rb_eArgError);
|
262
|
+
eOverloadError = rb_define_class_under(mStrongTyping,
|
263
|
+
"OverloadError",
|
264
|
+
eArgumentTypeError);
|
265
|
+
eArgList = rb_define_class_under(mStrongTyping,
|
266
|
+
"%ArgList",
|
267
|
+
rb_eException);
|
268
|
+
|
269
|
+
rb_define_module_function(mStrongTyping, "expect",
|
270
|
+
strongtyping_expect, -1);
|
271
|
+
rb_define_module_function(mStrongTyping, "overload",
|
272
|
+
strongtyping_overload, -1);
|
273
|
+
rb_define_module_function(mStrongTyping, "overload_exception",
|
274
|
+
strongtyping_overload_exception, -1);
|
275
|
+
rb_define_module_function(mStrongTyping, "overload_default",
|
276
|
+
strongtyping_overload_error, 1);
|
277
|
+
rb_define_module_function(mStrongTyping, "overload_error",
|
278
|
+
strongtyping_overload_error, 1);
|
279
|
+
rb_define_module_function(mStrongTyping, "get_arg_types",
|
280
|
+
strongtyping_get_arg_types, 1);
|
281
|
+
rb_define_module_function(mStrongTyping, "verify_args_for",
|
282
|
+
strongtyping_verify_args_for, 2);
|
283
|
+
}
|