rm-extensions 0.4.5 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 79fc90e02999956244b32c6716f3ef47c6c40dce
4
- data.tar.gz: 7afd5219125217fe0fe730b8c9ed629ef3dacc91
3
+ metadata.gz: 5508660ca09f24056f99da66f92638543dd64bbb
4
+ data.tar.gz: 4d03e2678ca57e1906faf277637ede4029d677b1
5
5
  SHA512:
6
- metadata.gz: 4972527b0fcb5f5148c1a134e32cec2d458bb2f3c7db95ef83124f3f2bb3c49560b440735b0e5e568b3bbe017dc83431b6a38fee9fd3d52b543c89fc18bb8c94
7
- data.tar.gz: c2a1c7fb7cc86cd69eb1bf9d4c8275234d3c2c1d9462a74cffbf1badd342daf42631c72ca5189368de738523756cd1e9498a46dc798a49220dbf574d8642c6f8
6
+ metadata.gz: 36069726b1441e036f165666810d61e9a6c1e1df5c8ac16e1ca9ead8d00ec8a7beca81f3d5b2ee22201654155e82842ed91ebb4b1ce8892ca67d386f621f44fa
7
+ data.tar.gz: 264354fcf310ccca5b4b7f729b1a1f79fe3b90ea9145ebdf195c7fea8381def8619acfde5fad8a25be4f1c125b1e7093985b1eedb32a9f6de2236b9182e192f6
@@ -19,6 +19,26 @@ module RMExtensions
19
19
  end
20
20
  end
21
21
 
22
+ # creates an +attr_accessor+ like behavior, but the objects are
23
+ # stored in an NSMapTable with strong keys (the attrs) and
24
+ # weak values. If the value deallocates, it becomes nil, unlike
25
+ # a traditional WeakRef.
26
+ # does not conform to KVO like attr_accessor does.
27
+ def rmext_zeroing_weak_attr_accessor(*attrs)
28
+ attrs.each do |attr|
29
+ define_method(attr) do
30
+ if @__zeroing_weak_holders
31
+ @__zeroing_weak_holders.objectForKey(attr)
32
+ end
33
+ end
34
+ define_method("#{attr}=") do |val|
35
+ @__zeroing_weak_holders ||= NSMapTable.strongToWeakObjectsMapTable
36
+ @__zeroing_weak_holders.setObject(val, forKey:attr)
37
+ val
38
+ end
39
+ end
40
+ end
41
+
22
42
  end
23
43
 
24
44
  end
data/lib/motion/events.rb CHANGED
@@ -4,56 +4,59 @@ module RMExtensions
4
4
 
5
5
  module Events
6
6
 
7
- def rmext_events_proxy
8
- @rmext_events_proxy ||= EventsProxy.new(self)
7
+ def rmext_events_from_proxy
8
+ @rmext_events_from_proxy ||= EventsFromProxy.new(self)
9
9
  end
10
10
 
11
- def rmext_events_proxy?
12
- !@rmext_events_proxy.nil?
11
+ def rmext_events_from_proxy?
12
+ !!@rmext_events_from_proxy
13
13
  end
14
14
 
15
- # register a callback when an event is triggered on this object.
16
- def rmext_on(object, event, &block)
17
- object.rmext_events_proxy.on(event, limit:-1, inContext:self, withBlock:block)
15
+ def rmext_events_to_proxy
16
+ @rmext_events_to_proxy ||= EventsToProxy.new(self)
18
17
  end
19
18
 
20
- def rmext_now_and_on(object, event, &block)
21
- object.rmext_events_proxy.now_and_on(event, inContext:self, withBlock:block)
19
+ def rmext_events_to_proxy?
20
+ !!@rmext_events_to_proxy
22
21
  end
23
22
 
24
- # register a callback when an event is triggered on this object and remove it after it fires once
25
- def rmext_once(object, event, &block)
26
- object.rmext_events_proxy.on(event, limit:1, inContext:self, withBlock:block)
23
+ # register a callback when an event is triggered on this object.
24
+ def rmext_on(event, opts={}, &block)
25
+ rmext_events_from_proxy.on(event, opts, &block)
27
26
  end
28
27
 
29
- # remove a specific callback for an event on object
30
- def rmext_off(object, event, &block)
31
- if object.rmext_events_proxy?
32
- object.rmext_events_proxy.off(event, inContext:self, withBlock:block)
33
- end
28
+ def rmext_now_and_on(event, opts={}, &block)
29
+ rmext_events_from_proxy.now_and_on(event, opts, &block)
34
30
  end
35
31
 
36
- # remove all event callbacks on this object,
37
- # remove all event callbacks from other objects in this object's "self"
38
- def rmext_cleanup
39
- if @rmext_events_proxy
40
- @rmext_events_proxy.cleanup
32
+ # register a callback when an event is triggered on this object and remove it after it fires once
33
+ def rmext_once(event, opts={}, &block)
34
+ opts[:limit] = 1
35
+ rmext_events_from_proxy.on(event, opts, &block)
36
+ end
37
+
38
+ # @model.rmext_off(:fire, self) # remove :fire in context "self"
39
+ # @model.rmext_off(:fire, &block) # remove :fire for specific handler
40
+ # @model.rmext_off(:fire) # remove all :fire in all knowns contexts
41
+ # @model.rmext_off(self) # remove all events in context "self"
42
+ # @model.rmext_off # remove all events in all known contexts
43
+ def rmext_off(event=nil, context=nil, &block)
44
+ if rmext_events_from_proxy?
45
+ rmext_events_from_proxy.off(event, context, &block)
41
46
  end
42
47
  end
43
48
 
44
- ### these get called on the object: ie. @model.rmext_off_all
45
-
46
- # remove all event callbacks on this object
47
- def rmext_off_all
48
- if @rmext_events_proxy
49
- @rmext_events_proxy.off_all
49
+ # remove all event callbacks from other objects in this object's "self"
50
+ def rmext_cleanup
51
+ if rmext_events_to_proxy?
52
+ rmext_events_to_proxy.cleanup
50
53
  end
51
54
  end
52
55
 
53
56
  # trigger an event with value on this object
54
57
  def rmext_trigger(event, value=nil)
55
- if @rmext_events_proxy
56
- @rmext_events_proxy.trigger(event, value)
58
+ if rmext_events_from_proxy?
59
+ rmext_events_from_proxy.trigger(event, value)
57
60
  end
58
61
  end
59
62
 
@@ -65,39 +68,80 @@ module RMExtensions
65
68
  attr_accessor :context, :value, :target, :event
66
69
  end
67
70
 
68
- # Proxy class used to hold the actual listeners and contexts where listening
69
- # and watches for the real class intended to hold the observation to be
70
- # deallocated, so the events can be cleaned up.
71
- class EventsProxy
71
+ # Proxy object used to hold the firing objects that this real object's
72
+ # "self" owns handlers for.
73
+ # Can be used to cleanup all handlers across all firing objects that have
74
+ # the hanlder's owner (Proc#owner) == this real object.
75
+ # Does not need to perform deallocation logic as nothing is retained
76
+ # and the real object will fall out of the cooresponding EventsFromProxy
77
+ # automatically.
78
+ class EventsToProxy
79
+
80
+ rmext_zeroing_weak_attr_accessor :weak_object
81
+
82
+ def initialize(obj)
83
+ self.weak_object = obj
84
+ @has_handlers_for = NSHashTable.weakObjectsHashTable
85
+ end
86
+
87
+ def has_handlers_for!(firing_object)
88
+ if ::RMExtensions.debug?
89
+ p "CONTEXT:", weak_object.rmext_object_desc, "LISTENING TO:", firing_object.rmext_object_desc
90
+ end
91
+ @has_handlers_for.addObject(firing_object)
92
+ end
93
+
94
+ def cleanup(firing_object=nil)
95
+ # p "cleanup caller", caller
96
+ if firing_object
97
+ if @has_handlers_for.containsObject(firing_object)
98
+ if ::RMExtensions.debug?
99
+ p "CONTEXT:", weak_object.rmext_object_desc, "UNLISTENING TO:", firing_object.rmext_object_desc
100
+ end
101
+ @has_handlers_for.removeObject(firing_object)
102
+ firing_object.rmext_off(weak_object)
103
+ end
104
+ else
105
+ while firing_object = @has_handlers_for.anyObject
106
+ if ::RMExtensions.debug?
107
+ p "CONTEXT:", weak_object.rmext_object_desc, "UNLISTENING TO:", firing_object.rmext_object_desc
108
+ end
109
+ @has_handlers_for.removeObject(firing_object)
110
+ firing_object.rmext_off(weak_object)
111
+ end
112
+ end
113
+ true
114
+ end
115
+
116
+ end
117
+
118
+ # Proxy class used to hold the actual handlers and contexts of handlers.
119
+ # When the real class deallocates, all handlers are removed.
120
+ class EventsFromProxy
121
+
122
+ rmext_zeroing_weak_attr_accessor :weak_object
72
123
 
73
124
  def initialize(obj)
74
- @weak_object = WeakRef.new(obj)
125
+ self.weak_object = obj
75
126
  @events = NSMapTable.weakToStrongObjectsMapTable
76
- @listenings = NSHashTable.weakObjectsHashTable
77
127
  if ::RMExtensions.debug?
78
- p "CREATED EventsProxy: #{@weak_object.rmext_object_desc}"
128
+ p "CREATED #{className}: #{weak_object.rmext_object_desc}"
79
129
  end
80
130
  end
81
131
 
82
132
  def dealloc
83
- @did_dealloc = true
84
- cleanup
133
+ # @did_dealloc = true
134
+ off
85
135
  if ::RMExtensions.debug?
86
- p "DEALLOC EventsProxy: #{@weak_object.rmext_object_desc}"
136
+ p "DEALLOC #{className}: #{weak_object.rmext_object_desc}"
87
137
  end
88
138
  super
89
139
  end
90
140
 
91
- def cleanup
92
- off_all
93
- off_all_context
94
- true
95
- end
96
-
97
- def on(event, limit:limit, inContext:context, withBlock:block)
141
+ def on(event, opts={}, &block)
98
142
  return if event.nil? || block.nil?
99
143
  event = event.to_s
100
- context ||= self.class
144
+ context = block.owner
101
145
  unless context_events = @events.objectForKey(context)
102
146
  context_events = {}
103
147
  @events.setObject(context_events, forKey:context)
@@ -107,64 +151,66 @@ module RMExtensions
107
151
  context_events.setObject(context_event_blocks, forKey:event)
108
152
  end
109
153
  block.weak!
110
- context_event_blocks[block] = limit
111
- # i.e.: controller/view listening_to model
112
- context.rmext_events_proxy.listening_to(@weak_object)
113
- end
114
-
115
- # this is called in the reverse direction than normal
116
- def listening_to(object)
117
- if ::RMExtensions.debug?
118
- p "CONTEXT:", @weak_object.rmext_object_desc, "LISTENING TO:", object.rmext_object_desc
119
- end
120
- @listenings.addObject(object)
154
+ context_event_blocks[block] = opts[:limit] || -1
155
+ # i.e.: controller/view has handlers for object
156
+ context.rmext_events_to_proxy.has_handlers_for!(weak_object)
121
157
  end
122
158
 
123
- def now_and_on(event, inContext:context, withBlock:block)
159
+ def now_and_on(event, opts={}, &block)
124
160
  rmext_inline_or_on_main_q do
125
161
  res = EventResponse.new
126
- res.context = context
162
+ res.context = block.owner
127
163
  res.value = nil
128
- res.target = @weak_object
164
+ res.target = weak_object
129
165
  res.event = event
130
166
  block.call(res)
131
167
  end
132
- on(event, limit:-1, inContext:context, withBlock:block)
133
- end
134
-
135
- def off(event, inContext:context, withBlock:block)
136
- return if event.nil? || block.nil?
137
- event = event.to_s
138
- context ||= self.class
139
- return unless context_events = @events.objectForKey(context)
140
- return unless context_event_blocks = context_events.objectForKey(event)
141
- context_event_blocks.delete block
142
- nil
143
- end
144
-
145
- def off_all
146
- @events.removeAllObjects
168
+ on(event, opts, &block)
147
169
  end
148
170
 
149
- def off_context(context)
150
- @events.setObject(nil, forKey:context)
151
- end
152
-
153
- def off_all_context
154
- while object = @listenings.anyObject
155
- if ::RMExtensions.debug?
156
- p "CONTEXT:", @weak_object.rmext_object_desc, "UNLISTENING TO:", object.rmext_object_desc
157
- end
158
- @listenings.removeObject(object)
159
- if object.rmext_events_proxy?
160
- object.rmext_events_proxy.off_context(@weak_object)
171
+ def off(event=nil, context=nil, &block)
172
+ if event.is_a?(String) || event.is_a?(Symbol)
173
+ event = event.to_s
174
+ if context
175
+ # remove all handlers for the given event in the given context
176
+ if context_events = @events.objectForKey(context)
177
+ context_events.delete(event)
178
+ end
179
+ elsif block
180
+ # remove the one block for the event in the blocks #owner
181
+ context = block.owner
182
+ if context_events = @events.objectForKey(context)
183
+ if context_event_blocks = context_events.objectForKey(event)
184
+ context_event_blocks.delete block
185
+ end
186
+ end
187
+ else
188
+ # remove all handlers for the event in all contexts known
189
+ keyEnumerator = @events.keyEnumerator
190
+ contexts = []
191
+ while context = keyEnumerator.nextObject
192
+ contexts.push context
193
+ end
194
+ while context = contexts.pop
195
+ if context_events = @events.objectForKey(context)
196
+ context_events.delete event
197
+ end
198
+ end
161
199
  end
200
+ elsif event
201
+ # event is really a context. remove all events and handlers for the context
202
+ context = event
203
+ @events.removeObjectForKey(context)
204
+ else
205
+ # remove everything
206
+ @events.removeAllObjects
162
207
  end
208
+ nil
163
209
  end
164
210
 
165
211
  def trigger(event, value)
166
212
  rmext_inline_or_on_main_q do
167
- next if @did_dealloc
213
+ # next if @did_dealloc
168
214
  next if event.nil?
169
215
  event = event.to_s
170
216
  keyEnumerator = @events.keyEnumerator
@@ -177,24 +223,24 @@ module RMExtensions
177
223
  if event_blocks = context_events[event]
178
224
  blocks = event_blocks.keys
179
225
  if ::RMExtensions.debug?
180
- p "TRIGGER:", event, "OBJECT:", @weak_object.rmext_object_desc, "CONTEXT:", context.rmext_object_desc, "BLOCKS SIZE:", blocks.size
226
+ p "TRIGGER:", event, "OBJECT:", weak_object.rmext_object_desc, "CONTEXT:", context.rmext_object_desc, "BLOCKS SIZE:", blocks.size
181
227
  end
182
228
  while block = blocks.pop
183
229
  limit = event_blocks[block]
184
230
  res = EventResponse.new
185
231
  res.context = context
186
232
  res.value = value
187
- res.target = @weak_object
233
+ res.target = weak_object
188
234
  res.event = event
189
235
  block.call(res)
190
236
  if limit == 1
191
237
  # off
192
238
  if ::RMExtensions.debug?
193
- p "LIMIT REACHED:", event, "OBJECT:", @weak_object.rmext_object_desc, "CONTEXT:", context.rmext_object_desc
239
+ p "LIMIT REACHED:", event, "OBJECT:", weak_object.rmext_object_desc, "CONTEXT:", context.rmext_object_desc
194
240
  end
195
- off(event, inContext:context, withBlock:block)
241
+ off(event, context, &block)
196
242
  elsif limit > 1
197
- context_events[block] -= 1
243
+ event_blocks[block] -= 1
198
244
  end
199
245
  end
200
246
  end
data/lib/motion/util.rb CHANGED
@@ -34,7 +34,7 @@ module RMExtensions
34
34
  if ::RMExtensions::LongTask.outstanding_tasks.size.zero?
35
35
  rmext_block_on_main_q(block)
36
36
  else
37
- rmext_once(::RMExtensions::LongTask, :all_complete) do |opts|
37
+ ::RMExtensions::LongTask.rmext_once(:all_complete) do |opts|
38
38
  block.call
39
39
  end
40
40
  end
@@ -115,7 +115,7 @@ module RMExtensions
115
115
  if @tracking
116
116
  ::RMExtensions::LongTask.outstanding_queue.sync do
117
117
  ::RMExtensions::LongTask.outstanding_tasks.delete(self)
118
- ::RMExtensions::LongTask.internal do |internal_task|
118
+ ::RMExtensions::LongTask.internal("check for all complete") do |internal_task|
119
119
  rmext_on_main_q do
120
120
  if ::RMExtensions::LongTask.outstanding_tasks.size.zero?
121
121
  ::RMExtensions::LongTask.rmext_trigger(:all_complete)
@@ -1,3 +1,3 @@
1
1
  module RMExtensions
2
- VERSION = "0.4.5"
2
+ VERSION = "0.5.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rm-extensions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Noon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-26 00:00:00.000000000 Z
11
+ date: 2014-01-28 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Extensions and helpers for dealing with various areas of rubymotion
14
14
  email: