bubble-wrap 1.1.2 → 1.1.3
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.
- data/Gemfile.lock +1 -1
- data/README.md +32 -9
- data/Rakefile +1 -5
- data/lib/bubble-wrap/all.rb +1 -1
- data/lib/bubble-wrap/http.rb +1 -0
- data/lib/bubble-wrap/media.rb +8 -0
- data/lib/bubble-wrap/version.rb +1 -1
- data/motion/core/app.rb +5 -0
- data/motion/core/device.rb +1 -1
- data/motion/core/json.rb +1 -0
- data/motion/core/ns_url_request.rb +4 -2
- data/motion/core/persistence.rb +7 -1
- data/motion/core/string.rb +2 -2
- data/motion/http.rb +81 -65
- data/motion/location/location.rb +2 -2
- data/motion/media/media.rb +15 -0
- data/motion/media/player.rb +139 -0
- data/motion/test_suite_delegate.rb +1 -0
- data/resources/test.mp3 +0 -0
- data/samples/camera/Gemfile +3 -0
- data/samples/camera/README.md +4 -0
- data/samples/camera/Rakefile +9 -0
- data/samples/camera/app/app_delegate.rb +8 -0
- data/samples/camera/app/controllers/camera_controller.rb +61 -0
- data/samples/camera/spec/main_spec.rb +9 -0
- data/samples/location/.gitignore +5 -0
- data/samples/location/Gemfile +3 -0
- data/samples/location/README.md +4 -0
- data/samples/location/Rakefile +10 -0
- data/samples/location/app/app_delegate.rb +8 -0
- data/samples/location/app/controllers/image_list_controller.rb +30 -0
- data/samples/location/app/models/places.rb +15 -0
- data/samples/location/spec/main_spec.rb +9 -0
- data/spec/motion/core/app_spec.rb +7 -1
- data/spec/motion/core/json_spec.rb +4 -0
- data/spec/motion/core/persistence_spec.rb +7 -3
- data/spec/motion/core/string_spec.rb +24 -0
- data/spec/motion/http_spec.rb +73 -61
- data/spec/motion/media/player_spec.rb +77 -0
- metadata +57 -14
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -65,6 +65,12 @@ If you wish to only include the `Location` wrapper:
|
|
65
65
|
require 'bubble-wrap/location'
|
66
66
|
```
|
67
67
|
|
68
|
+
If you wish to only include the `Media` wrapper:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
require 'bubble-wrap/media'
|
72
|
+
```
|
73
|
+
|
68
74
|
If you want to include everything (ie kitchen sink mode) you can save time and do:
|
69
75
|
|
70
76
|
```ruby
|
@@ -162,6 +168,7 @@ Other available methods:
|
|
162
168
|
* `App.states`
|
163
169
|
* `App.frame`
|
164
170
|
* `App.delegate`
|
171
|
+
* `App.shared`
|
165
172
|
* `App.current_locale`
|
166
173
|
|
167
174
|
|
@@ -243,7 +250,7 @@ def viewWillAppear(animated)
|
|
243
250
|
@foreground_observer = App.notification_center.observe UIApplicationWillEnterForegroundNotification do |notification|
|
244
251
|
loadAndRefresh
|
245
252
|
end
|
246
|
-
|
253
|
+
|
247
254
|
@reload_observer = App.notification_center.observe ReloadNotification do |notification|
|
248
255
|
loadAndRefresh
|
249
256
|
end
|
@@ -290,7 +297,7 @@ class ExampleViewController < UIViewController
|
|
290
297
|
@label = UILabel.alloc.initWithFrame [[20,20],[280,44]]
|
291
298
|
@label.text = ""
|
292
299
|
view.addSubview @label
|
293
|
-
|
300
|
+
|
294
301
|
observe(@label, :text) do |old_value, new_value|
|
295
302
|
puts "Hello from viewDidLoad!"
|
296
303
|
end
|
@@ -341,6 +348,22 @@ end
|
|
341
348
|
|
342
349
|
Also available is `BW::Location.get_significant`, for monitoring significant location changes.
|
343
350
|
|
351
|
+
## Media
|
352
|
+
|
353
|
+
Added wrapper for playing remote and local media. Available are `modal` and custom presentation styles:
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
# Plays in your custom frame
|
357
|
+
local_file = NSURL.fileURLWithPath(File.join(NSBundle.mainBundle.resourcePath, 'test.mp3'))
|
358
|
+
BW::Media.play(local_file) do |media_player|
|
359
|
+
media_player.view.frame = [[10, 100], [100, 100]]
|
360
|
+
self.view.addSubview media_player.view
|
361
|
+
end
|
362
|
+
|
363
|
+
# Plays in an independent modal controller
|
364
|
+
BW::Media.play_modal("http://www.hrupin.com/wp-content/uploads/mp3/testsong_20_sec.mp3")
|
365
|
+
```
|
366
|
+
|
344
367
|
## UI
|
345
368
|
|
346
369
|
### Gestures
|
@@ -498,7 +521,7 @@ feed_parser = BW::RSSParser.new(feed, true)
|
|
498
521
|
**Since: > version 1.0.0**
|
499
522
|
|
500
523
|
`BubbleWrap::Reactor` is a simplified, mostly complete implementation of
|
501
|
-
the [Event Machine](http://rubyeventmachine.com/) API. In fact
|
524
|
+
the [Event Machine](http://rubyeventmachine.com/) API. In fact
|
502
525
|
`BubbleWrap::Reactor` is aliased to `EM` in the runtime environment.
|
503
526
|
|
504
527
|
### Deferables
|
@@ -588,11 +611,11 @@ Great scott!
|
|
588
611
|
|
589
612
|
### Scheduling operations
|
590
613
|
|
591
|
-
You can use `EM.schedule` to schedule blocks to be executed
|
614
|
+
You can use `EM.schedule` to schedule blocks to be executed
|
592
615
|
asynchronously. BubbleWrap deviates from the EventMachine
|
593
616
|
API here in that it also provides `EM.schedule_on_main` which
|
594
|
-
makes sure that the task is run asynchronously, but on the
|
595
|
-
application's main thread - this is necessary if you are
|
617
|
+
makes sure that the task is run asynchronously, but on the
|
618
|
+
application's main thread - this is necessary if you are
|
596
619
|
updating the user interface.
|
597
620
|
|
598
621
|
```ruby
|
@@ -607,15 +630,15 @@ updating the user interface.
|
|
607
630
|
### Deferrable operations
|
608
631
|
|
609
632
|
You can also use `EM.defer` in much the same way as `EM.schedule`
|
610
|
-
with one important difference, you can pass in a second `proc`
|
633
|
+
with one important difference, you can pass in a second `proc`
|
611
634
|
which will be called when the first has completed, and be passed
|
612
635
|
it's result as an argument. Just like `EM.schedule`, `EM.defer`
|
613
636
|
also has an `EM.defer_on_main` version.
|
614
637
|
|
615
638
|
```ruby
|
616
|
-
> operation = proc {
|
639
|
+
> operation = proc { 88 }
|
617
640
|
=> #<Proc:0x6d763c0>
|
618
|
-
> callback = proc { |speed| puts speed >=
|
641
|
+
> callback = proc { |speed| puts speed >= 88 ? "Time travel!" : "Conventional travel!" }
|
619
642
|
=> #<Proc:0x8bd3910>
|
620
643
|
> EM.defer(operation, callback)
|
621
644
|
=> nil
|
data/Rakefile
CHANGED
@@ -10,11 +10,7 @@ require 'bubble-wrap/test'
|
|
10
10
|
Motion::Project::App.setup do |app|
|
11
11
|
app.name = 'testSuite'
|
12
12
|
app.identifier = 'io.bubblewrap.testSuite'
|
13
|
-
app.specs_dir = './spec/motion
|
14
|
-
# Hold your breath, we're going API spelunking!
|
15
|
-
spec_files = app.spec_files + Dir.glob(File.join(app.specs_dir, '**/*.rb'))
|
16
|
-
spec_files.uniq!
|
17
|
-
app.instance_variable_set(:@spec_files, spec_files)
|
13
|
+
app.specs_dir = './spec/motion'
|
18
14
|
end
|
19
15
|
|
20
16
|
namespace :spec do
|
data/lib/bubble-wrap/all.rb
CHANGED
data/lib/bubble-wrap/http.rb
CHANGED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'bubble-wrap/loader'
|
2
|
+
BubbleWrap.require('motion/core/string.rb')
|
3
|
+
BubbleWrap.require('motion/core/ns_notification_center.rb')
|
4
|
+
BubbleWrap.require('motion/media/**/*.rb') do
|
5
|
+
file('motion/media/media.rb').depends_on('motion/media/player.rb')
|
6
|
+
file('motion/media/player.rb').depends_on 'motion/core/string.rb'
|
7
|
+
file('motion/media/player.rb').uses_framework('MediaPlayer')
|
8
|
+
end
|
data/lib/bubble-wrap/version.rb
CHANGED
data/motion/core/app.rb
CHANGED
@@ -105,6 +105,11 @@ module BubbleWrap
|
|
105
105
|
UIApplication.sharedApplication.delegate
|
106
106
|
end
|
107
107
|
|
108
|
+
# the Application object.
|
109
|
+
def shared
|
110
|
+
UIApplication.sharedApplication
|
111
|
+
end
|
112
|
+
|
108
113
|
# @return [NSLocale] locale of user settings
|
109
114
|
def current_locale
|
110
115
|
languages = NSLocale.preferredLanguages
|
data/motion/core/device.rb
CHANGED
@@ -23,7 +23,7 @@ module BubbleWrap
|
|
23
23
|
|
24
24
|
# Verifies that the device running has a front facing camera.
|
25
25
|
# @return [TrueClass, FalseClass] true will be returned if the device has a front facing camera, false otherwise.
|
26
|
-
def front_camera?
|
26
|
+
def front_camera?(picker=UIImagePickerController)
|
27
27
|
p "This method (front_camera?) is DEPRECATED. Transition to using Device.camera.front?"
|
28
28
|
picker.isCameraDeviceAvailable(UIImagePickerControllerCameraDeviceFront)
|
29
29
|
end
|
data/motion/core/json.rb
CHANGED
@@ -13,6 +13,7 @@ module BubbleWrap
|
|
13
13
|
#
|
14
14
|
# TODO: support options like the C Ruby module does
|
15
15
|
def self.parse(str_data, &block)
|
16
|
+
return nil unless str_data
|
16
17
|
data = str_data.respond_to?(:to_data) ? str_data.to_data : str_data
|
17
18
|
opts = NSJSONReadingMutableContainers & NSJSONReadingMutableLeaves & NSJSONReadingAllowFragments
|
18
19
|
error = Pointer.new(:id)
|
@@ -3,8 +3,10 @@ class NSURLRequest
|
|
3
3
|
# Provides a to_s method so we can use inspect in instances and get
|
4
4
|
# valuable information.
|
5
5
|
def to_s
|
6
|
-
"#<#{self.class}:#{self.object_id} - url: #{self.URL.description},
|
7
|
-
|
6
|
+
"#<#{self.class}:#{self.object_id} - url: #{self.URL.description},
|
7
|
+
headers: #{self.allHTTPHeaderFields.inspect},
|
8
|
+
cache policy: #{self.cachePolicy}, Pipelining: #{self.HTTPShouldUsePipelining}, main doc url: #{mainDocumentURL},\
|
9
|
+
timeout: #{self.timeoutInterval}, network service type: #{self.networkServiceType} >"
|
8
10
|
end
|
9
11
|
|
10
12
|
end
|
data/motion/core/persistence.rb
CHANGED
@@ -13,7 +13,13 @@ module BubbleWrap
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def [](key)
|
16
|
-
storage.objectForKey storage_key(key)
|
16
|
+
value = storage.objectForKey storage_key(key)
|
17
|
+
|
18
|
+
# RubyMotion currently has a bug where the strings returned from
|
19
|
+
# standardUserDefaults are missing some methods (e.g. to_data).
|
20
|
+
# And because the returned object is slightly different than a normal
|
21
|
+
# String, we can't just use `value.is_a?(String)`
|
22
|
+
value.class.to_s == 'String' ? value.dup : value
|
17
23
|
end
|
18
24
|
|
19
25
|
def merge(values)
|
data/motion/core/string.rb
CHANGED
@@ -13,7 +13,7 @@ module BubbleWrap
|
|
13
13
|
new_word = "/#{new_word}" if $1 == '/'
|
14
14
|
new_word
|
15
15
|
end
|
16
|
-
if uppercase_first_letter
|
16
|
+
if uppercase_first_letter && uppercase_first_letter != :lower
|
17
17
|
string[0] = string[0].upcase
|
18
18
|
else
|
19
19
|
string[0] = string[0].downcase
|
@@ -35,7 +35,7 @@ module BubbleWrap
|
|
35
35
|
|
36
36
|
def to_color
|
37
37
|
# First check if it is a color keyword
|
38
|
-
keyword_selector = "#{self.camelize(
|
38
|
+
keyword_selector = "#{self.camelize(:lower)}Color"
|
39
39
|
return UIColor.send(keyword_selector) if UIColor.respond_to? keyword_selector
|
40
40
|
|
41
41
|
# Next attempt to convert from hex
|
data/motion/http.rb
CHANGED
@@ -19,38 +19,37 @@ module BubbleWrap
|
|
19
19
|
# end
|
20
20
|
#
|
21
21
|
def self.get(url, options={}, &block)
|
22
|
-
|
23
|
-
HTTP::Query.new(url, :get, options)
|
22
|
+
create_query(url, :get, options, block)
|
24
23
|
end
|
25
24
|
|
26
25
|
# Make a POST request
|
27
26
|
def self.post(url, options={}, &block)
|
28
|
-
|
29
|
-
HTTP::Query.new(url, :post, options)
|
27
|
+
create_query(url, :post, options, block)
|
30
28
|
end
|
31
29
|
|
32
30
|
# Make a PUT request
|
33
31
|
def self.put(url, options={}, &block)
|
34
|
-
|
35
|
-
HTTP::Query.new(url, :put, options)
|
32
|
+
create_query(url, :put, options, block)
|
36
33
|
end
|
37
34
|
|
38
35
|
# Make a DELETE request
|
39
36
|
def self.delete(url, options={}, &block)
|
40
|
-
|
41
|
-
HTTP::Query.new(url, :delete, options)
|
37
|
+
create_query(url, :delete, options, block)
|
42
38
|
end
|
43
39
|
|
44
40
|
# Make a HEAD request
|
45
41
|
def self.head(url, options={}, &block)
|
46
|
-
|
47
|
-
HTTP::Query.new(url, :head, options)
|
42
|
+
create_query(url, :head, options, block)
|
48
43
|
end
|
49
44
|
|
50
45
|
# Make a PATCH request
|
51
46
|
def self.patch(url, options={}, &block)
|
52
|
-
|
53
|
-
|
47
|
+
create_query(url, :patch, options, block)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.create_query(url, method, options, block)
|
51
|
+
options[:action] = block if block
|
52
|
+
HTTP::Query.new(url, method, options)
|
54
53
|
end
|
55
54
|
|
56
55
|
# Response class wrapping the results of a Query's response
|
@@ -95,7 +94,7 @@ module BubbleWrap
|
|
95
94
|
attr_reader :response_headers
|
96
95
|
attr_reader :response_size
|
97
96
|
attr_reader :options
|
98
|
-
|
97
|
+
CLRF = "\r\n"
|
99
98
|
# ==== Parameters
|
100
99
|
# url<String>:: url of the resource to download
|
101
100
|
# http_method<Symbol>:: Value representing the HTTP method to use
|
@@ -127,7 +126,7 @@ module BubbleWrap
|
|
127
126
|
@url = create_url(url_string)
|
128
127
|
@body = create_request_body
|
129
128
|
@request = create_request
|
130
|
-
|
129
|
+
|
131
130
|
@connection = create_connection(request, self)
|
132
131
|
@connection.start
|
133
132
|
|
@@ -157,15 +156,18 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
157
156
|
end
|
158
157
|
|
159
158
|
def connection(connection, willSendRequest:request, redirectResponse:redirect_response)
|
160
|
-
@
|
161
|
-
@
|
162
|
-
log "##{@
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
159
|
+
@redirect_count ||= 0
|
160
|
+
@redirect_count += 1
|
161
|
+
log "##{@redirect_count} HTTP redirect_count: #{request.inspect} - #{self.description}"
|
162
|
+
|
163
|
+
if @redirect_count >= 30
|
164
|
+
@response.error_message = "Too many redirections"
|
165
|
+
@request.done_loading!
|
166
|
+
call_delegator_with_response
|
167
|
+
nil
|
168
|
+
else
|
169
|
+
request
|
170
|
+
end
|
169
171
|
end
|
170
172
|
|
171
173
|
def connection(connection, didFailWithError: error)
|
@@ -212,6 +214,7 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
212
214
|
cachePolicy:@cache_policy,
|
213
215
|
timeoutInterval:@timeout)
|
214
216
|
request.setHTTPMethod(@method)
|
217
|
+
set_content_type
|
215
218
|
request.setAllHTTPHeaderFields(@headers)
|
216
219
|
request.setHTTPBody(@body)
|
217
220
|
patch_nsurl_request(request)
|
@@ -219,6 +222,30 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
219
222
|
request
|
220
223
|
end
|
221
224
|
|
225
|
+
def set_content_type
|
226
|
+
return if headers_provided?
|
227
|
+
return if (@method == "GET" || @method == "HEAD")
|
228
|
+
@headers ||= {}
|
229
|
+
@headers["Content-Type"] = case @format
|
230
|
+
when :json
|
231
|
+
"application/json"
|
232
|
+
when :xml
|
233
|
+
"application/xml"
|
234
|
+
when :text
|
235
|
+
"text/plain"
|
236
|
+
else
|
237
|
+
if @format == :form_data || @payload_or_files_were_appended
|
238
|
+
"multipart/form-data; boundary=#{@boundary}"
|
239
|
+
else
|
240
|
+
"application/x-www-form-urlencoded"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def headers_provided?
|
246
|
+
@headers && @headers.keys.find {|k| k.downcase == 'content-type'}
|
247
|
+
end
|
248
|
+
|
222
249
|
def create_request_body
|
223
250
|
return nil if (@method == "GET" || @method == "HEAD")
|
224
251
|
return nil unless (@payload || @files)
|
@@ -227,36 +254,17 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
227
254
|
|
228
255
|
append_payload(body) if @payload
|
229
256
|
append_files(body) if @files
|
230
|
-
append_body_boundary(body) if @
|
257
|
+
append_body_boundary(body) if @payload_or_files_were_appended
|
231
258
|
|
232
259
|
log "Built HTTP body: \n #{body.to_str}"
|
233
260
|
body
|
234
261
|
end
|
235
262
|
|
236
|
-
def set_content_type
|
237
|
-
# if no headers provided, set content-type automatically
|
238
|
-
if @headers.nil? || !@headers.keys.find {|k| k.downcase == 'content-type'}
|
239
|
-
@headers ||= {}
|
240
|
-
@headers["Content-Type"] = case @format
|
241
|
-
when :json
|
242
|
-
"application/json"
|
243
|
-
when :xml
|
244
|
-
"application/xml"
|
245
|
-
when :text
|
246
|
-
"text/plain"
|
247
|
-
else
|
248
|
-
if @format == :form_data || @set_body_to_close_boundary
|
249
|
-
"multipart/form-data; boundary=#{@boundary}"
|
250
|
-
else
|
251
|
-
"application/x-www-form-urlencoded"
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
263
|
def append_payload(body)
|
258
264
|
if @payload.is_a?(NSData)
|
259
265
|
body.appendData(@payload)
|
266
|
+
elsif @payload.is_a?(String)
|
267
|
+
body.appendData(@payload.dataUsingEncoding NSUTF8StringEncoding)
|
260
268
|
else
|
261
269
|
append_form_params(body)
|
262
270
|
end
|
@@ -264,40 +272,37 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
264
272
|
end
|
265
273
|
|
266
274
|
def append_form_params(body)
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
s += value.to_s
|
277
|
-
form_data.appendData(s.dataUsingEncoding NSUTF8StringEncoding)
|
278
|
-
body.appendData(form_data)
|
279
|
-
end
|
280
|
-
@set_body_to_close_boundary = true
|
275
|
+
list = process_payload_hash(@payload)
|
276
|
+
list.each do |key, value|
|
277
|
+
form_data = NSMutableData.new
|
278
|
+
s = "--#{@boundary}\r\n"
|
279
|
+
s += "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n"
|
280
|
+
s += value.to_s
|
281
|
+
s += "\r\n"
|
282
|
+
form_data.appendData(s.dataUsingEncoding NSUTF8StringEncoding)
|
283
|
+
body.appendData(form_data)
|
281
284
|
end
|
285
|
+
@payload_or_files_were_appended = true
|
282
286
|
body
|
283
287
|
end
|
284
288
|
|
285
289
|
def append_files(body)
|
286
290
|
@files.each do |key, value|
|
287
291
|
file_data = NSMutableData.new
|
288
|
-
s = "
|
292
|
+
s = "--#{@boundary}\r\n"
|
289
293
|
s += "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{key}\"\r\n"
|
290
294
|
s += "Content-Type: application/octet-stream\r\n\r\n"
|
291
295
|
file_data.appendData(s.dataUsingEncoding NSUTF8StringEncoding)
|
292
296
|
file_data.appendData(value)
|
297
|
+
file_data.appendData("\r\n".dataUsingEncoding NSUTF8StringEncoding)
|
293
298
|
body.appendData(file_data)
|
294
299
|
end
|
295
|
-
@
|
300
|
+
@payload_or_files_were_appended = true
|
296
301
|
body
|
297
302
|
end
|
298
303
|
|
299
304
|
def append_body_boundary(body)
|
300
|
-
body.appendData("
|
305
|
+
body.appendData("--#{@boundary}--\r\n".dataUsingEncoding NSUTF8StringEncoding)
|
301
306
|
end
|
302
307
|
|
303
308
|
def create_url(url_string)
|
@@ -305,7 +310,16 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
305
310
|
convert_payload_to_url if @payload.is_a?(Hash)
|
306
311
|
url_string += "?#{@payload}"
|
307
312
|
end
|
308
|
-
NSURL.URLWithString(url_string.stringByAddingPercentEscapesUsingEncoding NSUTF8StringEncoding)
|
313
|
+
url = NSURL.URLWithString(url_string.stringByAddingPercentEscapesUsingEncoding NSUTF8StringEncoding)
|
314
|
+
|
315
|
+
validate_url(url)
|
316
|
+
url
|
317
|
+
end
|
318
|
+
|
319
|
+
def validate_url(url)
|
320
|
+
if !NSURLConnection.canHandleRequest(NSURLRequest.requestWithURL(url))
|
321
|
+
raise InvalidURLError, "Invalid URL provided (Make sure you include a valid URL scheme, e.g. http:// or similar)."
|
322
|
+
end
|
309
323
|
end
|
310
324
|
|
311
325
|
def convert_payload_to_url
|
@@ -342,14 +356,14 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
342
356
|
return nil if hash.nil?
|
343
357
|
escaped_hash = {}
|
344
358
|
|
345
|
-
hash.each{|k,v| escaped_hash[k] = v.gsub("\n",
|
359
|
+
hash.each{|k,v| escaped_hash[k] = v.gsub("\n", CLRF) }
|
346
360
|
escaped_hash
|
347
361
|
end
|
348
362
|
|
349
363
|
def patch_nsurl_request(request)
|
350
364
|
request.instance_variable_set("@done_loading", false)
|
351
365
|
|
352
|
-
def request.done_loading
|
366
|
+
def request.done_loading?; @done_loading; end
|
353
367
|
def request.done_loading!; @done_loading = true; end
|
354
368
|
end
|
355
369
|
|
@@ -367,3 +381,5 @@ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
|
|
367
381
|
end
|
368
382
|
end
|
369
383
|
end
|
384
|
+
|
385
|
+
class InvalidURLError < StandardError; end
|