rm-extensions 0.4.5 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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: