bubble-wrap 1.7.1 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.travis.yml +1 -1
  4. data/GETTING_STARTED.md +1 -1
  5. data/Gemfile.lock +10 -10
  6. data/README.md +266 -122
  7. data/Rakefile +1 -1
  8. data/bubble-wrap.gemspec +7 -6
  9. data/lib/bubble-wrap.rb +0 -1
  10. data/lib/bubble-wrap/all.rb +13 -1
  11. data/lib/bubble-wrap/location.rb +1 -1
  12. data/lib/bubble-wrap/motion.rb +10 -0
  13. data/lib/bubble-wrap/rss_parser.rb +0 -1
  14. data/lib/bubble-wrap/test.rb +1 -0
  15. data/lib/bubble-wrap/version.rb +1 -1
  16. data/motion/core/device/ios/camera.rb +12 -1
  17. data/motion/core/ios/device.rb +7 -1
  18. data/motion/core/json.rb +1 -1
  19. data/motion/core/osx/app.rb +11 -1
  20. data/motion/core/time.rb +27 -4
  21. data/motion/location/location.rb +6 -2
  22. data/motion/mail/mail.rb +4 -0
  23. data/motion/media/player.rb +2 -1
  24. data/motion/motion/motion.rb +421 -0
  25. data/motion/reactor/deferrable.rb +29 -3
  26. data/motion/reactor/eventable.rb +3 -1
  27. data/motion/reactor/thread_aware_deferrable.rb +37 -0
  28. data/motion/rss_parser.rb +11 -21
  29. data/motion/sms/sms.rb +4 -0
  30. data/motion/ui/ui_alert_view.rb +3 -1
  31. data/motion/ui/ui_control_wrapper.rb +27 -0
  32. data/motion/ui/ui_view_wrapper.rb +1 -7
  33. data/motion/util/constants.rb +1 -1
  34. data/samples/alert/Gemfile +1 -0
  35. data/samples/alert/Gemfile.lock +16 -0
  36. data/samples/alert/Rakefile +1 -1
  37. data/samples/camera/Gemfile +2 -1
  38. data/samples/camera/Gemfile.lock +16 -0
  39. data/samples/camera/Rakefile +1 -1
  40. data/samples/gesture/Gemfile +2 -1
  41. data/samples/gesture/Gemfile.lock +9 -3
  42. data/samples/gesture/Rakefile +1 -1
  43. data/samples/location/Gemfile +3 -1
  44. data/samples/location/Gemfile.lock +18 -0
  45. data/samples/location/Rakefile +4 -2
  46. data/samples/location/app/controllers/{image_list_controller.rb → places_list_controller.rb} +0 -0
  47. data/samples/media/Gemfile +4 -0
  48. data/samples/media/Gemfile.lock +16 -0
  49. data/samples/media/Rakefile +1 -1
  50. data/samples/osx/Gemfile +3 -1
  51. data/samples/osx/Gemfile.lock +5 -1
  52. data/spec/lib/bubble-wrap/requirement_spec.rb +2 -2
  53. data/spec/motion/core/app_spec.rb +23 -0
  54. data/spec/motion/core/device/ios/camera_spec.rb +1 -1
  55. data/spec/motion/core/device/ios/device_spec.rb +6 -0
  56. data/spec/motion/core/ios/app_spec.rb +9 -24
  57. data/spec/motion/core/json_spec.rb +30 -10
  58. data/spec/motion/core/osx/app_spec.rb +2 -1
  59. data/spec/motion/core/time_spec.rb +34 -1
  60. data/spec/motion/location/location_spec.rb +6 -0
  61. data/spec/motion/mail/mail_spec.rb +20 -16
  62. data/spec/motion/motion/core_motion_spec.rb +231 -0
  63. data/spec/motion/reactor/deferrable_spec.rb +81 -0
  64. data/spec/motion/reactor/eventable_spec.rb +11 -0
  65. data/spec/motion/reactor/thread_aware_deferrable_spec.rb +85 -0
  66. data/spec/motion/rss_parser_spec.rb +11 -21
  67. data/spec/motion/sms/sms_spec.rb +11 -6
  68. data/spec/motion/ui/ui_alert_view_spec.rb +23 -0
  69. data/spec/motion/ui/ui_control_wrapper_spec.rb +24 -0
  70. metadata +58 -38
  71. data/lib/bubble-wrap/http.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: faee2e15d6887fcd2cdb83c67b5cc2a62a421624
4
- data.tar.gz: 2689f157cacaef27b81b313fff35803f78f8ea88
3
+ metadata.gz: bf1fb0a71b37c7e7473219036a4d8304dd7c9fd1
4
+ data.tar.gz: 1f57a00000623dac2a425cceb6ce5f74ae40eb3e
5
5
  SHA512:
6
- metadata.gz: 9dcf5e4a7c790d9908499f9a0caafbfec344b264ff150741e2d86ad306f4b22ffe6335cdf552c1a6c2b4638b0e74771b705735b9315fbb54c3817c65d0cd8f3e
7
- data.tar.gz: d1f7d8a3a0c0765c3da1ce7760b60ba2f55499b963e791c8df4ecec4d2587e3c3a6ee1dfb15a1c393efa557c7b1527b3079b5e8a74cc52b57be551d804327f12
6
+ metadata.gz: e259382ebbc9970d3b264f9a95961807944bb17da2aec89f6af873d4b267e1319d2ac1dfc86cd0a2ef08d205cf9719160a10d7db5bf32c51c8926fdc2f332ec2
7
+ data.tar.gz: edced4a22d3af5a7f067a08a84176206aa039632adf97b46411582a33f0cd8a6b256f730aecbf73c3a639a51d404d563d163d76973680fecb38b73d661d28c01
data/.gitignore CHANGED
@@ -3,4 +3,3 @@ pkg/*
3
3
  build/
4
4
  .DS_Store
5
5
  .repl_history
6
- 0
data/.travis.yml CHANGED
@@ -9,4 +9,4 @@ script:
9
9
  - bundle exec rake clean
10
10
  - bundle exec rake spec
11
11
  - bundle exec rake clean
12
- - bundle exec rake spec osx=true
12
+ - bundle exec rake spec osx=true
data/GETTING_STARTED.md CHANGED
@@ -98,7 +98,7 @@ BW.require '/path/to/some/files/**/*.rb'
98
98
  ```
99
99
 
100
100
  For more information in using `BW.require` take a look at
101
- [the bubblewrap hacking guide](hacking.html).
101
+ [the bubblewrap hacking guide](HACKING.md).
102
102
 
103
103
  ## Go forth and conquer!
104
104
 
data/Gemfile.lock CHANGED
@@ -1,27 +1,27 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bubble-wrap (1.7.1)
5
- bubble-wrap-http (= 1.7.1)
4
+ bubble-wrap (1.8.0)
6
5
 
7
6
  GEM
8
7
  remote: https://rubygems.org/
9
8
  specs:
10
- bacon (1.1.0)
11
- bubble-wrap-http (1.7.1)
12
- metaclass (0.0.1)
9
+ bacon (1.2.0)
10
+ metaclass (0.0.4)
13
11
  mocha (0.11.4)
14
12
  metaclass (~> 0.0.1)
15
13
  mocha-on-bacon (0.2.1)
16
14
  mocha (>= 0.9.8)
17
- rake (0.9.2.2)
15
+ rake (10.3.2)
16
+ webstub (1.1.2)
18
17
 
19
18
  PLATFORMS
20
19
  ruby
21
20
 
22
21
  DEPENDENCIES
23
- bacon
22
+ bacon (~> 1.2)
24
23
  bubble-wrap!
25
- mocha (= 0.11.4)
26
- mocha-on-bacon
27
- rake
24
+ mocha (~> 0.11)
25
+ mocha-on-bacon (~> 0.2)
26
+ rake (~> 10.0)
27
+ webstub (~> 1.1)
data/README.md CHANGED
@@ -5,7 +5,7 @@ A collection of (tested) helpers and wrappers used to wrap Cocoa Touch and AppKi
5
5
  [BubbleWrap website](http://rubymotion.github.io/BubbleWrap/)
6
6
  [BubbleWrap mailing list](https://groups.google.com/forum/#!forum/bubblewrap)
7
7
 
8
- [![Code Climate](https://codeclimate.com/github/rubymotion/BubbleWrap.png)](https://codeclimate.com/github/rubymotion/BubbleWrap)
8
+ [![Code Climate](https://codeclimate.com/github/rubymotion/BubbleWrap.svg)](https://codeclimate.com/github/rubymotion/BubbleWrap)
9
9
  [![Build Status](https://travis-ci.org/rubymotion/BubbleWrap.svg?branch=master)](https://travis-ci.org/rubymotion/BubbleWrap)
10
10
  [![Dependency Status](https://gemnasium.com/rubymotion/BubbleWrap.png)](https://gemnasium.com/rubymotion/BubbleWrap)
11
11
 
@@ -26,24 +26,10 @@ require 'bubble-wrap'
26
26
  If you using Bundler:
27
27
 
28
28
  ```ruby
29
- gem "bubble-wrap", "~> 1.7.0"
29
+ gem "bubble-wrap", "~> 1.8.0"
30
30
  ```
31
31
 
32
- BubbleWrap is split into multiple modules so that you can easily choose which parts
33
- are included at compile-time.
34
-
35
- The above example requires the `core` and `http` modules. If you wish to only
36
- include the core modules use the following line of code instead:
37
-
38
- ```ruby
39
- require 'bubble-wrap/core'
40
- ```
41
-
42
- If you wish to only include the `HTTP` wrapper:
43
-
44
- ```ruby
45
- require 'bubble-wrap/http'
46
- ```
32
+ BubbleWrap is split into multiple modules so that you can easily choose which parts are included at compile-time.
47
33
 
48
34
  If you wish to only include the `RSS Parser` wrapper:
49
35
 
@@ -105,6 +91,11 @@ If you want to include everything (ie kitchen sink mode) you can save time and d
105
91
  require 'bubble-wrap/all'
106
92
  ```
107
93
 
94
+ You can also do this directly in your `Gemfile` like so:
95
+
96
+ ```ruby
97
+ gem 'bubble-wrap', require: %w[bubble-wrap/core bubble-wrap/location, bubble-wrap/reactor]
98
+ ```
108
99
 
109
100
  Note: **DON'T** use `app.files =` in your Rakefile to set up your files once you've required BubbleWrap.
110
101
  Make sure to append onto the array or use `+=`.
@@ -246,6 +237,8 @@ Examples:
246
237
  # 480
247
238
  > Device.screen.height_for_orientation(:landscape_left)
248
239
  # 320
240
+ > Device.vendor_identifier
241
+ # <NSUUID>
249
242
  ```
250
243
 
251
244
  ### Camera
@@ -273,8 +266,12 @@ BW::Device.camera.any.picture(allows_editing: true, media_types: [:image]) do |r
273
266
  edited_image_view = UIImageView.alloc.initWithImage(result[:edited_image])
274
267
  original_image_view = UIImageView.alloc.initWithImage(result[:original_image])
275
268
  end
276
- ```
277
269
 
270
+ # Capture a low quality movie with a limit of 10 seconds
271
+ BW::Device.camera.front.picture(media_types: [:movie], video_quality: :low, video_maximum_duration: 10) do |result|
272
+ video_file_path = result[:media_url]
273
+ end
274
+ ```
278
275
 
279
276
  Options include:
280
277
 
@@ -282,13 +279,15 @@ Options include:
282
279
  - `:animated` - Boolean; whether to display the camera with an animation (default true)
283
280
  - `:on_dismiss` - Lambda; called instead of the default dismissal logic
284
281
  - `:media_types` - Array; containing any of `[:movie, :image]`
282
+ - `:video_quality` - Symbol; one of `:high`, `:medium`, `low`, `"640x480".to_sym`, `iframe1280x720`, or `iframe960x540`. Defaults to `:medium`
283
+ - `:video_maximum_duration` - Integer; limits movie recording length. Defaults to 600.
285
284
 
286
285
  ### JSON
287
286
 
288
287
  `BW::JSON` wraps `NSJSONSerialization` available in iOS5 and offers the same API as Ruby's JSON std lib. For apps building for iOS4, we suggest a different JSON alternative, like [AnyJSON](https://github.com/mattt/AnyJSON).
289
288
 
290
289
  ```ruby
291
- BW::JSON.generate({'foo => 1, 'bar' => [1,2,3], 'baz => 'awesome'})
290
+ BW::JSON.generate({'foo' => 1, 'bar' => [1,2,3], 'baz' => 'awesome'})
292
291
  => "{\"foo\":1,\"bar\":[1,2,3],\"baz\":\"awesome\"}"
293
292
  BW::JSON.parse "{\"foo\":1,\"bar\":[1,2,3],\"baz\":\"awesome\"}"
294
293
  => {"foo"=>1, "bar"=>[1, 2, 3], "baz"=>"awesome"}
@@ -359,9 +358,9 @@ class ExampleViewController < UIViewController
359
358
  include BW::KVO
360
359
 
361
360
  def viewDidLoad
362
- @label = UILabel.alloc.initWithFrame [[20,20],[280,44]]
363
- @label.text = ""
364
- view.addSubview @label
361
+ @label = UILabel.alloc.initWithFrame [[20,20],[280,44]]
362
+ @label.text = ""
363
+ view.addSubview @label
365
364
 
366
365
  observe(@label, :text) do |old_value, new_value|
367
366
  puts "Hello from viewDidLoad!"
@@ -402,7 +401,7 @@ iso8601 formatted string into a Time instance.
402
401
 
403
402
  ## Location
404
403
 
405
- Added interface for Ruby-like GPS and compass access:
404
+ Interface for Ruby-like GPS and compass access (the CoreLocation framework):
406
405
 
407
406
  ```ruby
408
407
  > BW::Location.enabled? # Whether location services are enabled on the device
@@ -449,6 +448,177 @@ BW::Location.get_compass_once do |heading|
449
448
  end
450
449
  ```
451
450
 
451
+ ### iOS 8 Location Requirements
452
+
453
+ iOS 8 introduced stricter location services requirements. Although BubbleWrap will handle most of this for you automatically, you are required to add a few key/value pairs to the `Info.plist`. Add these two lines to your `Rakefile` (with your descriptions, obviously):
454
+
455
+ ```ruby
456
+ app.info_plist['NSLocationAlwaysUsageDescription'] = 'Description'
457
+ app.info_plist['NSLocationWhenInUseUsageDescription'] = 'Description'
458
+ ```
459
+
460
+ *Note: you need both keys to use `get_once`, so it's probably best to just include both no matter what.* See [Apple's documentation](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW18) on iOS 8 location services requirements for more information.
461
+
462
+ ## Motion
463
+
464
+ Interface for the accelerometer, gyroscope, and magnetometer sensors (the
465
+ CoreMotion framework). You can access each sensor individually, or you can get
466
+ data from all of them at once using the `BW::Motion.device` interface, which
467
+ delegates to `CMMotionManager#deviceMotion`.
468
+
469
+ Each sensor has an `every` and `once` method. `every` expects a time interval,
470
+ and you will need to retain the object it returns and call `#stop` on it when
471
+ you are done with the data.
472
+
473
+ The `every` and `once` methods can accept a `:queue` option. The default value
474
+ is a queue that runs on the main loop, so that UI updates can be processed in
475
+ the block. This is useful, but not recommended by Apple, since the events can
476
+ come in at a high rate. If you want to use a background queue, you can either
477
+ specify an NSOperationQueue object, or you can use one of these symbols:
478
+
479
+ - `:main` - `NSOperationQueue.mainQueue`, this is the default value.
480
+ - `:background` - BubbleWrap will create a new `NSOperationQueue`.
481
+ - `:current` - BubbleWrap will use `NSOperationQueue.currentQueue`.
482
+
483
+ If you pass a string instead, a new queue will be created and its `name`
484
+ property will be set to that string.
485
+
486
+ The `CMDeviceMotion` interface (`BW::Motion.device`) accepts a `:reference`
487
+ option, which specifies the `CMAttitudeReferenceFrame`. The default value is
488
+ the same as the one that `CMMotionManager` uses, which is returned by the
489
+ `CMMotionManager#attitudeReferenceFrame` method. This option should be passed
490
+ to the `repeat`, `every` or `once` methods.
491
+
492
+ ###### Accelerometer
493
+ ```ruby
494
+ BW::Motion.accelerometer.available?
495
+ BW::Motion.accelerometer.data # returns CMAccelerometerData object or nil
496
+
497
+ # ask the CMMotionManager to update every 5 seconds
498
+ BW::Motion.accelerometer.every(5) do |result|
499
+ # result contains the following data (from CMAccelerometerData#acceleration):
500
+ p result[:data] # the CMAccelerometerData object
501
+ p result[:acceleration] # the CMAcceleration struct
502
+ p result[:x] # acceleration in the x direction
503
+ p result[:y] # " y direction
504
+ p result[:z] # " z direction
505
+ end
506
+
507
+ # every, start, and repeat all need to be stopped later.
508
+ BW::Motion.accelerometer.stop
509
+
510
+ # repeat, but don't set the interval
511
+ BW::Motion.accelerometer.repeat do |result|
512
+ end
513
+
514
+ # you can specify a :queue where the operations will be executed. See above for details
515
+ BW::Motion.accelerometer.every(5, queue: :background) { |result| ... }
516
+ BW::Motion.accelerometer.every(5, queue: :main) { |result| ... }
517
+ BW::Motion.accelerometer.every(5, queue: :current) { |result| ... }
518
+ BW::Motion.accelerometer.every(5, queue: 'my queue') { |result| ... }
519
+
520
+ BW::Motion.accelerometer.once do |result|
521
+ # ...
522
+ end
523
+ ```
524
+
525
+ ###### Gyroscope
526
+ ```ruby
527
+ BW::Motion.gyroscope.available?
528
+ BW::Motion.gyroscope.data # returns CMGyroData object or nil
529
+
530
+ # ask the CMMotionManager to update every second.
531
+ BW::Motion.gyroscope.every(1) do |result|
532
+ # result contains the following data (from CMGyroData#rotationRate):
533
+ p result[:data] # the CMGyroData object
534
+ p result[:rotation] # the CMRotationRate struct
535
+ p result[:x] # rotation in the x direction
536
+ p result[:y] # " y direction
537
+ p result[:z] # " z direction
538
+ end
539
+ BW::Motion.gyroscope.stop
540
+
541
+ BW::Motion.gyroscope.once do |result|
542
+ # ...
543
+ end
544
+ ```
545
+
546
+ ###### Magnetometer
547
+ ```ruby
548
+ BW::Motion.magnetometer.available?
549
+ BW::Motion.magnetometer.data # returns CMMagnetometerData object or nil
550
+
551
+ # ask the CMMotionManager to update every second
552
+ BW::Motion.magnetometer.every(1) do |result|
553
+ # result contains the following data (from CMMagnetometerData#magneticField):
554
+ p result[:data] # the CMMagnetometerData object
555
+ p result[:field] # the CMMagneticField struct
556
+ p result[:x] # magnetic field in the x direction
557
+ p result[:y] # " y direction
558
+ p result[:z] # " z direction
559
+ end
560
+ BW::Motion.magnetometer.stop
561
+
562
+ BW::Motion.magnetometer.once do |result|
563
+ # ...
564
+ end
565
+ ```
566
+
567
+ ###### Device Motion
568
+
569
+ This is an amalgam of all the motion sensor data.
570
+
571
+ ```ruby
572
+ BW::Motion.device.available?
573
+ BW::Motion.device.data # returns CMDeviceMotion object or nil
574
+
575
+ BW::Motion.device.every(1) do |result|
576
+ # result contains the following data:
577
+ p result[:data] # the CMDeviceMotion object
578
+ # orientation data, from CMDeviceMotion#attitude
579
+ p result[:attitude] # the CMAttitude struct
580
+ p result[:roll]
581
+ p result[:pitch]
582
+ p result[:yaw]
583
+ # rotation data, from CMDeviceMotion#rotationRate
584
+ p result[:rotation] # the CMRotationRate struct
585
+ p result[:rotation_x]
586
+ p result[:rotation_y]
587
+ p result[:rotation_z]
588
+ # gravity+acceleration vector, from CMDeviceMotion#gravity
589
+ p result[:gravity] # the CMAcceleration struct
590
+ p result[:gravity_x]
591
+ p result[:gravity_y]
592
+ p result[:gravity_z]
593
+ # just the acceleration vector, from CMDeviceMotion#userAcceleration
594
+ p result[:acceleration] # the CMAcceleration struct
595
+ p result[:acceleration_x]
596
+ p result[:acceleration_y]
597
+ p result[:acceleration_z]
598
+ # the magnetic data, from CMDeviceMotion#magneticField
599
+ p result[:magnetic] # the CMCalibratedMagneticField struct
600
+ p result[:magnetic_field] # the CMMagneticField struct from the CMCalibratedMagneticField
601
+ p result[:magnetic_x]
602
+ p result[:magnetic_y]
603
+ p result[:magnetic_z]
604
+ p result[:magnetic_accuracy] # this will be a symbol, :low, :medium, :high, or nil if the magnetic data is uncalibrated
605
+
606
+ # less useful data from CMAttitude, unless you're into the whole linear algebra thing:
607
+ p result[:matrix] # CMAttitude#rotationMatrix
608
+ p result[:quarternion] # CMAttitude#quarternion
609
+ end
610
+
611
+ # the reference frame should be one of the CMAttitudeReferenceFrame constants...
612
+ ref = CMAttitudeReferenceFrameXArbitraryZVertical
613
+ # ... or one of these symbols: :arbitrary_z, :corrected_z, :magnetic_north, :true_north
614
+ ref = :corrected_z
615
+ BW::Motion.device.every(1, queue: :background, reference: ref) { |result| ... }
616
+
617
+ BW::Motion.device.once do |result|
618
+ # ...
619
+ end
620
+ ```
621
+
452
622
  ## Media
453
623
 
454
624
  Added wrapper for playing remote and local media. Available are `modal` and custom presentation styles:
@@ -469,6 +639,8 @@ BW::Media.play_modal("http://www.hrupin.com/wp-content/uploads/mp3/testsong_20_s
469
639
 
470
640
  Wrapper for showing an in-app mail composer view.
471
641
 
642
+ You should always determine if the device your app is running on is configured to send mail before displaying a mail composer window. `BW::Mail.can_send_mail?` will return `true` or `false`.
643
+
472
644
  ```ruby
473
645
  # Opens as a modal in the current UIViewController
474
646
  BW::Mail.compose(
@@ -493,6 +665,8 @@ end
493
665
 
494
666
  Wrapper for showing an in-app message (SMS) composer view.
495
667
 
668
+ You should always determine if the device your app is running on can send SMS messages before displaying a SMS composer window. `BW::SMS.can_send_sms?` will return `true` or `false`.
669
+
496
670
  ```ruby
497
671
  # Opens as a modal in the current UIViewController
498
672
  BW::SMS.compose (
@@ -556,6 +730,12 @@ Extra methods on `UIView` for working with gesture recognizers. A gesture recogn
556
730
 
557
731
  There are similar methods for `pinched`, `rotated`, `swiped`, `panned`, and `pressed` (for long presses). All of the methods return the actual recognizer object, so it is possible to set the delegate if more fine-grained control is needed.
558
732
 
733
+ In order to prevent retain cycles due to strong references within the passed block, use the use_weak_callbacks flag so the blocks do not retain a strong reference to self:
734
+
735
+ ```ruby
736
+ BubbleWrap.use_weak_callbacks = true
737
+ ```
738
+
559
739
  ### UIViewController
560
740
 
561
741
  A custom method was added to `UIViewController` to return the content
@@ -571,6 +751,26 @@ button.when(UIControlEventTouchUpInside) do
571
751
  end
572
752
  ```
573
753
 
754
+ The `#when` method also accepts bitwise combinations of events:
755
+
756
+ ```ruby
757
+ button.when(UIControlEventTouchUpInside | UIControlEventTouchUpOutside) do
758
+ self.view.backgroundColor = UIColor.redColor
759
+ end
760
+ ```
761
+
762
+ You can use symbols for events (but won't work with the bitwise operator):
763
+
764
+ ```ruby
765
+ button.when(:touch_up_inside) do
766
+ self.view.backgroundColor = UIColor.redColor
767
+ end
768
+
769
+ button.when(:value_changed) do
770
+ self.view.backgroundColor = UIColor.blueColor
771
+ end
772
+ ```
773
+
574
774
  Set the use_weak_callbacks flag so the blocks do not retain a strong reference to self:
575
775
 
576
776
  ```ruby
@@ -729,105 +929,6 @@ Built in activities that can be passed to the `excluded` option are defined as `
729
929
  :air_drop
730
930
  ```
731
931
 
732
-
733
- ## HTTP
734
-
735
- `BW::HTTP` wraps `NSURLRequest`, `NSURLConnection` and friends to provide Ruby developers with a more familiar and easier to use API.
736
- The API uses async calls and blocks to stay as simple as possible.
737
-
738
- To enable it add the following require line to your `Rakefile`:
739
- ```ruby
740
- require 'bubble-wrap/http'
741
- ```
742
-
743
- Usage example:
744
-
745
- ```ruby
746
- BW::HTTP.get("https://api.github.com/users/mattetti") do |response|
747
- p response.body.to_str
748
- end
749
- ```
750
-
751
- ```ruby
752
- BW::HTTP.get("https://api.github.com/users/mattetti", {credentials: {username: 'matt', password: 'aimonetti'}}) do |response|
753
- p response.body.to_str # prints the response's body
754
- end
755
- ```
756
-
757
- ```ruby
758
- data = {first_name: 'Matt', last_name: 'Aimonetti'}
759
- BW::HTTP.post("http://foo.bar.com/", {payload: data}) do |response|
760
- if response.ok?
761
- json = BW::JSON.parse(response.body.to_str)
762
- p json['id']
763
- elsif response.status_code.to_s =~ /40\d/
764
- App.alert("Login failed")
765
- else
766
- App.alert(response.error_message)
767
- end
768
- end
769
- ```
770
-
771
- To upload files to a server, provide a `files:` hash:
772
-
773
- ```ruby
774
- data = {token: "some-api-token"}
775
- avatar_data = UIImagePNGRepresentation(UIImage.imageNamed("some-image"))
776
- avatar = { data: avatar_data, filename: "some-image.png", content_type: "image/png" }
777
-
778
- BW::HTTP.post("http://foo.bar.com/", {payload: data}, files: { avatar: avatar }) do |response|
779
- if response.ok?
780
- # files are uploaded
781
- end
782
- end
783
- ```
784
-
785
- A `:download_progress` option can also be passed. The expected object
786
- would be a Proc that takes two arguments: a float representing the
787
- amount of data currently received and another float representing the
788
- total amount of data expected.
789
-
790
- Connections can also be cancelled. Just keep a refrence,
791
-
792
- ```ruby
793
- @conn = BW::HTTP.get("https://api.github.com/users/mattetti") do |response|
794
- p response.body.to_str
795
- end
796
- ```
797
-
798
- and send the `cancel` method to it asynchronously as desired. The block will not be executed.
799
-
800
- ```ruby
801
- @conn.cancel
802
- ```
803
-
804
- ### Gotchas
805
-
806
- Because of how RubyMotion currently works, you sometimes need to assign objects as `@instance_variables` in order to retain their callbacks.
807
-
808
- For example:
809
-
810
- ```ruby
811
- class HttpClient
812
- def get_user(user_id, &callback)
813
- BW::HTTP.get(user_url(user_id)) do |response|
814
- # ..
815
- end
816
- end
817
- end
818
- ```
819
-
820
- This class should be invoked in your code as:
821
-
822
- ```ruby
823
- @http_client = HttpClient.new
824
- @http_client.get_user(user_id) do |user|
825
- # ..
826
- end
827
- ```
828
-
829
- (instead of doing an instance-variable-less `HttpClient.new.get_user`)
830
-
831
932
  ## RSS Parser
832
933
  **Since: > version 1.0.0**
833
934
 
@@ -928,6 +1029,14 @@ and timeout. When you initially create a deferrable it is in an unknown
928
1029
  state, however you can assign callbacks to be run when the object
929
1030
  changes to either successful or failure state.
930
1031
 
1032
+ Using `delegate`, `errback_delegate` and `callback_delegate` you can link
1033
+ deferrables together.
1034
+
1035
+ By default, callbacks will be made on the thread that the deferrable
1036
+ succeeds/fails on. For multithreaded environments, it can be useful to use
1037
+ EM::ThreadAwareDeferrable so that callbacks will be made on the threads they
1038
+ are declared on.
1039
+
931
1040
  #### Success
932
1041
 
933
1042
  ```ruby
@@ -951,6 +1060,37 @@ Great justice!
951
1060
  Great sadness!
952
1061
  => nil
953
1062
  ```
1063
+ #### Delegate
1064
+
1065
+ ```ruby
1066
+ > d = EM::DefaultDeferrable.new
1067
+ => #<BW::Reactor::DefaultDeferrable:0x8bf3ee0>
1068
+ > delegate = EM::DefaultDeferrable.new
1069
+ => #<BW::Reactor::DefaultDeferrable:0x8bf5910>
1070
+ > d.delegate delegate
1071
+ => #<BW::Reactor::DefaultDeferrable:0x8bf3ee0>
1072
+ > delegate.callback { |*args| puts args }
1073
+ => [#<Proc:0x8bf3ef0>]
1074
+ > d.succeed :passed
1075
+ => nil
1076
+ => [:passed]
1077
+ ```
1078
+
1079
+ #### ThreadAwareDeferrable
1080
+
1081
+ ```ruby
1082
+ > d = EM::ThreadAwareDeferrable.new
1083
+ => #<BW::Reactor::ThreadAwareDeferrable:0x8bf3ee0>
1084
+
1085
+ > queue = Dispatch::Queue.new(:deferrable.to_s)
1086
+ > queue.async do
1087
+ > d.callback do |*args|
1088
+ > Dispatch::Queue.current == queue
1089
+ > => true # this is normally false
1090
+ > end
1091
+ > end
1092
+ > d.succeed true
1093
+ ```
954
1094
 
955
1095
  #### Timeout
956
1096
 
@@ -1064,6 +1204,10 @@ Flux capacitor!
1064
1204
  > o.trigger(:november_5_1955)
1065
1205
  Ow!
1066
1206
  => [nil]
1207
+ > o.on(:november_5_1955) { puts "Ow!" }
1208
+ > o.on(:november_5_1955) { puts "Another Ow!" }
1209
+ > o.off(:november_5_1955)
1210
+ => nil
1067
1211
  ```
1068
1212
 
1069
1213
  # Suggestions?