fancy 0.5.0 → 0.6.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/AUTHORS +2 -0
- data/README.md +6 -1
- data/bin/fancy +6 -0
- data/bin/ifancy +44 -3
- data/boot/fancy_ext/module.rb +4 -0
- data/boot/fancy_ext/object.rb +4 -0
- data/boot/rbx-compiler/compiler/ast/block.rb +29 -1
- data/boot/rbx-compiler/compiler/ast/identifier.rb +6 -0
- data/boot/rbx-compiler/compiler/ast/message_send.rb +1 -0
- data/boot/rbx-compiler/parser/fancy_parser.bundle +0 -0
- data/boot/rbx-compiler/parser/lexer.lex +2 -0
- data/boot/rbx-compiler/parser/parser.rb +6 -0
- data/boot/rbx-compiler/parser/parser.y +14 -1
- data/doc/api/fancy.jsonp +1 -1
- data/doc/features.md +24 -0
- data/examples/99bottles.fy +5 -0
- data/examples/conditions_exceptions.fy +9 -0
- data/examples/conditions_parsing.fy +68 -0
- data/examples/greeter.fy +9 -0
- data/examples/html_generator.fy +59 -29
- data/examples/webserver/webserver.fy +8 -11
- data/lib/argv.fy +6 -0
- data/lib/array.fy +17 -35
- data/lib/block.fy +82 -1
- data/lib/boot.fy +4 -2
- data/lib/compiler.fy +2 -2
- data/lib/compiler/ast/block.fy +24 -20
- data/lib/compiler/ast/message_send.fy +11 -0
- data/lib/contracts.fy +60 -0
- data/lib/dynamic_slot_object.fy +61 -0
- data/lib/enumerable.fy +432 -394
- data/lib/enumerator.fy +152 -150
- data/lib/fdoc.fy +4 -17
- data/lib/fiber.fy +4 -10
- data/lib/file.fy +33 -25
- data/lib/future.fy +59 -5
- data/lib/hash.fy +54 -1
- data/lib/html.fy +107 -0
- data/lib/kvo.fy +173 -0
- data/lib/main.fy +6 -2
- data/lib/message_sink.fy +19 -0
- data/lib/number.fy +48 -0
- data/lib/object.fy +65 -13
- data/lib/package.fy +12 -2
- data/lib/package/dependency.fy +13 -0
- data/lib/package/dependency_installer.fy +27 -0
- data/lib/package/installer.fy +4 -10
- data/lib/package/uninstaller.fy +1 -3
- data/lib/parser/ext/lexer.lex +8 -3
- data/lib/parser/ext/parser.y +4 -1
- data/lib/parser/methods.fy +7 -3
- data/lib/range.fy +1 -1
- data/lib/rbx.fy +2 -1
- data/lib/rbx/array.fy +28 -12
- data/lib/rbx/bignum.fy +1 -1
- data/lib/rbx/block.fy +27 -0
- data/lib/rbx/console.fy +6 -6
- data/lib/rbx/date.fy +6 -1
- data/lib/rbx/documentation.fy +8 -3
- data/lib/rbx/exception.fy +5 -0
- data/lib/rbx/file.fy +40 -7
- data/lib/rbx/fixnum.fy +12 -1
- data/lib/rbx/method.fy +9 -2
- data/lib/rbx/module.fy +24 -0
- data/lib/rbx/regexp.fy +8 -0
- data/lib/rbx/string.fy +23 -7
- data/lib/rbx/tcp_server.fy +4 -2
- data/lib/rbx/tcp_socket.fy +14 -0
- data/lib/remote_object.fy +59 -0
- data/lib/set.fy +15 -4
- data/lib/string.fy +38 -5
- data/lib/stringio.fy +1 -0
- data/lib/symbol.fy +4 -0
- data/lib/system.fy +22 -0
- data/lib/thread_pool.fy +2 -2
- data/lib/tuple.fy +18 -1
- data/lib/vars.fy +17 -0
- data/lib/version.fy +1 -1
- data/ruby_lib/fancy +6 -0
- data/tests/array.fy +30 -0
- data/tests/block.fy +106 -0
- data/tests/class.fy +19 -0
- data/tests/enumerable.fy +1 -1
- data/tests/enumerator.fy +5 -5
- data/tests/file.fy +28 -0
- data/tests/fixnum.fy +0 -50
- data/tests/future.fy +9 -24
- data/tests/hash.fy +35 -0
- data/tests/html.fy +33 -0
- data/tests/kvo.fy +101 -0
- data/tests/number.fy +75 -0
- data/tests/object.fy +50 -3
- data/tests/string.fy +19 -10
- data/tests/symbol.fy +5 -0
- data/tests/tuple.fy +7 -0
- data/tools/fancy-mode.el +5 -1
- metadata +22 -21
- data/boot/compiler/parser/ext/fancy_parser.bundle +0 -0
- data/boot/rbx-compiler/parser/Makefile +0 -156
- data/boot/rbx-compiler/parser/lexer.c +0 -2310
- data/boot/rbx-compiler/parser/lexer.h +0 -315
- data/boot/rbx-compiler/parser/parser.c +0 -2946
- data/boot/rbx-compiler/parser/parser.h +0 -151
- data/lib/fiber_pool.fy +0 -78
- data/lib/method.fy +0 -6
- data/lib/parser/ext/Makefile +0 -156
- data/lib/parser/ext/fancy_parser.bundle +0 -0
- data/lib/parser/ext/lexer.c +0 -2392
- data/lib/parser/ext/lexer.h +0 -315
- data/lib/parser/ext/parser.c +0 -3251
- data/lib/parser/ext/parser.h +0 -161
data/lib/future.fy
CHANGED
|
@@ -5,6 +5,7 @@ class FutureSend {
|
|
|
5
5
|
@condvar = ConditionVariable new
|
|
6
6
|
@completed = false
|
|
7
7
|
@failed = false
|
|
8
|
+
@continuations = []
|
|
8
9
|
@actor ! ('future, (@message, @params), self)
|
|
9
10
|
}
|
|
10
11
|
|
|
@@ -26,11 +27,19 @@ class FutureSend {
|
|
|
26
27
|
|
|
27
28
|
def completed! {
|
|
28
29
|
@condvar broadcast
|
|
30
|
+
unless: @failed do: {
|
|
31
|
+
@continuations each: @{ call: [@value] }
|
|
32
|
+
}
|
|
33
|
+
@continuations = []
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
private: 'completed!
|
|
32
37
|
|
|
33
38
|
def completed? {
|
|
39
|
+
"""
|
|
40
|
+
@return @true if FutureSend completed (success or failure), @false otherwise.
|
|
41
|
+
"""
|
|
42
|
+
|
|
34
43
|
completed = false
|
|
35
44
|
@completed_mutex synchronize: {
|
|
36
45
|
completed = @completed
|
|
@@ -38,7 +47,25 @@ class FutureSend {
|
|
|
38
47
|
return completed true?
|
|
39
48
|
}
|
|
40
49
|
|
|
50
|
+
def succeeded? {
|
|
51
|
+
"""
|
|
52
|
+
@return @true if FutureSend completed without failure, @false otherwise.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
completed = false
|
|
56
|
+
failed = false
|
|
57
|
+
@completed_mutex synchronize: {
|
|
58
|
+
completed = @completed
|
|
59
|
+
failed = @failed
|
|
60
|
+
}
|
|
61
|
+
return completed true? && (failed false?)
|
|
62
|
+
}
|
|
63
|
+
|
|
41
64
|
def failed? {
|
|
65
|
+
"""
|
|
66
|
+
@return @true if FutureSend failed, @false otherwise.
|
|
67
|
+
"""
|
|
68
|
+
|
|
42
69
|
failed = false
|
|
43
70
|
@completed_mutex synchronize: {
|
|
44
71
|
failed = @failed
|
|
@@ -47,6 +74,13 @@ class FutureSend {
|
|
|
47
74
|
}
|
|
48
75
|
|
|
49
76
|
def failure {
|
|
77
|
+
"""
|
|
78
|
+
@return @Exception@ that caused the FutureSend to fail, or @nil, if no failure.
|
|
79
|
+
|
|
80
|
+
Returns the @Exception@ that caused @self to fail, or @nil, if it didn't fail.
|
|
81
|
+
Will block the calling @Thread@ if @self hasn't completed or failed yet.
|
|
82
|
+
"""
|
|
83
|
+
|
|
50
84
|
@completed_mutex synchronize: {
|
|
51
85
|
if: @failed then: {
|
|
52
86
|
return @fail_reason
|
|
@@ -58,6 +92,13 @@ class FutureSend {
|
|
|
58
92
|
}
|
|
59
93
|
|
|
60
94
|
def value {
|
|
95
|
+
"""
|
|
96
|
+
@return Return value of performing @self.
|
|
97
|
+
|
|
98
|
+
Returns the value returned by performing @self.
|
|
99
|
+
Will block the calling @Thread@ if @self hasn't completed or failed yet.
|
|
100
|
+
"""
|
|
101
|
+
|
|
61
102
|
@completed_mutex synchronize: {
|
|
62
103
|
if: @completed then: {
|
|
63
104
|
return @value
|
|
@@ -73,9 +114,24 @@ class FutureSend {
|
|
|
73
114
|
}
|
|
74
115
|
|
|
75
116
|
def when_done: block {
|
|
76
|
-
|
|
117
|
+
"""
|
|
118
|
+
@block @Block@ to be registered as a continuation when @self succeeds.
|
|
119
|
+
|
|
120
|
+
Registers @block as a continuation to be called with @self's value on success.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
{ return nil } if: failed?
|
|
124
|
+
@completed_mutex synchronize: {
|
|
125
|
+
if: @completed then: {
|
|
126
|
+
block call: [@value]
|
|
127
|
+
} else: {
|
|
128
|
+
@continuations << block
|
|
129
|
+
}
|
|
130
|
+
}
|
|
77
131
|
}
|
|
78
132
|
|
|
133
|
+
alias_method: 'with_value: for: 'when_done:
|
|
134
|
+
|
|
79
135
|
def && block {
|
|
80
136
|
when_done: block
|
|
81
137
|
}
|
|
@@ -132,15 +188,13 @@ class PooledFuture {
|
|
|
132
188
|
}
|
|
133
189
|
|
|
134
190
|
class FutureCollection {
|
|
135
|
-
include:
|
|
191
|
+
include: Fancy Enumerable
|
|
136
192
|
|
|
137
193
|
def initialize: @futures {
|
|
138
194
|
}
|
|
139
195
|
|
|
140
196
|
def each: block {
|
|
141
|
-
@futures each:
|
|
142
|
-
f when_done: block
|
|
143
|
-
}
|
|
197
|
+
@futures each: @{ when_done: block }
|
|
144
198
|
}
|
|
145
199
|
|
|
146
200
|
def await_all {
|
data/lib/hash.fy
CHANGED
|
@@ -4,7 +4,7 @@ class Hash {
|
|
|
4
4
|
Maps a key to a value.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
include:
|
|
7
|
+
include: Fancy Enumerable
|
|
8
8
|
|
|
9
9
|
def [key] {
|
|
10
10
|
"""
|
|
@@ -17,6 +17,21 @@ class Hash {
|
|
|
17
17
|
at: key
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
def at: key else: else_block {
|
|
21
|
+
"""
|
|
22
|
+
@key Key of the value to get.
|
|
23
|
+
@else_block @Block@ to be called if @key is not found.
|
|
24
|
+
@return Value for @key or value of calling @else_block, if @key is not found.
|
|
25
|
+
|
|
26
|
+
Returns the value for a given key.
|
|
27
|
+
If the key is not found, calls @else_block and returns the value it yields.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
if: (includes?: key) then: {
|
|
31
|
+
at: key
|
|
32
|
+
} else: else_block
|
|
33
|
+
}
|
|
34
|
+
|
|
20
35
|
def each: block {
|
|
21
36
|
"""
|
|
22
37
|
@block @Block@ to be called with each key and value in @self.
|
|
@@ -80,6 +95,26 @@ class Hash {
|
|
|
80
95
|
to_a to_s
|
|
81
96
|
}
|
|
82
97
|
|
|
98
|
+
def to_object {
|
|
99
|
+
"""
|
|
100
|
+
@return New @Object@ with slots defined by keys and values in @self.
|
|
101
|
+
|
|
102
|
+
Creates and returns a new @Object@ with slot names and values based on keys and values in @self.
|
|
103
|
+
|
|
104
|
+
Example:
|
|
105
|
+
o = <['name => \"Christopher Bertels\", 'interest => \"programming languages\"]> to_object
|
|
106
|
+
o name # => \"Christopher Bertels\"
|
|
107
|
+
o interest # => 42
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
o = Object new
|
|
111
|
+
self each: |k v| {
|
|
112
|
+
o set_slot: k value: v
|
|
113
|
+
}
|
|
114
|
+
o metaclass read_write_slots: keys
|
|
115
|
+
o
|
|
116
|
+
}
|
|
117
|
+
|
|
83
118
|
def inspect {
|
|
84
119
|
str = "<["
|
|
85
120
|
each: |key val| {
|
|
@@ -104,4 +139,22 @@ class Hash {
|
|
|
104
139
|
|
|
105
140
|
keys map: |k| { at: k }
|
|
106
141
|
}
|
|
142
|
+
|
|
143
|
+
def fetch: key else: else_block {
|
|
144
|
+
"""
|
|
145
|
+
@key Key of value to get.
|
|
146
|
+
@else_block @Block@ that gets called if @key not in @self.
|
|
147
|
+
|
|
148
|
+
Example:
|
|
149
|
+
<['foo => 'bar]> fetch: 'foo else: { 42 } # => 'bar
|
|
150
|
+
<['foo => 'bar]> fetch: 'unknown else: { 42 } # => 42
|
|
151
|
+
<['nil => nil]> fetch: 'nil else: { 'not_found } # => nil
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
if: (includes?: key) then: {
|
|
155
|
+
at: key
|
|
156
|
+
} else: {
|
|
157
|
+
else_block call: [key]
|
|
158
|
+
}
|
|
159
|
+
}
|
|
107
160
|
}
|
data/lib/html.fy
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
class HTML {
|
|
2
|
+
"""
|
|
3
|
+
HTML generator class.
|
|
4
|
+
|
|
5
|
+
Example:
|
|
6
|
+
require: \"html\"
|
|
7
|
+
html = HTML new: @{
|
|
8
|
+
html: @{
|
|
9
|
+
head: @{ title: \"My Fancy Website\" }
|
|
10
|
+
body: @{
|
|
11
|
+
div: { id: \”main\” } with: \"Hello, Fancy World!\"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
} . to_s
|
|
15
|
+
|
|
16
|
+
# html is now:
|
|
17
|
+
\"\"\"
|
|
18
|
+
<html>
|
|
19
|
+
<head>
|
|
20
|
+
<title>
|
|
21
|
+
My Fancy Website
|
|
22
|
+
</title>
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<div id=\"main\">
|
|
26
|
+
Hello, Fancy World!
|
|
27
|
+
</div>
|
|
28
|
+
</body>
|
|
29
|
+
</html>
|
|
30
|
+
\"\"\"
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def initialize {
|
|
35
|
+
@buf = ""
|
|
36
|
+
@indent = 0
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
def initialize: block {
|
|
40
|
+
initialize
|
|
41
|
+
block call: [self]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
def open_tag: name attrs: attrs (<[]>) indent: indent (true) {
|
|
45
|
+
@buf << "\n"
|
|
46
|
+
@buf << (" " * @indent)
|
|
47
|
+
@indent = @indent + 2
|
|
48
|
+
|
|
49
|
+
@buf << "<" << name
|
|
50
|
+
unless: (attrs empty?) do: {
|
|
51
|
+
@buf << " "
|
|
52
|
+
attrs each: |k v| {
|
|
53
|
+
@buf << k << "=" << (v to_s inspect)
|
|
54
|
+
} in_between: {
|
|
55
|
+
@buf << " "
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
@buf << ">"
|
|
59
|
+
|
|
60
|
+
{ @indent = @indent - 2 } unless: indent
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
def close_tag: name {
|
|
64
|
+
@buf << "\n"
|
|
65
|
+
@indent = @indent - 2
|
|
66
|
+
@buf << (" " * @indent)
|
|
67
|
+
|
|
68
|
+
@buf << "</" << name << ">"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
def html_block: tag body: body attrs: attrs (<[]>) {
|
|
72
|
+
tag = tag from: 0 to: -2
|
|
73
|
+
open_tag: tag attrs: attrs
|
|
74
|
+
match body first {
|
|
75
|
+
case Block -> @buf << (body first call: [self])
|
|
76
|
+
case _ -> @buf << "\n" << (" " * @indent) << (body first)
|
|
77
|
+
}
|
|
78
|
+
close_tag: tag
|
|
79
|
+
nil
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
def unknown_message: m with_params: p {
|
|
83
|
+
match m to_s {
|
|
84
|
+
case /with:$/ ->
|
|
85
|
+
tag = m to_s substitute: /with:$/ with: ""
|
|
86
|
+
html_block: tag body: (p rest) attrs: (p first to_hash)
|
|
87
|
+
case _ ->
|
|
88
|
+
html_block: (m to_s) body: p
|
|
89
|
+
}
|
|
90
|
+
nil
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
def br {
|
|
94
|
+
@buf << "\n" << (" " * @indent)
|
|
95
|
+
@buf << "<br/>"
|
|
96
|
+
nil
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
def input: attrs {
|
|
100
|
+
open_tag: "input" attrs: (attrs to_hash) indent: false
|
|
101
|
+
nil
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
def to_s {
|
|
105
|
+
@buf from: 1 to: -1 . to_s
|
|
106
|
+
}
|
|
107
|
+
}
|
data/lib/kvo.fy
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
class KVO {
|
|
2
|
+
"""
|
|
3
|
+
Key-Value Observing Mixin class.
|
|
4
|
+
Include this Class into any class to add support for Key-Value Observing.
|
|
5
|
+
Inspired by Objective-C's KVO, but using @Block@s, as it fits nicer
|
|
6
|
+
with Fancy's semantics.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
class Person {
|
|
10
|
+
include: KVO
|
|
11
|
+
read_write_slots: ('name, 'age, 'city)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
tom = Person new tap: @{
|
|
15
|
+
name: \"Tom Cruise\"
|
|
16
|
+
age: 55
|
|
17
|
+
city: \"Hollywood\"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
tom observe: 'name with: |new old| {
|
|
21
|
+
new println
|
|
22
|
+
}
|
|
23
|
+
tom name: \"Tommy Cruise\" # will cause \"Tommy Cruise\" to be printed
|
|
24
|
+
tom age: 56 # No observer Blocks defined, so nothing will happen
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
class ClassMethods {
|
|
28
|
+
def define_slot_writer: slotname {
|
|
29
|
+
slotname = slotname to_sym
|
|
30
|
+
define_method: "#{slotname}:" with: |new_val| {
|
|
31
|
+
old_val = get_slot: slotname
|
|
32
|
+
set_slot: slotname value: new_val
|
|
33
|
+
match new_val {
|
|
34
|
+
case old_val -> nil # do nothing if no change
|
|
35
|
+
case _ ->
|
|
36
|
+
__kvo_slot_change__: slotname new: new_val old: old_val
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
def define_slot_reader: slotname {
|
|
42
|
+
slotname = slotname to_sym
|
|
43
|
+
define_method: slotname with: {
|
|
44
|
+
val = get_slot: slotname
|
|
45
|
+
if: (val is_a?: Fancy Enumerable) then: {
|
|
46
|
+
unless: (val get_slot: '__kvo_wrappers_defined?__) do: {
|
|
47
|
+
__kvo_wrap_collection_methods__: val for_slot: slotname
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
val
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
def KVO included: class {
|
|
56
|
+
class extend: ClassMethods
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
def observe: slotname with: block {
|
|
60
|
+
"""
|
|
61
|
+
@slotname Name of slot to be observed with @block.
|
|
62
|
+
@block @Block@ to be called with old and new value of @slotname in @self.
|
|
63
|
+
|
|
64
|
+
Registers a new observer @Block@ for @slotname in @self.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
__kvo_add_observer__: block for: slotname to: __kvo_slot_observers__
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def observe_insertion: slotname with: block {
|
|
71
|
+
"""
|
|
72
|
+
@slotname Name of collection slot to be observed with @block.
|
|
73
|
+
@block @Block@ to be called with value inserted in collection named @slotname in @self.
|
|
74
|
+
|
|
75
|
+
Registers a new insertion observer @Block@ for collection named @slotname in @self.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
__kvo_add_observer__: block for: slotname to: __kvo_insertion_observers__
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
def observe_removal: slotname with: block {
|
|
82
|
+
"""
|
|
83
|
+
@slotname Name of collection slot to be observed with @block.
|
|
84
|
+
@block @Block@ to be called with value removed from collection named @slotname in @self.
|
|
85
|
+
|
|
86
|
+
Registers a new removal observer @Block@ for collection named @slotname in @self.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
__kvo_add_observer__: block for: slotname to: __kvo_removal_observers__
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# PRIVATE METHODS
|
|
93
|
+
# OMG this looks FUGLY but this shall never be seen anyway
|
|
94
|
+
|
|
95
|
+
def __kvo_slot_observers__ {
|
|
96
|
+
{ @__kvo_slot_observers__ = <[]> } unless: @__kvo_slot_observers__
|
|
97
|
+
@__kvo_slot_observers__
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private: '__kvo_slot_observers__
|
|
101
|
+
|
|
102
|
+
def __kvo_insertion_observers__ {
|
|
103
|
+
{ @__kvo_insertion_observers__ = <[]> } unless: @__kvo_insertion_observers__
|
|
104
|
+
@__kvo_insertion_observers__
|
|
105
|
+
}
|
|
106
|
+
private: '__kvo_insertion_observers__
|
|
107
|
+
|
|
108
|
+
def __kvo_removal_observers__ {
|
|
109
|
+
{ @__kvo_removal_observers__ = <[]> } unless: @__kvo_removal_observers__
|
|
110
|
+
@__kvo_removal_observers__
|
|
111
|
+
}
|
|
112
|
+
private: '__kvo_removal_observers__
|
|
113
|
+
|
|
114
|
+
def __kvo_add_observer__: block for: slotname to: observer_list {
|
|
115
|
+
slotname = slotname to_sym
|
|
116
|
+
if: (observer_list[slotname]) then: |set| {
|
|
117
|
+
set << block
|
|
118
|
+
} else: {
|
|
119
|
+
observer_list[slotname]: $ Set new: [block]
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
private: '__kvo_add_observer__:for:to:
|
|
123
|
+
|
|
124
|
+
def __kvo_slot_change__: slotname new: new_val old: old_val {
|
|
125
|
+
if: (__kvo_slot_observers__[slotname]) then: @{
|
|
126
|
+
each: @{ call: [new_val, old_val] }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
private: '__kvo_slot_change__:new:old:
|
|
130
|
+
|
|
131
|
+
def __kvo_insertion__: value for_slot: slotname {
|
|
132
|
+
if: (__kvo_insertion_observers__[slotname]) then: @{
|
|
133
|
+
each: @{ call: [value] }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
def __kvo_removal__: value for_slot: slotname {
|
|
138
|
+
if: (__kvo_removal_observers__[slotname]) then: @{
|
|
139
|
+
each: @{ call: [value] }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
def __kvo_wrap_collection_methods__: collection for_slot: slotname {
|
|
144
|
+
object = self
|
|
145
|
+
collection metaclass tap: |c| {
|
|
146
|
+
try {
|
|
147
|
+
c alias_method: '__insert__: for: '<<
|
|
148
|
+
c define_method: '<< with: |val| {
|
|
149
|
+
__insert__: val
|
|
150
|
+
object __kvo_insertion__: val for_slot: slotname
|
|
151
|
+
}
|
|
152
|
+
} catch {}
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
c alias_method: '__remove__: for: 'remove:
|
|
156
|
+
c define_method: 'remove: with: |val| {
|
|
157
|
+
__remove__: val
|
|
158
|
+
object __kvo_removal__: val for_slot: slotname
|
|
159
|
+
}
|
|
160
|
+
} catch {}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
c alias_method: '__remove_at__: for: 'remove_at:
|
|
164
|
+
c define_method: 'remove_at: with: |index| {
|
|
165
|
+
obj = __remove_at__: index
|
|
166
|
+
object __kvo_removal__: obj for_slot: slotname
|
|
167
|
+
}
|
|
168
|
+
} catch {}
|
|
169
|
+
}
|
|
170
|
+
collection set_slot: '__kvo_wrappers_defined?__ value: true
|
|
171
|
+
}
|
|
172
|
+
private: '__kvo_wrap_collection_methods__:for_slot:
|
|
173
|
+
}
|