rm-extensions 0.0.1 → 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.
- data/lib/motion/rm-extensions.rb +178 -20
- data/lib/rm-extensions/version.rb +1 -1
- data/rm-extensions.gemspec +1 -0
- metadata +19 -3
data/lib/motion/rm-extensions.rb
CHANGED
@@ -3,6 +3,25 @@ module RMExtensions
|
|
3
3
|
# this module is included on Object, so these methods are available from anywhere in your code.
|
4
4
|
module ObjectExtensions
|
5
5
|
|
6
|
+
def rmext_weak_attr_accessor(*attrs)
|
7
|
+
attrs.each do |attr|
|
8
|
+
define_method(attr) do
|
9
|
+
if val = instance_variable_get("@#{attr}")
|
10
|
+
val.nonretainedObjectValue
|
11
|
+
end
|
12
|
+
end
|
13
|
+
define_method("#{attr}=") do |val|
|
14
|
+
if val.nil?
|
15
|
+
instance_variable_set("@#{attr}", nil)
|
16
|
+
else
|
17
|
+
# should we do an rmext_on_dealloc on the val?
|
18
|
+
instance_variable_set("@#{attr}", NSValue.valueWithNonretainedObject(val))
|
19
|
+
end
|
20
|
+
val
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
6
25
|
def rmext_assert_main_thread!
|
7
26
|
raise "This method must be called on the main thread." unless NSThread.currentThread.isMainThread
|
8
27
|
end
|
@@ -71,6 +90,11 @@ module RMExtensions
|
|
71
90
|
end
|
72
91
|
end
|
73
92
|
|
93
|
+
# #rmext_context yields an object you can treat like an openstruct (the "context")
|
94
|
+
def rmext_context(&block)
|
95
|
+
::RMExtensions::Context.create(self, &block)
|
96
|
+
end
|
97
|
+
|
74
98
|
# #rmext_retained_context yields an object you can treat like an openstruct. you can get/set any
|
75
99
|
# property on it. the context is globally retained, until #detach! is called on the context.
|
76
100
|
# this convention should fill the gap where local variables and scope bugs currently occur in RM,
|
@@ -100,16 +124,90 @@ module RMExtensions
|
|
100
124
|
#
|
101
125
|
# experimental feature:
|
102
126
|
#
|
103
|
-
# you can call #
|
127
|
+
# you can call #begin_background! on the context, and it will check-out a background task identifier,
|
104
128
|
# and automatically end the background task when you call #detach! as normal.
|
105
129
|
def rmext_retained_context(&block)
|
106
|
-
::RMExtensions::RetainedContext.
|
130
|
+
::RMExtensions::RetainedContext.create(self, &block)
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
def rmext_observe(object, key, &block)
|
135
|
+
# p "+ rmext_observe", self, object, key
|
136
|
+
rmext_observe_passive(object, key, &block)
|
137
|
+
block.call(object.send(key)) unless block.nil?
|
138
|
+
end
|
139
|
+
|
140
|
+
def rmext_observe_passive(object, key, &block)
|
141
|
+
wop = ::RMExtensions::WeakObserverProxy.get(self)
|
142
|
+
b = -> (old_value, new_value) do
|
143
|
+
block.call(new_value) unless block.nil?
|
144
|
+
end
|
145
|
+
wop.observe(object, key, &b)
|
146
|
+
end
|
147
|
+
|
148
|
+
def rmext_unobserve(object, key)
|
149
|
+
wop = ::RMExtensions::WeakObserverProxy.get(self)
|
150
|
+
wop.unobserve(object, key)
|
151
|
+
wop.clear_empty_targets!
|
152
|
+
end
|
153
|
+
|
154
|
+
def rmext_unobserve_all
|
155
|
+
wop = ::RMExtensions::WeakObserverProxy.get(self)
|
156
|
+
wop.unobserve_all
|
157
|
+
end
|
158
|
+
|
159
|
+
def rmext_on_dealloc(&block)
|
160
|
+
internalObject = ::RMExtensions::OnDeallocInternalObject.create(&block)
|
161
|
+
internalObject.obj = self
|
162
|
+
@rmext_on_dealloc_blocks ||= {}
|
163
|
+
@rmext_on_dealloc_blocks[internalObject] = internalObject
|
164
|
+
nil
|
165
|
+
end
|
166
|
+
|
167
|
+
def rmext_cancel_on_dealloc(block)
|
168
|
+
@rmext_on_dealloc_blocks ||= {}
|
169
|
+
if internalObject = @rmext_on_dealloc_blocks[block]
|
170
|
+
internalObject.block = nil
|
171
|
+
@rmext_on_dealloc_blocks.delete(block)
|
172
|
+
end
|
173
|
+
nil
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
Object.send(:include, ::RMExtensions::ObjectExtensions)
|
180
|
+
|
181
|
+
module RMExtensions
|
182
|
+
# You don't use these classes directly.
|
183
|
+
class Context
|
184
|
+
|
185
|
+
class << self
|
186
|
+
def create(origin, &block)
|
187
|
+
x = new
|
188
|
+
block.call(x) unless block.nil?
|
189
|
+
x
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
attr_accessor :hash
|
194
|
+
|
195
|
+
def initialize
|
196
|
+
self.hash = {}
|
197
|
+
end
|
198
|
+
|
199
|
+
def method_missing(method, *args)
|
200
|
+
m = method.to_s
|
201
|
+
if m =~ /(.+)?=$/
|
202
|
+
hash[$1] = args.first
|
203
|
+
else
|
204
|
+
hash[m]
|
205
|
+
end
|
107
206
|
end
|
108
207
|
|
109
208
|
end
|
110
209
|
|
111
|
-
|
112
|
-
class RetainedContext
|
210
|
+
class RetainedContext < Context
|
113
211
|
|
114
212
|
class << self
|
115
213
|
def rmext_retains
|
@@ -122,31 +220,25 @@ module RMExtensions
|
|
122
220
|
@rmext_retains_queue
|
123
221
|
end
|
124
222
|
|
125
|
-
def
|
223
|
+
def create(origin, &block)
|
126
224
|
x = new
|
127
225
|
x.hash["retained_origin"] = origin
|
128
226
|
x.hash["retained_block"] = block
|
129
227
|
x.rmext_retain!
|
130
|
-
block.call(x)
|
228
|
+
block.call(x) unless block.nil?
|
131
229
|
x
|
132
230
|
end
|
133
231
|
end
|
134
232
|
|
135
|
-
attr_accessor :hash
|
136
|
-
|
137
|
-
def initialize
|
138
|
-
self.hash = {}
|
139
|
-
end
|
140
|
-
|
141
233
|
# if you provide a block, you are responsible for calling #detach!,
|
142
234
|
# otherwise, the expiration handler will just call #detach!
|
143
|
-
def
|
235
|
+
def begin_background!(&block)
|
144
236
|
hash["bgTaskExpirationHandler"] = block
|
145
237
|
hash["bgTask"] = UIApplication.sharedApplication.beginBackgroundTaskWithExpirationHandler(-> do
|
146
238
|
if hash["bgTaskExpirationHandler"]
|
147
239
|
hash["bgTaskExpirationHandler"].call
|
148
240
|
else
|
149
|
-
|
241
|
+
detach!
|
150
242
|
end
|
151
243
|
end)
|
152
244
|
end
|
@@ -159,19 +251,85 @@ module RMExtensions
|
|
159
251
|
rmext_detach!
|
160
252
|
end
|
161
253
|
|
254
|
+
def detach_on_death_of(object)
|
255
|
+
object.rmext_on_dealloc(&detach_death_proc)
|
256
|
+
end
|
257
|
+
|
258
|
+
def detach_death_proc
|
259
|
+
proc { |x| detach! }
|
260
|
+
end
|
261
|
+
|
162
262
|
def method_missing(method, *args)
|
163
263
|
unless hash
|
164
264
|
raise "You detached this rmext_retained_context and then called: #{method}"
|
165
265
|
end
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
266
|
+
super
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
class WeakObserverProxy
|
272
|
+
include BW::KVO
|
273
|
+
rmext_weak_attr_accessor :obj
|
274
|
+
attr_accessor :strong_object_id, :strong_class_name
|
275
|
+
def initialize(strong_object)
|
276
|
+
self.obj = strong_object
|
277
|
+
self.strong_object_id = strong_object.object_id
|
278
|
+
self.strong_class_name = strong_object.class.name
|
279
|
+
self.class.weak_observer_map[strong_object_id] = self
|
280
|
+
strong_object.rmext_on_dealloc(&kill_observation_proc)
|
281
|
+
end
|
282
|
+
# isolate this in its own method so it wont create a retain cycle
|
283
|
+
def kill_observation_proc
|
284
|
+
proc { |x|
|
285
|
+
# uncomment to verify deallocation is working. if not, there is probably
|
286
|
+
# a retain cycle somewhere in your code.
|
287
|
+
# p "kill_observation_proc", self
|
288
|
+
self.obj = nil
|
289
|
+
unobserve_all
|
290
|
+
self.class.weak_observer_map.delete(strong_object_id)
|
291
|
+
}
|
292
|
+
end
|
293
|
+
def clear_empty_targets!
|
294
|
+
return if @targets.nil?
|
295
|
+
@targets.each_pair do |target, key_paths|
|
296
|
+
if !key_paths || key_paths.size == 0
|
297
|
+
@targets.delete(target)
|
298
|
+
end
|
171
299
|
end
|
300
|
+
nil
|
301
|
+
end
|
302
|
+
def inspect
|
303
|
+
"#{strong_class_name}:#{strong_object_id}"
|
304
|
+
end
|
305
|
+
def targets
|
306
|
+
@targets
|
307
|
+
end
|
308
|
+
def self.weak_observer_map
|
309
|
+
Dispatch.once { $weak_observer_map = {} }
|
310
|
+
$weak_observer_map
|
311
|
+
end
|
312
|
+
def self.get(obj)
|
313
|
+
return obj if obj.is_a?(WeakObserverProxy)
|
314
|
+
weak_observer_map[obj.object_id] || new(obj)
|
172
315
|
end
|
316
|
+
end
|
173
317
|
|
318
|
+
class OnDeallocInternalObject
|
319
|
+
attr_accessor :block
|
320
|
+
rmext_weak_attr_accessor :obj
|
321
|
+
def self.create(&block)
|
322
|
+
x = new
|
323
|
+
x.block = block
|
324
|
+
x
|
325
|
+
end
|
326
|
+
def dealloc
|
327
|
+
if block
|
328
|
+
block.call(obj)
|
329
|
+
self.block = nil
|
330
|
+
end
|
331
|
+
super
|
332
|
+
end
|
174
333
|
end
|
175
334
|
|
176
335
|
end
|
177
|
-
Object.send(:include, ::RMExtensions::ObjectExtensions)
|
data/rm-extensions.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rm-extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,24 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
13
|
-
dependencies:
|
12
|
+
date: 2013-05-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bubble-wrap
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.1.5
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.1.5
|
14
30
|
description: Extensions and helpers for dealing with various areas of rubymotion
|
15
31
|
email:
|
16
32
|
- joenoon@gmail.com
|