fancy 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +38 -86
- data/bin/fdoc +2 -22
- data/bin/fspec +8 -3
- data/bin/ifancy +1 -1
- data/boot/fancy_ext.rb +1 -0
- data/boot/fancy_ext/array.rb +19 -0
- data/boot/fancy_ext/class.rb +2 -4
- data/boot/fancy_ext/module.rb +2 -0
- data/boot/fancy_ext/object.rb +0 -17
- data/boot/rbx-compiler/compiler/ast/method_def.rb +0 -4
- data/boot/rbx-compiler/compiler/ast/singleton_method_def.rb +0 -7
- data/boot/rbx-compiler/parser/fancy_parser.bundle +0 -0
- data/boot/rbx-compiler/parser/fancy_parser.c +1 -0
- data/doc/api/fancy.css +10 -1
- data/doc/api/fancy.jsonp +1 -1
- data/doc/api/fdoc.js +22 -9
- data/doc/api/octocat.png +0 -0
- data/doc/features.md +1 -2
- data/examples/actors.fy +1 -2
- data/examples/armstrong_numbers.fy +7 -3
- data/examples/blocks.fy +3 -3
- data/examples/distributing_proxy.fy +31 -0
- data/examples/future_sends.fy +15 -0
- data/examples/person.fy +1 -2
- data/lib/argv.fy +1 -7
- data/lib/array.fy +7 -11
- data/lib/block.fy +15 -0
- data/lib/boot.fy +4 -3
- data/lib/class.fy +354 -10
- data/lib/compiler.fy +1 -1
- data/lib/compiler/ast/assign.fy +4 -8
- data/lib/compiler/ast/async_send.fy +1 -2
- data/lib/compiler/ast/block.fy +5 -0
- data/lib/compiler/ast/class_def.fy +2 -1
- data/lib/compiler/ast/expression_list.fy +1 -2
- data/lib/compiler/ast/future_send.fy +1 -2
- data/lib/compiler/ast/identifier.fy +34 -17
- data/lib/compiler/ast/literals.fy +31 -19
- data/lib/compiler/ast/match.fy +5 -4
- data/lib/compiler/ast/message_send.fy +3 -5
- data/lib/compiler/ast/method_def.fy +0 -3
- data/lib/compiler/ast/range.fy +2 -4
- data/lib/compiler/ast/return.fy +2 -4
- data/lib/compiler/ast/script.fy +2 -4
- data/lib/compiler/ast/singleton_method_def.fy +0 -3
- data/lib/compiler/ast/string_interpolation.fy +2 -2
- data/lib/compiler/ast/super.fy +2 -4
- data/lib/compiler/ast/try_catch.fy +13 -9
- data/lib/compiler/ast/tuple_literal.fy +1 -2
- data/lib/compiler/compiler.fy +2 -2
- data/lib/compiler/stages.fy +3 -6
- data/lib/contracts.fy +89 -57
- data/lib/dynamic_slot_object.fy +21 -3
- data/lib/enumerable.fy +140 -4
- data/lib/enumerator.fy +1 -1
- data/lib/eval.fy +23 -9
- data/lib/exception.fy +16 -0
- data/lib/false_class.fy +36 -5
- data/lib/fancy_spec.fy +64 -34
- data/lib/fdoc.fy +85 -24
- data/lib/file.fy +19 -0
- data/lib/future.fy +4 -46
- data/lib/hash.fy +113 -0
- data/lib/integer.fy +25 -6
- data/lib/iteration.fy +3 -3
- data/lib/main.fy +5 -0
- data/lib/matchers.fy +79 -0
- data/lib/nil_class.fy +8 -0
- data/lib/object.fy +109 -18
- data/lib/option_parser.fy +118 -0
- data/lib/package/dependency.fy +4 -8
- data/lib/package/dependency_installer.fy +1 -1
- data/lib/package/handler.fy +6 -0
- data/lib/package/installer.fy +43 -16
- data/lib/package/list.fy +1 -2
- data/lib/package/specification.fy +5 -5
- data/lib/package/uninstaller.fy +9 -2
- data/lib/parser.fy +1 -3
- data/lib/parser/ext/ext.c +1 -0
- data/lib/parser/ext/lexer.lex +5 -0
- data/lib/parser/methods.fy +48 -46
- data/lib/proxies.fy +151 -0
- data/lib/rbx.fy +1 -0
- data/lib/rbx/actor.fy +16 -18
- data/lib/rbx/array.fy +18 -3
- data/lib/rbx/block.fy +1 -7
- data/lib/rbx/class.fy +54 -9
- data/lib/rbx/code_loader.fy +2 -5
- data/lib/rbx/compiled_method.fy +31 -0
- data/lib/rbx/debugger.fy +66 -0
- data/lib/rbx/directory.fy +8 -3
- data/lib/rbx/documentation.fy +1 -1
- data/lib/rbx/file.fy +22 -0
- data/lib/rbx/integer.fy +1 -1
- data/lib/rbx/match_data.fy +2 -1
- data/lib/rbx/method.fy +26 -0
- data/lib/rbx/object.fy +8 -3
- data/lib/rbx/regexp.fy +6 -3
- data/lib/rbx/string.fy +9 -1
- data/lib/rbx/stringio.fy +12 -0
- data/lib/rbx/symbol.fy +4 -0
- data/lib/stack.fy +1 -1
- data/lib/string.fy +34 -0
- data/lib/stringio.fy +1 -1
- data/lib/symbol.fy +6 -2
- data/lib/system.fy +15 -1
- data/lib/tuple.fy +5 -2
- data/lib/version.fy +1 -1
- data/ruby_lib/fdoc +2 -22
- data/tests/array.fy +3 -17
- data/tests/class.fy +312 -10
- data/tests/contracts.fy +51 -0
- data/tests/distributing_proxy.fy +28 -0
- data/tests/enumerable.fy +104 -1
- data/tests/exception.fy +35 -0
- data/tests/fixnum.fy +1 -1
- data/tests/hash.fy +81 -1
- data/tests/integer.fy +9 -0
- data/tests/matchers.fy +18 -0
- data/tests/method.fy +8 -14
- data/tests/object.fy +76 -2
- data/tests/option_parser.fy +80 -0
- data/tests/string.fy +21 -0
- data/tests/stringio.fy +1 -1
- data/tests/tuple.fy +1 -1
- metadata +21 -44
- data/examples/arithmetic.fy +0 -7
- data/examples/array.fy +0 -50
- data/examples/boolean.fy +0 -24
- data/examples/class.fy +0 -68
- data/examples/constant_access.fy +0 -15
- data/examples/default_args.fy +0 -20
- data/examples/define_methods.fy +0 -15
- data/examples/dynamic_output.fy +0 -15
- data/examples/empty_catch.fy +0 -4
- data/examples/exception.fy +0 -9
- data/examples/files.fy +0 -23
- data/examples/finally.fy +0 -5
- data/examples/future.fy +0 -30
- data/examples/future_composition.fy +0 -20
- data/examples/futures.fy +0 -9
- data/examples/game_of_life.fy +0 -148
- data/examples/html_generator.fy +0 -84
- data/examples/implicit_return.fy +0 -3
- data/examples/matchers.fy +0 -6
- data/examples/nested_try.fy +0 -9
- data/examples/numbers.fy +0 -12
- data/examples/rbx/and_or.fy +0 -7
- data/examples/rbx/blocks.fy +0 -22
- data/examples/rbx/classes.fy +0 -32
- data/examples/rbx/hello.fy +0 -8
- data/examples/rbx/include.fy +0 -12
- data/examples/rbx/inherit.fy +0 -11
- data/examples/rbx/methods.fy +0 -15
- data/examples/rbx/nested_classes.fy +0 -9
- data/examples/rbx/require.fy +0 -3
- data/examples/rbx/strings.fy +0 -5
- data/examples/require.fy +0 -7
- data/examples/return.fy +0 -13
- data/examples/singleton_methods.fy +0 -21
- data/examples/threads.fy +0 -18
- data/examples/tuple.fy +0 -8
- data/examples/webserver/webserver.fy +0 -15
- data/lib/proxy.fy +0 -86
- data/lib/thread_pool.fy +0 -102
data/examples/blocks.fy
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# blocks.fy
|
2
2
|
# Examples of fancy code blocks
|
3
3
|
|
4
|
-
x = {
|
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| {
|
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
|
-
|
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
|
data/examples/person.fy
CHANGED
data/lib/argv.fy
CHANGED
data/lib/array.fy
CHANGED
@@ -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
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
374
|
+
@to End index for sub-array.
|
379
375
|
|
380
376
|
Returns sub-array starting at from: and going to to:
|
381
377
|
"""
|
data/lib/block.fy
CHANGED
@@ -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
|
}
|
data/lib/boot.fy
CHANGED
@@ -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: "
|
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"
|
data/lib/class.fy
CHANGED
@@ -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
|
-
|
197
|
-
|
198
|
-
|
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
|
+
}
|