strongtyping 2.0.6 → 2.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.en DELETED
@@ -1,420 +0,0 @@
1
- StrongTyping 2.0
2
- ================
3
-
4
- StrongTyping is a little ruby module that provides a convenient way
5
- for ruby methods to check parameter types, and also dynamically
6
- query them. In addition to merely checking a single set of types,
7
- it allows easy overloading based on a number of different templates.
8
-
9
- Changes
10
- -------
11
-
12
- 2.0.6b - Bugfix: Shouldn't get "too many arguments" on gcc
13
- anymore.
14
-
15
- 2.0.6a - Bugfix: Portability issues (zero-sized arrays and UNUSED
16
- for non-GNU platforms fixed).
17
-
18
- 2.0.6 - Bugfix: Types given after lists of types were not being
19
- checked, as in the following:
20
-
21
- expect(a, String, b, [Integer, String], d, String)
22
-
23
- Before, 'd' could be any type and pass; now this is fixed.
24
-
25
- 2.0.5 - Bugfix: overload "No matching template" was broken, and
26
- I didn't notice.
27
-
28
- Bugfix: get_arg_types fixed for no arguments; now returns []
29
- in the right places for both overload() and expect().
30
-
31
- Mod: Should compile without warnings with -W -Wall, at least
32
- on gcc.
33
-
34
- Mod: Added unit tests.
35
-
36
- 2.0.4 - Bugfix: Optional arguments are now handled correctly
37
- when doing get_arg_types.
38
-
39
- 2.0.3 - It's a bit of a hack, but overload_default's "No matching
40
- template" error now displays the given types, which makes
41
- debugging a lot easier.
42
-
43
- 2.0.2 - Change Object#type to Object#class for conformance with
44
- Ruby 1.8
45
-
46
- Bugfix: overload() range check that caused a segfault on
47
- some systems
48
-
49
- Bugfix: verify_args_for() should work now
50
-
51
- 2.0.1 - Bugfix: overload() blocks with one parameter now receive the
52
- parameter instead of an array
53
-
54
- 2.0 - Rewritten in C for speed.
55
- * As in 1.0.1, expect (and overload, etc) take arrays of
56
- classes/modules
57
-
58
- * overload_error is deprecated (but still available) in
59
- favor of overload_default
60
-
61
- * overload_exception acts as overload, but acts as an
62
- "invalid" case; useful for invalid cases where a
63
- specific exception should be thrown
64
-
65
- * More on "duck typing" in the FAQ
66
-
67
- 1.0.1 - [Not officially released] Added support for arrays of types
68
- to expect(), as in expect(a, [Integer, NilClass])
69
- 1.0 - First release
70
-
71
- Requirements
72
- ------------
73
-
74
- * ruby 1.6
75
- * C compiler
76
-
77
- Install
78
- -------
79
-
80
- De-Compress archive and enter its top directory.
81
- Then type:
82
-
83
- $ ruby extconf.rb
84
- $ make
85
- ($ su)
86
- # make install
87
-
88
- Usage
89
- -----
90
-
91
- Let's say you have the following function:
92
-
93
- def foo(a, b)
94
- ...
95
- end
96
-
97
- Now let's say this function wants 'a' to always be a String, and 'b'
98
- should be Numeric:
99
-
100
- require 'strongtyping'
101
- include StrongTyping
102
-
103
- def foo(a, b)
104
- expect(a, String, b, Numeric)
105
- ...
106
- end
107
-
108
- If 'a' or 'b' is of the wrong type, an ArgumentTypeError will be
109
- raised.
110
-
111
- Overloading is just as easy:
112
-
113
- require 'strongtyping'
114
- include StrongTyping
115
-
116
- def bar(*args)
117
- overload(args, String, String) {
118
- | s1, s2 |
119
- ...
120
- return
121
- }
122
-
123
- overload(args, String, Integer) {
124
- | s, i |
125
- ...
126
- return
127
- }
128
-
129
- overload_default args
130
- end
131
-
132
- If someone calls 'bar' with two Strings, or a String and an Integer,
133
- the appropriate block will be called. Otherwise, an OverloadError
134
- is raised.
135
-
136
- How about default parameters? Say we have the following function:
137
-
138
- def baz(a, b = nil)
139
- action = "You baz #{a}";
140
- action += " with a #{b}" if b
141
-
142
- print action, "\n"
143
- end
144
-
145
- Now, b can either be nil or a String. We don't want to have two
146
- full overload cases... that would duplicate code. So, expect()
147
- allows an array of types:
148
-
149
- expect(a, String, b, [String, NilClass]);
150
-
151
- This takes care of the above case nicely.
152
-
153
- What if your code is curious about which types are allowed? The
154
- get_arg_types function is provided for just this purpose. Given the
155
- above definitions for 'foo' and 'bar', consider the following code:
156
-
157
- p get_arg_types(method(:foo)) # => [[String, Numeric]]
158
- p get_arg_types(method(:bar)) # => [[String, String], [String, Integer]]
159
- p get_arg_types(method(:baz)) # => [[String, [String, NilClass]]]
160
-
161
- This is useful if you're converting user input into a form that the
162
- method expects. (If you get "1234", should you convert it to an
163
- integer, or is it best left a string? Now you know.)
164
-
165
- What if you have an array of arguments, 'arr', and you're worried
166
- that the method 'bar' won't accept them? You can check ahead of
167
- time:
168
-
169
- if not verify_args_for(method(:bar), arr)
170
- print "I can't let you do that, Dave\n"
171
- end
172
-
173
- Reference
174
- ---------
175
- Module: StrongTyping
176
-
177
- Methods:
178
-
179
- expect(obj0, Module0[, obj1, Module1[,...objN, ModuleN]])
180
-
181
- Verify the parameters obj0..objN are of the given class (or
182
- module) Module0..ModuleN
183
-
184
- overload(args, [Module0[, Module1[,...ModuleN]]]) { | o0, o1,..oN | }
185
-
186
- Call the block with 'args' if they match the pattern
187
- Module0..ModuleN. The block should _always_ call return at the
188
- end.
189
-
190
- overload_exception(args, [Module0[,...ModuleN]]]) { | o0, o1,..oN | }
191
-
192
- This acts identically to overload(), except the case specified
193
- is considered invalid, and thus not returned by get_arg_types().
194
- It is expected that the specified block will throw an exception.
195
-
196
- overload_default(args)
197
- overload_error(args)
198
-
199
- Raise OverloadError. This should _always_ be called after the
200
- last overload() block. In addition to raising the exception,
201
- it aids in checking parameters. As of 2.0, the overload_error
202
- name is deprecated; use overload_default.
203
-
204
- get_arg_types(Method)
205
-
206
- Return an array of parameter templates. This is an array of
207
- arrays, and will have multiple indices for functions using
208
- multiple overload() blocks.
209
-
210
- verify_args_for(method, args)
211
-
212
- Verify the method 'method' will accept the arguments in array
213
- 'args', returning a boolean result.
214
-
215
- Exceptions:
216
-
217
- ArgumentTypeError < ArgumentError
218
-
219
- This exception is raised by expect() if the arguments do not
220
- match the expected types.
221
-
222
- OverloadError < ArgumentTypeError
223
-
224
- This exception is raised by overload_default() if no overload()
225
- template matches the given arguments.
226
-
227
- FAQ
228
- ---
229
- These aren't actually FAQs (yet), but some issues that _have_ been
230
- brought up.
231
-
232
- Q: Why?
233
- A: Because I need it for Mephle. :-)
234
-
235
-
236
- Q: No really, why bother with static typing? Isn't ruby dynamic?
237
- A: This is not 'static typing'. This is 'strong typing'. Static
238
- typing is what you get when a variable can only be of a certain
239
- type, as in C or C++. Strong typing is enforcing types. These may
240
- seem similar, but they are actually not directly related.
241
-
242
- Some other languages, such as Common Lisp, allow for dynamic,
243
- strong typing. Strong typing and dynamic typing are not mutually
244
- exclusive.
245
-
246
-
247
- Q: Yeah, but really, why bother? Why not just let ruby sort out the
248
- errors as they occur?
249
- A: This is incorrect thinking. Allowing errors to just occur when
250
- they happen is naive programming. Consider the following:
251
-
252
- # Wait N seconds, then open the bridge for M seconds
253
- def sendMsg(bridge, n, m)
254
- sleep(n)
255
- bridge.open
256
- sleep(m)
257
- bridge.close
258
- end
259
-
260
- Now say 'm' is pased in as a string. Oops! A TypeError is
261
- raised. Now the bridge is open, and somewhere (hopefully!) someone
262
- caught the exception so the program didn't crash, but the bridge
263
- opening wasn't reversed, so it's going to stay open and back up
264
- traffic until someone fixes the problem.
265
-
266
- This is an academic example, but there are many cases when just
267
- letting an error happen will lead to an inconsistent system state.
268
- Ruby (and most systems) are not transactional, and inconsistent
269
- states are unacceptable.
270
-
271
- In addition, it is desireable to know _programmatically_ why
272
- something failed, as specific action can be taken if desired.
273
-
274
- "Wait," someone in the audience says, "you could just check to see
275
- if 'm' and 'n' are of the correct type!"
276
-
277
- Yes, yes you could.
278
-
279
- That's what this module is for. ;-)
280
-
281
-
282
- Q: Isn't it up to the caller to call my function correctly?
283
- A: The caller cannot know and deal with errors that may occur in your
284
- code. That's your job. Checking for errors ahead of time and
285
- informing the caller about problems is also your job. This module
286
- just makes it easy.
287
-
288
- In addition, it's nice for the caller to be able to ask and check
289
- what your method expects ahead of time to guard against error.
290
- The StrongTyping module also provides functionality for this.
291
-
292
-
293
- Q: OK, but strong typing is baaad. What if I want to pass something
294
- that acts like something else, or responds to a given symbol?
295
- Doesn't ruby have "duck" typing?
296
- A: First, what you're suggesting is evil. If you want that, go
297
- write C++. :-)
298
-
299
- Second, you should never depend on a function's implementation. If
300
- the documentation says "pass me a hash" and you pass it anything
301
- that responds to :[], your code may break when the next version
302
- comes out.
303
-
304
- Third, if you pass something that responds accurately to the
305
- _interface_ (methods provided by class or module) specified, then
306
- that should be _of_ that class or module. This may not be the case
307
- with all ruby objects yet; for instance, anything responding to :[]
308
- being something like a Mappable. You can make this the case in
309
- your code, or urge developers to create a standard set of interface
310
- mixins for just this purpose.
311
-
312
- "Duck" typing just a term for this sort of "maybe" behavior, much
313
- like what C++ STL templates use. However, the problem is that even
314
- if an object responds to a method, there is no guarantee that the
315
- method acts in an expected manner---and the interface may still
316
- change without notice. "Duck" typing sounds much like 'duct
317
- taping' depending on your accent, and I think duct-taping is a good
318
- description of this is in practice. :-)
319
-
320
- Another argument is that ruby allows one to change the behavior of
321
- methods at any time:
322
-
323
- a = String.new;
324
- def a.split
325
- print "hello world\n"
326
- end
327
-
328
- For this, I have two responses: first, if a method is deprecated
329
- or changed dramatically, StrongTyping can aid in letting the code
330
- know:
331
-
332
- a = String.new;
333
- def a.split(*args)
334
- overload(args) { print "hello world\n" }
335
- overload_default args
336
- end
337
-
338
- This case will drop any normal calls through to overload_default,
339
- raising an exception, which can be caught and analyzed. You can
340
- even provide another case that calls the superclass.
341
-
342
- Second, either you're changing the method in a subtle manner (it
343
- does what it used to, with added effect), or an outrageous manner
344
- (it acts nothing like it did before). In the former case, code
345
- should work fine anyway. In the latter case, as in the above
346
- example, you should ask yourself why you're changing it. The
347
- function no longer splits, why is it called split? This is not
348
- good design; the StrongTyping module is here to aid in good design,
349
- not prevent poor design.
350
-
351
- A more realistic example would be the academic "Shape" class
352
- example of inheritance, with "Ellipse" and "Circle". Ruby properly
353
- allows one to make Circle a subclass of Ellipse, and redefine
354
- "setSize" to the constrained definition of a circle. This change
355
- is visible to code---an ArgumentError will be raised (2 arguments
356
- for 1), or setSize can throw a ConstraintError. StrongTyping
357
- provides a useful function, overload_exception, for just this case:
358
-
359
- class Circle < Ellipse
360
- :
361
- def setSize(*args)
362
- overload(args, Integer) {
363
- | r |
364
- @radius = r
365
- return
366
- }
367
-
368
- overload_exception(args, Integer, Integer) {
369
- | a, b |
370
- raise ConstraintError
371
- }
372
-
373
- overload_default args
374
- end
375
- :
376
- end
377
-
378
- Of course, there are a number of good choices for handling
379
- this... you may still allow #setSize(a, b) if a == b. The
380
- important part is that the change in behavior can now be determined
381
- by code.
382
-
383
-
384
- Q: But I always write perfect code. I know what my functions do, and
385
- what they take, and what I'm passing them.
386
- A: No one writes perfect code. Additionally, not all environments are
387
- as controlled as yours may be. Especially in a networked
388
- environment when someone may be invoking a method remotely, you
389
- can't depend on calling code not to be malicious.
390
-
391
-
392
- Q: OK, OK. But, uh... what is Mephle?
393
- A: Mephle is a soon-to-be-released network-transparent persistant
394
- object system written in ruby. It uses many of the Unity concepts
395
- (http://unity-project.sf.net/). It will be on the RAA when
396
- released.
397
-
398
-
399
- License
400
- -------
401
-
402
- StrongTyping - Method parameter checking for Ruby
403
- Copyright (C) 2003 Ryan Pavlik
404
-
405
- This library is free software; you can redistribute it and/or
406
- modify it under the terms of the GNU Lesser General Public
407
- License as published by the Free Software Foundation; either
408
- version 2.1 of the License, or (at your option) any later version.
409
-
410
- This library is distributed in the hope that it will be useful,
411
- but WITHOUT ANY WARRANTY; without even the implied warranty of
412
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
413
- Lesser General Public License for more details.
414
-
415
- You should have received a copy of the GNU Lesser General Public
416
- License along with this library; if not, write to the Free Software
417
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
418
-
419
-
420
- Ryan Pavlik <rpav@mephle.com>
@@ -1,247 +0,0 @@
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
- static int check_args(int argc, VALUE *obj, VALUE *mod);
24
-
25
- static VALUE
26
- strongtyping_expect(int argc, VALUE *argv, VALUE self UNUSED) {
27
- int i = 0;
28
- VALUE obj[MAXARGS], mod[MAXARGS];
29
- VALUE typestr;
30
-
31
- if(!argc) return Qnil;
32
- if(argc % 2)
33
- rb_raise(rb_eSyntaxError, "expect() requires argument pairs");
34
-
35
- #ifndef __GNUC__
36
- if(argc*2 > MAXARGS*2)
37
- rb_raise(rb_eSyntaxError, "too many arguments to expect()");
38
- #endif
39
-
40
- for(i = 0; i < argc; i += 2) {
41
- obj[i/2] = argv[i];
42
- mod[(i+1)/2] = argv[i+1];
43
- }
44
-
45
- if(rb_funcall(obj[0], id_isa, 1, cQueryParams)) {
46
- rb_funcall(obj[0], rb_intern("<<"), 1, rb_ary_new4(argc/2, mod));
47
- rb_raise(eArgList, "");
48
- }
49
-
50
- i = check_args(argc / 2, obj, mod);
51
-
52
- if(i < 0) return Qnil;
53
-
54
- typestr = rb_funcall(mod[i], id_inspect, 0);
55
- rb_raise(eArgumentTypeError, "Expecting %s as argument %d, got %s",
56
- RSTRING(typestr)->ptr, i + 1,
57
- rb_class2name(rb_funcall(obj[i], id_class, 0)));
58
- }
59
-
60
- static VALUE
61
- strongtyping_overload(int argc, VALUE *argv, VALUE self UNUSED) {
62
- struct RArray *q;
63
-
64
- if(argc < 1)
65
- rb_raise(rb_eArgError, "At least one parameter required");
66
-
67
- Check_Type(argv[0], T_ARRAY);
68
- q = RARRAY(argv[0]);
69
-
70
- if(q->len && rb_funcall(q->ptr[0], id_isa, 1, cQueryParams)) {
71
- rb_funcall(q->ptr[0], rb_intern("<<"), 1,
72
- rb_ary_new4(argc - 1, argv + 1));
73
- return Qnil;
74
- }
75
-
76
- if(q->len != (argc - 1))
77
- return Qnil;
78
-
79
- if(check_args(argc - 1, q->ptr, argv + 1) < 0) {
80
- if(argc == 2) rb_yield(*RARRAY(*argv)->ptr);
81
- else rb_yield(*argv);
82
- }
83
-
84
- return Qnil;
85
- }
86
-
87
- static VALUE
88
- strongtyping_overload_exception(int argc, VALUE *argv, VALUE self UNUSED) {
89
- struct RArray *q;
90
-
91
- if(argc < 1)
92
- rb_raise(rb_eArgError, "At least one parameters required");
93
-
94
- Check_Type(argv[0], T_ARRAY);
95
- q = RARRAY(argv[0]);
96
-
97
- if(q->len && (argc - 1) == 0)
98
- return Qnil;
99
-
100
- if(check_args(argc - 1, q->ptr, argv + 1) < 0)
101
- rb_yield(argv[0]);
102
-
103
- return Qnil;
104
- }
105
-
106
- static VALUE
107
- strongtyping_overload_error(VALUE self UNUSED, VALUE args) {
108
- struct RArray *q;
109
- VALUE classlist;
110
- char *name = 0;
111
- int i = 0;
112
-
113
- Check_Type(args, T_ARRAY);
114
- q = RARRAY(args);
115
-
116
- if(q->len && rb_funcall(q->ptr[0], id_isa, 1, cQueryParams))
117
- rb_raise(eArgList, "");
118
-
119
- classlist = rb_str_new2("");
120
- for(i = 0; i < q->len; i++) {
121
- if(i > 0) rb_str_cat(classlist, ", ", 2);
122
- name = rb_class2name(rb_funcall(q->ptr[i], id_class, 0));
123
- rb_str_cat(classlist, name, strlen(name));
124
- }
125
- rb_raise(eOverloadError, "No matching template for arguments: [%s]",
126
- RSTRING(classlist)->ptr);
127
- }
128
-
129
- static int
130
- check_args(int argc, VALUE *obj, VALUE *mod) {
131
- int i = 0;
132
- VALUE ret;
133
-
134
- for(i = 0; i < argc; i++) {
135
- if(TYPE(mod[i]) == T_ARRAY) {
136
- int j = 0, ok = 0;
137
- for(j = 0; j < RARRAY(mod[i])->len; j++)
138
- if(rb_funcall(obj[i], id_isa, 1, RARRAY(mod[i])->ptr[j]) == Qtrue)
139
- ok = 1;
140
-
141
- if(ok) continue;
142
- else return i;
143
- } else {
144
- ret = rb_funcall(obj[i], id_isa, 1, mod[i]);
145
- if(ret == Qfalse) return i;
146
- }
147
- }
148
-
149
- return -1;
150
- }
151
-
152
- static VALUE
153
- call_method(VALUE ary) {
154
- VALUE method = RARRAY(ary)->ptr[0],
155
- query = RARRAY(ary)->ptr[1];
156
- VALUE *argv = NULL;
157
- VALUE ret;
158
- int argc = 0,
159
- i = 0;
160
-
161
- argc = FIX2INT(rb_funcall(method, rb_intern("arity"), 0));
162
- if(argc == 0) {
163
- rb_funcall(query, rb_intern("<<"), 1, rb_ary_new());
164
- rb_raise(eArgList, "");
165
- } else if(argc < 0) argc = -argc;
166
-
167
- argv = malloc(sizeof(VALUE) * argc);
168
- argv[0] = query;
169
- for(i = 1; i < argc - 1; i++) argv[i] = Qnil;
170
-
171
- ret = rb_funcall2(method, rb_intern("call"), argc, argv);
172
- free(argv);
173
-
174
- return ret;
175
- }
176
-
177
- static VALUE
178
- grab_types(VALUE query) {
179
- return query;
180
- }
181
-
182
- static VALUE
183
- strongtyping_get_arg_types(VALUE obj UNUSED, VALUE method) {
184
- VALUE query, ary;
185
- query = rb_funcall(cQueryParams, rb_intern("new"), 0);
186
- ary = rb_ary_new3(2, method, query);
187
-
188
- return rb_rescue2(call_method, ary, grab_types, query, eArgList, 0);
189
- }
190
-
191
-
192
- static VALUE
193
- strongtyping_verify_args_for(VALUE self, VALUE method, VALUE args) {
194
- struct RArray *list = NULL,
195
- *t = NULL,
196
- *a = NULL;
197
- int i = 0;
198
- VALUE template = strongtyping_get_arg_types(self, method);
199
-
200
- list = RARRAY(template);
201
- a = RARRAY(args);
202
-
203
- for(i = 0; i < list->len; i++) {
204
- t = RARRAY(list->ptr[i]);
205
-
206
- if(a->len != t->len) continue;
207
- if(check_args(a->len, a->ptr, t->ptr) < 0) return Qtrue;
208
- }
209
-
210
- return Qfalse;
211
- }
212
-
213
- void Init_strongtyping() {
214
- mStrongTyping = rb_define_module("StrongTyping");
215
- id_isa = rb_intern("is_a?");
216
- id_class = rb_intern("class");
217
- id_inspect = rb_intern("inspect");
218
-
219
- cQueryParams = rb_define_class_under(mStrongTyping,
220
- "%QueryParams",
221
- rb_cArray);
222
-
223
- eArgumentTypeError = rb_define_class_under(mStrongTyping,
224
- "ArgumentTypeError",
225
- rb_eArgError);
226
- eOverloadError = rb_define_class_under(mStrongTyping,
227
- "OverloadError",
228
- eArgumentTypeError);
229
- eArgList = rb_define_class_under(mStrongTyping,
230
- "%ArgList",
231
- rb_eException);
232
-
233
- rb_define_module_function(mStrongTyping, "expect",
234
- strongtyping_expect, -1);
235
- rb_define_module_function(mStrongTyping, "overload",
236
- strongtyping_overload, -1);
237
- rb_define_module_function(mStrongTyping, "overload_exception",
238
- strongtyping_overload_exception, -1);
239
- rb_define_module_function(mStrongTyping, "overload_default",
240
- strongtyping_overload_error, 1);
241
- rb_define_module_function(mStrongTyping, "overload_error",
242
- strongtyping_overload_error, 1);
243
- rb_define_module_function(mStrongTyping, "get_arg_types",
244
- strongtyping_get_arg_types, 1);
245
- rb_define_module_function(mStrongTyping, "verify_args_for",
246
- strongtyping_verify_args_for, 2);
247
- }