motion-wiretap 1.1.5 → 1.1.6
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 +4 -4
- data/README.md +99 -48
- data/lib/motion-wiretap/ios/wiretap_gestures.rb +6 -6
- data/lib/motion-wiretap/ios/wiretap_ios.rb +33 -5
- data/lib/motion-wiretap/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d28f1e832294f594579798c62718339c9822a31
|
4
|
+
data.tar.gz: aeeec36e2613e003f5d90788ffad2b9a06c4e56c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19c5b44d1a0b39827bf145baa0664f099ea239e487936a24246609aac88422a56cd2f8809c31043281618153c3fdf78748044a4a6808dc6e22721c004e534ee2
|
7
|
+
data.tar.gz: 6043ff1c69787e815f115d616399d19ad1296dc9fc9b08b3b2a67ce50f0febf8d9fc9795bbb566a577c51a20f4cf8a68cb55a6c6d7fdd67f0829c8aecc4944c2
|
data/README.md
CHANGED
@@ -50,7 +50,9 @@ like this style, but the default is to have a non-polluting system.
|
|
50
50
|
|
51
51
|
Wiretaps can be composed all sorts of ways: you can filter them, map them,
|
52
52
|
combine multiple wiretaps into a single value, use them to respond to
|
53
|
-
notifications or control events.
|
53
|
+
notifications or control events. When you compose them in this way you only
|
54
|
+
need to retain the "bottom most" wiretap. Examples follow.
|
55
|
+
|
54
56
|
|
55
57
|
### Let's start with something practical!
|
56
58
|
|
@@ -60,17 +62,17 @@ In these examples I will use all three ways of creating a Wiretap
|
|
60
62
|
```ruby
|
61
63
|
# assign the label to the text view; changes to the text view will be reflected
|
62
64
|
# on the label.
|
63
|
-
MW(@label, :text).bind_to(MW(@text_view, :text))
|
65
|
+
@wiretap = MW(@label, :text).bind_to(MW(@text_view, :text))
|
64
66
|
|
65
67
|
# assign the attributedText of the label to the text view, doing some
|
66
68
|
# highlighting in-between.
|
67
|
-
@label.wiretap(:attributedText).bind_to(@text_view.wiretap(:text).map do |text|
|
69
|
+
@wiretap = @label.wiretap(:attributedText).bind_to(@text_view.wiretap(:text).map do |text|
|
68
70
|
NSAttributedString.alloc.initWithString(text, attributes: { NSForegroundColorAttributeName => UIColor.blueColor })
|
69
71
|
end)
|
70
72
|
|
71
73
|
# This code will set the 'enabled' property depending on whether the username
|
72
74
|
# and password are not empty.
|
73
|
-
Motion.wiretap(@login_button, :enabled).bind_to(
|
75
|
+
@wiretap = Motion.wiretap(@login_button, :enabled).bind_to(
|
74
76
|
Motion.wiretap([
|
75
77
|
Motion.wiretap(@username_field, :text),
|
76
78
|
Motion.wiretap(@password_field, :text),
|
@@ -81,6 +83,10 @@ Motion.wiretap(@login_button, :enabled).bind_to(
|
|
81
83
|
)
|
82
84
|
```
|
83
85
|
|
86
|
+
See how in the example above I only retain the "final" signal (the return value
|
87
|
+
from `#combine` is what gets retained by `@wiretap`). That's what I mean by the
|
88
|
+
"bottom most" wiretap. Don't worry, the intermediate wiretaps get retained.
|
89
|
+
|
84
90
|
### Types of wiretaps
|
85
91
|
|
86
92
|
- Key-Value Observation / KVO
|
@@ -102,35 +108,35 @@ end
|
|
102
108
|
person = Person.new
|
103
109
|
# you need to store the wiretap object in memory; when the object is
|
104
110
|
# deallocated, it will no longer receive updates.
|
105
|
-
wiretap = Motion.wiretap(person, :name)
|
111
|
+
@wiretap = Motion.wiretap(person, :name)
|
106
112
|
# react to change events
|
107
|
-
wiretap.listen do |name|
|
113
|
+
@wiretap.listen do |name|
|
108
114
|
puts "name is now #{name}"
|
109
115
|
end
|
110
116
|
person.name = 'Joe'
|
111
117
|
# puts => "name is now Joe"
|
112
118
|
|
113
119
|
# Since listening is very common, you can easily shorthand this to:
|
114
|
-
wiretap = Motion.wiretap(person, :name) do |name|
|
120
|
+
@wiretap = Motion.wiretap(person, :name) do |name|
|
115
121
|
puts "name is now #{name}"
|
116
122
|
end
|
117
123
|
|
118
124
|
# bind the property of one object to the value of another
|
119
125
|
person_1 = Person.new
|
120
126
|
person_2 = Person.new
|
121
|
-
wiretap = Motion.wiretap(person_1, :name)
|
122
|
-
wiretap.bind_to(person_2, :name) # creates a new Wiretap object for person_2; changes to person_2.name will affect person_1
|
127
|
+
@wiretap = Motion.wiretap(person_1, :name)
|
128
|
+
@wiretap.bind_to(person_2, :name) # creates a new Wiretap object for person_2; changes to person_2.name will affect person_1
|
123
129
|
person_2.name = 'Bob'
|
124
130
|
person_1.name # => "Bob"
|
125
131
|
|
126
132
|
# cancel a wiretap
|
127
|
-
wiretap.cancel!
|
133
|
+
@wiretap.cancel!
|
128
134
|
person_2.name = 'Jane'
|
129
135
|
person_1.name
|
130
136
|
# => "BOB"
|
131
137
|
|
132
138
|
# bind the property of one object to the value of another, but change it using `map`
|
133
|
-
wiretap = Motion.wiretap(person_1, :name).bind_to(Motion.wiretap(person_2, :name).map { |value| value.upcase })
|
139
|
+
@wiretap = Motion.wiretap(person_1, :name).bind_to(Motion.wiretap(person_2, :name).map { |value| value.upcase })
|
134
140
|
person_2.name = 'Bob'
|
135
141
|
person_1.name # => "BOB"
|
136
142
|
```
|
@@ -138,70 +144,82 @@ person_1.name # => "BOB"
|
|
138
144
|
### Working with arrays
|
139
145
|
|
140
146
|
```ruby
|
141
|
-
# combine the values `name` and `email
|
147
|
+
# combine the values `name` and `email`, which means they'll be sent together
|
148
|
+
# when either one changes
|
142
149
|
person = Person.new
|
143
|
-
|
150
|
+
@info = nil
|
151
|
+
@taps = Motion.wiretap([
|
144
152
|
Motion.wiretap(person, :name),
|
145
153
|
Motion.wiretap(person, :email),
|
146
154
|
]).combine do |name, email|
|
147
|
-
|
155
|
+
@info = "#{name} <#{email}>"
|
148
156
|
end
|
149
157
|
|
150
158
|
person.name = 'Kazuo'
|
151
|
-
#
|
159
|
+
# @info => "Kazuo <>"
|
152
160
|
person.email = 'kaz@example.com'
|
153
|
-
#
|
161
|
+
# @info => "Kazuo <kaz@example.com>"
|
154
162
|
|
155
|
-
# reduce
|
163
|
+
# With reduce you get a memo and a value, and you should return a new value. Be
|
164
|
+
# careful about mutable structures: the initial memo is retained, not copied.
|
156
165
|
person_1 = Person.new
|
157
166
|
person_2 = Person.new
|
158
|
-
|
167
|
+
@names = []
|
168
|
+
@taps = Motion.wiretap([
|
159
169
|
Motion.wiretap(person_1, :name),
|
160
170
|
Motion.wiretap(person_2, :name),
|
161
|
-
]).reduce do |memo, name|
|
162
|
-
memo ||= []
|
171
|
+
]).reduce([]) do |memo, name|
|
163
172
|
memo + [name]
|
164
|
-
end
|
165
|
-
|
166
|
-
puts names.inspect
|
173
|
+
end.listen do |names|
|
174
|
+
@names = names.inspect
|
167
175
|
end
|
168
176
|
person_1.name = 'Mr. White'
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
# you can provide an initial 'memo' (default: nil)
|
173
|
-
Motion.wiretap([
|
174
|
-
Motion.wiretap(person_1, :name),
|
175
|
-
Motion.wiretap(person_2, :name),
|
176
|
-
]).reduce([]) do |memo, name|
|
177
|
-
memo + [name] # you should not change memo in place, the same one will be used on every change event
|
178
|
-
end
|
177
|
+
# @names => ["Mr. White", nil]
|
178
|
+
person_2.name = 'Mr. Blue'
|
179
|
+
# @names => ["Mr. White", "Mr. Blue"]
|
179
180
|
```
|
180
181
|
|
181
182
|
### Monitoring jobs
|
182
183
|
|
184
|
+
There is a "short form" and "long form" to these wiretaps. The "short form" is
|
185
|
+
something like `Motion.wiretap(proc) do (block) end`. The `proc` will be
|
186
|
+
executed, and when it's done, the `block` will execute.
|
187
|
+
|
188
|
+
The "long form" is different only in that the block is passed to the `and_then`
|
189
|
+
method, not the initializer: `Motion.wiretap(proc).and_then do ... end`. In
|
190
|
+
this form, you must call `start` on the wiretap:
|
191
|
+
|
183
192
|
```ruby
|
184
|
-
# Monitor for background job completion:
|
185
|
-
Motion.wiretap(-> do
|
193
|
+
# Monitor for background job completion, short form:
|
194
|
+
@wiretap = Motion.wiretap(-> do
|
195
|
+
this_will_take_forever!
|
196
|
+
end) do
|
197
|
+
puts "done!"
|
198
|
+
end
|
199
|
+
|
200
|
+
# Monitor for background job completion, long form:
|
201
|
+
@wiretap = Motion.wiretap(-> do
|
186
202
|
this_will_take_forever!
|
187
203
|
end).and_then do
|
188
204
|
puts "done!"
|
189
|
-
end.start
|
205
|
+
end.start # you must call 'start' explicitly
|
190
206
|
|
191
|
-
# Same, but specifying the thread to run on. The completion block will be
|
192
|
-
# on this thread, too. The queue conveniently accepts a completion
|
193
|
-
# (delegates to the `Wiretap#and_then` method).
|
194
|
-
Motion.wiretap(-> do
|
207
|
+
# Same again, but specifying the thread to run on. The completion block will be
|
208
|
+
# called on this thread, too. The queue conveniently accepts a completion
|
209
|
+
# handler (delegates to the `Wiretap#and_then` method).
|
210
|
+
@wiretap = Motion.wiretap(-> do
|
195
211
|
this_will_take_forever!
|
196
212
|
end).queue(Dispatch::Queue.concurrent).and_then do
|
197
213
|
puts "done!"
|
198
|
-
end
|
214
|
+
end
|
215
|
+
@wiretap.start
|
199
216
|
|
200
217
|
# Send a stream of values from a block. A lambda is passed in that will forward
|
201
218
|
# change events to the `listen` block
|
202
|
-
Motion.wiretap(-> (on_change) do
|
219
|
+
@wiretap = Motion.wiretap(-> (on_change) do
|
203
220
|
5.times do |count|
|
204
221
|
on_change.call count
|
222
|
+
sleep(1)
|
205
223
|
end
|
206
224
|
end).listen do |index|
|
207
225
|
puts "...#{index}"
|
@@ -211,17 +229,50 @@ end.start
|
|
211
229
|
# => puts "...0", "...1", "...2", "...3", "...4", "done!"
|
212
230
|
```
|
213
231
|
|
214
|
-
###
|
232
|
+
### Gestures
|
233
|
+
|
234
|
+
Possible gestures:
|
235
|
+
|
236
|
+
:tap, :pinch, :rotate, :swipe, :pan, :press
|
237
|
+
|
238
|
+
Options:
|
239
|
+
|
240
|
+
:taps (:tap, :press)
|
241
|
+
:fingers (:tap, :swipe, :pan, :press)
|
242
|
+
:direction (:direction)
|
243
|
+
:min_fingers (:pan)
|
244
|
+
:max_fingers (:pan)
|
245
|
+
:duration (:press)
|
215
246
|
|
216
247
|
```ruby
|
217
|
-
|
218
|
-
|
248
|
+
@wiretap = Motion.wiretap(view).on(:tap) do |gesture|
|
249
|
+
point = gesture.locationInView(view)
|
250
|
+
puts "you tapped #{point.x}, #{point.y}"
|
219
251
|
end
|
220
|
-
# the notification observer will be removed when the Wiretap is dealloc'd
|
221
252
|
```
|
222
253
|
|
223
|
-
###
|
254
|
+
### Control events
|
255
|
+
|
256
|
+
Possible events:
|
257
|
+
|
258
|
+
:touch, :touch_up, :touch_down, :touch_start, :touch_stop, :change, :begin,
|
259
|
+
:end, :touch_down_repeat, :touch_drag_inside, :touch_drag_outside,
|
260
|
+
:touch_drag_enter, :touch_drag_exit, :touch_up_inside, :touch_up_outside,
|
261
|
+
:touch_cancel, :value_changed, :editing_did_begin, :editing_changed,
|
262
|
+
:editing_did_change, :editing_did_end, :editing_did_end_on_exit
|
263
|
+
:all_touch, :all_editing, :application, :system, :all
|
224
264
|
|
225
265
|
```ruby
|
266
|
+
@wiretap = Motion.wiretap(control).on(:touch) do |event|
|
267
|
+
end
|
268
|
+
```
|
226
269
|
|
227
|
-
|
270
|
+
### Notifications
|
271
|
+
|
272
|
+
```ruby
|
273
|
+
@wiretap = Motion.wiretap('NotificationName') do |object, user_info|
|
274
|
+
puts 'notification received!'
|
275
|
+
end
|
276
|
+
|
277
|
+
NSNotificationCenter.defaultCenter.postNotificationName('NotificationName', object: nil, userInfo: info)
|
278
|
+
```
|
@@ -23,7 +23,7 @@ module MotionWiretap
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
recognizer = UITapGestureRecognizer.alloc.initWithTarget(target, action: :
|
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: :
|
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: :
|
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: :
|
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: :
|
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: :
|
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
|
@@ -4,6 +4,11 @@ module MotionWiretap
|
|
4
4
|
|
5
5
|
class WiretapView < WiretapTarget
|
6
6
|
|
7
|
+
def initialize(target, &block)
|
8
|
+
super
|
9
|
+
@gesture_recognizers = []
|
10
|
+
end
|
11
|
+
|
7
12
|
def on(recognizer, options=nil, &block)
|
8
13
|
if recognizer
|
9
14
|
case recognizer
|
@@ -24,6 +29,7 @@ module MotionWiretap
|
|
24
29
|
end
|
25
30
|
|
26
31
|
self.target.addGestureRecognizer(recognizer)
|
32
|
+
@gesture_recognizers << recognizer
|
27
33
|
end
|
28
34
|
|
29
35
|
listen(&block)
|
@@ -31,14 +37,27 @@ module MotionWiretap
|
|
31
37
|
return self
|
32
38
|
end
|
33
39
|
|
34
|
-
def handle_gesture
|
35
|
-
trigger_changed
|
40
|
+
def handle_gesture(gesture)
|
41
|
+
trigger_changed(gesture)
|
42
|
+
end
|
43
|
+
|
44
|
+
def teardown
|
45
|
+
remove_gesture = (-> (recognizer) {
|
46
|
+
self.target.removeGestureRecognizer(recognizer)
|
47
|
+
}).weak!
|
48
|
+
@gesture_recognizers.each(&remove_gesture)
|
49
|
+
super
|
36
50
|
end
|
37
51
|
|
38
52
|
end
|
39
53
|
|
40
54
|
class WiretapControl < WiretapView
|
41
55
|
|
56
|
+
def initialize(target, &block)
|
57
|
+
super
|
58
|
+
@control_events = []
|
59
|
+
end
|
60
|
+
|
42
61
|
# control_event can be any UIControlEventConstant, or any symbol found in
|
43
62
|
# wiretap_control_events.rb, or an array of UIControlEventConstants or
|
44
63
|
# symbols. Since UIView implements `on` to accept a gesture, this method
|
@@ -46,7 +65,8 @@ module MotionWiretap
|
|
46
65
|
def on(control_event, options={}, &block)
|
47
66
|
begin
|
48
67
|
control_event = ControlEvents.convert(control_event)
|
49
|
-
self.target.addTarget(self, action: :
|
68
|
+
self.target.addTarget(self, action: 'handle_event:', forControlEvents: control_event)
|
69
|
+
@control_events << control_event
|
50
70
|
rescue ControlEventNotFound
|
51
71
|
super(control_event, options, &block)
|
52
72
|
else
|
@@ -56,8 +76,16 @@ module MotionWiretap
|
|
56
76
|
return self
|
57
77
|
end
|
58
78
|
|
59
|
-
def handle_event
|
60
|
-
trigger_changed
|
79
|
+
def handle_event(event)
|
80
|
+
trigger_changed(event)
|
81
|
+
end
|
82
|
+
|
83
|
+
def teardown
|
84
|
+
remove_event = (-> (event) {
|
85
|
+
self.target.removeTarget(self, action: 'handle_event:', forControlEvents: event)
|
86
|
+
}).weak!
|
87
|
+
@control_events.each(&remove_event)
|
88
|
+
super
|
61
89
|
end
|
62
90
|
|
63
91
|
end
|