bubble-wrap 1.3.0 → 1.4.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 +6 -14
- data/.travis.yml +2 -1
- data/CHANGELOG.md +26 -2
- data/GETTING_STARTED.md +1 -1
- data/Gemfile.lock +1 -1
- data/HACKING.md +6 -4
- data/README.md +83 -3
- data/Rakefile +9 -7
- data/lib/bubble-wrap/all.rb +2 -2
- data/lib/bubble-wrap/mail.rb +9 -0
- data/lib/bubble-wrap/sms.rb +9 -0
- data/lib/bubble-wrap/ui.rb +6 -2
- data/lib/bubble-wrap/version.rb +1 -1
- data/motion/core.rb +1 -1
- data/motion/core/app.rb +7 -2
- data/motion/core/device/ios/camera.rb +3 -2
- data/motion/core/device/ios/screen.rb +19 -0
- data/motion/core/ios/app.rb +13 -1
- data/motion/core/ios/device.rb +5 -0
- data/motion/core/json.rb +1 -3
- data/motion/core/kvo.rb +11 -13
- data/motion/core/persistence.rb +8 -1
- data/motion/core/string.rb +3 -3
- data/motion/core/time.rb +5 -0
- data/motion/http.rb +1 -1
- data/motion/http/query.rb +39 -19
- data/motion/http/response.rb +4 -4
- data/motion/mail/mail.rb +59 -0
- data/motion/mail/result.rb +29 -0
- data/motion/reactor/eventable.rb +3 -2
- data/motion/reactor/periodic_timer.rb +12 -8
- data/motion/reactor/timer.rb +11 -7
- data/motion/sms/result.rb +24 -0
- data/motion/sms/sms.rb +47 -0
- data/motion/ui/pollute.rb +4 -1
- data/motion/ui/ui_alert_view.rb +15 -8
- data/motion/ui/ui_control_wrapper.rb +16 -0
- data/motion/ui/ui_view_controller_wrapper.rb +13 -0
- data/motion/ui/ui_view_wrapper.rb +55 -0
- data/resources/Localizable.strings +1 -0
- data/samples/gesture/Gemfile +2 -2
- data/samples/location/Gemfile +1 -1
- data/samples/osx/Gemfile +2 -2
- data/spec/motion/core/app_spec.rb +6 -0
- data/spec/motion/core/device/ios/camera_spec.rb +3 -3
- data/spec/motion/core/device/ios/screen_spec.rb +45 -0
- data/spec/motion/core/ios/app_spec.rb +29 -0
- data/spec/motion/core/persistence_spec.rb +25 -0
- data/spec/motion/core/string_spec.rb +2 -2
- data/spec/motion/core/time_spec.rb +14 -4
- data/spec/motion/core_spec.rb +9 -8
- data/spec/motion/http/query_spec.rb +92 -15
- data/spec/motion/http/response_spec.rb +4 -3
- data/spec/motion/location/location_spec.rb +1 -1
- data/spec/motion/mail/mail_spec.rb +125 -0
- data/spec/motion/mail/result_spec.rb +53 -0
- data/spec/motion/reactor/eventable_spec.rb +85 -0
- data/spec/motion/reactor_spec.rb +0 -20
- data/spec/motion/sms/result_spec.rb +38 -0
- data/spec/motion/sms/sms_spec.rb +71 -0
- data/spec/motion/ui/pollute_spec.rb +13 -0
- data/spec/motion/ui/ui_bar_button_item_spec.rb +8 -8
- data/spec/motion/ui/ui_control_wrapper_spec.rb +35 -0
- data/spec/motion/ui/ui_view_controller_wrapper_spec.rb +34 -0
- data/spec/motion/ui/ui_view_wrapper_spec.rb +62 -0
- data/travis.sh +7 -0
- metadata +52 -22
- data/motion/ui/gestures.rb +0 -57
- data/motion/ui/ui_control.rb +0 -14
- data/motion/ui/ui_view_controller.rb +0 -11
- data/spec/motion/ui/gestures_spec.rb +0 -62
- data/spec/motion/ui/ui_control_spec.rb +0 -42
data/motion/core/ios/app.rb
CHANGED
@@ -63,9 +63,21 @@ module BubbleWrap
|
|
63
63
|
UIApplication.sharedApplication
|
64
64
|
end
|
65
65
|
|
66
|
+
def windows
|
67
|
+
UIApplication.sharedApplication.windows
|
68
|
+
end
|
69
|
+
|
66
70
|
# the Application Window
|
67
71
|
def window
|
68
|
-
|
72
|
+
normal_windows = App.windows.select { |w|
|
73
|
+
w.windowLevel == UIWindowLevelNormal
|
74
|
+
}
|
75
|
+
|
76
|
+
key_window = normal_windows.select {|w|
|
77
|
+
w == UIApplication.sharedApplication.keyWindow
|
78
|
+
}.first
|
79
|
+
|
80
|
+
key_window || normal_windows.first
|
69
81
|
end
|
70
82
|
end
|
71
83
|
end
|
data/motion/core/ios/device.rb
CHANGED
data/motion/core/json.rb
CHANGED
@@ -28,9 +28,7 @@ module BubbleWrap
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def self.generate(obj)
|
31
|
-
|
32
|
-
data = NSJSONSerialization.dataWithJSONObject(obj, options:0, error:nil)
|
33
|
-
data.to_str
|
31
|
+
NSJSONSerialization.dataWithJSONObject(obj, options:0, error:nil).to_str
|
34
32
|
end
|
35
33
|
|
36
34
|
end
|
data/motion/core/kvo.rb
CHANGED
@@ -64,9 +64,7 @@ module BubbleWrap
|
|
64
64
|
return if @targets.nil? || target.nil? || key_path.nil?
|
65
65
|
|
66
66
|
key_paths = @targets[target]
|
67
|
-
if !key_paths.nil?
|
68
|
-
key_paths.delete(key_path.to_s)
|
69
|
-
end
|
67
|
+
key_paths.delete(key_path.to_s) if !key_paths.nil?
|
70
68
|
end
|
71
69
|
|
72
70
|
def remove_all_observer_blocks
|
@@ -75,19 +73,19 @@ module BubbleWrap
|
|
75
73
|
|
76
74
|
# NSKeyValueObserving Protocol
|
77
75
|
|
78
|
-
def observeValueForKeyPath(key_path, ofObject:target, change:change, context:context)
|
76
|
+
def observeValueForKeyPath(key_path, ofObject: target, change: change, context: context)
|
79
77
|
key_paths = @targets[target] || {}
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
78
|
+
blocks = key_paths[key_path] || []
|
79
|
+
blocks.each do |block|
|
80
|
+
args = [change[NSKeyValueChangeOldKey], change[NSKeyValueChangeNewKey]]
|
81
|
+
args << change[NSKeyValueChangeIndexesKey] if collection?(change)
|
82
|
+
block.call(*args)
|
86
83
|
end
|
84
|
+
end
|
87
85
|
|
88
|
-
|
89
|
-
|
90
|
-
|
86
|
+
def collection?(change)
|
87
|
+
COLLECTION_OPERATIONS.include?(change[NSKeyValueChangeKindKey])
|
88
|
+
end
|
91
89
|
|
92
90
|
end
|
93
91
|
end
|
data/motion/core/persistence.rb
CHANGED
@@ -29,12 +29,19 @@ module BubbleWrap
|
|
29
29
|
storage.synchronize
|
30
30
|
end
|
31
31
|
|
32
|
+
def delete(key)
|
33
|
+
value = storage.objectForKey storage_key(key)
|
34
|
+
storage.removeObjectForKey(storage_key(key))
|
35
|
+
storage.synchronize
|
36
|
+
value
|
37
|
+
end
|
38
|
+
|
32
39
|
def storage
|
33
40
|
NSUserDefaults.standardUserDefaults
|
34
41
|
end
|
35
42
|
|
36
43
|
def storage_key(key)
|
37
|
-
app_key
|
44
|
+
"#{app_key}_#{key}"
|
38
45
|
end
|
39
46
|
end
|
40
47
|
|
data/motion/core/string.rb
CHANGED
@@ -55,9 +55,9 @@ module BubbleWrap
|
|
55
55
|
hex_color = self.gsub("#", "")
|
56
56
|
case hex_color.size
|
57
57
|
when 3
|
58
|
-
colors = hex_color.scan(%r{[0-9A-Fa-f]}).map{ |el| (el * 2).to_i(16) }
|
58
|
+
colors = hex_color.scan(%r{[0-9A-Fa-f]}).map!{ |el| (el * 2).to_i(16) }
|
59
59
|
when 6
|
60
|
-
colors = hex_color.scan(%r<[0-9A-Fa-f]{2}>).map{ |el| el.to_i(16) }
|
60
|
+
colors = hex_color.scan(%r<[0-9A-Fa-f]{2}>).map!{ |el| el.to_i(16) }
|
61
61
|
else
|
62
62
|
raise ArgumentError
|
63
63
|
end
|
@@ -71,4 +71,4 @@ module BubbleWrap
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
-
|
74
|
+
NSString.send(:include, BubbleWrap::String)
|
data/motion/core/time.rb
CHANGED
data/motion/http.rb
CHANGED
@@ -19,7 +19,7 @@ module BubbleWrap
|
|
19
19
|
# end
|
20
20
|
#
|
21
21
|
|
22
|
-
[:get, :post, :put, :delete, :head, :patch].each do |http_verb|
|
22
|
+
[:get, :post, :put, :delete, :head, :options, :patch].each do |http_verb|
|
23
23
|
|
24
24
|
define_singleton_method(http_verb) do |url, options = {}, &block|
|
25
25
|
options[:action] = block if block
|
data/motion/http/query.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Class wrapping NSConnection and often used indirectly by the BubbleWrap::HTTP module methods.
|
2
|
-
|
2
|
+
module BubbleWrap; module HTTP; class Query
|
3
3
|
attr_accessor :request
|
4
4
|
attr_accessor :connection
|
5
5
|
attr_accessor :credentials # username & password has a hash
|
@@ -20,7 +20,7 @@ class BubbleWrap::HTTP::Query
|
|
20
20
|
#
|
21
21
|
# ==== Options
|
22
22
|
# :payload<String> - data to pass to a POST, PUT, DELETE query.
|
23
|
-
# :
|
23
|
+
# :action - Proc, class or object to call when the file is downloaded.
|
24
24
|
# a proc will receive a Response object while the passed object
|
25
25
|
# will receive the handle_query_response method
|
26
26
|
# :headers<Hash> - headers send with the request
|
@@ -31,6 +31,7 @@ class BubbleWrap::HTTP::Query
|
|
31
31
|
@method = http_method.upcase.to_s
|
32
32
|
@delegator = options.delete(:action) || self
|
33
33
|
@payload = options.delete(:payload)
|
34
|
+
@encoding = options.delete(:encoding) || NSUTF8StringEncoding
|
34
35
|
@files = options.delete(:files)
|
35
36
|
@boundary = options.delete(:boundary) || BW.create_uuid
|
36
37
|
@credentials = options.delete(:credentials) || {}
|
@@ -70,9 +71,7 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
70
71
|
if App.osx? && !response.is_a?(NSHTTPURLResponse)
|
71
72
|
return
|
72
73
|
end
|
73
|
-
|
74
|
-
@response_headers = response.allHeaderFields
|
75
|
-
@response_size = response.expectedContentLength.to_f
|
74
|
+
did_receive_response(response)
|
76
75
|
end
|
77
76
|
|
78
77
|
# This delegate method get called every time a chunk of data is being received
|
@@ -95,7 +94,11 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
95
94
|
log "##{@redirect_count} HTTP redirect_count: #{request.inspect} - #{self.description}"
|
96
95
|
|
97
96
|
if @redirect_count >= 30
|
98
|
-
@response.
|
97
|
+
@response.error = NSError.errorWithDomain('BubbleWrap::HTTP', code:NSURLErrorHTTPTooManyRedirects,
|
98
|
+
userInfo:NSDictionary.dictionaryWithObject("Too many redirections",
|
99
|
+
forKey: NSLocalizedDescriptionKey))
|
100
|
+
@response.error_message = @response.error.localizedDescription
|
101
|
+
show_status_indicator false
|
99
102
|
@request.done_loading!
|
100
103
|
call_delegator_with_response
|
101
104
|
nil
|
@@ -109,6 +112,7 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
109
112
|
log "HTTP Connection to #{@url.absoluteString} failed #{error.localizedDescription}"
|
110
113
|
show_status_indicator false
|
111
114
|
@request.done_loading!
|
115
|
+
@response.error = error
|
112
116
|
@response.error_message = error.localizedDescription
|
113
117
|
call_delegator_with_response
|
114
118
|
end
|
@@ -139,15 +143,27 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
139
143
|
log "auth challenged, answered with credentials: #{credentials.inspect}"
|
140
144
|
end
|
141
145
|
else
|
146
|
+
did_receive_response(challenge.failureResponse)
|
147
|
+
@response.update(status_code: status_code, headers: response_headers, url: @url, original_url: @original_url)
|
142
148
|
challenge.sender.cancelAuthenticationChallenge(challenge)
|
143
149
|
log 'Auth Failed :('
|
144
150
|
end
|
145
151
|
end
|
146
152
|
|
153
|
+
def cancel
|
154
|
+
@connection.cancel
|
155
|
+
show_status_indicator false
|
156
|
+
@request.done_loading!
|
157
|
+
end
|
147
158
|
|
148
159
|
private
|
149
160
|
|
150
|
-
|
161
|
+
def did_receive_response(response)
|
162
|
+
@status_code = response.statusCode
|
163
|
+
@response_headers = response.allHeaderFields
|
164
|
+
@response_size = response.expectedContentLength.to_f
|
165
|
+
end
|
166
|
+
|
151
167
|
def show_status_indicator(show)
|
152
168
|
if App.ios?
|
153
169
|
UIApplication.sharedApplication.networkActivityIndicatorVisible = show
|
@@ -173,7 +189,7 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
173
189
|
|
174
190
|
def set_content_type
|
175
191
|
return if headers_provided?
|
176
|
-
return if (@method == "GET" || @method == "HEAD")
|
192
|
+
return if (@method == "GET" || @method == "HEAD" || @method == "OPTIONS")
|
177
193
|
@headers ||= {}
|
178
194
|
@headers["Content-Type"] = case @format
|
179
195
|
when :json
|
@@ -200,7 +216,7 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
200
216
|
end
|
201
217
|
|
202
218
|
def create_request_body
|
203
|
-
return nil if (@method == "GET" || @method == "HEAD")
|
219
|
+
return nil if (@method == "GET" || @method == "HEAD" || @method == "OPTIONS")
|
204
220
|
return nil unless (@payload || @files)
|
205
221
|
|
206
222
|
body = NSMutableData.data
|
@@ -217,7 +233,10 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
217
233
|
if @payload.is_a?(NSData)
|
218
234
|
body.appendData(@payload)
|
219
235
|
elsif @payload.is_a?(String)
|
220
|
-
body.appendData(@payload.
|
236
|
+
body.appendData(@payload.to_encoded_data @encoding)
|
237
|
+
elsif @format == :json
|
238
|
+
json_string = BW::JSON.generate(@payload)
|
239
|
+
body.appendData(json_string.to_encoded_data @encoding)
|
221
240
|
else
|
222
241
|
append_form_params(body)
|
223
242
|
end
|
@@ -231,7 +250,7 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
231
250
|
s += "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n"
|
232
251
|
s += value.to_s
|
233
252
|
s += "\r\n"
|
234
|
-
body.appendData(s.
|
253
|
+
body.appendData(s.to_encoded_data @encoding)
|
235
254
|
end
|
236
255
|
@payload_or_files_were_appended = true
|
237
256
|
body
|
@@ -251,7 +270,7 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
251
270
|
|
252
271
|
def parse_file(key, value)
|
253
272
|
if value.is_a?(Hash)
|
254
|
-
raise
|
273
|
+
raise(InvalidFileError, "You need to supply a `:data` entry in #{value} for file '#{key}' in your HTTP `:files`") if value[:data].nil?
|
255
274
|
{data: value[:data], filename: value[:filename] || key}
|
256
275
|
else
|
257
276
|
{data: value, filename: key}
|
@@ -265,9 +284,9 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
265
284
|
s += "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{file[:filename]}\"\r\n"
|
266
285
|
s += "Content-Type: application/octet-stream\r\n\r\n"
|
267
286
|
file_data = NSMutableData.new
|
268
|
-
file_data.appendData(s.
|
287
|
+
file_data.appendData(s.to_encoded_data @encoding)
|
269
288
|
file_data.appendData(file[:data])
|
270
|
-
file_data.appendData("\r\n".
|
289
|
+
file_data.appendData("\r\n".to_encoded_data @encoding)
|
271
290
|
body.appendData(file_data)
|
272
291
|
end
|
273
292
|
@payload_or_files_were_appended = true
|
@@ -275,12 +294,12 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
275
294
|
end
|
276
295
|
|
277
296
|
def append_body_boundary(body)
|
278
|
-
body.appendData("--#{@boundary}--\r\n".
|
297
|
+
body.appendData("--#{@boundary}--\r\n".to_encoded_data @encoding)
|
279
298
|
end
|
280
299
|
|
281
300
|
def create_url(url_string)
|
282
301
|
url_string = url_string.stringByAddingPercentEscapesUsingEncoding NSUTF8StringEncoding
|
283
|
-
if (@method == "GET" || @method == "HEAD") && @payload
|
302
|
+
if (@method == "GET" || @method == "HEAD" || @method == "OPTIONS") && @payload
|
284
303
|
unless @payload.empty?
|
285
304
|
convert_payload_to_url if @payload.is_a?(Hash)
|
286
305
|
url_string += "?#{@payload}"
|
@@ -299,8 +318,9 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
299
318
|
end
|
300
319
|
|
301
320
|
def escape(string)
|
302
|
-
|
303
|
-
|
321
|
+
string_to_escape = string.to_s
|
322
|
+
if string_to_escape
|
323
|
+
CFURLCreateStringByAddingPercentEscapes nil, string_to_escape, nil, "!*'();:@&=+$,/?%#[]", KCFStringEncodingUTF8
|
304
324
|
end
|
305
325
|
end
|
306
326
|
|
@@ -364,4 +384,4 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
364
384
|
NSURLConnection.connectionWithRequest(request, delegate:delegate)
|
365
385
|
end
|
366
386
|
|
367
|
-
end
|
387
|
+
end; end; end
|
data/motion/http/response.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Response class wrapping the results of a Query's response
|
2
|
-
|
2
|
+
module BubbleWrap; module HTTP; class Response
|
3
3
|
attr_reader :body
|
4
4
|
attr_reader :headers
|
5
|
-
attr_accessor :status_code, :status_description, :error_message
|
5
|
+
attr_accessor :status_code, :status_description, :error_message, :error
|
6
6
|
attr_reader :url
|
7
7
|
attr_reader :original_url
|
8
8
|
|
@@ -18,7 +18,7 @@ class BubbleWrap::HTTP::Response
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def ok?
|
21
|
-
status_code.to_s =~ /
|
21
|
+
status_code.to_s =~ /2\d\d/ ? true : false
|
22
22
|
end
|
23
23
|
|
24
24
|
def to_s
|
@@ -29,4 +29,4 @@ class BubbleWrap::HTTP::Response
|
|
29
29
|
def update_status_description
|
30
30
|
@status_description = status_code.nil? ? nil : NSHTTPURLResponse.localizedStringForStatusCode(status_code)
|
31
31
|
end
|
32
|
-
end
|
32
|
+
end; end; end
|
data/motion/mail/mail.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module BubbleWrap
|
2
|
+
module Mail
|
3
|
+
|
4
|
+
module_function
|
5
|
+
|
6
|
+
# Base method to create your in-app mail
|
7
|
+
# ---------------------------------------
|
8
|
+
# EX
|
9
|
+
# BW::Mail.compose {
|
10
|
+
# delegate: self, # optional, will use root view controller by default
|
11
|
+
# to: [ "tom@example.com" ],
|
12
|
+
# cc: [ "itchy@example.com", "scratchy@example.com" ],
|
13
|
+
# bcc: [ "jerry@example.com" ],
|
14
|
+
# html: false,
|
15
|
+
# subject: "My Subject",
|
16
|
+
# message: "This is my message. It isn't very long.",
|
17
|
+
# animated: false
|
18
|
+
# } do |result, error|
|
19
|
+
# result.sent? # => boolean
|
20
|
+
# result.canceled? # => boolean
|
21
|
+
# result.saved? # => boolean
|
22
|
+
# result.failed? # => boolean
|
23
|
+
# error # => NSError
|
24
|
+
# end
|
25
|
+
def compose(options={}, &callback)
|
26
|
+
@delegate = options[:delegate] || App.window.rootViewController
|
27
|
+
|
28
|
+
@callback = callback
|
29
|
+
|
30
|
+
@mail_controller = create_mail_controller(options)
|
31
|
+
|
32
|
+
@mailer_is_animated = options[:animated] == false ? false : true
|
33
|
+
@delegate.presentModalViewController(@mail_controller, animated: @mailer_is_animated)
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_mail_controller(options={})
|
37
|
+
mail_controller = MFMailComposeViewController.alloc.init
|
38
|
+
|
39
|
+
mail_controller.mailComposeDelegate = self
|
40
|
+
mail_controller.setToRecipients(Array(options[:to]))
|
41
|
+
mail_controller.setCcRecipients(Array(options[:cc]))
|
42
|
+
mail_controller.setBccRecipients(Array(options[:bcc]))
|
43
|
+
mail_controller.setSubject(options[:subject] || "Contact")
|
44
|
+
is_html = !!options[:html]
|
45
|
+
mail_controller.setMessageBody(options[:message], isHTML: is_html)
|
46
|
+
|
47
|
+
mail_controller
|
48
|
+
end
|
49
|
+
|
50
|
+
# Event when the MFMailComposeViewController is closed
|
51
|
+
# -------------------------------------------------------------
|
52
|
+
# the callback is fired if it was present in the constructor
|
53
|
+
|
54
|
+
def mailComposeController(controller, didFinishWithResult: result, error: error)
|
55
|
+
@delegate.dismissModalViewControllerAnimated(@mailer_is_animated)
|
56
|
+
@callback.call Result.new(result, error) if @callback
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module BubbleWrap
|
2
|
+
module Mail
|
3
|
+
class Result
|
4
|
+
attr_accessor :result, :error
|
5
|
+
|
6
|
+
def initialize(result, error)
|
7
|
+
self.result = result
|
8
|
+
self.error = error
|
9
|
+
end
|
10
|
+
|
11
|
+
def sent?
|
12
|
+
self.result == MFMailComposeResultSent
|
13
|
+
end
|
14
|
+
|
15
|
+
def canceled?
|
16
|
+
self.result == MFMailComposeResultCancelled
|
17
|
+
end
|
18
|
+
|
19
|
+
def saved?
|
20
|
+
self.result == MFMailComposeResultSaved
|
21
|
+
end
|
22
|
+
|
23
|
+
def failed?
|
24
|
+
self.result == MFMailComposeResultFailed || self.error
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/motion/reactor/eventable.rb
CHANGED