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.
- 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
|
+
}
|