rmx-firebase 0.0.2
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.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +11 -0
- data/Rakefile +18 -0
- data/app/app_delegate.rb +5 -0
- data/lib/motion/FQuery+RMXFirebase.rb +178 -0
- data/lib/motion/Firebase+RMXFirebase.rb +93 -0
- data/lib/motion/RMXFirebase.rb +31 -0
- data/lib/motion/RMXFirebaseBatch.rb +102 -0
- data/lib/motion/RMXFirebaseCollection.rb +372 -0
- data/lib/motion/RMXFirebaseCoordinator.rb +109 -0
- data/lib/motion/RMXFirebaseDataSnapshot.rb +53 -0
- data/lib/motion/RMXFirebaseHandleModel.rb +10 -0
- data/lib/motion/RMXFirebaseListener.rb +76 -0
- data/lib/motion/RMXFirebaseModel.rb +202 -0
- data/lib/motion/RMXFirebaseTableViewCell.rb +43 -0
- data/lib/motion/RMXFirebaseView.rb +38 -0
- data/lib/motion/RMXFirebaseViewController.rb +46 -0
- data/lib/rmx-firebase/version.rb +3 -0
- data/lib/rmx-firebase.rb +26 -0
- data/resources/Default-568h@2x.png +0 -0
- data/rmx-firebase.gemspec +20 -0
- data/spec/collection_spec.rb +146 -0
- metadata +82 -0
@@ -0,0 +1,372 @@
|
|
1
|
+
class RMXFirebaseCollection
|
2
|
+
|
3
|
+
include RMXCommonMethods
|
4
|
+
|
5
|
+
attr_reader :ref, :snaps
|
6
|
+
|
7
|
+
# public
|
8
|
+
def ready?
|
9
|
+
!!@ready
|
10
|
+
end
|
11
|
+
|
12
|
+
# public
|
13
|
+
def cancelled?
|
14
|
+
!!@cancelled
|
15
|
+
end
|
16
|
+
|
17
|
+
# overridable
|
18
|
+
def transform(snap)
|
19
|
+
if block = @transform_block
|
20
|
+
block.call(snap)
|
21
|
+
else
|
22
|
+
snap
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def transformed_async(&block)
|
27
|
+
transformed(:async, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
# public, completes with ready transformations
|
31
|
+
def transformed(queue=nil, &block)
|
32
|
+
items = @transformations.dup
|
33
|
+
if (snap = items.first) && snap.is_a?(RMXFirebaseModel)
|
34
|
+
RMXFirebaseBatch.new(items).once(queue, &block)
|
35
|
+
else
|
36
|
+
RMXFirebase.block_on_queue(queue, items, &block)
|
37
|
+
end
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def once_async(&block)
|
42
|
+
once(:async, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
# completes with `self` once, when the collection is ready.
|
46
|
+
# retains `self` and the sender until complete
|
47
|
+
def once(queue=nil, &block)
|
48
|
+
RMXFirebase::QUEUE.barrier_async do
|
49
|
+
if ready?
|
50
|
+
RMXFirebase.block_on_queue(queue, self, &block)
|
51
|
+
else
|
52
|
+
RMX(self).once(:ready, :strong => true, :queue => queue, &block)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def always_async(&block)
|
59
|
+
always(:async, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
# completes with `self` immediately if ready, and every time the collection :ready fires.
|
63
|
+
# does not retain `self` or the sender.
|
64
|
+
# returns an "unbinder" that can be called to stop listening.
|
65
|
+
def always(queue=nil, &block)
|
66
|
+
return false if cancelled?
|
67
|
+
if ready?
|
68
|
+
RMXFirebase.block_on_queue(queue, self, &block)
|
69
|
+
end
|
70
|
+
RMX(self).on(:ready, :queue => queue, &block)
|
71
|
+
end
|
72
|
+
|
73
|
+
# completes with `self` every time the collection :changed fires.
|
74
|
+
# does not retain `self` or the sender.
|
75
|
+
# returns an "unbinder" that can be called to stop listening.
|
76
|
+
def changed(queue=nil, &block)
|
77
|
+
return false if cancelled?
|
78
|
+
RMX(self).on(:changed, :queue => queue, &block)
|
79
|
+
end
|
80
|
+
|
81
|
+
# completes with `self`, `snap`, `prev` every time the collection :added fires.
|
82
|
+
# does not retain `self` or the sender.
|
83
|
+
# returns an "unbinder" that can be called to stop listening.
|
84
|
+
def added(queue=nil, &block)
|
85
|
+
return false if cancelled?
|
86
|
+
RMX(self).on(:added, :queue => queue, &block)
|
87
|
+
end
|
88
|
+
|
89
|
+
# completes with `self`, `snap` every time the collection :removed fires.
|
90
|
+
# does not retain `self` or the sender.
|
91
|
+
# returns an "unbinder" that can be called to stop listening.
|
92
|
+
def removed(queue=nil, &block)
|
93
|
+
return false if cancelled?
|
94
|
+
RMX(self).on(:removed, :queue => queue, &block)
|
95
|
+
end
|
96
|
+
|
97
|
+
# completes with `self`, `snap`, `prev` every time the collection :moved fires.
|
98
|
+
# does not retain `self` or the sender.
|
99
|
+
# returns an "unbinder" that can be called to stop listening.
|
100
|
+
def moved(queue=nil, &block)
|
101
|
+
return false if cancelled?
|
102
|
+
RMX(self).on(:moved, :queue => queue, &block)
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.new(ref)
|
106
|
+
x = super()
|
107
|
+
RMXFirebase::QUEUE.barrier_async do
|
108
|
+
x.setup_ref(ref)
|
109
|
+
end
|
110
|
+
x
|
111
|
+
end
|
112
|
+
|
113
|
+
# internal
|
114
|
+
def initialize
|
115
|
+
@snaps_by_name = {}
|
116
|
+
@snaps = []
|
117
|
+
@transformations_table = {}
|
118
|
+
@transformations = []
|
119
|
+
end
|
120
|
+
|
121
|
+
# internal
|
122
|
+
def setup_ref(_ref)
|
123
|
+
RMX(self).require_queue!(RMXFirebase::QUEUE, __FILE__, __LINE__) if RMX::DEBUG_QUEUES
|
124
|
+
_clear_current_ref!
|
125
|
+
@ref = _ref
|
126
|
+
@ready = false
|
127
|
+
@cancelled = false
|
128
|
+
cancel_block = lambda do |err|
|
129
|
+
@cancelled = err
|
130
|
+
cancelled!
|
131
|
+
end
|
132
|
+
@added_handler = @ref.on(:added) do |snap, prev|
|
133
|
+
# p "NORMAL ", snap.name, prev
|
134
|
+
RMXFirebase::QUEUE.barrier_async do
|
135
|
+
# p "BARRIER", snap.name, prev
|
136
|
+
add(snap, prev)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
@removed_handler = @ref.on(:removed) do |snap|
|
140
|
+
RMXFirebase::QUEUE.barrier_async do
|
141
|
+
remove(snap)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
@moved_handler = @ref.on(:moved) do |snap, prev|
|
145
|
+
RMXFirebase::QUEUE.barrier_async do
|
146
|
+
add(snap, prev)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
@value_handler = @ref.once(:value, { :disconnect => cancel_block }) do |collection|
|
150
|
+
@value_handler = nil
|
151
|
+
RMXFirebase::QUEUE.barrier_async do
|
152
|
+
ready!
|
153
|
+
end
|
154
|
+
end
|
155
|
+
RMX(self).on(:cancelled, :exclusive => [ :ready, :finished, :changed, :added, :removed, :moved ], :queue => :async)
|
156
|
+
end
|
157
|
+
|
158
|
+
def refresh_order!
|
159
|
+
RMXFirebase::QUEUE.barrier_async do
|
160
|
+
next unless @ref
|
161
|
+
if @added_handler
|
162
|
+
@ref.off(@added_handler)
|
163
|
+
@added_handler = nil
|
164
|
+
end
|
165
|
+
@added_handler = @ref.on(:added) do |snap, prev|
|
166
|
+
# p "NORMAL ", snap.name, prev
|
167
|
+
RMXFirebase::QUEUE.barrier_async do
|
168
|
+
# p "BARRIER", snap.name, prev
|
169
|
+
add(snap, prev)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# mess up the order on purpose
|
176
|
+
def _test_scatter!
|
177
|
+
RMXFirebase::QUEUE.barrier_async do
|
178
|
+
_snaps = @snaps.dup
|
179
|
+
p "before scatter", @snaps.map(&:name)
|
180
|
+
p "before scatter snaps_by_name", @snaps_by_name
|
181
|
+
|
182
|
+
_snaps.each do |snap|
|
183
|
+
others = _snaps - [ snap ]
|
184
|
+
random = others.sample
|
185
|
+
add(snap, random.name)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def rmx_dealloc
|
191
|
+
_clear_current_ref!
|
192
|
+
end
|
193
|
+
|
194
|
+
def _clear_current_ref!
|
195
|
+
if @ref
|
196
|
+
if @added_handler
|
197
|
+
@ref.off(@added_handler)
|
198
|
+
@added_handler = nil
|
199
|
+
end
|
200
|
+
if @removed_handler
|
201
|
+
@ref.off(@removed_handler)
|
202
|
+
@removed_handler = nil
|
203
|
+
end
|
204
|
+
if @moved_handler
|
205
|
+
@ref.off(@moved_handler)
|
206
|
+
@moved_handler = nil
|
207
|
+
end
|
208
|
+
if @value_handler
|
209
|
+
@ref.off(@value_handler)
|
210
|
+
@value_handler = nil
|
211
|
+
end
|
212
|
+
@ref = nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
# internal
|
218
|
+
def ready!
|
219
|
+
RMXFirebase::QUEUE.barrier_async do
|
220
|
+
@ready = true
|
221
|
+
RMX(self).trigger(:ready, self)
|
222
|
+
RMX(self).trigger(:changed, self)
|
223
|
+
RMX(self).trigger(:finished, self)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# internal
|
228
|
+
def cancelled!
|
229
|
+
RMXFirebase::QUEUE.barrier_async do
|
230
|
+
@cancelled = true
|
231
|
+
RMX(self).trigger(:cancelled, self)
|
232
|
+
RMX(self).trigger(:finished, self)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# internal, allows the user to pass a block for transformations instead of subclassing
|
237
|
+
# and overriding #transform, for one-off cases
|
238
|
+
def transform=(block)
|
239
|
+
if block
|
240
|
+
@transform_block = RMX.safe_block(block)
|
241
|
+
else
|
242
|
+
@transform_block = nil
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# internal
|
247
|
+
def store_transform(snap)
|
248
|
+
RMX(self).require_queue!(RMXFirebase::QUEUE, __FILE__, __LINE__) if RMX::DEBUG_QUEUES
|
249
|
+
@transformations_table[snap] ||= transform(snap)
|
250
|
+
end
|
251
|
+
|
252
|
+
def _log_snap_names
|
253
|
+
RMXFirebase::QUEUE.barrier_async do
|
254
|
+
puts "snaps_by_name:"
|
255
|
+
_log_hash(@snaps_by_name)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
|
260
|
+
def _log_hash(hash)
|
261
|
+
hash.to_a.sort_by { |x| x[1] }.each do |pair|
|
262
|
+
puts pair.inspect
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# internal
|
267
|
+
def add(snap, prev)
|
268
|
+
RMX(self).require_queue!(RMXFirebase::QUEUE, __FILE__, __LINE__) if RMX::DEBUG_QUEUES
|
269
|
+
moved = false
|
270
|
+
|
271
|
+
if current_index = @snaps_by_name[snap.name]
|
272
|
+
if current_index == 0 && prev.nil?
|
273
|
+
return
|
274
|
+
elsif current_index > 0 && prev && @snaps_by_name[prev] == current_index - 1
|
275
|
+
return
|
276
|
+
end
|
277
|
+
moved = true
|
278
|
+
@snaps.delete_at(current_index)
|
279
|
+
@transformations.delete_at(current_index)
|
280
|
+
if was_index = @snaps_by_name.delete(snap.name)
|
281
|
+
@snaps_by_name.keys.each do |k|
|
282
|
+
v = @snaps_by_name[k]
|
283
|
+
if v > was_index
|
284
|
+
@snaps_by_name[k] -= 1
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
# raise if snaps_by_name.values.uniq.size != snaps_by_name.values.size
|
289
|
+
end
|
290
|
+
# raise if snaps_by_name.values.uniq.size != snaps_by_name.values.size
|
291
|
+
if prev && (index = @snaps_by_name[prev])
|
292
|
+
new_index = index + 1
|
293
|
+
@snaps.insert(new_index, snap)
|
294
|
+
@transformations.insert(new_index, store_transform(snap))
|
295
|
+
@snaps_by_name.keys.each do |k|
|
296
|
+
v = @snaps_by_name[k]
|
297
|
+
if v >= new_index
|
298
|
+
@snaps_by_name[k] += 1
|
299
|
+
end
|
300
|
+
end
|
301
|
+
@snaps_by_name[snap.name] = new_index
|
302
|
+
# raise if snaps_by_name.values.uniq.size != snaps_by_name.values.size
|
303
|
+
else
|
304
|
+
@snaps.unshift(snap)
|
305
|
+
@transformations.unshift(store_transform(snap))
|
306
|
+
@snaps_by_name.keys.each do |k|
|
307
|
+
v = @snaps_by_name[k]
|
308
|
+
@snaps_by_name[k] += 1
|
309
|
+
end
|
310
|
+
@snaps_by_name[snap.name] = 0
|
311
|
+
# raise if snaps_by_name.values.uniq.size != snaps_by_name.values.size
|
312
|
+
end
|
313
|
+
if moved
|
314
|
+
RMX(self).trigger(:moved, self, snap, prev)
|
315
|
+
else
|
316
|
+
RMX(self).trigger(:added, self, snap, prev)
|
317
|
+
end
|
318
|
+
RMX(self).trigger(:changed, self)
|
319
|
+
if ready?
|
320
|
+
RMX(self).trigger(:ready, self)
|
321
|
+
RMX(self).trigger(:finished, self)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# internal
|
326
|
+
def remove(snap)
|
327
|
+
if current_index = @snaps_by_name[snap.name]
|
328
|
+
@snaps.delete_at(current_index)
|
329
|
+
@transformations.delete_at(current_index)
|
330
|
+
@snaps_by_name.keys.each do |k|
|
331
|
+
v = @snaps_by_name[k]
|
332
|
+
if v > current_index
|
333
|
+
@snaps_by_name[k] -= 1
|
334
|
+
end
|
335
|
+
end
|
336
|
+
RMX(self).trigger(:removed, self, snap)
|
337
|
+
RMX(self).trigger(:changed, self)
|
338
|
+
if ready?
|
339
|
+
RMX(self).trigger(:ready, self)
|
340
|
+
RMX(self).trigger(:finished, self)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
# this is the method you should call
|
346
|
+
def self.get(ref)
|
347
|
+
if ref && existing = identity_map[[ className, ref.description ]]
|
348
|
+
if RMXFirebase::DEBUG_IDENTITY_MAP
|
349
|
+
p "HIT!", className, ref.description, existing.retainCount
|
350
|
+
end
|
351
|
+
return existing
|
352
|
+
else
|
353
|
+
if RMXFirebase::DEBUG_IDENTITY_MAP
|
354
|
+
p "MISS!", className, ref.description
|
355
|
+
end
|
356
|
+
res = new(ref)
|
357
|
+
if ref
|
358
|
+
identity_map[[ className, ref.description ]] = res
|
359
|
+
end
|
360
|
+
res
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
Dispatch.once do
|
365
|
+
@@identity_map = RMXSynchronizedStrongToWeakHash.new
|
366
|
+
end
|
367
|
+
|
368
|
+
def self.identity_map
|
369
|
+
@@identity_map
|
370
|
+
end
|
371
|
+
|
372
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
class RMXFirebaseCoordinator
|
2
|
+
|
3
|
+
include RMXCommonMethods
|
4
|
+
|
5
|
+
attr_reader :watches
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@watches = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def clear!
|
12
|
+
RMXFirebase::QUEUE.barrier_async do
|
13
|
+
@cancelled = false
|
14
|
+
@ready = false
|
15
|
+
keys = watches.keys.dup
|
16
|
+
while keys.size > 0
|
17
|
+
name = keys.shift
|
18
|
+
watch = watches[name]
|
19
|
+
watch.stop!
|
20
|
+
end
|
21
|
+
watches.clear
|
22
|
+
end
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def ready?
|
27
|
+
!!@ready
|
28
|
+
end
|
29
|
+
|
30
|
+
def cancelled?
|
31
|
+
!!@cancelled
|
32
|
+
end
|
33
|
+
|
34
|
+
def ready!
|
35
|
+
RMXFirebase::QUEUE.barrier_async do
|
36
|
+
@ready = true
|
37
|
+
RMX(self).trigger(:ready, self)
|
38
|
+
RMX(self).trigger(:finished, self)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def cancelled!
|
43
|
+
RMXFirebase::QUEUE.barrier_async do
|
44
|
+
@cancelled = true
|
45
|
+
RMX(self).trigger(:cancelled, self)
|
46
|
+
RMX(self).trigger(:finished, self)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def stub(name)
|
51
|
+
RMX(self).require_queue!(RMXFirebase::QUEUE, __FILE__, __LINE__) if RMX::DEBUG_QUEUES
|
52
|
+
watches[name] ||= RMXFirebaseListener.new
|
53
|
+
end
|
54
|
+
|
55
|
+
def watch(name, ref, opts={}, &block)
|
56
|
+
RMX(self).require_queue!(RMXFirebase::QUEUE, __FILE__, __LINE__) if RMX::DEBUG_QUEUES
|
57
|
+
data = RMXFirebaseListener.new
|
58
|
+
data.ref = ref
|
59
|
+
if opts[:required]
|
60
|
+
data.value_required = true
|
61
|
+
end
|
62
|
+
unless block.nil?
|
63
|
+
data.callback = block.weak!
|
64
|
+
data.callback_owner = block.owner
|
65
|
+
end
|
66
|
+
if current = watches[name]
|
67
|
+
if current == data || current.description == data.description
|
68
|
+
return
|
69
|
+
end
|
70
|
+
current.stop!
|
71
|
+
RMX(current).off(:finished, self)
|
72
|
+
end
|
73
|
+
watches[name] = data
|
74
|
+
RMX(data).on(:finished, :queue => RMXFirebase::QUEUE) do
|
75
|
+
RMX(self).require_queue!(RMXFirebase::QUEUE, __FILE__, __LINE__) if RMX::DEBUG_QUEUES
|
76
|
+
readies = 0
|
77
|
+
cancelled = 0
|
78
|
+
size = watches.size
|
79
|
+
watches.each_pair do |k, v|
|
80
|
+
if v.ready?
|
81
|
+
readies += 1
|
82
|
+
elsif v.cancelled?
|
83
|
+
cancelled += 1
|
84
|
+
break
|
85
|
+
end
|
86
|
+
end
|
87
|
+
if cancelled > 0
|
88
|
+
cancelled!
|
89
|
+
elsif readies == size
|
90
|
+
ready!
|
91
|
+
end
|
92
|
+
end
|
93
|
+
data.start!
|
94
|
+
data
|
95
|
+
end
|
96
|
+
|
97
|
+
def attr(keypath)
|
98
|
+
valueForKeyPath(keypath)
|
99
|
+
end
|
100
|
+
|
101
|
+
def valueForKey(key)
|
102
|
+
watches[key]
|
103
|
+
end
|
104
|
+
|
105
|
+
def valueForUndefinedKey(key)
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class RMXFirebaseDataSnapshot
|
2
|
+
|
3
|
+
include RMXCommonMethods
|
4
|
+
|
5
|
+
attr_accessor :snap
|
6
|
+
|
7
|
+
def initialize(snap)
|
8
|
+
@snap = snap
|
9
|
+
end
|
10
|
+
|
11
|
+
def hasValue?
|
12
|
+
!value.nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
def attr(keypath)
|
16
|
+
valueForKeyPath(keypath)
|
17
|
+
end
|
18
|
+
|
19
|
+
def valueForKey(key)
|
20
|
+
if v = value
|
21
|
+
v[key]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def valueForUndefinedKey(key)
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def value
|
30
|
+
snap.value
|
31
|
+
end
|
32
|
+
|
33
|
+
def ref
|
34
|
+
snap.ref
|
35
|
+
end
|
36
|
+
|
37
|
+
def name
|
38
|
+
snap.name
|
39
|
+
end
|
40
|
+
|
41
|
+
def priority
|
42
|
+
snap.priority
|
43
|
+
end
|
44
|
+
|
45
|
+
def count
|
46
|
+
snap.childrenCount
|
47
|
+
end
|
48
|
+
|
49
|
+
def children
|
50
|
+
snap.children.each.map { |x| RMXFirebaseDataSnapshot.new(x) }
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
class RMXFirebaseListener
|
2
|
+
|
3
|
+
include RMXCommonMethods
|
4
|
+
|
5
|
+
attr_accessor :snapshot, :ref, :callback, :handle, :value_required
|
6
|
+
RMX(self).weak_attr_accessor :callback_owner
|
7
|
+
|
8
|
+
def rmx_dealloc
|
9
|
+
if ref && handle
|
10
|
+
ref.off(handle)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def ready?
|
15
|
+
!!@ready
|
16
|
+
end
|
17
|
+
|
18
|
+
def cancelled?
|
19
|
+
!!@cancelled
|
20
|
+
end
|
21
|
+
|
22
|
+
def start!
|
23
|
+
RMX(self).require_queue!(RMXFirebase::QUEUE, __FILE__, __LINE__) if RMX::DEBUG_QUEUES
|
24
|
+
cancel_block = lambda do |err|
|
25
|
+
@cancelled = err
|
26
|
+
RMX(self).trigger(:cancelled, self)
|
27
|
+
RMX(self).trigger(:finished, self)
|
28
|
+
end
|
29
|
+
@handle = ref.on(:value, { :disconnect => cancel_block }) do |snap|
|
30
|
+
RMXFirebase::QUEUE.barrier_async do
|
31
|
+
@snapshot = snap
|
32
|
+
if value_required && !snap.hasValue?
|
33
|
+
cancel_block.call(NSError.errorWithDomain("requirement failure", code:0, userInfo:{
|
34
|
+
:error => "requirement_failure"
|
35
|
+
}))
|
36
|
+
else
|
37
|
+
callback.call(snap) if callback && callback_owner
|
38
|
+
@ready = true
|
39
|
+
RMX(self).trigger(:ready, self)
|
40
|
+
RMX(self).trigger(:finished, self)
|
41
|
+
# p "ready__"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def stop!
|
48
|
+
RMX(self).require_queue!(RMXFirebase::QUEUE, __FILE__, __LINE__) if RMX::DEBUG_QUEUES
|
49
|
+
@cancelled = false
|
50
|
+
@ready = false
|
51
|
+
if ref && handle
|
52
|
+
ref.off(handle)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def hasValue?
|
57
|
+
!!(snapshot && snapshot.hasValue?)
|
58
|
+
end
|
59
|
+
|
60
|
+
def toValue
|
61
|
+
snapshot && snapshot.value
|
62
|
+
end
|
63
|
+
|
64
|
+
def attr(keypath)
|
65
|
+
valueForKeyPath(keypath)
|
66
|
+
end
|
67
|
+
|
68
|
+
def valueForKey(key)
|
69
|
+
snapshot && snapshot.valueForKey(key)
|
70
|
+
end
|
71
|
+
|
72
|
+
def valueForUndefinedKey(key)
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|