motion-wiretap 0.2.1 → 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 446d7bb23634641466df4e37ffa85daaac5fdc8c
4
+ data.tar.gz: f7b9033df4a62d4f235b47f4d5f39e1efa0bac11
5
+ SHA512:
6
+ metadata.gz: 17981158a67d7424e538351070ac732a7591e364c515882e176f83156f6ad850d5922bbf0754cdc1a704e089f15a15fbc676c9b58b02c381d48dc76cf8f8ed34
7
+ data.tar.gz: 196a340c6afe69833db31d76f51751ede875d75d38e6e9b294fde372031a5c2de60dc17b689174dd7aea6fe07ca8d53186b9dbf192bde535b99d36fb4893fa1d
data/README.md CHANGED
@@ -1,17 +1,25 @@
1
- Wiretap
2
- -------
1
+ MotionWiretap
2
+ -------------
3
3
 
4
- An iOS / OS X wrapper heavily inspired by ReactiveCocoa.
4
+ An iOS / OS X library heavily inspired by ReactiveCocoa.
5
5
 
6
6
  gem install motion-wiretap
7
7
 
8
+ Run the iOS specs using `rake spec`
8
9
  Run the OSX specs using `rake spec platform=osx`
9
10
 
10
11
  First things first
11
12
  ------------------
12
13
 
13
- You **MUST** call `cancel!` on any wiretaps you create, otherwise they will live
14
- in memory forever. The upside is that you can create
14
+ You **MUST** retain any wiretaps you create, otherwise they will be garbage
15
+ collected immediately. I don't yet have a clever way to avoid this unfortunate
16
+ state of affairs, other than keeping some global list of wiretaps. UG to that.
17
+
18
+ How does ReactiveCocoa do it, anyone know? I'd love to mimic that.
19
+
20
+ This isn't a terrible thing, though, since most wiretaps require that you call
21
+ the `cancel!` method. Again, I'm not sure how ReactiveCocoa accomplishes the
22
+ "auto shutdown" of signals that rely on notifications and key-value observation.
15
23
 
16
24
  Showdown
17
25
  --------
@@ -24,7 +32,61 @@ Open these side by side to see the comparison:
24
32
  Usage
25
33
  -----
26
34
 
27
- These are taken from the specs.
35
+ Creating a wiretap is done using the factory method `Motion.wiretap()`.
36
+
37
+ ```ruby
38
+ @wiretap = Motion.wiretap(obj, :property)
39
+ ```
40
+
41
+ If you want to use a more literate style, you can include the
42
+ `motion-wiretap-polluting` to have a `wiretap` method added to your objects. *I*
43
+ like this style, but the default is to have a non-polluting system.
44
+
45
+ ```ruby
46
+ @wiretap = obj.wiretap(:property)
47
+ ```
48
+
49
+ Wiretaps can be composed all sorts of ways: you can filter them, map them,
50
+ combine multiple wiretaps into a single value, use them to respond to
51
+ notifications or control events.
52
+
53
+ ### Let's start with something practical!
54
+
55
+ This first example will use `motion-wiretap-polluting` methods, because that's
56
+ my preferred aesthetic style, but the rest of this document will focus on the
57
+ non-polluting version.
58
+
59
+ ```ruby
60
+ # assign the label to the text view; changes to the text view will be reflected
61
+ # on the label.
62
+ @label.wiretap(:text).bind_to(@text_view.wiretap(:text))
63
+
64
+ # assign the attributedText of the label to the text view, doing some
65
+ # highlighting in-between
66
+ @label.wiretap(:attributedText).bind_to(@text_view.wiretap(:text).map do |text|
67
+ NSAttributedString.alloc.initWithString(text, attributes: { NSForegroundColorAttributeName => UIColor.blueColor })
68
+ end)
69
+
70
+ # bind the `enabled` property to check on whether `username_field.text` and
71
+ # `password_field.text` are blank
72
+ @login_button.wiretap(:enabled).bind_to(
73
+ [
74
+ @username_field.wiretap(:text),
75
+ @password_field.wiretap(:text),
76
+ ].wiretap.combine do |username, password|
77
+ username && username.length > 0 && password && password.length > 0
78
+ end
79
+ )
80
+ ```
81
+
82
+ ### Types of wiretaps
83
+
84
+ - Key-Value Observation / KVO
85
+ - Arrays (map/reduce)
86
+ - Jobs (event stream, completion)
87
+ - NSNotificationCenter
88
+ - UIView Gestures
89
+ - UIControl events
28
90
 
29
91
  ### Key-Value Observation
30
92
 
@@ -37,16 +99,26 @@ end
37
99
 
38
100
  # listen for changes
39
101
  person = Person.new
40
- person.wiretap(:name) do |name|
102
+ # you need to store the wiretap object in memory; when the object is
103
+ # deallocated, it will no longer receive updates.
104
+ wiretap = Motion.wiretap(person, :name)
105
+ # react to change events
106
+ wiretap.listen do |name|
41
107
  puts "name is now #{name}"
42
108
  end
43
109
  person.name = 'Joe'
44
110
  # puts => "name is now Joe"
45
111
 
112
+ # Since listening is very common, you can easily shorthand this to:
113
+ wiretap = Motion.wiretap(person, :name) do |name|
114
+ puts "name is now #{name}"
115
+ end
116
+
46
117
  # bind the property of one object to the value of another
47
118
  person_1 = Person.new
48
119
  person_2 = Person.new
49
- wiretap = person_1.wiretap(:name).bind_to(person_2.wiretap(:name))
120
+ wiretap = Motion.wiretap(person_1, :name)
121
+ wiretap.bind_to(person_2, :name) # creates a new Wiretap object for person_2; changes to person_2.name will affect person_1
50
122
  person_2.name = 'Bob'
51
123
  person_1.name # => "Bob"
52
124
 
@@ -57,20 +129,20 @@ person_1.name
57
129
  # => "BOB"
58
130
 
59
131
  # bind the property of one object to the value of another, but change it using `map`
60
- wiretap = person_1.wiretap(:name).bind_to(person_2.wiretap(:name).map { |value| value.upcase })
132
+ wiretap = Motion.wiretap(person_1, :name).bind_to(person_2.wiretap(:name).map { |value| value.upcase })
61
133
  person_2.name = 'Bob'
62
134
  person_1.name # => "BOB"
63
135
  ```
64
136
 
65
137
  ### Working with arrays
66
138
 
67
- ```
139
+ ```ruby
68
140
  # combine the values `name` and `email`
69
141
  person = Person.new
70
- [
71
- person.wiretap(:name),
72
- person.wiretap(:email),
73
- ].wiretap.combine do |name, email|
142
+ taps = Motion.wiretap([
143
+ Motion.wiretap(person, :name),
144
+ Motion.wiretap(person, :email),
145
+ ]).combine do |name, email|
74
146
  puts "#{name} <#{email}>"
75
147
  end
76
148
 
@@ -82,22 +154,25 @@ person.email = 'kaz@example.com'
82
154
  # reduce/inject
83
155
  person_1 = Person.new
84
156
  person_2 = Person.new
85
- [
86
- person_1.wiretap(:name),
87
- person_2.wiretap(:name),
88
- ].wiretap.reduce do |memo, name|
157
+ taps = Motion.wiretap([
158
+ Motion.wiretap(person_1, :name),
159
+ Motion.wiretap(person_2, :name),
160
+ ]).reduce do |memo, name|
89
161
  memo ||= []
90
162
  memo + [name]
91
163
  end
164
+ wiretap.listen do |names|
165
+ puts names.inspect
166
+ end
92
167
  person_1.name = 'Mr. White'
93
168
  person_1.name = 'Mr. Blue'
94
- # => ['Mr. White', 'Mr. Blue']
169
+ # => ["Mr. White", "Mr. Blue"]
95
170
 
96
171
  # you can provide an initial 'memo' (default: nil)
97
- [
98
- person_1.wiretap(:name),
99
- person_2.wiretap(:name),
100
- ].wiretap.reduce([]) do |memo, name|
172
+ Motion.wiretap([
173
+ Motion.wiretap(person_1, :name),
174
+ Motion.wiretap(person_2, :name),
175
+ ]).reduce([]) do |memo, name|
101
176
  memo + [name] # you should not change memo in place, the same one will be used on every change event
102
177
  end
103
178
  ```
@@ -105,23 +180,29 @@ end
105
180
  ### Monitoring jobs
106
181
 
107
182
  ```ruby
108
- # monitor for background job completion
109
- -> {
183
+ # Monitor for background job completion:
184
+ Motion.wiretap(-> do
110
185
  this_will_take_forever!
111
- }.wiretap(Dispatch::Queue.concurrent) do
186
+ end).and_then do
112
187
  puts "done!"
113
- end
188
+ end.start
114
189
 
115
- # Note: this is convenient shorthand for calling `queue`, `and_then`, and `start`
116
- -> {}.wiretap.queue(...).and_then do ... end.start
190
+ # Same, but specifying the thread to run on. The completion block will be called
191
+ # on this thread, too. The queue conveniently accepts a completion handler
192
+ # (delegates to the `Wiretap#and_then` method).
193
+ Motion.wiretap(-> do
194
+ this_will_take_forever!
195
+ end).queue(Dispatch::Queue.concurrent).and_then do
196
+ puts "done!"
197
+ end.start
117
198
 
118
- # send a stream of values. a lambda is passed in that will forward change
119
- # events to the `listen` block
120
- -> (on_change) do
199
+ # Send a stream of values from a block. A lambda is passed in that will forward
200
+ # change events to the `listen` block
201
+ Motion.wiretap(-> (on_change) do
121
202
  5.times do |count|
122
203
  on_change.call count
123
204
  end
124
- end.wiretap.queue(Dispatch::Queue.concurrent).listen do |index|
205
+ end).listen do |index|
125
206
  puts "...#{index}"
126
207
  end.and_then do
127
208
  puts "done!"
@@ -129,28 +210,17 @@ end.start
129
210
  # => puts "...0", "...1", "...2", "...3", "...4", "done!"
130
211
  ```
131
212
 
132
- ### Let's do something practical!
213
+ ### Notifications
133
214
 
134
215
  ```ruby
135
- # bind the `enabled` property to the Wiretap object to a check on whether
136
- # `username_field.text` and `password_field.text` are blank
137
- @login_button.wiretap(:enabled).bind_to(
138
- [
139
- @username_field.wiretap(:text),
140
- @password_field.wiretap(:text),
141
- ].wiretap.combine do |username, password|
142
- username && username.length > 0 && password && password.length > 0
143
- end
144
- )
216
+ notification = Motion.wiretap('NotificationName') do
217
+ puts 'notification received!'
218
+ end
219
+ # the notification observer will be removed when the Wiretap is dealloc'd
145
220
  ```
146
221
 
147
- ### Notifications
222
+ ### Gestures
148
223
 
149
224
  ```ruby
150
- notification = "NotificationName".wiretap do
151
- puts "notification received!"
152
- end
153
225
 
154
- # rememder: it's *important* to cancel all wiretaps!
155
- notification.cancel!
156
226
  ```
@@ -1,20 +1,28 @@
1
- module Wiretap
1
+ module MotionWiretap
2
2
 
3
3
  # a Wiretap::Signal is much like a Promise in functional programming. A
4
4
  # Signal is triggered with a new value, or it is completed, or canceled with
5
5
  # an error event.
6
- class Signal
6
+ class Signal < Wiretap
7
+ attr :value
7
8
 
8
- def initialize
9
+ def initialize(value=nil)
10
+ super()
11
+ @value = value
12
+ trigger_changed(@value)
9
13
  end
10
14
 
11
15
  def next(value)
16
+ @value = value
17
+ trigger_changed(@value)
12
18
  end
13
19
 
14
20
  def complete
21
+ trigger_completed
15
22
  end
16
23
 
17
- def error
24
+ def error(error)
25
+ trigger_error(error)
18
26
  end
19
27
 
20
28
  end
@@ -1,11 +1,8 @@
1
-
2
1
  module MotionWiretap
3
2
 
4
3
  class Wiretap
5
4
 
6
5
  def initialize(&block)
7
- # MotionWiretap.wiretaps << self # signal will be removed when it is completed
8
-
9
6
  @is_torn_down = false
10
7
  @is_completed = false
11
8
  @is_error = false
@@ -18,6 +15,11 @@ module MotionWiretap
18
15
  listen &block if block
19
16
  end
20
17
 
18
+ def dealloc
19
+ self.cancel!
20
+ super
21
+ end
22
+
21
23
  # this is the preferred way to turn off a wiretap; child classes override
22
24
  # `teardown`, which is only ever called once.
23
25
  def cancel!
@@ -26,7 +28,8 @@ module MotionWiretap
26
28
  teardown
27
29
  end
28
30
 
29
- # for internal use
31
+ # Overridden by subclasses to turn off observation, unregister
32
+ # notifications, etc.
30
33
  def teardown
31
34
  end
32
35
 
@@ -53,7 +56,7 @@ module MotionWiretap
53
56
  def listen(wiretap=nil, &block)
54
57
  raise "Block or Wiretap is expected in #{self.class.name}##{__method__}" unless block || wiretap
55
58
  raise "Only Block *or* Wiretap is expected in #{self.class.name}##{__method__}" if block && wiretap
56
- @listener_handlers << (block || wiretap)
59
+ @listener_handlers << (block ? block.weak! : wiretap)
57
60
  self
58
61
  end
59
62
 
@@ -81,11 +84,11 @@ module MotionWiretap
81
84
  def and_then(wiretap=nil, &block)
82
85
  raise "Block or Wiretap is expected in #{self.class.name}##{__method__}" unless block || wiretap
83
86
  raise "Only Block *or* Wiretap is expected in #{self.class.name}##{__method__}" if block && wiretap
84
- @completion_handlers << (block || wiretap)
87
+ @completion_handlers << (block ? block.weak! : wiretap)
85
88
  if @is_completed
86
89
  trigger_completed_on(block || wiretap)
87
90
  end
88
- self
91
+ return self
89
92
  end
90
93
 
91
94
  def trigger_completed
@@ -114,11 +117,11 @@ module MotionWiretap
114
117
  def on_error(wiretap=nil, &block)
115
118
  raise "Block or Wiretap is expected in #{self.class.name}##{__method__}" unless block || wiretap
116
119
  raise "Only Block *or* Wiretap is expected in #{self.class.name}##{__method__}" if block && wiretap
117
- @error_handlers << (block || wiretap)
120
+ @error_handlers << (block ? block.weak! : wiretap)
118
121
  if @is_error
119
122
  trigger_error_on(block || wiretap, @is_error)
120
123
  end
121
- self
124
+ return self
122
125
  end
123
126
 
124
127
  def trigger_error(error)
@@ -156,6 +159,10 @@ module MotionWiretap
156
159
 
157
160
  # Returns a Wiretap that combines all the values into one value (the values
158
161
  # are all passed in at the same time)
162
+ # @example
163
+ # wiretap.combine do |one, two|
164
+ # one ? two : nil
165
+ # end
159
166
  def combine(&block)
160
167
  return WiretapCombiner.new(self, block)
161
168
  end
@@ -163,15 +170,19 @@ module MotionWiretap
163
170
  # Returns a Wiretap that passes each value through the block, and also the
164
171
  # previous return value (memo).
165
172
  # @example
173
+ # # returns the total of all the prices
166
174
  # wiretap.reduce(0) do |memo, item|
167
175
  # memo + item.price
168
176
  # end
169
- # # returns the total of all the prices
170
177
  def reduce(memo=nil, &block)
171
178
  return WiretapReducer.new(self, memo, block)
172
179
  end
173
180
 
174
181
  # Returns a Wiretap that passes each value through the provided block
182
+ # @example
183
+ # wiretap.map do |item|
184
+ # item.to_s
185
+ # end
175
186
  def map(&block)
176
187
  return WiretapMapper.new(self, block)
177
188
  end
@@ -186,11 +197,6 @@ module MotionWiretap
186
197
  super(&block)
187
198
  end
188
199
 
189
- def teardown
190
- @target = nil
191
- super
192
- end
193
-
194
200
  end
195
201
 
196
202
  class WiretapKvo < WiretapTarget
@@ -201,6 +207,7 @@ module MotionWiretap
201
207
  @property = property
202
208
  @value = nil
203
209
  @initial_is_set = false
210
+ @bound_to = []
204
211
  super(target, &block)
205
212
 
206
213
  @target.addObserver(self,
@@ -214,8 +221,7 @@ module MotionWiretap
214
221
  @target.removeObserver(self,
215
222
  forKeyPath: @property.to_s
216
223
  )
217
- @property = nil
218
- @value = nil
224
+ @bound_to.each &:cancel!
219
225
  super
220
226
  end
221
227
 
@@ -224,10 +230,13 @@ module MotionWiretap
224
230
  end
225
231
 
226
232
  def bind_to(wiretap)
233
+ @bound_to << wiretap
227
234
  wiretap.listen do |value|
228
- @target.send("#{@property}=", value)
235
+ @target.send("#{@property}=".to_sym, value)
229
236
  end
230
237
  wiretap.trigger_changed
238
+
239
+ return self
231
240
  end
232
241
 
233
242
  def observeValueForKeyPath(path, ofObject: target, change: change, context: context)
@@ -256,26 +265,26 @@ module MotionWiretap
256
265
 
257
266
  # gets assigned to the wiretap value if it's a Wiretap, or the object
258
267
  # itself if it is anything else.
259
- @value = []
268
+ @values = []
260
269
  @initial_is_set = true
261
270
  # maps the wiretap object (which is unique)
262
271
  @wiretaps = {}
263
272
 
264
- targets.each_with_index do |wiretap,index|
273
+ targets.each_with_index do |wiretap, index|
265
274
  unless wiretap.is_a? Wiretap
266
- @value << wiretap
275
+ @values << wiretap
267
276
  # not a wiretap, so doesn't need to be "completed"
268
277
  @uncompleted -= 1
269
278
  else
270
279
  raise "You cannot store a Wiretap twice in the same sequence (for now - do you really need this?)" if @wiretaps.key?(wiretap)
271
280
  @wiretaps[wiretap] = index
272
281
 
273
- @value << wiretap.value
282
+ @values << wiretap.value
274
283
 
275
284
  wiretap.listen do |value|
276
285
  indx = @wiretaps[wiretap]
277
- @value[index] = wiretap.value
278
- trigger_changed(*@value)
286
+ @values[index] = wiretap.value
287
+ trigger_changed(*@values)
279
288
  end
280
289
 
281
290
  wiretap.on_error do |error|
@@ -292,36 +301,41 @@ module MotionWiretap
292
301
  end
293
302
  end
294
303
 
295
- def cancel!
296
- @wiretaps.each do |wiretap,index|
297
- wiretap.cancel!
298
- end
299
- super
300
- end
301
-
302
304
  def teardown
303
- @wiretaps = nil
305
+ cancel = (->(wiretap){ wiretap.cancel! }).weak!
306
+ @wiretaps.keys.each &cancel
304
307
  super
305
308
  end
306
309
 
307
310
  def trigger_changed(*values)
308
- values = @value if values.length == 0
311
+ values = @values if values.length == 0
309
312
  super(*values)
310
313
  end
311
314
 
312
315
  end
313
316
 
314
- class WiretapFilter < Wiretap
317
+ class WiretapChild < Wiretap
315
318
 
316
- def initialize(parent, filter)
319
+ def initialize(parent)
317
320
  @parent = parent
318
- @filter = filter
319
-
320
321
  @parent.listen(self)
321
-
322
322
  super()
323
323
  end
324
324
 
325
+ def teardown
326
+ @parent.cancel!
327
+ super
328
+ end
329
+
330
+ end
331
+
332
+ class WiretapFilter < WiretapChild
333
+
334
+ def initialize(parent, filter)
335
+ @filter = filter
336
+ super(parent)
337
+ end
338
+
325
339
  # passes the values through the filter before passing up to the parent
326
340
  # implementation
327
341
  def trigger_changed(*values)
@@ -330,22 +344,14 @@ module MotionWiretap
330
344
  end
331
345
  end
332
346
 
333
- def teardown
334
- @parent = nil
335
- super
336
- end
337
-
338
347
  end
339
348
 
340
- class WiretapCombiner < Wiretap
349
+ class WiretapCombiner < WiretapChild
341
350
 
342
351
  def initialize(parent, combiner)
343
- @parent = parent
344
352
  @combiner = combiner
345
353
 
346
- @parent.listen(self)
347
-
348
- super()
354
+ super(parent)
349
355
  end
350
356
 
351
357
  # passes the values through the combiner before passing up to the parent
@@ -354,23 +360,15 @@ module MotionWiretap
354
360
  super(@combiner.call(*values))
355
361
  end
356
362
 
357
- def teardown
358
- @parent = nil
359
- super
360
- end
361
-
362
363
  end
363
364
 
364
- class WiretapReducer < Wiretap
365
+ class WiretapReducer < WiretapChild
365
366
 
366
367
  def initialize(parent, memo, reducer)
367
- @parent = parent
368
368
  @reducer = reducer
369
369
  @memo = memo
370
370
 
371
- @parent.listen(self)
372
-
373
- super()
371
+ super(parent)
374
372
  end
375
373
 
376
374
  # passes each value through the @reducer, passing in the return value of the
@@ -379,22 +377,14 @@ module MotionWiretap
379
377
  super(values.inject(@memo, &@reducer))
380
378
  end
381
379
 
382
- def teardown
383
- @parent = nil
384
- super
385
- end
386
-
387
380
  end
388
381
 
389
- class WiretapMapper < Wiretap
382
+ class WiretapMapper < WiretapChild
390
383
 
391
384
  def initialize(parent, mapper)
392
- @parent = parent
393
385
  @mapper = mapper
394
386
 
395
- @parent.listen(self)
396
-
397
- super()
387
+ super(parent)
398
388
  end
399
389
 
400
390
  # passes the values through the mapper before passing up to the parent
@@ -403,22 +393,17 @@ module MotionWiretap
403
393
  super(*values.map { |value| @mapper.call(value) })
404
394
  end
405
395
 
406
- def teardown
407
- @parent = nil
408
- super
409
- end
410
-
411
396
  end
412
397
 
413
398
  class WiretapProc < WiretapTarget
414
399
 
415
- def initialize(target, queue, block)
400
+ def initialize(target, queue, and_then)
416
401
  @started = false
417
402
  super(target)
418
- and_then(&block) if block
403
+ and_then(&and_then) if and_then
419
404
  queue(queue) if queue
420
405
 
421
- start if block
406
+ start if and_then
422
407
  end
423
408
 
424
409
  def start
@@ -448,7 +433,7 @@ module MotionWiretap
448
433
  @notification = notification
449
434
  @object = object
450
435
 
451
- NSNotificationCenter.defaultCenter.addObserver(self, selector: 'notify:', name:@notification, object:@object)
436
+ NSNotificationCenter.defaultCenter.addObserver(self, selector: 'notify:', name: @notification, object: @object)
452
437
  listen(&block) if block
453
438
  end
454
439
 
@@ -457,16 +442,10 @@ module MotionWiretap
457
442
  end
458
443
 
459
444
  def teardown
460
- NSNotificationCenter.defaultCenter.removeObserver(self, name:@notification, object:@object)
445
+ NSNotificationCenter.defaultCenter.removeObserver(self, name: @notification, object: @object)
461
446
  super
462
447
  end
463
448
 
464
449
  end
465
450
 
466
- module_function
467
-
468
- def wiretaps
469
- @wiretaps ||= []
470
- end
471
-
472
451
  end
@@ -0,0 +1,30 @@
1
+ module Motion
2
+
3
+ module_function
4
+
5
+ def wiretap(target, property=nil, &block)
6
+ case target
7
+ when NSString
8
+ MotionWiretap::WiretapNotification.new(target, property, block)
9
+ when Proc
10
+ MotionWiretap::WiretapProc.new(target, property, block)
11
+ when NSArray
12
+ MotionWiretap::WiretapArray.new(target, &block)
13
+ when UIControl
14
+ if property.nil?
15
+ MotionWiretap::WiretapControl.new(target, &block)
16
+ else
17
+ MotionWiretap::WiretapKvo.new(target, property, &block)
18
+ end
19
+ when UIView
20
+ if property.nil?
21
+ MotionWiretap::WiretapView.new(target, &block)
22
+ else
23
+ MotionWiretap::WiretapKvo.new(target, property, &block)
24
+ end
25
+ else
26
+ MotionWiretap::WiretapKvo.new(target, property, &block)
27
+ end
28
+ end
29
+
30
+ end
@@ -23,7 +23,7 @@ module MotionWiretap
23
23
  end
24
24
  end
25
25
 
26
- recognizer = UITapGestureRecognizer.alloc.initWithTarget(target, action:'handle_gesture:')
26
+ recognizer = UITapGestureRecognizer.alloc.initWithTarget(target, action: :handle_gesture)
27
27
  recognizer.numberOfTapsRequired = taps if taps
28
28
  recognizer.numberOfTouchesRequired = fingers if fingers
29
29
  return recognizer
@@ -31,13 +31,13 @@ module MotionWiretap
31
31
 
32
32
  # @yield [recognizer] Handles the gesture event, and passes the recognizer instance to the block.
33
33
  def pinch(target)
34
- recognizer = UIPinchGestureRecognizer.alloc.initWithTarget(target, action:'handle_gesture:')
34
+ recognizer = UIPinchGestureRecognizer.alloc.initWithTarget(target, action: :handle_gesture)
35
35
  return recognizer
36
36
  end
37
37
 
38
38
  # @yield [recognizer] Handles the gesture event, and passes the recognizer instance to the block.
39
39
  def rotate(target)
40
- recognizer = UIRotationGestureRecognizer.alloc.initWithTarget(target, action:'handle_gesture:')
40
+ recognizer = UIRotationGestureRecognizer.alloc.initWithTarget(target, action: :handle_gesture)
41
41
  return recognizer
42
42
  end
43
43
 
@@ -71,7 +71,7 @@ module MotionWiretap
71
71
  direction = UISwipeGestureRecognizerDirectionDown
72
72
  end
73
73
 
74
- recognizer = UISwipeGestureRecognizer.alloc.initWithTarget(target, action:'handle_gesture:')
74
+ recognizer = UISwipeGestureRecognizer.alloc.initWithTarget(target, action: :handle_gesture)
75
75
  recognizer.direction = direction if direction
76
76
  recognizer.numberOfTouchesRequired = fingers if fingers
77
77
  return recognizer
@@ -103,7 +103,7 @@ module MotionWiretap
103
103
  min_fingers ||= fingers
104
104
  max_fingers ||= fingers
105
105
 
106
- recognizer = UIPanGestureRecognizer.alloc.initWithTarget(target, action:'handle_gesture:')
106
+ recognizer = UIPanGestureRecognizer.alloc.initWithTarget(target, action: :handle_gesture)
107
107
  recognizer.maximumNumberOfTouches = min_fingers if min_fingers
108
108
  recognizer.minimumNumberOfTouches = max_fingers if max_fingers
109
109
  return recognizer
@@ -131,7 +131,7 @@ module MotionWiretap
131
131
  end
132
132
  end
133
133
 
134
- recognizer = UILongPressGestureRecognizer.alloc.initWithTarget(target, action:'handle_gesture:')
134
+ recognizer = UILongPressGestureRecognizer.alloc.initWithTarget(target, action: :handle_gesture)
135
135
  recognizer.minimumPressDuration = duration if duration
136
136
  recognizer.numberOfTapsRequired = taps if taps
137
137
  recognizer.numberOfTouchesRequired = fingers if fingers
@@ -2,7 +2,11 @@ module MotionWiretap
2
2
  class GestureNotFound < Exception
3
3
  end
4
4
 
5
- class WiretapView < Wiretap
5
+ class WiretapView < WiretapTarget
6
+
7
+ def dummy
8
+ UIButton.new.enabled = true
9
+ end
6
10
 
7
11
  def on(recognizer, options=nil, &block)
8
12
  if recognizer
@@ -26,12 +30,12 @@ module MotionWiretap
26
30
  self.target.addGestureRecognizer(recognizer)
27
31
  end
28
32
 
29
- super(&block)
33
+ listen(&block)
30
34
 
31
35
  return self
32
36
  end
33
37
 
34
- def handle_gesture(recognizer)
38
+ def handle_gesture
35
39
  trigger_changed
36
40
  end
37
41
 
@@ -46,7 +50,7 @@ module MotionWiretap
46
50
  def on(control_event, options={}, &block)
47
51
  begin
48
52
  control_event = ControlEvents.convert(control_event)
49
- self.target.addTarget(self, action:'handle_event:', forControlEvents:control_event)
53
+ self.target.addTarget(self, action: :handle_event, forControlEvents: control_event)
50
54
  rescue ControlEventNotFound
51
55
  super(control_event, options, &block)
52
56
  else
@@ -56,7 +60,7 @@ module MotionWiretap
56
60
  return self
57
61
  end
58
62
 
59
- def handle_event(event)
63
+ def handle_event
60
64
  trigger_changed
61
65
  end
62
66
 
@@ -0,0 +1,24 @@
1
+ module Motion
2
+
3
+ module_function
4
+
5
+ def wiretap(target, property=nil, &block)
6
+ case target
7
+ when NSString
8
+ MotionWiretap::WiretapNotification.new(target, property, block)
9
+ when Proc
10
+ MotionWiretap::WiretapProc.new(target, property, block)
11
+ when NSArray
12
+ MotionWiretap::WiretapArray.new(target, &block)
13
+ when NSView
14
+ if property.nil?
15
+ MotionWiretap::WiretapView.new(target, &block)
16
+ else
17
+ MotionWiretap::WiretapKvo.new(target, property, &block)
18
+ end
19
+ when NSObject
20
+ MotionWiretap::WiretapKvo.new(target, property, &block)
21
+ end
22
+ end
23
+
24
+ end
@@ -1,3 +1,3 @@
1
1
  module MotionWiretap
2
- Version = '0.2.1'
2
+ VERSION = '1.0.0'
3
3
  end
@@ -28,7 +28,7 @@ end
28
28
  class NSString
29
29
 
30
30
  def wiretap(object=nil, &block)
31
- MotionWiretap::WiretapNotification(self, object, block)
31
+ MotionWiretap::WiretapNotification.new(self, object, block)
32
32
  end
33
33
 
34
34
  end
@@ -0,0 +1,17 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "The motion-wiretap gem must be required within a RubyMotion project Rakefile."
3
+ end
4
+
5
+
6
+ require 'motion-wiretap'
7
+
8
+
9
+ Motion::Project::App.setup do |app|
10
+ # scans app.files until it finds app/ (the default)
11
+ # if found, it inserts just before those files, otherwise it will insert to
12
+ # the end of the list
13
+ insert_point = app.files.find_index { |file| file =~ /^(?:\.\/)?app\// } || 0
14
+
15
+ app.files.insert(insert_point, *Dir.glob(File.join(File.dirname(__FILE__), "motion-wiretap-polluting/#{app.template.to_s}/*.rb")))
16
+ app.files.insert(insert_point, *Dir.glob(File.join(File.dirname(__FILE__), "motion-wiretap-polluting/all/*.rb")))
17
+ end
@@ -3,7 +3,8 @@ require File.expand_path('../lib/motion-wiretap/version.rb', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
5
  gem.name = 'motion-wiretap'
6
- gem.version = MotionWiretap::Version
6
+ gem.version = MotionWiretap::VERSION
7
+ gem.licenses = ['BSD']
7
8
 
8
9
  gem.authors = ['Colin T.A. Gray']
9
10
  gem.email = ['colinta@gmail.com']
metadata CHANGED
@@ -1,36 +1,26 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: motion-wiretap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
5
- prerelease:
4
+ version: 1.0.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Colin T.A. Gray
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-09-24 00:00:00.000000000 Z
11
+ date: 2014-02-28 00:00:00.000000000 Z
13
12
  dependencies: []
14
- description: ! 'ReactiveCocoa is an amazing system, and RubyMotion could benefit from
15
- the
16
-
13
+ description: |
14
+ ReactiveCocoa is an amazing system, and RubyMotion could benefit from the
17
15
  lessons learned there!
18
16
 
19
-
20
17
  Motion-Wiretap is, essentially, a wrapper for Key-Value coding and observation.
21
-
22
18
  It exposes a +Wiretap+ class that you can use as a signal, or add listeners to
23
-
24
19
  it.
25
20
 
26
-
27
21
  Extensions are provided to listen to an +Array+ of +Wiretap+ objects, and the
28
-
29
22
  `UIKit`/`AppKit` classes are augmented to provide actions as events (gestures,
30
-
31
23
  mouse events, value changes).
32
-
33
- '
34
24
  email:
35
25
  - colinta@gmail.com
36
26
  executables: []
@@ -39,39 +29,42 @@ extra_rdoc_files: []
39
29
  files:
40
30
  - lib/motion-wiretap/all/signal.rb
41
31
  - lib/motion-wiretap/all/wiretap.rb
42
- - lib/motion-wiretap/all/wiretap_exts.rb
43
32
  - lib/motion-wiretap/ios/wiretap_control_events.rb
44
- - lib/motion-wiretap/ios/wiretap_exts_ios.rb
33
+ - lib/motion-wiretap/ios/wiretap_factory.rb
45
34
  - lib/motion-wiretap/ios/wiretap_gestures.rb
46
35
  - lib/motion-wiretap/ios/wiretap_ios.rb
47
- - lib/motion-wiretap/osx/wiretap_exts_osx.rb
36
+ - lib/motion-wiretap/osx/wiretap_factory.rb
48
37
  - lib/motion-wiretap/osx/wiretap_osx.rb
49
38
  - lib/motion-wiretap/version.rb
39
+ - lib/motion-wiretap-polluting/all/polluting_all.rb
40
+ - lib/motion-wiretap-polluting/ios/polluting_ios.rb
41
+ - lib/motion-wiretap-polluting/osx/polluting_osx.rb
42
+ - lib/motion-wiretap-polluting.rb
50
43
  - lib/motion-wiretap.rb
51
44
  - README.md
52
45
  - motion-wiretap.gemspec
53
46
  homepage: https://github.com/colinta/motion-wiretap
54
- licenses: []
47
+ licenses:
48
+ - BSD
49
+ metadata: {}
55
50
  post_install_message:
56
51
  rdoc_options: []
57
52
  require_paths:
58
53
  - lib
59
54
  required_ruby_version: !ruby/object:Gem::Requirement
60
- none: false
61
55
  requirements:
62
- - - ! '>='
56
+ - - '>='
63
57
  - !ruby/object:Gem::Version
64
58
  version: '0'
65
59
  required_rubygems_version: !ruby/object:Gem::Requirement
66
- none: false
67
60
  requirements:
68
- - - ! '>='
61
+ - - '>='
69
62
  - !ruby/object:Gem::Version
70
63
  version: '0'
71
64
  requirements: []
72
65
  rubyforge_project:
73
- rubygems_version: 1.8.25
66
+ rubygems_version: 2.0.3
74
67
  signing_key:
75
- specification_version: 3
68
+ specification_version: 4
76
69
  summary: It's like ReactiveCocoa, but in RubyMotion
77
70
  test_files: []