motion-wiretap 0.2.1 → 1.0.0

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