bubble-wrap 1.1.2 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
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