rmx-firebase 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e1d4fa77899f71b7fcf8ab6349245bb008b65f4d
|
4
|
+
data.tar.gz: 2076ec5160ae4c60a402b94db77714b9796ff4cc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 187ab0df77bf3026ff184ddf87c42b77265bc8f1592ff2f214d5412386800db64310f0a5c5a05875213e4456911b08bf0b23bde02d971024fecc354ae95ef4d0
|
7
|
+
data.tar.gz: ff8bfed7d390c6d6196d291ea86ad370f123ffe7b1b6ec79e6162d02b632bcd8d2fb5b2d67eb549e8d3e0f4bdd45d85fff1e65e6d091b4c198a7018277c09454
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Joe Noon
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
if ENV['rubymotion']
|
3
|
+
$:.unshift('/Library/RubyMotion/lib')
|
4
|
+
if ENV.fetch('platform', 'ios') == 'ios'
|
5
|
+
require 'motion/project/template/ios'
|
6
|
+
elsif ENV['platform'] == 'osx'
|
7
|
+
require 'motion/project/template/osx'
|
8
|
+
else
|
9
|
+
raise "Unsupported platform #{ENV['platform']}"
|
10
|
+
end
|
11
|
+
require 'bundler'
|
12
|
+
Bundler.require
|
13
|
+
|
14
|
+
Motion::Project::App.setup do |app|
|
15
|
+
# Use `rake config' to see complete project settings.
|
16
|
+
app.name = 'rmx-firebase'
|
17
|
+
end
|
18
|
+
end
|
data/app/app_delegate.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
class FQuery
|
2
|
+
|
3
|
+
EVENT_TYPES_MAP = {
|
4
|
+
:child_added => FEventTypeChildAdded,
|
5
|
+
:added => FEventTypeChildAdded,
|
6
|
+
:child_moved => FEventTypeChildMoved,
|
7
|
+
:moved => FEventTypeChildMoved,
|
8
|
+
:child_changed => FEventTypeChildChanged,
|
9
|
+
:changed => FEventTypeChildChanged,
|
10
|
+
:child_removed => FEventTypeChildRemoved,
|
11
|
+
:removed => FEventTypeChildRemoved,
|
12
|
+
:value => FEventTypeValue
|
13
|
+
}
|
14
|
+
|
15
|
+
def rmx_object_desc
|
16
|
+
"#{super}:#{description}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def limited(limit)
|
20
|
+
queryLimitedToNumberOfChildren(limit)
|
21
|
+
end
|
22
|
+
|
23
|
+
def starting_at(priority)
|
24
|
+
queryStartingAtPriority(priority)
|
25
|
+
end
|
26
|
+
|
27
|
+
def ending_at(priority)
|
28
|
+
queryEndingAtPriority(priority)
|
29
|
+
end
|
30
|
+
|
31
|
+
def on(_event_type, options={}, &and_then)
|
32
|
+
and_then ||= options[:completion]
|
33
|
+
raise "event handler is required" unless and_then
|
34
|
+
raise "event handler must accept one or two arguments" unless and_then.arity == 1 || and_then.arity == 2
|
35
|
+
completion = RMX.safe_block(and_then)
|
36
|
+
|
37
|
+
event_type = EVENT_TYPES_MAP[_event_type]
|
38
|
+
raise "event handler is unknown: #{_event_type.inspect}" unless event_type
|
39
|
+
|
40
|
+
_disconnect_block = options[:disconnect]
|
41
|
+
raise ":disconnect handler must not accept any arguments" if _disconnect_block && _disconnect_block.arity != 1
|
42
|
+
disconnect_block = nil
|
43
|
+
if _disconnect_block
|
44
|
+
disconnect_inner_block = RMX.safe_block(_disconnect_block)
|
45
|
+
disconnect_block = lambda do |err|
|
46
|
+
disconnect_inner_block.call(err)
|
47
|
+
end.weak!
|
48
|
+
end
|
49
|
+
handler = nil
|
50
|
+
if and_then.arity == 1
|
51
|
+
inner_block = RMX.safe_block(lambda do |snap|
|
52
|
+
datasnap = RMXFirebaseDataSnapshot.new(snap)
|
53
|
+
completion.call(datasnap)
|
54
|
+
end)
|
55
|
+
wrapped_block = lambda do |snap|
|
56
|
+
inner_block.call(snap)
|
57
|
+
end.weak!
|
58
|
+
if disconnect_block
|
59
|
+
if options[:once]
|
60
|
+
RMXFirebase::INTERNAL_QUEUE.sync do
|
61
|
+
handler = observeSingleEventOfType(event_type, withBlock:wrapped_block, withCancelBlock:disconnect_block)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
RMXFirebase::INTERNAL_QUEUE.sync do
|
65
|
+
handler = observeEventType(event_type, withBlock:wrapped_block, withCancelBlock:disconnect_block)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
else
|
69
|
+
if options[:once]
|
70
|
+
RMXFirebase::INTERNAL_QUEUE.sync do
|
71
|
+
handler = observeSingleEventOfType(event_type, withBlock:wrapped_block)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
RMXFirebase::INTERNAL_QUEUE.sync do
|
75
|
+
handler = observeEventType(event_type, withBlock:wrapped_block)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
else
|
80
|
+
inner_block = RMX.safe_block(lambda do |snap, prev|
|
81
|
+
datasnap = RMXFirebaseDataSnapshot.new(snap)
|
82
|
+
completion.call(datasnap, prev)
|
83
|
+
end)
|
84
|
+
wrapped_block = lambda do |snap, prev|
|
85
|
+
inner_block.call(snap, prev)
|
86
|
+
end.weak!
|
87
|
+
if disconnect_block
|
88
|
+
if options[:once]
|
89
|
+
RMXFirebase::INTERNAL_QUEUE.sync do
|
90
|
+
handler = observeSingleEventOfType(event_type, andPreviousSiblingNameWithBlock:wrapped_block, withCancelBlock:disconnect_block)
|
91
|
+
end
|
92
|
+
else
|
93
|
+
RMXFirebase::INTERNAL_QUEUE.sync do
|
94
|
+
handler = observeEventType(event_type, andPreviousSiblingNameWithBlock:wrapped_block, withCancelBlock:disconnect_block)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
else
|
98
|
+
if options[:once]
|
99
|
+
RMXFirebase::INTERNAL_QUEUE.sync do
|
100
|
+
handler = observeSingleEventOfType(event_type, andPreviousSiblingNameWithBlock:wrapped_block)
|
101
|
+
end
|
102
|
+
else
|
103
|
+
RMXFirebase::INTERNAL_QUEUE.sync do
|
104
|
+
handler = observeEventType(event_type, andPreviousSiblingNameWithBlock:wrapped_block)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
unless options[:once]
|
110
|
+
@_outstanding_handlers ||= OutstandingHandlers.new(self)
|
111
|
+
@_outstanding_handlers << handler
|
112
|
+
end
|
113
|
+
handler
|
114
|
+
end
|
115
|
+
|
116
|
+
class OutstandingHandlers
|
117
|
+
|
118
|
+
RMX(self).weak_attr_accessor :scope
|
119
|
+
|
120
|
+
def initialize(_scope)
|
121
|
+
self.scope = _scope
|
122
|
+
@handlers = []
|
123
|
+
end
|
124
|
+
|
125
|
+
def <<(handler)
|
126
|
+
@handlers << handler
|
127
|
+
end
|
128
|
+
|
129
|
+
def handlers
|
130
|
+
@handlers
|
131
|
+
end
|
132
|
+
|
133
|
+
def off(handle=nil)
|
134
|
+
if s = scope
|
135
|
+
if _handle = @handlers.delete(handle)
|
136
|
+
# p s.rmx_object_desc, "remove handle", _handle
|
137
|
+
RMXFirebase::INTERNAL_QUEUE.sync do
|
138
|
+
s.removeObserverWithHandle(_handle)
|
139
|
+
end
|
140
|
+
else
|
141
|
+
_handlers = @handlers.dup
|
142
|
+
while _handlers.size > 0
|
143
|
+
_handle = _handlers.shift
|
144
|
+
off(_handle)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def dealloc
|
151
|
+
off
|
152
|
+
super
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def once(event_type, options={}, &and_then)
|
157
|
+
on(event_type, options.merge(:once => true), &and_then)
|
158
|
+
end
|
159
|
+
|
160
|
+
def _off(handle)
|
161
|
+
if @_outstanding_handlers
|
162
|
+
@_outstanding_handlers.off(handle)
|
163
|
+
end
|
164
|
+
self
|
165
|
+
end
|
166
|
+
|
167
|
+
def off(handle)
|
168
|
+
RMXFirebase::QUEUE.barrier_async do
|
169
|
+
_off(handle)
|
170
|
+
end
|
171
|
+
self
|
172
|
+
end
|
173
|
+
|
174
|
+
def description
|
175
|
+
AFHTTPRequestSerializer.serializer.requestWithMethod("GET", URLString: "https://#{repo.repoInfo.host}#{path.toString}", parameters:queryParams.queryObject).URL.absoluteString
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
class Firebase
|
2
|
+
|
3
|
+
DEBUG_SETVALUE = RMX::Env['rmx_firebase_debug_setvalue'] == '1'
|
4
|
+
|
5
|
+
def rmx_object_desc
|
6
|
+
"#{super}:#{description}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](*names)
|
10
|
+
if names.length == 0
|
11
|
+
childByAutoId
|
12
|
+
else
|
13
|
+
childByAppendingPath(names.join('/'))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def limited(limit)
|
18
|
+
queryLimitedToNumberOfChildren(limit)
|
19
|
+
end
|
20
|
+
|
21
|
+
def starting_at(priority)
|
22
|
+
queryStartingAtPriority(priority)
|
23
|
+
end
|
24
|
+
|
25
|
+
def ending_at(priority)
|
26
|
+
queryEndingAtPriority(priority)
|
27
|
+
end
|
28
|
+
|
29
|
+
def rmx_arrayToHash(array)
|
30
|
+
hash = {}
|
31
|
+
array.each_with_index do |item, i|
|
32
|
+
hash[i.to_s] = item
|
33
|
+
end
|
34
|
+
hash
|
35
|
+
end
|
36
|
+
|
37
|
+
def rmx_castValue(value, key=nil)
|
38
|
+
if value == true
|
39
|
+
elsif value == false
|
40
|
+
elsif value.is_a?(NSString)
|
41
|
+
elsif value.is_a?(NSNumber)
|
42
|
+
elsif value.nil?
|
43
|
+
p "FIREBASE_BAD_TYPE FIXED NIL: #{File.join(*[ description, key ].compact.map(&:to_s))}", "!"
|
44
|
+
value = {}
|
45
|
+
elsif value.is_a?(Array)
|
46
|
+
value = rmx_arrayToHash(value)
|
47
|
+
p "FIREBASE_BAD_TYPE FIXED ARRAY: #{File.join(*[ description, key ].compact.map(&:to_s))}: #{value.inspect} (type: #{value.className.to_s})", "!"
|
48
|
+
elsif value.is_a?(NSDictionary)
|
49
|
+
_value = value.dup
|
50
|
+
new_value = {}
|
51
|
+
_value.keys.each do |k|
|
52
|
+
new_value[k.to_s] = rmx_castValue(_value[k], k)
|
53
|
+
end
|
54
|
+
value = new_value
|
55
|
+
else
|
56
|
+
p "FIREBASE_BAD_TYPE FATAL: #{File.join(*[ description, key ].compact.map(&:to_s))}: #{value.inspect} (type: #{value.className.to_s})", "!"
|
57
|
+
end
|
58
|
+
# always return the value, corrected or not
|
59
|
+
value
|
60
|
+
end
|
61
|
+
|
62
|
+
def rmx_setValue(value, andPriority:priority)
|
63
|
+
# value = rmx_castValue(value)
|
64
|
+
setValue(value, andPriority:priority)
|
65
|
+
end
|
66
|
+
def rmx_setValue(value)
|
67
|
+
# value = rmx_castValue(value)
|
68
|
+
setValue(value)
|
69
|
+
end
|
70
|
+
def rmx_onDisconnectSetValue(value)
|
71
|
+
value = rmx_castValue(value)
|
72
|
+
onDisconnectSetValue(value)
|
73
|
+
end
|
74
|
+
|
75
|
+
alias_method 'orig_setValue', 'setValue'
|
76
|
+
alias_method 'orig_setValueAndPriority', 'setValue:andPriority'
|
77
|
+
|
78
|
+
def setValue(value, andPriority:priority)
|
79
|
+
if DEBUG_SETVALUE
|
80
|
+
p description, "setValue:andPriority", value, priority
|
81
|
+
end
|
82
|
+
value = rmx_castValue(value)
|
83
|
+
orig_setValueAndPriority(value, priority)
|
84
|
+
end
|
85
|
+
def setValue(value)
|
86
|
+
if DEBUG_SETVALUE
|
87
|
+
p description, "setValue:", value
|
88
|
+
end
|
89
|
+
value = rmx_castValue(value)
|
90
|
+
orig_setValue(value)
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RMXFirebase
|
2
|
+
|
3
|
+
QUEUE = Dispatch::Queue.new("RMXFirebase")
|
4
|
+
INTERNAL_QUEUE = Dispatch::Queue.new("RMXFirebase.internal")
|
5
|
+
|
6
|
+
DEBUG_IDENTITY_MAP = RMX::Env['rmx_firebase_debug_identity_map'] == '1'
|
7
|
+
DEBUG_MODEL_DEALLOC = RMX::Env['rmx_firebase_debug_model_dealloc'] == '1'
|
8
|
+
|
9
|
+
def self.queue_for(queueish)
|
10
|
+
if queueish == :main || queueish.nil?
|
11
|
+
Dispatch::Queue.main
|
12
|
+
elsif queueish == :async
|
13
|
+
QUEUE
|
14
|
+
else
|
15
|
+
queueish
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.block_on_queue(queue, *args, &block)
|
20
|
+
queue = queue_for(queue)
|
21
|
+
if queue == Dispatch::Queue.main && NSThread.currentThread.isMainThread
|
22
|
+
block.call(*args)
|
23
|
+
else
|
24
|
+
queue.barrier_async do
|
25
|
+
block.call(*args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
Firebase.setDispatchQueue(RMXFirebase::INTERNAL_QUEUE.dispatch_object)
|
@@ -0,0 +1,102 @@
|
|
1
|
+
class RMXFirebaseBatch
|
2
|
+
|
3
|
+
include RMXCommonMethods
|
4
|
+
|
5
|
+
def self.new(*models)
|
6
|
+
_models = models.dup
|
7
|
+
_models = _models.flatten.compact
|
8
|
+
x = super()
|
9
|
+
RMXFirebase::QUEUE.barrier_async do
|
10
|
+
x.setup_models(_models)
|
11
|
+
end
|
12
|
+
x
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@models = []
|
17
|
+
@ready_models = []
|
18
|
+
@complete_blocks = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def setup_models(the_models)
|
22
|
+
RMX(self).require_queue!(RMXFirebase::QUEUE, __FILE__, __LINE__) if RMX::DEBUG_QUEUES
|
23
|
+
@ready = false
|
24
|
+
@models = the_models.dup
|
25
|
+
@ready_count = 0
|
26
|
+
@pending_count = @models.size
|
27
|
+
if @models.any?
|
28
|
+
_models = @models.dup
|
29
|
+
_pairs = []
|
30
|
+
i = 0
|
31
|
+
while _models.size > 0
|
32
|
+
ii = i # strange: proc doesnt seem to close over i correctly
|
33
|
+
model = _models.shift
|
34
|
+
blk = proc do
|
35
|
+
RMX(self).require_queue!(RMXFirebase::QUEUE, __FILE__, __LINE__) if RMX::DEBUG_QUEUES
|
36
|
+
# p "COMPLETE!", ii, model
|
37
|
+
@complete_blocks.delete(model)
|
38
|
+
@ready_models[ii] = model
|
39
|
+
@ready_count += 1
|
40
|
+
@pending_count -= 1
|
41
|
+
if @pending_count == 0
|
42
|
+
ready!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
@complete_blocks[model] = blk
|
46
|
+
_pairs << [ model, blk ]
|
47
|
+
i += 1
|
48
|
+
end
|
49
|
+
RMXFirebase::QUEUE.barrier_async do
|
50
|
+
while pair = _pairs.shift
|
51
|
+
pair[0].once(RMXFirebase::QUEUE, &pair[1])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
else
|
55
|
+
RMXFirebase::QUEUE.barrier_async do
|
56
|
+
ready!
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def ready!
|
62
|
+
RMXFirebase::QUEUE.barrier_async do
|
63
|
+
@ready = true
|
64
|
+
# p "models", models.dup
|
65
|
+
# p "ready_models", ready_models.dup
|
66
|
+
RMX(self).trigger(:ready, @ready_models.dup)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def cancel!
|
71
|
+
RMXFirebase::QUEUE.barrier_async do
|
72
|
+
models_outstanding = @complete_blocks.keys.dup
|
73
|
+
while models_outstanding.size > 0
|
74
|
+
model = models_outstanding.shift
|
75
|
+
if blk = @complete_blocks[model]
|
76
|
+
@complete_blocks.delete(model)
|
77
|
+
model.cancel_block(&blk)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def ready?
|
84
|
+
!!@ready
|
85
|
+
end
|
86
|
+
|
87
|
+
def once(queue=nil, &block)
|
88
|
+
RMXFirebase::QUEUE.barrier_async do
|
89
|
+
if ready?
|
90
|
+
RMXFirebase.block_on_queue(queue, @ready_models.dup, &block)
|
91
|
+
else
|
92
|
+
RMX(self).once(:ready, :strong => true, :queue => queue, &block)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
def cancel_block(&block)
|
99
|
+
RMX(self).off(:ready, &block)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|