constraint 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/AUTHORS.TXT ADDED
@@ -0,0 +1 @@
1
+ constraint.txt was written by Thomas Link (samul AT web.de)
data/LICENSE.TXT ADDED
@@ -0,0 +1,340 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Library General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License
307
+ along with this program; if not, write to the Free Software
308
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
309
+
310
+
311
+ Also add information on how to contact you by electronic and paper mail.
312
+
313
+ If the program is interactive, make it output a short notice like this
314
+ when it starts in an interactive mode:
315
+
316
+ Gnomovision version 69, Copyright (C) year name of author
317
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318
+ This is free software, and you are welcome to redistribute it
319
+ under certain conditions; type `show c' for details.
320
+
321
+ The hypothetical commands `show w' and `show c' should show the appropriate
322
+ parts of the General Public License. Of course, the commands you use may
323
+ be called something other than `show w' and `show c'; they could even be
324
+ mouse-clicks or menu items--whatever suits your program.
325
+
326
+ You should also get your employer (if you work as a programmer) or your
327
+ school, if any, to sign a "copyright disclaimer" for the program, if
328
+ necessary. Here is a sample; alter the names:
329
+
330
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
332
+
333
+ <signature of Ty Coon>, 1 April 1989
334
+ Ty Coon, President of Vice
335
+
336
+ This General Public License does not permit incorporating your program into
337
+ proprietary programs. If your program is a subroutine library, you may
338
+ consider it more useful to permit linking proprietary applications with the
339
+ library. If this is what you want to do, use the GNU Library General
340
+ Public License instead of this License.
data/README.TXT ADDED
@@ -0,0 +1,81 @@
1
+ This library provides a way to ensure that object always satisfy a
2
+ specified set of constraints. An object that can be constrained must be
3
+ of kind <tt>Constraint::Shell</tt>. Constraints can be added to classes
4
+ and single objects. It is possible to define methods that handle
5
+ constraint violations and can make the object fit the demands.
6
+
7
+ Project:: http://rubyforge.org/projects/constraint/
8
+ Download:: http://rubyforge.org/frs/?group_id=748
9
+ Support:: http://rubyforge.org/forum/?group_id=748
10
+
11
+ In my experience, a certain type of errors in dynamically typed
12
+ languages are caused by pushing some wrong object to a collection type
13
+ of object. What makes this type of error so awkward is that they often
14
+ result in an exception in some totally different part of your program,
15
+ which is why it sometimes can be quite hard to track down what is
16
+ actually causing the problem. The goal of this library is to raise an
17
+ exception right where such a thing happens. While ruby is sometimes a
18
+ little bit hesitant in raising exceptions, this library tries to
19
+ counteract this tendency a little bit.
20
+
21
+ Be aware that this library introduces yet another level of indirection
22
+ and yet additional runtime checks what slows things down a little. If
23
+ you define CONSTRAINT_DISABLE before requiring the library, constraint
24
+ checks will be deactivated.
25
+
26
+
27
+ = Documentation
28
+
29
+ Constrained classes should inherit from <tt>Constraint::Shell</tt>. The
30
+ subclass should provide a way for defining new instances (by defining
31
+ #new_constrained) and redefine #process_constrained_value. The
32
+ @constrained_value instance variable contains the shell's actual values.
33
+
34
+ Examples:
35
+
36
+ class ConstrainedArray < Constraint::Shell
37
+ def new_constrained
38
+ []
39
+ end
40
+
41
+ def process_constrained_value
42
+ @constrained_value.collect! {|e| yield e}
43
+ end
44
+ end
45
+
46
+ class NumericArray < ConstrainedArray
47
+ or_constraint("Numeric") {|e| e.kind_of?(Numeric)}
48
+ end
49
+
50
+ class EvenNumericArray < NumericArray
51
+ and_constraint("Even") {|e| e.modulo(2) == 0}
52
+ end
53
+
54
+ class EvenInteger < Constraint::Shell
55
+ and_constraint("Numeric") {|e| e.kind_of?(Integer)}
56
+ and_constraint("Even") {|e| e.modulo(2) == 0}
57
+ end
58
+
59
+ enum = EvenInteger.new(2)
60
+ enum.and_constraint("LT10") {|e| e < 10}
61
+
62
+ or:
63
+
64
+ class DuckPalace < ConstrainedArray
65
+ and_constraint('Quack') {|e| e.do_you_quack?}
66
+ and_constraint('Ticket') {|e| e.your_ticket_please == :valid}
67
+ end
68
+
69
+ whiteduckpalace = DuckPalace.new
70
+ whiteduckpalace.and_constraint('White') {|e| e.colour == 'white'}
71
+ class << whiteduckpalace
72
+ def handle_constraint_violation(contraint_name, object)
73
+ case contraint_name
74
+ when 'white'
75
+ return object.paint_me('white') if object.respond_to?(:paint_me)
76
+ end
77
+ return super
78
+ end
79
+ end
80
+
81
+
data/VERSION.TXT ADDED
@@ -0,0 +1 @@
1
+ 0.1
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+ # primes.rb
3
+ # @Author: Thomas Link (samul@web.de)
4
+ # @Website: http://members.a1.net/t.link/
5
+ # @License: GPL (see http://www.gnu.org/licenses/gpl.txt)
6
+ # @Created: 09-Jun-2005.
7
+ # @Last Change: 09-Jun-2005.
8
+ # @Revision: 0.30
9
+
10
+ require 'benchmark'
11
+ # CONSTRAINT_DISABLE = true
12
+ require 'constraint'
13
+
14
+ # A simple dumb-minded brute force prime finder.
15
+ module Primes
16
+ class << self
17
+ include Benchmark
18
+ end
19
+ module_function
20
+
21
+ class DumbCArray < Constraint::Shell
22
+ def new_constrained
23
+ []
24
+ end
25
+
26
+ def process_constrained_value
27
+ @constrained_value.collect! {|e| yield e}
28
+ end
29
+ end
30
+
31
+ def find_primes1(max, prs)
32
+ j = 3
33
+ while j < max do
34
+ if prs.each {|i| if (j % i) == 0 then break end}
35
+ prs << j
36
+ end
37
+ j += 2
38
+ end
39
+ end
40
+
41
+ def find_primes2(max, prs)
42
+ j = 3
43
+ while j < max do
44
+ if prs.each {|i| if (j % i) == 0 then break end}
45
+ prs = prs + [j]
46
+ end
47
+ j += 2
48
+ end
49
+ end
50
+
51
+ def find_primes(*args)
52
+ find_primes1(*args)
53
+ find_primes2(*args)
54
+ end
55
+
56
+ def test(max)
57
+ bm(6) do |x|
58
+ x.report('array') {find_primes(max, [2])}
59
+ x.report('dumb') {find_primes(max, DumbCArray.new([2]))}
60
+ x.report('opt1') {find_primes(max, Constraint::CArray.new([2]))}
61
+ end
62
+ end
63
+ end
64
+
65
+
66
+ if __FILE__ == $0
67
+ arg = ARGV[0]
68
+ Primes.test(arg ? arg.to_i : 50000)
69
+ end
70
+
data/lib/constraint.rb ADDED
@@ -0,0 +1,371 @@
1
+ # constraint.rb
2
+ # @Author: Thomas Link (samul AT web.de)
3
+ # @License: GPL (see http://www.gnu.org/licenses/gpl.txt)
4
+ # @Created: 23-Mai-2005.
5
+ # @Last Change: 09-Jun-2005.
6
+ # @Revision: 607
7
+
8
+
9
+ class ConstraintViolation < Exception
10
+ end
11
+
12
+
13
+ # If CONSTRAINT_DISABLE is set to true *before* loading this library,
14
+ # constraint checking is turned off.
15
+ CONSTRAINT_DISABLE = false unless defined?(CONSTRAINT_DISABLE)
16
+
17
+
18
+ module Constraint
19
+ VERSION = '0.1'
20
+
21
+ # The base constraint class. Always evaluates to true.
22
+ class NilConstraint
23
+ # shell:: The Shell class containing this constraint
24
+ # name:: This constraint's name
25
+ # description:: Optional constraint description
26
+ # block:: The constraint that must evaluate to true in order for this constraint to succeed
27
+ def initialize(shell, name, description=nil, &block)
28
+ @shell = shell
29
+ @other = shell.constraints
30
+ @name = name
31
+ @description = description
32
+ @this = block
33
+ end
34
+
35
+ # Return true if +args+ complies with @this constraint.
36
+ def evaluate(object)
37
+ begin
38
+ rv = @this.call(object)
39
+ rescue Exception => e
40
+ @shell.log_constraint_exception(e)
41
+ end
42
+ begin
43
+ if @other
44
+ return evaluate_control(rv, object)
45
+ elsif rv
46
+ return object
47
+ else
48
+ return evaluate(handle_violation(object))
49
+ end
50
+ rescue ConstraintViolation => e
51
+ raise e
52
+ end
53
+ end
54
+
55
+ # Delegate to Shell#handle_constraint_violation.
56
+ def handle_violation(object)
57
+ return @shell.handle_constraint_violation(@name, object)
58
+ end
59
+
60
+ private
61
+ # Combine constraints. In the base class, always evaluate to
62
+ # true.
63
+ def evaluate_control(value, object)
64
+ raise "Subclass responsibility"
65
+ # value ? object : handle_violation(object)
66
+ end
67
+ end
68
+
69
+ class OrConstraint < NilConstraint
70
+ private
71
+ # Return true if +value+ or the @other constraint are true.
72
+ def evaluate_control(value, object)
73
+ value ? object : @other.evaluate(object)
74
+ end
75
+ end
76
+
77
+ class AndConstraint < NilConstraint
78
+ private
79
+ # Return true if +value+ and the @other constraint are true.
80
+ def evaluate_control(value, object)
81
+ return value ? @other.evaluate(object) : handle_violation(object)
82
+ end
83
+ end
84
+
85
+ module Helper
86
+ # The current class's/object's head constraint.
87
+ attr_reader :constraints
88
+ # A hash of constraint descriptions.
89
+ attr_reader :descriptions
90
+
91
+ # Define an OrConstraint. If the new constraint succeedes, all
92
+ # previous constraints become obsolete.
93
+ # name:: This constraint's name
94
+ # description:: Optional constraint description
95
+ # block:: The constraint that must evaluate to true in order for this constraint to succeed
96
+ def or_constraint(name, desc=nil, &block)
97
+ describe_constraint(name, desc)
98
+ @constraints = OrConstraint.new(self, name, desc, &block)
99
+ end
100
+
101
+ # Define an AndConstraint. This and all previous constraints
102
+ # have to succeed.
103
+ # name:: This constraint's name
104
+ # description:: Optional constraint description
105
+ # block:: The constraint that must evaluate to true in order for this constraint to succeed
106
+ def and_constraint(name, desc=nil, &block)
107
+ describe_constraint(name, desc)
108
+ @constraints = AndConstraint.new(self, name, desc, &block)
109
+ end
110
+
111
+ # If $VERBOSE is set, print the exception to $stderr.
112
+ def log_constraint_exception(exception)
113
+ if $VERBOSE
114
+ $stderr.puts exception
115
+ end
116
+ end
117
+
118
+ # Handle constraint violations. Can be overwritten by subclasses
119
+ # in order to rescue the constraint. The return value will
120
+ # replace the original object.
121
+ # name:: The constraint's name that didn't succeed
122
+ # object:: The object that caused the violation
123
+ def handle_constraint_violation(name, object)
124
+ c = @descriptions.collect {|name, desc| desc}.join(', ')
125
+ raise ConstraintViolation,
126
+ "#{self.class}: #{object.class} didn't meet constraints (#{c}): #{object.inspect}",
127
+ caller[5..-1]
128
+ end
129
+
130
+ private
131
+ # Copy constraint-related class-local variables from +klass+ to self.
132
+ def inherit_constraint(klass)
133
+ @descriptions = klass.descriptions.dup
134
+ @constraints = klass.constraints
135
+ end
136
+
137
+ # Register a constraint description. If no description is
138
+ # provided, the +name+ is used.
139
+ # name:: This constraint's name
140
+ # desc:: Optional constraint description
141
+ def describe_constraint(name, desc=nil)
142
+ @descriptions[name] = (desc || name)
143
+ end
144
+ end
145
+
146
+ # If CONSTRAINT_DISABLE is set to true *before* loading this library,
147
+ # constraint checking is turned off.
148
+ class Shell
149
+ # The actual value that is covered by the Shell.
150
+ attr_reader :constrained_value
151
+
152
+ @descriptions = {}
153
+ @constraints = nil
154
+
155
+ class << self
156
+ include Helper
157
+
158
+ def inherited(subclass)
159
+ parent = self
160
+ subclass.class_eval do
161
+ inherit_constraint(parent)
162
+ end
163
+ end
164
+
165
+ # Wrap +methods+ so that all arguments are filtered through
166
+ # #check_constraints.
167
+ # methods:: Method names as symbols
168
+ def with_all_arguments_constrained(*methods)
169
+ with_arguments_constrained(*methods) do |checker, *args|
170
+ args.collect {|e| checker.call(e)}
171
+ end
172
+ end
173
+
174
+ # Wrap +methods+ so that some arguments are filtered through
175
+ # #check_constraints. This is useful in two situations:
176
+ #
177
+ # * if filtering the return value would be inefficient --
178
+ # e.g. with methods performing trivial Array operations
179
+ # where the return value will comply with the constraints
180
+ # if its argument do.
181
+ #
182
+ # * if the return value of the method call is not an
183
+ # instance of the actual value's class (only then will the
184
+ # value's integrity be checked)
185
+ #
186
+ # See also the notes on Shell#with_constraints.
187
+ #
188
+ # methods:: Method names as symbols
189
+ # block:: Select the arguments that should be filtered
190
+
191
+ def with_arguments_constrained(*methods, &block)
192
+ unless CONSTRAINT_DISABLE
193
+ methods.each do |method|
194
+ define_method(method) do |*args|
195
+ unless args.empty?
196
+ checker = lambda {|a| check_constraints(a)}
197
+ args = block.call(checker, *args)
198
+ end
199
+ with_constraints(@constrained_value.send(method, *args))
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ # Delegate +methods+ to @constrained_value without taking any
206
+ # measures.
207
+ def without_constraints(*methods)
208
+ methods.each do |method|
209
+ define_method(method) do |*args|
210
+ with_constraints(@constrained_value.send(method, *args))
211
+ end
212
+ end
213
+ end
214
+ end
215
+
216
+ include Helper
217
+
218
+ def initialize(core=nil, predecessor=nil)
219
+ if predecessor
220
+ @descriptions = predecessor.descriptions
221
+ @constraints = predecessor.constraints
222
+ else
223
+ @descriptions = self.class.descriptions.dup
224
+ @constraints = self.class.constraints
225
+ @constraints = @constraints.dup if @constraints
226
+ end
227
+
228
+ if core
229
+ @constrained_value = core
230
+ else
231
+ @constrained_value = new_constrained
232
+ end
233
+
234
+ unless CONSTRAINT_DISABLE
235
+ process_constrained_value {|e| check_constraints(e)}
236
+ end
237
+ end
238
+
239
+ def dup
240
+ replicate_constraint_shell(super)
241
+ end
242
+
243
+ def clone
244
+ replicate_constraint_shell(super)
245
+ end
246
+
247
+ # Construct a new instance.
248
+ def new_constrained
249
+ # nil
250
+ raise 'Subclass responsibility'
251
+ end
252
+
253
+ # Filters @constrained_value through the supplied block (usually a
254
+ # call to #check_constraints) and collects the output in
255
+ # @constrained_value).
256
+ # block:: The filter
257
+ def process_constrained_value
258
+ @constrained_value = yield @constrained_value
259
+ end
260
+
261
+ if CONSTRAINT_DISABLE
262
+ def check_constraints(object)
263
+ object
264
+ end
265
+ else
266
+ # Check if +object+ complies with @constraints.
267
+ def check_constraints(object)
268
+ if @constraints
269
+ return @constraints.evaluate(object)
270
+ else
271
+ return object
272
+ end
273
+ end
274
+ end
275
+
276
+ # Maintain object identity according to the return value of
277
+ # +block+. This method can be used when overwriting specific
278
+ # methods and if you don't want to use the provided wrapper
279
+ # methods (e.g., Shell.with_arguments_constrained).
280
+ #
281
+ # If the return value is of the same kind as @constrained_value
282
+ # and if +block+ can't be +trusted+, then the return value is
283
+ # filtered through #check_constraints.
284
+ #
285
+ # In order to filter the arguments of methods that don't return
286
+ # an object of the same kind as @constrained_value, you have to
287
+ # define an explicit delegator using
288
+ # Shell.with_arguments_constrained and friends.
289
+ #
290
+ # new_value:: The result from the method call
291
+ # [block]:: Optional, called @constrained_value is +new_value+
292
+ def with_constraints(new_value)
293
+ if new_value.equal?(@constrained_value)
294
+ yield if block_given?
295
+ return self
296
+ elsif new_value.instance_of?(@constrained_value.class)
297
+ return self.class.new(new_value, self)
298
+ else
299
+ return new_value
300
+ end
301
+ end
302
+
303
+ if CONSTRAINT_DISABLE
304
+ def method_missing(method, *args, &block)
305
+ with_constraints(@constrained_value.send(method, *args, &block))
306
+ end
307
+ else
308
+ # Delegate unknown methods to @constrained_value and filter the
309
+ # return value through #check_constraints.
310
+ #
311
+ # See also the notes on #with_constraints and
312
+ # Shell.with_arguments_constrained.
313
+ def method_missing(method, *args, &block)
314
+ # $stderr.puts "Constraint::Shell: Delegate method: #{method}" if $DEBUG
315
+ with_constraints(@constrained_value.send(method, *args, &block)) do
316
+ process_constrained_value {|e| check_constraints(e)}
317
+ end
318
+ end
319
+ end
320
+
321
+ # Forward to @constrained_value.
322
+ def respond_to?(*args)
323
+ super or @constrained_value.respond_to?(*args)
324
+ end
325
+
326
+ private
327
+ # Some kind of semi-deep copy.
328
+ def replicate_constraint_shell(new_instance)
329
+ cv = @constrained_value.clone
330
+ new_instance.instance_eval do
331
+ @constrained_value = cv
332
+ end
333
+ new_instance
334
+ end
335
+ end
336
+
337
+ # This is a simple test wrapper class for Array. This currently
338
+ # overwrites only a few methods so that most of the time the return
339
+ # value if filtered. As this often is the array itself, this isn't
340
+ # very efficient.
341
+ class CArray < Constraint::Shell
342
+ def new_constrained
343
+ []
344
+ end
345
+
346
+ def process_constrained_value
347
+ @constrained_value.collect! {|e| yield e}
348
+ end
349
+
350
+ with_all_arguments_constrained(:<<, :unshift)
351
+ with_arguments_constrained(:[]=) do |checker, pos, val|
352
+ [pos, checker.call(val)]
353
+ end
354
+ with_arguments_constrained(:+, :&, :|, :concat) do |checker, args|
355
+ [args.collect {|e| checker.call(e)}]
356
+ end
357
+ without_constraints(:[], :-, :*, :delete_at, :delete_if, :find_all)
358
+ end
359
+
360
+ # This is a simple test wrapper class for Hash.
361
+ # class CHash < Constraint::Shell
362
+ # def new_constrained
363
+ # {}
364
+ # end
365
+ #
366
+ # def process_constrained_value
367
+ # @constrained_value.each {|key, val| @constrained_value[key] = yield val}
368
+ # end
369
+ # end
370
+ end
371
+
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env ruby
2
+ # tc_contraint.rb
3
+ # @Author: Thomas Link (samul@web.de)
4
+ # @Created: 27-M�r-2005.
5
+ # @Last Change: 09-Jun-2005.
6
+ # @Revision: 0.105
7
+
8
+ require 'test/unit'
9
+ require 'constraint'
10
+
11
+
12
+ class NumericArray < Constraint::CArray
13
+ or_constraint("Numeric") {|e| e.kind_of?(Numeric)}
14
+ end
15
+
16
+ class NumericArray123 < NumericArray
17
+ @constructor = lambda {[1,2,3]}
18
+ end
19
+
20
+ class EvenNumericArray < NumericArray
21
+ and_constraint("Even") {|e| e.modulo(2) == 0}
22
+ end
23
+
24
+ class EvenInteger < Constraint::Shell
25
+ and_constraint("Even") {|e| e.modulo(2) == 0}
26
+ end
27
+
28
+
29
+ class TC_Contraint < Test::Unit::TestCase
30
+ # def setup
31
+ # end
32
+
33
+ # def teardown
34
+ # end
35
+
36
+ def test_numarr_new
37
+ assert_equal([1,2,3], NumericArray.new([1,2,3]).constrained_value)
38
+ assert_equal([], NumericArray123.new.constrained_value)
39
+ end
40
+
41
+ def test_numarr
42
+ numarr = NumericArray.new
43
+ numarr << 1 << 2
44
+ assert_equal([1, 2], numarr.constrained_value)
45
+ assert_equal([1,2,3], (numarr + [3]).constrained_value)
46
+ assert_same(numarr, numarr << 4)
47
+ assert_not_same(numarr, numarr + [4])
48
+ assert_raise(ConstraintViolation) {NumericArray.new(["a",2,3])}
49
+ assert_raise(ConstraintViolation) {NumericArray.new([2,nil])}
50
+ assert_raise(ConstraintViolation) {numarr << "a"}
51
+ assert_equal(true, numarr.respond_to?(:[]))
52
+ assert_equal(true, numarr.respond_to?(:with_constraints))
53
+ assert_equal(false, numarr.respond_to?(:inexistent))
54
+ end
55
+
56
+ def test_enumarr
57
+ numarr = EvenNumericArray.new
58
+ numarr << 0 << 2
59
+ assert_equal(EvenNumericArray, numarr.class)
60
+ assert_equal([0, 2], numarr.constrained_value)
61
+ assert_equal([0, 2, 4], (numarr + [4]).constrained_value)
62
+ assert_same(numarr, numarr << 4)
63
+ assert_not_same(numarr, numarr + [4])
64
+ assert_instance_of(EvenNumericArray, numarr + [4])
65
+ assert_raise(ConstraintViolation) {EvenNumericArray.new([1,2,3])}
66
+ assert_raise(ConstraintViolation) {EvenNumericArray.new([nil,2])}
67
+ assert_raise(ConstraintViolation) {numarr.dup << 3}
68
+ assert_raise(ConstraintViolation) {numarr.dup << "a"}
69
+ assert_raise(ConstraintViolation) {numarr.dup << nil}
70
+ assert_raise(ConstraintViolation) {numarr.dup + [1]}
71
+ assert_raise(ConstraintViolation) {numarr.dup + [[2]]}
72
+ assert_raise(ConstraintViolation) {numarr.dup[0] = 1}
73
+ n = numarr.dup
74
+ assert_nothing_raised {n[0] = 2}
75
+ assert_equal([2,2,4], n.constrained_value)
76
+ assert_equal([4], (n - [2]).constrained_value)
77
+ end
78
+
79
+ def test_evenint
80
+ enum = EvenInteger.new(2)
81
+ assert_equal(2, enum.constrained_value)
82
+ assert_equal(4, (enum + 2).constrained_value)
83
+ assert_raise(ConstraintViolation) {enum + 1}
84
+ end
85
+
86
+ def test_evenint_lt10
87
+ enum = EvenInteger.new(2)
88
+ enum.and_constraint("LT10") {|e| e < 10}
89
+ assert_equal(2, enum.constrained_value)
90
+ assert_equal(4, (enum + 2).constrained_value)
91
+ assert_raise(ConstraintViolation) {enum + 10}
92
+ enum.or_constraint("GT20") {|e| e > 20}
93
+ assert_nothing_raised {enum + 30}
94
+ # should this one raise an exception or not?
95
+ # assert_raise(ConstraintViolation) {enum + 111}
96
+ assert_equal(22, (enum + 20).constrained_value)
97
+ enum = EvenInteger.new(2)
98
+ assert_nothing_raised {enum + 10}
99
+ end
100
+
101
+ def test_evenint_lt10_rescue
102
+ enum = EvenInteger.new(2)
103
+ enum.and_constraint("LT10") {|e| e < 10}
104
+ assert_raise(ConstraintViolation) {enum + 10}
105
+ class << enum
106
+ def handle_constraint_violation(name, object)
107
+ case name
108
+ when "LT10"
109
+ if object > 10
110
+ return 10
111
+ else
112
+ return 0
113
+ end
114
+ else
115
+ return super
116
+ end
117
+ end
118
+ end
119
+ assert_equal(10, (enum + 20).constrained_value)
120
+ assert_raise(ConstraintViolation) {enum + 1}
121
+ assert_nothing_raised {enum + 101}
122
+ end
123
+ end
124
+
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ load 'test/tc_contraint.rb'
4
+
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.10
3
+ specification_version: 1
4
+ name: constraint
5
+ version: !ruby/object:Gem::Version
6
+ version: "0.1"
7
+ date: 2005-06-10
8
+ summary: Ensure that objects comply with some constraints
9
+ require_paths:
10
+ - lib
11
+ email: samul@web.de?subject=constraint
12
+ homepage: http://constraint.sourceforge.net
13
+ rubyforge_project:
14
+ description: This library provides a way to define constrained classes in order to ensure that its instances always satisfy some specified set of constraints.
15
+ autorequire: constraint.rb
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ -
22
+ - ">"
23
+ - !ruby/object:Gem::Version
24
+ version: 0.0.0
25
+ version:
26
+ platform: ruby
27
+ authors:
28
+ - Thomas Link
29
+ files:
30
+ - benchmarks/primes.rb
31
+ - lib/constraint.rb
32
+ - test/tc_contraint.rb
33
+ - test/ts_constraint.rb
34
+ - README.TXT
35
+ - AUTHORS.TXT
36
+ - LICENSE.TXT
37
+ - VERSION.TXT
38
+ test_files:
39
+ - test/ts_constraint.rb
40
+ rdoc_options: []
41
+ extra_rdoc_files:
42
+ - README.TXT
43
+ - AUTHORS.TXT
44
+ - LICENSE.TXT
45
+ - VERSION.TXT
46
+ executables: []
47
+ extensions: []
48
+ requirements: []
49
+ dependencies: []