fancy 0.7.0 → 0.8.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.
Files changed (165) hide show
  1. data/README.md +38 -86
  2. data/bin/fdoc +2 -22
  3. data/bin/fspec +8 -3
  4. data/bin/ifancy +1 -1
  5. data/boot/fancy_ext.rb +1 -0
  6. data/boot/fancy_ext/array.rb +19 -0
  7. data/boot/fancy_ext/class.rb +2 -4
  8. data/boot/fancy_ext/module.rb +2 -0
  9. data/boot/fancy_ext/object.rb +0 -17
  10. data/boot/rbx-compiler/compiler/ast/method_def.rb +0 -4
  11. data/boot/rbx-compiler/compiler/ast/singleton_method_def.rb +0 -7
  12. data/boot/rbx-compiler/parser/fancy_parser.bundle +0 -0
  13. data/boot/rbx-compiler/parser/fancy_parser.c +1 -0
  14. data/doc/api/fancy.css +10 -1
  15. data/doc/api/fancy.jsonp +1 -1
  16. data/doc/api/fdoc.js +22 -9
  17. data/doc/api/octocat.png +0 -0
  18. data/doc/features.md +1 -2
  19. data/examples/actors.fy +1 -2
  20. data/examples/armstrong_numbers.fy +7 -3
  21. data/examples/blocks.fy +3 -3
  22. data/examples/distributing_proxy.fy +31 -0
  23. data/examples/future_sends.fy +15 -0
  24. data/examples/person.fy +1 -2
  25. data/lib/argv.fy +1 -7
  26. data/lib/array.fy +7 -11
  27. data/lib/block.fy +15 -0
  28. data/lib/boot.fy +4 -3
  29. data/lib/class.fy +354 -10
  30. data/lib/compiler.fy +1 -1
  31. data/lib/compiler/ast/assign.fy +4 -8
  32. data/lib/compiler/ast/async_send.fy +1 -2
  33. data/lib/compiler/ast/block.fy +5 -0
  34. data/lib/compiler/ast/class_def.fy +2 -1
  35. data/lib/compiler/ast/expression_list.fy +1 -2
  36. data/lib/compiler/ast/future_send.fy +1 -2
  37. data/lib/compiler/ast/identifier.fy +34 -17
  38. data/lib/compiler/ast/literals.fy +31 -19
  39. data/lib/compiler/ast/match.fy +5 -4
  40. data/lib/compiler/ast/message_send.fy +3 -5
  41. data/lib/compiler/ast/method_def.fy +0 -3
  42. data/lib/compiler/ast/range.fy +2 -4
  43. data/lib/compiler/ast/return.fy +2 -4
  44. data/lib/compiler/ast/script.fy +2 -4
  45. data/lib/compiler/ast/singleton_method_def.fy +0 -3
  46. data/lib/compiler/ast/string_interpolation.fy +2 -2
  47. data/lib/compiler/ast/super.fy +2 -4
  48. data/lib/compiler/ast/try_catch.fy +13 -9
  49. data/lib/compiler/ast/tuple_literal.fy +1 -2
  50. data/lib/compiler/compiler.fy +2 -2
  51. data/lib/compiler/stages.fy +3 -6
  52. data/lib/contracts.fy +89 -57
  53. data/lib/dynamic_slot_object.fy +21 -3
  54. data/lib/enumerable.fy +140 -4
  55. data/lib/enumerator.fy +1 -1
  56. data/lib/eval.fy +23 -9
  57. data/lib/exception.fy +16 -0
  58. data/lib/false_class.fy +36 -5
  59. data/lib/fancy_spec.fy +64 -34
  60. data/lib/fdoc.fy +85 -24
  61. data/lib/file.fy +19 -0
  62. data/lib/future.fy +4 -46
  63. data/lib/hash.fy +113 -0
  64. data/lib/integer.fy +25 -6
  65. data/lib/iteration.fy +3 -3
  66. data/lib/main.fy +5 -0
  67. data/lib/matchers.fy +79 -0
  68. data/lib/nil_class.fy +8 -0
  69. data/lib/object.fy +109 -18
  70. data/lib/option_parser.fy +118 -0
  71. data/lib/package/dependency.fy +4 -8
  72. data/lib/package/dependency_installer.fy +1 -1
  73. data/lib/package/handler.fy +6 -0
  74. data/lib/package/installer.fy +43 -16
  75. data/lib/package/list.fy +1 -2
  76. data/lib/package/specification.fy +5 -5
  77. data/lib/package/uninstaller.fy +9 -2
  78. data/lib/parser.fy +1 -3
  79. data/lib/parser/ext/ext.c +1 -0
  80. data/lib/parser/ext/lexer.lex +5 -0
  81. data/lib/parser/methods.fy +48 -46
  82. data/lib/proxies.fy +151 -0
  83. data/lib/rbx.fy +1 -0
  84. data/lib/rbx/actor.fy +16 -18
  85. data/lib/rbx/array.fy +18 -3
  86. data/lib/rbx/block.fy +1 -7
  87. data/lib/rbx/class.fy +54 -9
  88. data/lib/rbx/code_loader.fy +2 -5
  89. data/lib/rbx/compiled_method.fy +31 -0
  90. data/lib/rbx/debugger.fy +66 -0
  91. data/lib/rbx/directory.fy +8 -3
  92. data/lib/rbx/documentation.fy +1 -1
  93. data/lib/rbx/file.fy +22 -0
  94. data/lib/rbx/integer.fy +1 -1
  95. data/lib/rbx/match_data.fy +2 -1
  96. data/lib/rbx/method.fy +26 -0
  97. data/lib/rbx/object.fy +8 -3
  98. data/lib/rbx/regexp.fy +6 -3
  99. data/lib/rbx/string.fy +9 -1
  100. data/lib/rbx/stringio.fy +12 -0
  101. data/lib/rbx/symbol.fy +4 -0
  102. data/lib/stack.fy +1 -1
  103. data/lib/string.fy +34 -0
  104. data/lib/stringio.fy +1 -1
  105. data/lib/symbol.fy +6 -2
  106. data/lib/system.fy +15 -1
  107. data/lib/tuple.fy +5 -2
  108. data/lib/version.fy +1 -1
  109. data/ruby_lib/fdoc +2 -22
  110. data/tests/array.fy +3 -17
  111. data/tests/class.fy +312 -10
  112. data/tests/contracts.fy +51 -0
  113. data/tests/distributing_proxy.fy +28 -0
  114. data/tests/enumerable.fy +104 -1
  115. data/tests/exception.fy +35 -0
  116. data/tests/fixnum.fy +1 -1
  117. data/tests/hash.fy +81 -1
  118. data/tests/integer.fy +9 -0
  119. data/tests/matchers.fy +18 -0
  120. data/tests/method.fy +8 -14
  121. data/tests/object.fy +76 -2
  122. data/tests/option_parser.fy +80 -0
  123. data/tests/string.fy +21 -0
  124. data/tests/stringio.fy +1 -1
  125. data/tests/tuple.fy +1 -1
  126. metadata +21 -44
  127. data/examples/arithmetic.fy +0 -7
  128. data/examples/array.fy +0 -50
  129. data/examples/boolean.fy +0 -24
  130. data/examples/class.fy +0 -68
  131. data/examples/constant_access.fy +0 -15
  132. data/examples/default_args.fy +0 -20
  133. data/examples/define_methods.fy +0 -15
  134. data/examples/dynamic_output.fy +0 -15
  135. data/examples/empty_catch.fy +0 -4
  136. data/examples/exception.fy +0 -9
  137. data/examples/files.fy +0 -23
  138. data/examples/finally.fy +0 -5
  139. data/examples/future.fy +0 -30
  140. data/examples/future_composition.fy +0 -20
  141. data/examples/futures.fy +0 -9
  142. data/examples/game_of_life.fy +0 -148
  143. data/examples/html_generator.fy +0 -84
  144. data/examples/implicit_return.fy +0 -3
  145. data/examples/matchers.fy +0 -6
  146. data/examples/nested_try.fy +0 -9
  147. data/examples/numbers.fy +0 -12
  148. data/examples/rbx/and_or.fy +0 -7
  149. data/examples/rbx/blocks.fy +0 -22
  150. data/examples/rbx/classes.fy +0 -32
  151. data/examples/rbx/hello.fy +0 -8
  152. data/examples/rbx/include.fy +0 -12
  153. data/examples/rbx/inherit.fy +0 -11
  154. data/examples/rbx/methods.fy +0 -15
  155. data/examples/rbx/nested_classes.fy +0 -9
  156. data/examples/rbx/require.fy +0 -3
  157. data/examples/rbx/strings.fy +0 -5
  158. data/examples/require.fy +0 -7
  159. data/examples/return.fy +0 -13
  160. data/examples/singleton_methods.fy +0 -21
  161. data/examples/threads.fy +0 -18
  162. data/examples/tuple.fy +0 -8
  163. data/examples/webserver/webserver.fy +0 -15
  164. data/lib/proxy.fy +0 -86
  165. data/lib/thread_pool.fy +0 -102
@@ -1,15 +1,15 @@
1
1
  # blocks.fy
2
2
  # Examples of fancy code blocks
3
3
 
4
- x = { Console println: "Println from within block!" }
4
+ x = { "Println from within block!" println }
5
5
  x call # calls x and prints: "Println from within block!"
6
6
 
7
- y = |x y| { Console println: $ x + y }
7
+ y = |x y| { x + y println }
8
8
  y call: [2, 3] # calls y and prints: 5
9
9
 
10
10
  # prints numbers 0 to 20
11
11
  num = 0
12
12
  while: { num <= 20 } do: {
13
- Console println: num
13
+ num println
14
14
  num = num + 1
15
15
  }
@@ -0,0 +1,31 @@
1
+ class Worker {
2
+ def initialize: @name supervisor: @supervisor
3
+ def work! {
4
+ Thread sleep: 0.5
5
+ @supervisor @@ done: @name
6
+ }
7
+ }
8
+
9
+ class Supervisor {
10
+ def initialize: @amount {
11
+ @done = []
12
+ @workers = Proxies DistributingProxy new: $ (1..@amount) map: |i| { Worker new: i supervisor: self }
13
+ }
14
+
15
+ def start {
16
+ "Starting: #{(0..@amount) to_a inspect}" println
17
+ @amount times: {
18
+ @workers @@ work!
19
+ }
20
+ }
21
+
22
+ def done: worker {
23
+ @done << worker
24
+ if: (@done size == @amount) then: {
25
+ "Done: #{@done inspect}" println
26
+ }
27
+ }
28
+ }
29
+
30
+ Supervisor new: 10 . start
31
+ Console readln
@@ -0,0 +1,15 @@
1
+ class Bar {
2
+ def foo {
3
+ Thread sleep: 5
4
+ 42
5
+ }
6
+ }
7
+
8
+ b = Bar new
9
+ f = b @ foo timeouts: <[
10
+ 3.5 => { "3.5 secs passed!" println; System abort: "Timeout" }
11
+ ]>
12
+
13
+ f value inspect println
14
+
15
+ Console readln: "..."
@@ -3,8 +3,7 @@
3
3
 
4
4
  class City {
5
5
  read_slot: 'name
6
- def initialize: @name {
7
- }
6
+ def initialize: @name
8
7
 
9
8
  def to_s {
10
9
  "City: " ++ @name
@@ -38,10 +38,4 @@ def ARGV for_options: op_names do: block {
38
38
  return true
39
39
  }
40
40
  }
41
- }
42
-
43
- def ARGV main?: filename {
44
- if: (ARGV[0]) then: {
45
- File expand_path: (ARGV[0]) . == filename
46
- }
47
- }
41
+ }
@@ -98,22 +98,18 @@ class Array {
98
98
  def each: block {
99
99
  """
100
100
  @block @Block@ to be called for each element in @self.
101
- @return @self
101
+ @return @self.
102
102
 
103
103
  Calls a given @Block@ with each element in the @Array@.
104
104
  """
105
105
 
106
- try {
107
- size times: |i| {
108
- try {
109
- block call: [at: i]
110
- } catch (Fancy NextIteration) => ex {
111
- }
106
+ size times: |i| {
107
+ try {
108
+ block call: [at: i]
109
+ } catch Fancy NextIteration {
112
110
  }
113
- return self
114
- } catch (Fancy BreakIteration) => ex {
115
- ex result
116
111
  }
112
+ self
117
113
  }
118
114
 
119
115
  def reverse_each: block {
@@ -375,7 +371,7 @@ class Array {
375
371
  def from: from to: to {
376
372
  """
377
373
  @from Start index for sub-array.
378
- @to End index ofr sub-array.
374
+ @to End index for sub-array.
379
375
 
380
376
  Returns sub-array starting at from: and going to to:
381
377
  """
@@ -203,4 +203,19 @@ class Block {
203
203
 
204
204
  DynamicValueArray new do: self . array
205
205
  }
206
+
207
+ def * iterations {
208
+ """
209
+ @iterations @Fixnum@ of amount of iterations to run @self.
210
+
211
+ Runs @self @iteration times.
212
+ Same as: `iterations times: self`
213
+
214
+ Example:
215
+ { \"Hello, World\" println } * 2
216
+ # => prints \"Hello, World\" 2 times
217
+ """
218
+
219
+ iterations times: self
220
+ }
206
221
  }
@@ -18,6 +18,7 @@ require: "class"
18
18
  require: "true_class"
19
19
  require: "nil_class"
20
20
  require: "false_class"
21
+ require: "exception"
21
22
  require: "number"
22
23
  require: "enumerable"
23
24
  require: "string"
@@ -35,8 +36,7 @@ require: "hash"
35
36
  require: "set"
36
37
  require: "symbol"
37
38
  require: "stack"
38
- require: "proxy"
39
- require: "thread_pool"
39
+ require: "proxies"
40
40
  require: "fiber"
41
41
  require: "future"
42
42
  require: "struct"
@@ -50,5 +50,6 @@ require: "vars"
50
50
  require: "system"
51
51
 
52
52
  require: "documentation"
53
-
54
53
  require: "package"
54
+ require: "contracts"
55
+ require: "matchers"
@@ -8,6 +8,112 @@ class Class {
8
8
 
9
9
  forwards_unary_ruby_methods
10
10
 
11
+ alias_method: '__private__: for: 'private:
12
+ alias_method: '__protected__: for: 'protected:
13
+ alias_method: '__public__: for: 'public:
14
+
15
+ def private: private_methods {
16
+ """
17
+ @private_methods @Block@ or @Fancy::Enumerable@ of method names to be private.
18
+
19
+ Sets any given method names to private on this @Class@.
20
+
21
+ Example:
22
+ class MyClass {
23
+ def foo {}
24
+ def bar {}
25
+
26
+ private: 'foo
27
+ private: 'bar
28
+
29
+ # same as:
30
+ private: ('foo, 'bar)
31
+
32
+ # same as:
33
+ private: {
34
+ def foo {}
35
+ def bar {}
36
+ }
37
+ }
38
+ """
39
+
40
+ __private__: $ match private_methods {
41
+ case Block ->
42
+ methods = instance_methods: false
43
+ private_methods call
44
+ instance_methods: false - methods
45
+ case _ -> private_methods
46
+ }
47
+ }
48
+
49
+ def protected: protected_methods {
50
+ """
51
+ @protected_methods @Block@ or @Fancy::Enumerable@ of method names to be protected.
52
+
53
+ Sets any given method names to protected on this @Class@.
54
+
55
+ Example:
56
+ class MyClass {
57
+ def foo {}
58
+ def bar {}
59
+
60
+ protected: 'foo
61
+ protected: 'bar
62
+
63
+ # same as:
64
+ protected: ('foo, 'bar)
65
+
66
+ # same as:
67
+ protected: {
68
+ def foo {}
69
+ def bar {}
70
+ }
71
+ }
72
+ """
73
+
74
+ __protected__: $ match protected_methods {
75
+ case Block ->
76
+ methods = instance_methods: false
77
+ protected_methods call
78
+ instance_methods: false - methods
79
+ case _ -> protected_methods
80
+ }
81
+ }
82
+
83
+ def public: public_methods {
84
+ """
85
+ @public_methods @Block@ or @Fancy::Enumerable@ of method names to be public.
86
+
87
+ Sets any given method names to public on this @Class@.
88
+
89
+ Example:
90
+ class MyClass {
91
+ def foo {}
92
+ def bar {}
93
+
94
+ public: 'foo
95
+ public: 'bar
96
+
97
+ # same as:
98
+ public: ('foo, 'bar)
99
+
100
+ # same as:
101
+ public: {
102
+ def foo {}
103
+ def bar {}
104
+ }
105
+ }
106
+ """
107
+
108
+ __public__: $ match public_methods {
109
+ case Block ->
110
+ methods = instance_methods: false
111
+ public_methods call
112
+ instance_methods: false - methods
113
+ case _ -> public_methods
114
+ }
115
+ }
116
+
11
117
  def define_slot_reader: slotname {
12
118
  """
13
119
  @slotname Name of the slot to define a getter method for.
@@ -125,6 +231,23 @@ class Class {
125
231
  }
126
232
  }
127
233
 
234
+ def remove_slot_accessors_for: slotnames {
235
+ """
236
+ @slotnames Name of slot or @Array@ of slotnames to remove accessor methods for.
237
+
238
+ Removes both reader and writer methods for slots in @slotnames.
239
+ """
240
+
241
+ slotnames to_a each: |s| {
242
+ try {
243
+ undefine_method: s
244
+ } catch NameError {}
245
+ try {
246
+ undefine_method: "#{s}:"
247
+ } catch NameError {}
248
+ }
249
+ }
250
+
128
251
  def subclass?: class_obj {
129
252
  """
130
253
  @class_obj Class object to check for, if @self is a subclass of @class_obj.
@@ -193,16 +316,11 @@ class Class {
193
316
  }
194
317
  }
195
318
 
196
- mdef = "def #{message_with_args}"
197
-
198
- mdef << " {\n"
199
- mdef << "@#{slotname} #{message_with_args}"
200
-
201
- mdef << "\n}"
202
-
203
- class_eval: {
204
- mdef eval
319
+ class_eval: """
320
+ def #{message_with_args} {
321
+ @#{slotname} #{message_with_args}
205
322
  }
323
+ """
206
324
  }
207
325
  }
208
326
 
@@ -221,4 +339,230 @@ class Class {
221
339
  name
222
340
  }
223
341
  }
224
- }
342
+
343
+ def fancy_instance_methods {
344
+ """
345
+ @return @Array@ of all instance methods defined in Fancy.
346
+ """
347
+
348
+ instance_methods select: @{ includes?: ":" }
349
+ }
350
+
351
+ def ruby_instance_methods {
352
+ """
353
+ @return @Array@ of all instance methods defined in Ruby.
354
+ """
355
+
356
+ instance_methods - fancy_instance_methods
357
+ }
358
+
359
+ # dummy methods, get replaced at end of startup in lib/contracts.fy
360
+ def provides_interface: methods {
361
+ @provided_interface_methods = @provided_interface_methods to_a append: (methods to_a)
362
+ }
363
+
364
+ def expects_interface_on_inclusion: methods {
365
+ @expected_interface_methods = methods to_a
366
+ }
367
+ alias_method: 'expects_interface: for: 'expects_interface_on_inclusion:
368
+
369
+ def before_method: method_name run: block_or_method_name {
370
+ """
371
+ @method_name Name of @Method@ to run another @Method@ or @Block@ before, when called.
372
+ @block_or_method_name @Block@ or name of other @Method@ to be called before @method_name.
373
+
374
+ Runs / Calls @block_or_method everytime before running @method_name.
375
+
376
+ Example:
377
+ Array before_method: 'inspect run: 'compact!
378
+ [1, nil, 2, nil, 3] inspect # => \"[1, 2, 3]\"
379
+
380
+ # Or pass a Block:
381
+ Array before_method: 'inspect run: @{ compact! }
382
+ """
383
+
384
+ define_calling_chain: [block_or_method_name, method_name] for_method: method_name
385
+ }
386
+
387
+ def after_method: method_name run: block_or_method_name {
388
+ """
389
+ @method_name Name of @Method@ to run another @Method@ or @Block@ after, when called.
390
+ @block_or_method_name @Block@ or name of other @Method@ to be called after @method_name.
391
+
392
+ Runs / Calls @block_or_method_name everytime after running @method_name.
393
+
394
+ Example:
395
+ Array after_method: 'inspect run: 'compact!
396
+ x = [1, nil, 2, nil, 3]
397
+ x inspect # => \"[1, nil, 2, nil, 3]\"
398
+ x inspect # => \"[1, 2, 3]\"
399
+
400
+ # Or pass a Block:
401
+ Array after_method: 'inspect run: @{ compact! }
402
+ """
403
+
404
+ define_calling_chain: [method_name, block_or_method_name] for_method: method_name
405
+ }
406
+
407
+ def around_method: method_name run: block_or_method_name {
408
+ """
409
+ @method_name Name of @Method@ to run another @Method@ or @Block@ before & after, when called.
410
+ @block_or_method_name @Block@ or name of other @Method@ to be called before & after @method_name.
411
+
412
+ Runs / Calls @block_or_method_name everytime before & after running @method_name.
413
+
414
+ Example:
415
+ class MyController {
416
+ def do_stuff {
417
+ \"Doing stuff\" println
418
+ }
419
+
420
+ def log_data {
421
+ \"Log data\" println
422
+ }
423
+
424
+ around_method: 'do_stuff run: 'log_data
425
+ }
426
+
427
+ controller = MyController new
428
+ controller do_stuff
429
+
430
+ # which will print:
431
+ # Log data
432
+ # Doing stuff
433
+ # Log data
434
+
435
+ # Or pass a Block:
436
+ MyController around_method: 'do_stuff run: { \"Log data\" println }
437
+ """
438
+
439
+ define_calling_chain: [block_or_method_name, method_name, block_or_method_name] for_method: method_name
440
+ }
441
+
442
+ def define_calling_chain: blocks_or_method_names for_method: method_name {
443
+ """
444
+ @blocks_or_method_names @Array@ of either method names or @Block@s to be called in order.
445
+ @method_name Name of method to define calling chain for.
446
+
447
+ Example:
448
+ class Foo {
449
+ def foo { 'foo println }
450
+ def bar { 'bar println }
451
+ def baz { 'baz println }
452
+
453
+ define_calling_chain: ['foo, 'bar, 'baz] for_method: 'foo
454
+ }
455
+
456
+ Foo new foo
457
+ # prints:
458
+ # foo
459
+ # bar
460
+ # baz
461
+
462
+ # You can also pass in Blocks:
463
+ Foo define_calling_chain: [@{ foo }, @{ bar }, @{ baz }] for_method: 'bar
464
+
465
+ Foo new bar
466
+ # prints:
467
+ # foo
468
+ # bar
469
+ # baz
470
+ """
471
+
472
+ # optimize for methods if all arguments are symbols (generate code instead of using dynamic #call: messages)
473
+ if: (blocks_or_method_names all?: @{ is_a?: Symbol }) then: {
474
+ return define_method_calling_chain: blocks_or_method_names for_method: method_name
475
+ }
476
+
477
+ method = instance_method: method_name
478
+
479
+ define_method: (method_name message_name) with: |args1| {
480
+ bound_method = method bind(self)
481
+
482
+ new_method_body = |args2| {
483
+ match bound_method arity {
484
+ case 0 -> args2 = []
485
+ case 1 -> args2 = [args2]
486
+ }
487
+
488
+ args_with_self = args2 dup unshift: self
489
+ return_val = nil
490
+ blocks_or_method_names each: |block| {
491
+ match block {
492
+ case method_name -> return_val = bound_method call: args2
493
+ case _ -> block call: args_with_self
494
+ }
495
+ }
496
+ return_val
497
+ }
498
+
499
+ # define new method body and run it afterwards as we lazily
500
+ # define the method the first time it's called for each new
501
+ # instance of this class
502
+ metaclass define_method: (method_name message_name) with: new_method_body
503
+
504
+ match method arity {
505
+ case 0 -> args1 = []
506
+ case 1 -> args1 = [args1]
507
+ }
508
+
509
+ new_method_body call: args1
510
+ }
511
+ }
512
+
513
+ def define_method_calling_chain: method_names for_method: method_name {
514
+ orig_method_name = "__original__#{method_name}"
515
+ alias_method: orig_method_name for: (method_name message_name)
516
+
517
+ method = instance_method: (method_name message_name)
518
+ orig_method = instance_method: orig_method_name
519
+
520
+ before_methods = method_names take_while: |m| { m != method_name }
521
+ _orig_method, *after_methods = method_names drop_while: |m| { m != method_name }
522
+
523
+ before_methods = before_methods map: |m| { instance_method: m . selector_with_args } . join: ";"
524
+ after_methods = after_methods map: |m| { instance_method: m . selector_with_args } . join: ";"
525
+
526
+ class_eval: """
527
+ def #{method selector_with_args} {
528
+ #{before_methods}
529
+ return_val = #{orig_method selector_with_args}
530
+ #{after_methods}
531
+ return_val
532
+ }
533
+ """
534
+ }
535
+
536
+ def rebind_instance_method: method_name with: rebind_callable within: within_block receiver: receiver (self) {
537
+ """
538
+ @method_name Name of instance method to rebind in @self.
539
+ @rebind_callable Name of method or @Block@ to rebind @method_name to.
540
+ @within_block @Block@ to be called with @receiver or @self.
541
+ @receiver Argument to @within_block. Defaults to @self.
542
+ @return Value of calling @within_block with @receiver.
543
+
544
+ Rebinds @method_name to @rebind_callable within @within_block.
545
+ If @within_block takes an argument, it will be called with @receiver (defaults to @self).
546
+ """
547
+
548
+ try {
549
+ old_method = nil
550
+ try {
551
+ old_method = instance_method: method_name
552
+ } catch NameError {}
553
+
554
+ match rebind_callable {
555
+ case Symbol -> alias_method: method_name for: rebind_callable
556
+ case _ -> define_method: method_name with: rebind_callable
557
+ }
558
+
559
+ return within_block call: [receiver]
560
+ } finally {
561
+ if: old_method then: {
562
+ define_method: method_name with: old_method
563
+ } else: {
564
+ undefine_method: method_name
565
+ }
566
+ }
567
+ }
568
+ }