bubble-wrap 1.5.0 → 1.6.0.rc1
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/.travis.yml +6 -1
- data/Gemfile.lock +3 -1
- data/README.md +98 -5
- data/Rakefile +1 -1
- data/bubble-wrap.gemspec +1 -0
- data/lib/bubble-wrap/all.rb +1 -1
- data/lib/bubble-wrap/http.rb +5 -5
- data/lib/bubble-wrap/network-indicator.rb +6 -0
- data/lib/bubble-wrap/ui.rb +1 -0
- data/lib/bubble-wrap/version.rb +1 -1
- data/motion/core.rb +3 -0
- data/motion/core/ios/app.rb +14 -2
- data/motion/core/string.rb +4 -0
- data/motion/location/location.rb +31 -1
- data/motion/network-indicator/network-indicator.rb +67 -0
- data/motion/reactor/eventable.rb +6 -4
- data/motion/ui/ui_activity_view_controller_wrapper.rb +51 -0
- data/motion/util/constants.rb +10 -5
- data/spec/motion/core/ios/app_spec.rb +27 -0
- data/spec/motion/core/string_spec.rb +13 -2
- data/spec/motion/location/location_spec.rb +65 -0
- data/spec/motion/network-indicator/network_indicator_spec.rb +112 -0
- data/spec/motion/reactor/eventable_spec.rb +54 -2
- data/spec/motion/ui/ui_activity_view_controller_wrapper_spec.rb +58 -0
- data/spec/motion/util/constants_spec.rb +6 -0
- metadata +29 -20
- data/motion/http.rb +0 -34
- data/motion/http/query.rb +0 -393
- data/motion/http/response.rb +0 -32
- data/spec/motion/http/query_spec.rb +0 -808
- data/spec/motion/http/response_spec.rb +0 -45
- data/spec/motion/http_spec.rb +0 -57
- data/travis.sh +0 -7
@@ -0,0 +1,58 @@
|
|
1
|
+
describe BubbleWrap::UIActivityViewController do
|
2
|
+
before do
|
3
|
+
@controller = UIViewController.alloc.init
|
4
|
+
@controller.instance_eval do
|
5
|
+
def presentViewController(*args)
|
6
|
+
true
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
@options = options = {
|
11
|
+
item:"BubbleWrap!!!",
|
12
|
+
animated:false
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
after do
|
17
|
+
presenting_controller ||= App.window.rootViewController.presentedViewController
|
18
|
+
presenting_controller ||= App.window.rootViewController
|
19
|
+
presenting_controller.dismissViewControllerAnimated(false, completion:nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'Creates an instance of UIActivityViewController' do
|
23
|
+
activity = BW::UIActivityViewController.new(@options)
|
24
|
+
|
25
|
+
activity.kind_of?(UIActivityViewController).should == true
|
26
|
+
activity.excludedActivityTypes.should == nil
|
27
|
+
activity.activityItems.is_a?(Array).should == true
|
28
|
+
activity.activityItems.count.should == 1
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'Sets a completion block' do
|
32
|
+
activity = BW::UIActivityViewController.new(@options) do |activity_type, completed|
|
33
|
+
test = 2
|
34
|
+
end
|
35
|
+
activity.completionHandler.should.not == nil
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'Sets multiple items' do
|
39
|
+
options = @options.tap { |o| o.delete(:item) }.merge(items: ["Hello", "BubbleWrap!"])
|
40
|
+
|
41
|
+
activity = BW::UIActivityViewController.new(options)
|
42
|
+
activity.activityItems.is_a?(Array).should == true
|
43
|
+
activity.activityItems.count.should == 2
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'Sets a single excluded activity' do
|
47
|
+
activity = BW::UIActivityViewController.new(@options.merge(excluded: :print))
|
48
|
+
activity.excludedActivityTypes.is_a?(Array).should == true
|
49
|
+
activity.excludedActivityTypes.count.should == 1
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'Sets multiple excluded activities' do
|
53
|
+
activity = BW::UIActivityViewController.new(@options.merge(excluded: [:print, :add_to_reading_list]))
|
54
|
+
activity.excludedActivityTypes.is_a?(Array).should == true
|
55
|
+
activity.excludedActivityTypes.count.should == 2
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -22,5 +22,11 @@ describe BubbleWrap::Constants do
|
|
22
22
|
it "should bitmask array values" do
|
23
23
|
BW::Constants.get("NSStringEncodingConversion", :allow_lossy, :external_representation).should == (NSStringEncodingConversionAllowLossy | NSStringEncodingConversionExternalRepresentation)
|
24
24
|
end
|
25
|
+
|
26
|
+
if App.ios?
|
27
|
+
it "should return an array of string constant values" do
|
28
|
+
BW::Constants.get("UIActivityType", [:air_drop, :print]).should == ["com.apple.UIKit.activity.AirDrop", "com.apple.UIKit.activity.Print"]
|
29
|
+
end
|
30
|
+
end
|
25
31
|
end
|
26
32
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bubble-wrap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Aimonetti
|
@@ -14,8 +14,22 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date: 2014-
|
17
|
+
date: 2014-04-16 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: bubble-wrap-http
|
21
|
+
requirement: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - '='
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 1.6.0.rc1
|
26
|
+
type: :runtime
|
27
|
+
prerelease: false
|
28
|
+
version_requirements: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - '='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.6.0.rc1
|
19
33
|
- !ruby/object:Gem::Dependency
|
20
34
|
name: mocha
|
21
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,15 +123,13 @@ extra_rdoc_files:
|
|
109
123
|
- motion/core/string.rb
|
110
124
|
- motion/core/time.rb
|
111
125
|
- motion/font/font.rb
|
112
|
-
- motion/http.rb
|
113
|
-
- motion/http/query.rb
|
114
|
-
- motion/http/response.rb
|
115
126
|
- motion/location/location.rb
|
116
127
|
- motion/location/pollute.rb
|
117
128
|
- motion/mail/mail.rb
|
118
129
|
- motion/mail/result.rb
|
119
130
|
- motion/media/media.rb
|
120
131
|
- motion/media/player.rb
|
132
|
+
- motion/network-indicator/network-indicator.rb
|
121
133
|
- motion/reactor.rb
|
122
134
|
- motion/reactor/default_deferrable.rb
|
123
135
|
- motion/reactor/deferrable.rb
|
@@ -132,6 +144,7 @@ extra_rdoc_files:
|
|
132
144
|
- motion/sms/sms.rb
|
133
145
|
- motion/test_suite_delegate.rb
|
134
146
|
- motion/ui/pollute.rb
|
147
|
+
- motion/ui/ui_activity_view_controller_wrapper.rb
|
135
148
|
- motion/ui/ui_alert_view.rb
|
136
149
|
- motion/ui/ui_bar_button_item.rb
|
137
150
|
- motion/ui/ui_control_wrapper.rb
|
@@ -160,19 +173,18 @@ extra_rdoc_files:
|
|
160
173
|
- spec/motion/core/time_spec.rb
|
161
174
|
- spec/motion/core_spec.rb
|
162
175
|
- spec/motion/font/font_spec.rb
|
163
|
-
- spec/motion/http/query_spec.rb
|
164
|
-
- spec/motion/http/response_spec.rb
|
165
|
-
- spec/motion/http_spec.rb
|
166
176
|
- spec/motion/location/location_spec.rb
|
167
177
|
- spec/motion/mail/mail_spec.rb
|
168
178
|
- spec/motion/mail/result_spec.rb
|
169
179
|
- spec/motion/media/player_spec.rb
|
180
|
+
- spec/motion/network-indicator/network_indicator_spec.rb
|
170
181
|
- spec/motion/reactor/eventable_spec.rb
|
171
182
|
- spec/motion/reactor_spec.rb
|
172
183
|
- spec/motion/rss_parser_spec.rb
|
173
184
|
- spec/motion/sms/result_spec.rb
|
174
185
|
- spec/motion/sms/sms_spec.rb
|
175
186
|
- spec/motion/ui/pollute_spec.rb
|
187
|
+
- spec/motion/ui/ui_activity_view_controller_wrapper_spec.rb
|
176
188
|
- spec/motion/ui/ui_alert_view_spec.rb
|
177
189
|
- spec/motion/ui/ui_bar_button_item_spec.rb
|
178
190
|
- spec/motion/ui/ui_control_wrapper_spec.rb
|
@@ -206,6 +218,7 @@ files:
|
|
206
218
|
- lib/bubble-wrap/location.rb
|
207
219
|
- lib/bubble-wrap/mail.rb
|
208
220
|
- lib/bubble-wrap/media.rb
|
221
|
+
- lib/bubble-wrap/network-indicator.rb
|
209
222
|
- lib/bubble-wrap/reactor.rb
|
210
223
|
- lib/bubble-wrap/requirement.rb
|
211
224
|
- lib/bubble-wrap/requirement/path_manipulation.rb
|
@@ -237,15 +250,13 @@ files:
|
|
237
250
|
- motion/core/string.rb
|
238
251
|
- motion/core/time.rb
|
239
252
|
- motion/font/font.rb
|
240
|
-
- motion/http.rb
|
241
|
-
- motion/http/query.rb
|
242
|
-
- motion/http/response.rb
|
243
253
|
- motion/location/location.rb
|
244
254
|
- motion/location/pollute.rb
|
245
255
|
- motion/mail/mail.rb
|
246
256
|
- motion/mail/result.rb
|
247
257
|
- motion/media/media.rb
|
248
258
|
- motion/media/player.rb
|
259
|
+
- motion/network-indicator/network-indicator.rb
|
249
260
|
- motion/reactor.rb
|
250
261
|
- motion/reactor/default_deferrable.rb
|
251
262
|
- motion/reactor/deferrable.rb
|
@@ -260,6 +271,7 @@ files:
|
|
260
271
|
- motion/sms/sms.rb
|
261
272
|
- motion/test_suite_delegate.rb
|
262
273
|
- motion/ui/pollute.rb
|
274
|
+
- motion/ui/ui_activity_view_controller_wrapper.rb
|
263
275
|
- motion/ui/ui_alert_view.rb
|
264
276
|
- motion/ui/ui_bar_button_item.rb
|
265
277
|
- motion/ui/ui_control_wrapper.rb
|
@@ -340,19 +352,18 @@ files:
|
|
340
352
|
- spec/motion/core/time_spec.rb
|
341
353
|
- spec/motion/core_spec.rb
|
342
354
|
- spec/motion/font/font_spec.rb
|
343
|
-
- spec/motion/http/query_spec.rb
|
344
|
-
- spec/motion/http/response_spec.rb
|
345
|
-
- spec/motion/http_spec.rb
|
346
355
|
- spec/motion/location/location_spec.rb
|
347
356
|
- spec/motion/mail/mail_spec.rb
|
348
357
|
- spec/motion/mail/result_spec.rb
|
349
358
|
- spec/motion/media/player_spec.rb
|
359
|
+
- spec/motion/network-indicator/network_indicator_spec.rb
|
350
360
|
- spec/motion/reactor/eventable_spec.rb
|
351
361
|
- spec/motion/reactor_spec.rb
|
352
362
|
- spec/motion/rss_parser_spec.rb
|
353
363
|
- spec/motion/sms/result_spec.rb
|
354
364
|
- spec/motion/sms/sms_spec.rb
|
355
365
|
- spec/motion/ui/pollute_spec.rb
|
366
|
+
- spec/motion/ui/ui_activity_view_controller_wrapper_spec.rb
|
356
367
|
- spec/motion/ui/ui_alert_view_spec.rb
|
357
368
|
- spec/motion/ui/ui_bar_button_item_spec.rb
|
358
369
|
- spec/motion/ui/ui_control_wrapper_spec.rb
|
@@ -360,7 +371,6 @@ files:
|
|
360
371
|
- spec/motion/ui/ui_view_wrapper_spec.rb
|
361
372
|
- spec/motion/util/constants_spec.rb
|
362
373
|
- spec/motion/util/deprecated_spec.rb
|
363
|
-
- travis.sh
|
364
374
|
homepage: http://bubblewrap.io/
|
365
375
|
licenses: []
|
366
376
|
metadata: {}
|
@@ -375,9 +385,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
375
385
|
version: '0'
|
376
386
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
377
387
|
requirements:
|
378
|
-
- - '
|
388
|
+
- - '>'
|
379
389
|
- !ruby/object:Gem::Version
|
380
|
-
version:
|
390
|
+
version: 1.3.1
|
381
391
|
requirements: []
|
382
392
|
rubyforge_project:
|
383
393
|
rubygems_version: 2.0.3
|
@@ -410,19 +420,18 @@ test_files:
|
|
410
420
|
- spec/motion/core/time_spec.rb
|
411
421
|
- spec/motion/core_spec.rb
|
412
422
|
- spec/motion/font/font_spec.rb
|
413
|
-
- spec/motion/http/query_spec.rb
|
414
|
-
- spec/motion/http/response_spec.rb
|
415
|
-
- spec/motion/http_spec.rb
|
416
423
|
- spec/motion/location/location_spec.rb
|
417
424
|
- spec/motion/mail/mail_spec.rb
|
418
425
|
- spec/motion/mail/result_spec.rb
|
419
426
|
- spec/motion/media/player_spec.rb
|
427
|
+
- spec/motion/network-indicator/network_indicator_spec.rb
|
420
428
|
- spec/motion/reactor/eventable_spec.rb
|
421
429
|
- spec/motion/reactor_spec.rb
|
422
430
|
- spec/motion/rss_parser_spec.rb
|
423
431
|
- spec/motion/sms/result_spec.rb
|
424
432
|
- spec/motion/sms/sms_spec.rb
|
425
433
|
- spec/motion/ui/pollute_spec.rb
|
434
|
+
- spec/motion/ui/ui_activity_view_controller_wrapper_spec.rb
|
426
435
|
- spec/motion/ui/ui_alert_view_spec.rb
|
427
436
|
- spec/motion/ui/ui_bar_button_item_spec.rb
|
428
437
|
- spec/motion/ui/ui_control_wrapper_spec.rb
|
data/motion/http.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
module BubbleWrap
|
2
|
-
|
3
|
-
# The HTTP module provides a simple interface to make HTTP requests.
|
4
|
-
#
|
5
|
-
# TODO: preflight support, easier/better cookie support, better error handling
|
6
|
-
module HTTP
|
7
|
-
|
8
|
-
# Make a GET request and process the response asynchronously via a block.
|
9
|
-
#
|
10
|
-
# @examples
|
11
|
-
# # Simple GET request printing the body
|
12
|
-
# BubbleWrap::HTTP.get("https://api.github.com/users/mattetti") do |response|
|
13
|
-
# p response.body.to_str
|
14
|
-
# end
|
15
|
-
#
|
16
|
-
# # GET request with basic auth credentials
|
17
|
-
# BubbleWrap::HTTP.get("https://api.github.com/users/mattetti", {credentials: {username: 'matt', password: 'aimonetti'}}) do |response|
|
18
|
-
# p response.body.to_str # prints the response's body
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
|
22
|
-
[:get, :post, :put, :delete, :head, :options, :patch].each do |http_verb|
|
23
|
-
|
24
|
-
define_singleton_method(http_verb) do |url, options = {}, &block|
|
25
|
-
options[:action] = block if block
|
26
|
-
HTTP::Query.new(url, http_verb, options)
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class InvalidURLError < StandardError; end
|
34
|
-
class InvalidFileError < StandardError; end
|
data/motion/http/query.rb
DELETED
@@ -1,393 +0,0 @@
|
|
1
|
-
# Class wrapping NSConnection and often used indirectly by the BubbleWrap::HTTP module methods.
|
2
|
-
module BubbleWrap; module HTTP; class Query
|
3
|
-
attr_accessor :request
|
4
|
-
attr_accessor :connection
|
5
|
-
attr_accessor :credentials # username & password has a hash
|
6
|
-
attr_accessor :proxy_credential # credential supplied to proxy servers
|
7
|
-
attr_accessor :post_data
|
8
|
-
attr_reader :method
|
9
|
-
|
10
|
-
attr_reader :response
|
11
|
-
attr_reader :status_code
|
12
|
-
attr_reader :response_headers
|
13
|
-
attr_reader :response_size
|
14
|
-
attr_reader :options
|
15
|
-
CLRF = "\r\n"
|
16
|
-
# ==== Parameters
|
17
|
-
# url<String>:: url of the resource to download
|
18
|
-
# http_method<Symbol>:: Value representing the HTTP method to use
|
19
|
-
# options<Hash>:: optional options used for the query
|
20
|
-
#
|
21
|
-
# ==== Options
|
22
|
-
# :payload<String> - data to pass to a POST, PUT, DELETE query.
|
23
|
-
# :action - Proc, class or object to call when the file is downloaded.
|
24
|
-
# a proc will receive a Response object while the passed object
|
25
|
-
# will receive the handle_query_response method
|
26
|
-
# :headers<Hash> - headers send with the request
|
27
|
-
# :cookies<Boolean> - Set whether cookies should be sent with request or not (Default: true)
|
28
|
-
# Anything else will be available via the options attribute reader.
|
29
|
-
#
|
30
|
-
def initialize(url_string, http_method = :get, options={})
|
31
|
-
@method = http_method.upcase.to_s
|
32
|
-
@delegator = options.delete(:action) || self
|
33
|
-
if @delegator.respond_to?("weak!")
|
34
|
-
@delegator.weak! if BubbleWrap.use_weak_callbacks?
|
35
|
-
end
|
36
|
-
|
37
|
-
@payload = options.delete(:payload)
|
38
|
-
@encoding = options.delete(:encoding) || NSUTF8StringEncoding
|
39
|
-
@files = options.delete(:files)
|
40
|
-
@boundary = options.delete(:boundary) || BW.create_uuid
|
41
|
-
@credentials = options.delete(:credentials) || {}
|
42
|
-
@credentials = {:username => nil, :password => nil}.merge(@credentials)
|
43
|
-
@timeout = options.delete(:timeout) || 30.0
|
44
|
-
@headers = escape_line_feeds(options.delete :headers)
|
45
|
-
@format = options.delete(:format)
|
46
|
-
@cache_policy = options.delete(:cache_policy) || NSURLRequestUseProtocolCachePolicy
|
47
|
-
@credential_persistence = options.delete(:credential_persistence) || NSURLCredentialPersistenceForSession
|
48
|
-
@cookies = options.key?(:cookies) ? options.delete(:cookies) : true
|
49
|
-
@options = options
|
50
|
-
@response = BubbleWrap::HTTP::Response.new
|
51
|
-
@follow_urls = options[:follow_urls] || true
|
52
|
-
@present_credentials = options[:present_credentials] == nil ? true : options.delete(:present_credentials)
|
53
|
-
|
54
|
-
@url = create_url(url_string)
|
55
|
-
@body = create_request_body
|
56
|
-
@request = create_request
|
57
|
-
@original_url = @url.copy
|
58
|
-
|
59
|
-
@connection = create_connection(request, self)
|
60
|
-
@connection.scheduleInRunLoop(NSRunLoop.currentRunLoop, forMode:NSRunLoopCommonModes)
|
61
|
-
@connection.start
|
62
|
-
|
63
|
-
show_status_indicator true
|
64
|
-
end
|
65
|
-
|
66
|
-
def to_s
|
67
|
-
"#<#{self.class}:#{self.object_id} - Method: #{@method}, url: #{@url.description}, body: #{@body.description}, Payload: #{@payload}, Headers: #{@headers} Credentials: #{@credentials}, Timeout: #{@timeout}, \
|
68
|
-
Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
69
|
-
end
|
70
|
-
alias description to_s
|
71
|
-
|
72
|
-
def connection(connection, didReceiveResponse:response)
|
73
|
-
# On OSX, if using an FTP connection, this method will fire *immediately* after creating an
|
74
|
-
# NSURLConnection, even if the connection has not yet started. The `response`
|
75
|
-
# object will be a NSURLResponse, *not* an `NSHTTPURLResponse`, and so will start to crash.
|
76
|
-
if App.osx? && !response.is_a?(NSHTTPURLResponse)
|
77
|
-
return
|
78
|
-
end
|
79
|
-
did_receive_response(response)
|
80
|
-
end
|
81
|
-
|
82
|
-
# This delegate method get called every time a chunk of data is being received
|
83
|
-
def connection(connection, didReceiveData:received_data)
|
84
|
-
@received_data ||= NSMutableData.new
|
85
|
-
@received_data.appendData(received_data)
|
86
|
-
|
87
|
-
if download_progress = options[:download_progress]
|
88
|
-
download_progress.call(@received_data.length.to_f, response_size)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def connection(connection, willSendRequest:request, redirectResponse:redirect_response)
|
93
|
-
# abort early if the user has explicitly disabled redirects
|
94
|
-
if @options[:no_redirect] and redirect_response then
|
95
|
-
return nil
|
96
|
-
end
|
97
|
-
@redirect_count ||= 0
|
98
|
-
@redirect_count += 1
|
99
|
-
log "##{@redirect_count} HTTP redirect_count: #{request.inspect} - #{self.description}"
|
100
|
-
|
101
|
-
if @redirect_count >= 30
|
102
|
-
@response.error = NSError.errorWithDomain('BubbleWrap::HTTP', code:NSURLErrorHTTPTooManyRedirects,
|
103
|
-
userInfo:NSDictionary.dictionaryWithObject("Too many redirections",
|
104
|
-
forKey: NSLocalizedDescriptionKey))
|
105
|
-
@response.error_message = @response.error.localizedDescription
|
106
|
-
show_status_indicator false
|
107
|
-
@request.done_loading!
|
108
|
-
call_delegator_with_response
|
109
|
-
nil
|
110
|
-
else
|
111
|
-
@url = request.URL if @follow_urls
|
112
|
-
request
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def connection(connection, didFailWithError: error)
|
117
|
-
log "HTTP Connection to #{@url.absoluteString} failed #{error.localizedDescription}"
|
118
|
-
show_status_indicator false
|
119
|
-
@request.done_loading!
|
120
|
-
@response.error = error
|
121
|
-
@response.error_message = error.localizedDescription
|
122
|
-
call_delegator_with_response
|
123
|
-
end
|
124
|
-
|
125
|
-
def connection(connection, didSendBodyData:sending, totalBytesWritten:written, totalBytesExpectedToWrite:expected)
|
126
|
-
if upload_progress = options[:upload_progress]
|
127
|
-
upload_progress.call(sending, written, expected)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def connectionDidFinishLoading(connection)
|
132
|
-
show_status_indicator false
|
133
|
-
@request.done_loading!
|
134
|
-
response_body = NSData.dataWithData(@received_data) if @received_data
|
135
|
-
@response.update(status_code: status_code, body: response_body, headers: response_headers, url: @url, original_url: @original_url)
|
136
|
-
|
137
|
-
call_delegator_with_response
|
138
|
-
end
|
139
|
-
|
140
|
-
def connection(connection, didReceiveAuthenticationChallenge:challenge)
|
141
|
-
if (challenge.previousFailureCount == 0)
|
142
|
-
if credentials[:username].to_s.empty? && credentials[:password].to_s.empty?
|
143
|
-
challenge.sender.continueWithoutCredentialForAuthenticationChallenge(challenge)
|
144
|
-
log 'Continue without credentials to get 401 status in response'
|
145
|
-
else
|
146
|
-
new_credential = NSURLCredential.credentialWithUser(credentials[:username], password:credentials[:password], persistence:@credential_persistence)
|
147
|
-
challenge.sender.useCredential(new_credential, forAuthenticationChallenge:challenge)
|
148
|
-
log "auth challenged, answered with credentials: #{credentials.inspect}"
|
149
|
-
end
|
150
|
-
else
|
151
|
-
did_receive_response(challenge.failureResponse)
|
152
|
-
@response.update(status_code: status_code, headers: response_headers, url: @url, original_url: @original_url)
|
153
|
-
challenge.sender.cancelAuthenticationChallenge(challenge)
|
154
|
-
log 'Auth Failed :('
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def cancel
|
159
|
-
@connection.cancel
|
160
|
-
show_status_indicator false
|
161
|
-
@request.done_loading!
|
162
|
-
end
|
163
|
-
|
164
|
-
private
|
165
|
-
|
166
|
-
def did_receive_response(response)
|
167
|
-
@status_code = response.statusCode
|
168
|
-
@response_headers = response.allHeaderFields
|
169
|
-
@response_size = response.expectedContentLength.to_f
|
170
|
-
end
|
171
|
-
|
172
|
-
def show_status_indicator(show)
|
173
|
-
if App.ios?
|
174
|
-
UIApplication.sharedApplication.networkActivityIndicatorVisible = show
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
def create_request
|
179
|
-
log "BubbleWrap::HTTP building a NSRequest for #{@url.description}"
|
180
|
-
|
181
|
-
request = NSMutableURLRequest.requestWithURL(@url,
|
182
|
-
cachePolicy:@cache_policy,
|
183
|
-
timeoutInterval:@timeout)
|
184
|
-
request.setHTTPMethod(@method)
|
185
|
-
set_content_type
|
186
|
-
append_auth_header
|
187
|
-
request.setAllHTTPHeaderFields(@headers)
|
188
|
-
request.setHTTPBody(@body)
|
189
|
-
request.setHTTPShouldHandleCookies(@cookies)
|
190
|
-
patch_nsurl_request(request)
|
191
|
-
|
192
|
-
request
|
193
|
-
end
|
194
|
-
|
195
|
-
def set_content_type
|
196
|
-
return if headers_provided?
|
197
|
-
return if (@method == "GET" || @method == "HEAD" || @method == "OPTIONS")
|
198
|
-
@headers ||= {}
|
199
|
-
@headers["Content-Type"] = case @format
|
200
|
-
when :json
|
201
|
-
"application/json"
|
202
|
-
when :xml
|
203
|
-
"application/xml"
|
204
|
-
when :text
|
205
|
-
"text/plain"
|
206
|
-
else
|
207
|
-
if @format == :form_data || @payload_or_files_were_appended
|
208
|
-
"multipart/form-data; boundary=#{@boundary}"
|
209
|
-
else
|
210
|
-
"application/x-www-form-urlencoded"
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
def headers_provided?
|
216
|
-
@headers && @headers.keys.find {|k| k.downcase == 'content-type'}
|
217
|
-
end
|
218
|
-
|
219
|
-
def credentials_provided?
|
220
|
-
@credentials[:username] && @credentials[:password]
|
221
|
-
end
|
222
|
-
|
223
|
-
def create_request_body
|
224
|
-
return nil if (@method == "GET" || @method == "HEAD" || @method == "OPTIONS")
|
225
|
-
return nil unless (@payload || @files)
|
226
|
-
|
227
|
-
body = NSMutableData.data
|
228
|
-
|
229
|
-
append_payload(body) if @payload
|
230
|
-
append_files(body) if @files
|
231
|
-
append_body_boundary(body) if @payload_or_files_were_appended
|
232
|
-
|
233
|
-
log "Built HTTP body: \n #{body.to_str}"
|
234
|
-
body
|
235
|
-
end
|
236
|
-
|
237
|
-
def append_payload(body)
|
238
|
-
if @payload.is_a?(NSData)
|
239
|
-
body.appendData(@payload)
|
240
|
-
elsif @payload.is_a?(String)
|
241
|
-
body.appendData(@payload.to_encoded_data @encoding)
|
242
|
-
elsif @format == :json
|
243
|
-
json_string = BW::JSON.generate(@payload)
|
244
|
-
body.appendData(json_string.to_encoded_data @encoding)
|
245
|
-
else
|
246
|
-
append_form_params(body)
|
247
|
-
end
|
248
|
-
body
|
249
|
-
end
|
250
|
-
|
251
|
-
def append_form_params(body)
|
252
|
-
list = process_payload_hash(@payload)
|
253
|
-
list.each do |key, value|
|
254
|
-
s = "--#{@boundary}\r\n"
|
255
|
-
s += "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n"
|
256
|
-
s += value.to_s
|
257
|
-
s += "\r\n"
|
258
|
-
body.appendData(s.to_encoded_data @encoding)
|
259
|
-
end
|
260
|
-
@payload_or_files_were_appended = true
|
261
|
-
body
|
262
|
-
end
|
263
|
-
|
264
|
-
def append_auth_header
|
265
|
-
return if @headers && @headers["Authorization"]
|
266
|
-
|
267
|
-
if credentials_provided? && @present_credentials
|
268
|
-
mock_request = CFHTTPMessageCreateRequest(nil, nil, nil, nil)
|
269
|
-
CFHTTPMessageAddAuthentication(mock_request, nil, @credentials[:username], @credentials[:password], KCFHTTPAuthenticationSchemeBasic, false)
|
270
|
-
|
271
|
-
@headers ||= {}
|
272
|
-
@headers["Authorization"] = CFHTTPMessageCopyHeaderFieldValue(mock_request, "Authorization")
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
def parse_file(key, value)
|
277
|
-
value = {data: value} unless value.is_a?(Hash)
|
278
|
-
raise(InvalidFileError, "You need to supply a `:data` entry in #{value} for file '#{key}' in your HTTP `:files`") if value[:data].nil?
|
279
|
-
{
|
280
|
-
data: value[:data],
|
281
|
-
filename: value.fetch(:filename, key),
|
282
|
-
content_type: value.fetch(:content_type, "application/octet-stream")
|
283
|
-
}
|
284
|
-
end
|
285
|
-
|
286
|
-
def append_files(body)
|
287
|
-
@files.each do |key, value|
|
288
|
-
file = parse_file(key, value)
|
289
|
-
s = "--#{@boundary}\r\n"
|
290
|
-
s += "Content-Disposition: attachment; name=\"#{key}\"; filename=\"#{file[:filename]}\"\r\n"
|
291
|
-
s += "Content-Type: #{file[:content_type]}\r\n\r\n"
|
292
|
-
file_data = NSMutableData.new
|
293
|
-
file_data.appendData(s.to_encoded_data @encoding)
|
294
|
-
file_data.appendData(file[:data])
|
295
|
-
file_data.appendData("\r\n".to_encoded_data @encoding)
|
296
|
-
body.appendData(file_data)
|
297
|
-
end
|
298
|
-
@payload_or_files_were_appended = true
|
299
|
-
body
|
300
|
-
end
|
301
|
-
|
302
|
-
def append_body_boundary(body)
|
303
|
-
body.appendData("--#{@boundary}--\r\n".to_encoded_data @encoding)
|
304
|
-
end
|
305
|
-
|
306
|
-
def create_url(url_string)
|
307
|
-
url_string = url_string.stringByAddingPercentEscapesUsingEncoding NSUTF8StringEncoding
|
308
|
-
if (@method == "GET" || @method == "HEAD" || @method == "OPTIONS") && @payload
|
309
|
-
unless @payload.empty?
|
310
|
-
convert_payload_to_url if @payload.is_a?(Hash)
|
311
|
-
url_string += "?#{@payload}"
|
312
|
-
end
|
313
|
-
end
|
314
|
-
url = NSURL.URLWithString(url_string)
|
315
|
-
|
316
|
-
validate_url(url)
|
317
|
-
url
|
318
|
-
end
|
319
|
-
|
320
|
-
def validate_url(url)
|
321
|
-
if !NSURLConnection.canHandleRequest(NSURLRequest.requestWithURL(url))
|
322
|
-
raise InvalidURLError, "Invalid URL provided (Make sure you include a valid URL scheme, e.g. http:// or similar)."
|
323
|
-
end
|
324
|
-
end
|
325
|
-
|
326
|
-
def escape(string)
|
327
|
-
string_to_escape = string.to_s
|
328
|
-
if string_to_escape
|
329
|
-
CFURLCreateStringByAddingPercentEscapes nil, string_to_escape, nil, "!*'();:@&=+$,/?%#[]", KCFStringEncodingUTF8
|
330
|
-
end
|
331
|
-
end
|
332
|
-
|
333
|
-
def convert_payload_to_url
|
334
|
-
params_array = process_payload_hash(@payload)
|
335
|
-
params_array.map! { |key, value| "#{escape key}=#{escape value}" }
|
336
|
-
@payload = params_array.join("&")
|
337
|
-
end
|
338
|
-
|
339
|
-
def process_payload_hash(payload, prefix=nil)
|
340
|
-
list = []
|
341
|
-
payload.each do |k,v|
|
342
|
-
if v.is_a?(Hash)
|
343
|
-
new_prefix = prefix ? "#{prefix}[#{k.to_s}]" : k.to_s
|
344
|
-
param = process_payload_hash(v, new_prefix)
|
345
|
-
list += param
|
346
|
-
elsif v.is_a?(Array)
|
347
|
-
v.each do |val|
|
348
|
-
param = prefix ? "#{prefix}[#{k.to_s}][]" : "#{k.to_s}[]"
|
349
|
-
if val.is_a?(Hash)
|
350
|
-
list += process_payload_hash(val, param)
|
351
|
-
else
|
352
|
-
list << [param, val]
|
353
|
-
end
|
354
|
-
end
|
355
|
-
else
|
356
|
-
param = prefix ? "#{prefix}[#{k.to_s}]" : k.to_s
|
357
|
-
list << [param, v]
|
358
|
-
end
|
359
|
-
end
|
360
|
-
list
|
361
|
-
end
|
362
|
-
|
363
|
-
def log(message)
|
364
|
-
NSLog message if BubbleWrap.debug?
|
365
|
-
end
|
366
|
-
|
367
|
-
def escape_line_feeds(hash)
|
368
|
-
return nil if hash.nil?
|
369
|
-
escaped_hash = {}
|
370
|
-
|
371
|
-
hash.each{|k,v| escaped_hash[k] = v.gsub("\n", CLRF) if v }
|
372
|
-
escaped_hash
|
373
|
-
end
|
374
|
-
|
375
|
-
def patch_nsurl_request(request)
|
376
|
-
request.instance_variable_set("@done_loading", false)
|
377
|
-
|
378
|
-
def request.done_loading?; @done_loading; end
|
379
|
-
def request.done_loading!; @done_loading = true; end
|
380
|
-
end
|
381
|
-
|
382
|
-
def call_delegator_with_response
|
383
|
-
if @delegator.respond_to?(:call)
|
384
|
-
@delegator.call( @response, self )
|
385
|
-
end
|
386
|
-
end
|
387
|
-
|
388
|
-
# This is a temporary method used for mocking.
|
389
|
-
def create_connection(request, delegate)
|
390
|
-
NSURLConnection.alloc.initWithRequest(request, delegate:delegate, startImmediately:false)
|
391
|
-
end
|
392
|
-
|
393
|
-
end; end; end
|