fluid 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,469 @@
1
+ # Fluid variables for Ruby.
2
+ # Originally 2004/03/08 20:16:13
3
+ # Gemified 2007/07
4
+
5
+
6
+ $:.unshift("../lib")
7
+ require 'test/unit'
8
+ require 'fluid'
9
+ require 's4t-utils/more-assertions'
10
+
11
+ class TestFluid < Test::Unit::TestCase
12
+ ### Tests
13
+ def test_one_variable_uninitialized
14
+ Fluid.let(:a) {
15
+ assert_equal(nil, Fluid.a)
16
+ }
17
+ end
18
+
19
+ def test_one_variable_initialized
20
+ Fluid.let(:a, 1) {
21
+ assert_equal(1, Fluid.a)
22
+ }
23
+ end
24
+
25
+ # Couple of notes about this test.
26
+ # - It checks that fluid variable names can be strings or symbols.
27
+ # - It checks that the values can be complex objects or references.
28
+ def test_multiple_variables
29
+ dval = ["dval"]
30
+ Fluid.let([:a, 1],
31
+ [:b],
32
+ ["c", [1, 2]],
33
+ [:d, dval]) {
34
+ assert_equal(1, Fluid.a)
35
+ assert_equal(nil, Fluid.b)
36
+ assert_equal([1, 2], Fluid.c)
37
+ assert_equal(dval, Fluid.d)
38
+ }
39
+ end
40
+
41
+
42
+ def test_fluid_variable_assignment
43
+ Fluid.let("a", 1) {
44
+ assert_equal(1, Fluid.a)
45
+ Fluid.a = 2
46
+ assert_equal(2, Fluid.a)
47
+ Fluid.let("a") {
48
+ assert_equal(nil, Fluid.a)
49
+ }
50
+ assert_equal(2, Fluid.a)
51
+ }
52
+ end
53
+
54
+ # Assignment is parallel, as in Lisp let, not let*
55
+ # This is inherent in Ruby arg evaluation, but thought I would
56
+ # document it here. let* is impossible, I think.
57
+ def test_assignment_is_parallel
58
+ Fluid.let(:dawn, "best beloved") {
59
+ assert_equal("best beloved", Fluid.dawn)
60
+ Fluid.let([:dawn, "wife"],
61
+ [:paul, "child of #{Fluid.dawn}"]) {
62
+ assert_equal("wife", Fluid.dawn)
63
+ assert_equal("child of best beloved", Fluid.paul)
64
+ }
65
+ }
66
+ end
67
+
68
+ # Test that pushing and popping of values works correctly.
69
+ # Note that this also checks whether the no-op form (no vars)
70
+ # in fact has no effect.
71
+ def test_nesting_behavior_simple_within_method
72
+ Fluid.let(:a, 1) {
73
+ assert_equal(1, Fluid.a)
74
+ Fluid.let {
75
+ assert_equal(1, Fluid.a)
76
+ Fluid.let(:a, 2) {
77
+ assert_equal(2, Fluid.a)
78
+ }
79
+ assert_equal(1, Fluid.a)
80
+ }
81
+ assert_equal(1, Fluid.a)
82
+ }
83
+ end
84
+
85
+ # Check that multiple variables are pushed and popped appropriately.
86
+ def test_nesting_behavior_complex_within_method
87
+ Fluid.let([:a, 1], [:b]) {
88
+ assert_equal(1, Fluid.a)
89
+ assert_equal(nil, Fluid.b)
90
+ Fluid.let(:a) {
91
+ assert_equal(nil, Fluid.a)
92
+ assert_equal(nil, Fluid.b)
93
+ Fluid.let([:b, 'b'],
94
+ [:a, [1, 2]]) {
95
+ assert_equal([1,2], Fluid.a)
96
+ assert_equal('b', Fluid.b)
97
+ }
98
+ assert_equal(nil, Fluid.a)
99
+ assert_equal(nil, Fluid.b)
100
+ }
101
+ assert_equal(1, Fluid.a)
102
+ assert_equal(nil, Fluid.b)
103
+ }
104
+ end
105
+
106
+ # UNWINDING
107
+ # This is an example that shows how bindings are unwound or not
108
+ # unwound as control passes out of a block in various ways
109
+ # that involve calls to other methods. Shows how variable bindings
110
+ # are independent of the call stack.
111
+ def test_nesting_behavior_across_methods
112
+ Fluid.let([:paul, 6],
113
+ [:sophie, 5]) {
114
+ ordinary_subfunction # add one to age.
115
+ assert_equal(7, Fluid.paul)
116
+ assert_equal(6, Fluid.sophie)
117
+
118
+ # Values are unwound in presence of catch.
119
+ catch_value = catch(:catcher) {
120
+ Fluid.let(:paul, 66) {
121
+ assert_equal(66, Fluid.paul)
122
+ assert_equal(6, Fluid.sophie)
123
+ throwing_subfunction
124
+ # This changes both variables, but only
125
+ # the change to Sophie will be visible outside the let block
126
+ }
127
+ }
128
+ assert_equal(55600, catch_value)
129
+ assert_equal(7, Fluid.paul)
130
+ assert_equal("sophster", Fluid.sophie)
131
+
132
+ # Values are unwound with exceptions as well
133
+ begin
134
+ Fluid.let(:sophie, "leewit") {
135
+ assert_equal(7, Fluid.paul)
136
+ assert_equal("leewit", Fluid.sophie)
137
+ raising_subfunction
138
+ # This changes both variables, but only
139
+ # the change to Paul will be visible outside the let block
140
+ }
141
+ rescue RuntimeError
142
+ assert_equal(nil, Fluid.paul)
143
+ assert_equal("sophster", Fluid.sophie)
144
+ return
145
+ end
146
+ fail("Should not reach here.")
147
+ }
148
+ end
149
+
150
+ def ordinary_subfunction
151
+ assert_equal(6, Fluid.paul)
152
+ assert_equal(5, Fluid.sophie)
153
+ Fluid.paul += 1
154
+ Fluid.sophie += 1
155
+ end
156
+
157
+ def throwing_subfunction
158
+ assert_equal(66, Fluid.paul)
159
+ assert_equal(6, Fluid.sophie)
160
+ # This change will be unwound as computation passes
161
+ # outside the Let block, which binds Paul.
162
+ Fluid.paul += 55534
163
+ # This will not, since the let block does not bind
164
+ # sophie.
165
+ Fluid.sophie = "sophster"
166
+ throw :catcher, Fluid.paul
167
+ end
168
+
169
+ def raising_subfunction
170
+ assert_equal(7, Fluid.paul)
171
+ assert_equal("leewit", Fluid.sophie)
172
+ # This change will be unowund as computation passes
173
+ # outside the Let block, which binds sophie.
174
+ Fluid.sophie = nil
175
+
176
+ # This change will be visible outside the let block, which
177
+ # does not bind paul.
178
+ Fluid.paul = nil
179
+ raise "Here comes the exception"
180
+ end
181
+
182
+ # END UNWINDING
183
+
184
+
185
+ def test_unbound_reference
186
+ assert_raises_with_matching_message(NameError,
187
+ "'unbound' has not been defined with Fluid.let or Fluid.defvar.") {
188
+ Fluid.let(:a) {
189
+ puts(Fluid.unbound)
190
+ }
191
+ }
192
+ end
193
+
194
+ def test_unbound_assignment
195
+ assert_raises_with_matching_message(NameError,
196
+ "'also_unbound' has not been defined with Fluid.let or Fluid.defvar.") {
197
+ Fluid.also_unbound = 1
198
+ }
199
+ end
200
+
201
+ def test_bindings_vanish
202
+ Fluid.let(:establish_binding) {
203
+ assert_equal(nil, Fluid.establish_binding)
204
+ }
205
+ assert_raises_with_matching_message(NameError,
206
+ "'establish_binding' has not been defined with Fluid.let or Fluid.defvar.") {
207
+ Fluid.establish_binding = 1
208
+ }
209
+ end
210
+
211
+ def test_block_yields_last_value
212
+ result = Fluid.let([:a,1], [:b, 2]) {
213
+ Fluid.a + Fluid.b
214
+ }
215
+ assert_equal(3, result)
216
+ end
217
+
218
+ def test_some_names_are_not_allowed
219
+ assert_raises_with_matching_message(NameError,
220
+ "'let' cannot be a fluid variable. It's already a method of Fluid's.") {
221
+ Fluid.let(:let) {fail("How'd I get here?")}
222
+ }
223
+
224
+ assert_raises_with_matching_message(NameError,
225
+ "'send' cannot be a fluid variable. It's already a method of Fluid's.") {
226
+ Fluid.let(:send) {fail("How'd I get here?")}
227
+ }
228
+ end
229
+
230
+ def test_duplicates_in_let
231
+ assert_raises_with_matching_message(NameError,
232
+ "'duplicate' is defined twice in the same Fluid.let.") {
233
+ Fluid.let([:duplicate, 1], [:unique], ["duplicate", 1]) {
234
+ fail("How'd I get here?")
235
+ }
236
+ }
237
+ end
238
+
239
+ def test_defvar_simple
240
+ Fluid.defvar(:defvar_simple_a)
241
+ Fluid.defvar("defvar_simple_b", 1)
242
+ Fluid.defvar(:defvar_simple_c) { [1, 2] }
243
+
244
+ assert_equal(nil, Fluid.defvar_simple_a)
245
+ assert_equal(1, Fluid.defvar_simple_b)
246
+ assert_equal([1, 2], Fluid.defvar_simple_c)
247
+
248
+ Fluid.let([:defvar_simple_a, 99],
249
+ [:defvar_simple_b, 999],
250
+ [:defvar_simple_c, "000"]) {
251
+ assert_equal(99, Fluid.defvar_simple_a)
252
+ assert_equal(999, Fluid.defvar_simple_b)
253
+ assert_equal("000", Fluid.defvar_simple_c)
254
+ }
255
+
256
+ assert_equal(nil, Fluid.defvar_simple_a)
257
+ assert_equal(1, Fluid.defvar_simple_b)
258
+ assert_equal([1, 2], Fluid.defvar_simple_c)
259
+ end
260
+
261
+ def test_defvar_once_only
262
+ Fluid.defvar("defvar_once_hello_5", "first value")
263
+ Fluid.defvar(:defvar_once_hello_5, "second value")
264
+ assert_equal("first value", Fluid.defvar_once_hello_5)
265
+
266
+ # Same is true of implicit nil.
267
+ Fluid.defvar(:defvar_once_hello_5)
268
+ assert_equal("first value", Fluid.defvar_once_hello_5)
269
+
270
+ # Moreover, blocks are not evaluated at all the second time.
271
+ Fluid.defvar(:defvar_once_hello_5) { fail "Should not be reached" }
272
+ assert_equal("first value", Fluid.defvar_once_hello_5)
273
+ end
274
+
275
+ def test_defvar_within_let # has no effect
276
+ assert_raises_with_matching_message(NameError,
277
+ "'a' has not been defined with Fluid.let or Fluid.defvar.") {
278
+ Fluid.a
279
+ }
280
+
281
+ Fluid.let(:a, 1) {
282
+ assert_equal(1, Fluid.a)
283
+ Fluid.defvar(:a, 999)
284
+ assert_equal(1, Fluid.a)
285
+ }
286
+
287
+ assert_raises_with_matching_message(NameError,
288
+ "'a' has not been defined with Fluid.let or Fluid.defvar.") {
289
+ Fluid.a
290
+ }
291
+ end
292
+
293
+ def test_variable_names_are_method_names
294
+ assert_raises_with_matching_message(NameError,
295
+ "'blank name' is not a good fluid variable name. It can't be used as a method name.") {
296
+ Fluid.let("blank name") {}
297
+ }
298
+
299
+ assert_raises_with_matching_message(NameError,
300
+ "'9foo' is not a good fluid variable name. It can't be used as a method name.") {
301
+ Fluid.let("9foo".intern) {}
302
+ }
303
+
304
+ assert_raises_with_matching_message(NameError,
305
+ "'a=b' is not a good fluid variable name. It can't be used as a method name.") {
306
+ Fluid.defvar("a=b") {}
307
+ }
308
+
309
+ # Underscores are legal method names, though:
310
+ Fluid.defvar("_", 5)
311
+ assert_equal(5, Fluid._)
312
+ Fluid.defvar("_t", 7)
313
+ assert_equal(7, Fluid._t)
314
+ end
315
+
316
+ def incrementor
317
+ Fluid.counter += 1
318
+ end
319
+
320
+ def test_destructor_called
321
+ Fluid.let([:counter, 0]) do
322
+ Fluid.let(:unused, self, :incrementor) do
323
+ assert_equal(0, Fluid.counter)
324
+ end
325
+ assert_equal(1, Fluid.counter)
326
+ end
327
+ end
328
+
329
+ def test_block_destructor
330
+ value = "destructor_called"
331
+ destructor_called = false
332
+ p = proc { | x | destructor_called = x }
333
+
334
+ assert_equal(value * 2,
335
+ Fluid.let([:_t_d_, value, p]) {
336
+ Fluid._t_d_ * 2
337
+ })
338
+ assert_equal(value, destructor_called)
339
+ end
340
+
341
+ def test_var_checking
342
+ assert_equal(false, Fluid.has?(:log))
343
+ assert_equal(false, Fluid.has?('log'))
344
+ Fluid.defvar(:log)
345
+ assert_equal(true, Fluid.has?(:log))
346
+ assert_equal(true, Fluid.has?('log'))
347
+
348
+ assert_equal(false, Fluid.has?(:another_log))
349
+ assert_equal(false, Fluid.has?('another_log'))
350
+ Fluid.let(:another_log, 5) {
351
+ assert_equal(true, Fluid.has?(:another_log))
352
+ assert_equal(true, Fluid.has?('another_log'))
353
+ }
354
+ assert_equal(false, Fluid.has?(:another_log))
355
+ assert_equal(false, Fluid.has?('another_log'))
356
+ end
357
+
358
+
359
+ #### GLOBALS
360
+
361
+ def test_bound_global
362
+ $global = 'start value'
363
+ assert_equal($global, 'start value')
364
+ Fluid.let("$global", 5) {
365
+ assert_equal(5, $global)
366
+ Fluid.let([:local, 'local'], # mix fluid and globals, just in case.
367
+ ["$global", 'global']) {
368
+ assert_equal('global', $global)
369
+ assert_equal('local', Fluid.local)
370
+ $global = 2334
371
+ }
372
+ assert_equal(5, $global)
373
+ $global = 3434
374
+ }
375
+ assert_equal($global, 'start value')
376
+
377
+ # Binding a predefined global variable has an effect.
378
+ assert_equal(nil, "UP" =~ /up/)
379
+ Fluid.let(["$,", "+"]) {
380
+ assert_equal("1+2", [1, 2].join)
381
+ }
382
+ assert_equal("12", [1, 2].join)
383
+ end
384
+
385
+ def test_has_global
386
+ # Global variables are always considered bound.
387
+ assert_equal(true, Fluid.has?("$has_global_undef"))
388
+ $has_global = 5
389
+ assert_equal(true, Fluid.has?("$has_global"))
390
+ Fluid.let( [ "$has_global", 99 ] ) {
391
+ assert_equal(true, Fluid.has?("$has_global"))
392
+ }
393
+ assert_equal(true, Fluid.has?("$has_global"))
394
+ end
395
+
396
+ def test_exception_global
397
+ # Assigning $! causes odd behavior.
398
+ # Disallow it.
399
+
400
+ assert_raises_with_matching_message(NameError, /'\$\!' is not allowed/) {
401
+ Fluid.let("$!", Exception.new("overridden")) {
402
+ flunk
403
+ }
404
+ }
405
+ end
406
+
407
+ def test_stdin_global
408
+ # Stdin is buffered in such a way that text from the inner binding
409
+ # is visible after a let unbinds. See globals/stdin.rb (Ruby 1.6)
410
+
411
+ assert_raises_with_matching_message(NameError, /'\$stdin' is not allowed/) {
412
+ Fluid.let(["$stdin", nil]) {
413
+ flunk
414
+ }
415
+ }
416
+ end
417
+
418
+ def test_stdout_global
419
+ # $stdout can be bound, but when it exits from the let, it will be
420
+ # buffered differently than $defout. If both are used, lines will be
421
+ # out of order. See globals/stdout.rb. (Ruby 1.6)
422
+
423
+ assert_raises_with_matching_message(NameError, /'\$stdout' is not allowed/) {
424
+ Fluid.let(["$stdout", nil]) {
425
+ flunk
426
+ }
427
+ }
428
+ end
429
+
430
+ def test_global_parallel_assignment
431
+ $g1 = 1
432
+ $g2 = 2
433
+ Fluid.let(["$g1", 11],
434
+ ["$g2", $g1 + 1]) {
435
+ assert_equal(11, $g1)
436
+ assert_equal(2, $g2) # NOT 12
437
+ }
438
+ assert_equal(1, $g1)
439
+ assert_equal(2, $g2)
440
+ end
441
+
442
+ def test_sequential_globals
443
+ $g1 = 1
444
+ $g2 = 2
445
+ Fluid.let(["$g1", $g2],
446
+ ["$g2", $g1]) {
447
+ assert_equal(2, $g1)
448
+ assert_equal(1, $g2)
449
+ }
450
+ assert_equal(1, $g1)
451
+ assert_equal(2, $g2)
452
+
453
+ Fluid.let(["$g1", $g2],
454
+ ["$g2", $g1]) {
455
+ assert_equal(2, $g1)
456
+ assert_equal(1, $g2)
457
+ }
458
+ assert_equal(1, $g1)
459
+ assert_equal(2, $g2)
460
+ end
461
+
462
+ def test_global_defvar
463
+ assert_raises_with_matching_message(NameError,
464
+ "Fluid.defvar of a global can never have an effect, so it's not allowed.") {
465
+ Fluid.defvar("$defvar", 33)
466
+ }
467
+ end
468
+
469
+ end