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.
Files changed (40) hide show
  1. data/Gemfile.lock +1 -1
  2. data/README.md +32 -9
  3. data/Rakefile +1 -5
  4. data/lib/bubble-wrap/all.rb +1 -1
  5. data/lib/bubble-wrap/http.rb +1 -0
  6. data/lib/bubble-wrap/media.rb +8 -0
  7. data/lib/bubble-wrap/version.rb +1 -1
  8. data/motion/core/app.rb +5 -0
  9. data/motion/core/device.rb +1 -1
  10. data/motion/core/json.rb +1 -0
  11. data/motion/core/ns_url_request.rb +4 -2
  12. data/motion/core/persistence.rb +7 -1
  13. data/motion/core/string.rb +2 -2
  14. data/motion/http.rb +81 -65
  15. data/motion/location/location.rb +2 -2
  16. data/motion/media/media.rb +15 -0
  17. data/motion/media/player.rb +139 -0
  18. data/motion/test_suite_delegate.rb +1 -0
  19. data/resources/test.mp3 +0 -0
  20. data/samples/camera/Gemfile +3 -0
  21. data/samples/camera/README.md +4 -0
  22. data/samples/camera/Rakefile +9 -0
  23. data/samples/camera/app/app_delegate.rb +8 -0
  24. data/samples/camera/app/controllers/camera_controller.rb +61 -0
  25. data/samples/camera/spec/main_spec.rb +9 -0
  26. data/samples/location/.gitignore +5 -0
  27. data/samples/location/Gemfile +3 -0
  28. data/samples/location/README.md +4 -0
  29. data/samples/location/Rakefile +10 -0
  30. data/samples/location/app/app_delegate.rb +8 -0
  31. data/samples/location/app/controllers/image_list_controller.rb +30 -0
  32. data/samples/location/app/models/places.rb +15 -0
  33. data/samples/location/spec/main_spec.rb +9 -0
  34. data/spec/motion/core/app_spec.rb +7 -1
  35. data/spec/motion/core/json_spec.rb +4 -0
  36. data/spec/motion/core/persistence_spec.rb +7 -3
  37. data/spec/motion/core/string_spec.rb +24 -0
  38. data/spec/motion/http_spec.rb +73 -61
  39. data/spec/motion/media/player_spec.rb +77 -0
  40. metadata +57 -14
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bubble-wrap (1.1.2)
4
+ bubble-wrap (1.1.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
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 { 99 }
639
+ > operation = proc { 88 }
617
640
  => #<Proc:0x6d763c0>
618
- > callback = proc { |speed| puts speed >= 99 ? "Time travel!" : "Conventional travel!" }
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
@@ -1,4 +1,4 @@
1
1
  require File.expand_path('../loader', __FILE__)
2
- ['core', 'http', 'reactor', 'rss_parser', 'ui', 'location'].each { |sub|
2
+ ['core', 'http', 'reactor', 'rss_parser', 'ui', 'location', 'media'].each { |sub|
3
3
  require File.expand_path("../#{sub}", __FILE__)
4
4
  }
@@ -1,3 +1,4 @@
1
1
  require 'bubble-wrap/loader'
2
2
  BubbleWrap.require('motion/core/ns_url_request.rb')
3
+ BubbleWrap.require('motion/core.rb')
3
4
  BubbleWrap.require('motion/http.rb')
@@ -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
@@ -1,3 +1,3 @@
1
1
  module BubbleWrap
2
- VERSION = '1.1.2' unless defined?(BubbleWrap::VERSION)
2
+ VERSION = '1.1.3' unless defined?(BubbleWrap::VERSION)
3
3
  end
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
@@ -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}, cache policy: #{self.cachePolicy}, Pipelining: #{self.HTTPShouldUsePipelining}, main doc url: #{mainDocumentURL},\
7
- timeout: #{self.timeoutInterval}, network service type: #{self.networkServiceType} >"
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
@@ -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)
@@ -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(false)}Color"
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
- options[:action] = block if block_given?
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
- options[:action] = block if block_given?
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
- options[:action] = block if block_given?
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
- options[:action] = block if block_given?
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
- options[:action] = block if block_given?
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
- options[:action] = block if block_given?
53
- HTTP::Query.new(url, :patch, options)
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
- set_content_type
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
- @redirection ||= 0
161
- @redirection += 1
162
- log "##{@redirection} HTTP redirection: #{request} - #{self.description}"
163
- new_request = request.mutableCopy
164
- # new_request.setValue(@credentials.inspect, forHTTPHeaderField:'Authorization') # disabled while we figure this one out
165
- new_request.setAllHTTPHeaderFields(@headers) if @headers
166
- @connection.cancel
167
- @connection = create_connection(new_request, self)
168
- new_request
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 @set_body_to_close_boundary
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
- # puts "*** append_form #{@payload}"
268
- if @payload.is_a?(String)
269
- body.appendData(@payload.dataUsingEncoding NSUTF8StringEncoding)
270
- else
271
- list = process_payload_hash(@payload)
272
- list.each do |key, value|
273
- form_data = NSMutableData.new
274
- s = "\r\n--#{@boundary}\r\n"
275
- s += "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n"
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 = "\r\n--#{@boundary}\r\n"
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
- @set_body_to_close_boundary = true
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("\r\n--#{@boundary}--\r\n".dataUsingEncoding NSUTF8StringEncoding)
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", '\\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; @done_loading; end
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