bubble-wrap 1.3.0 → 1.4.0

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 (72) hide show
  1. checksums.yaml +6 -14
  2. data/.travis.yml +2 -1
  3. data/CHANGELOG.md +26 -2
  4. data/GETTING_STARTED.md +1 -1
  5. data/Gemfile.lock +1 -1
  6. data/HACKING.md +6 -4
  7. data/README.md +83 -3
  8. data/Rakefile +9 -7
  9. data/lib/bubble-wrap/all.rb +2 -2
  10. data/lib/bubble-wrap/mail.rb +9 -0
  11. data/lib/bubble-wrap/sms.rb +9 -0
  12. data/lib/bubble-wrap/ui.rb +6 -2
  13. data/lib/bubble-wrap/version.rb +1 -1
  14. data/motion/core.rb +1 -1
  15. data/motion/core/app.rb +7 -2
  16. data/motion/core/device/ios/camera.rb +3 -2
  17. data/motion/core/device/ios/screen.rb +19 -0
  18. data/motion/core/ios/app.rb +13 -1
  19. data/motion/core/ios/device.rb +5 -0
  20. data/motion/core/json.rb +1 -3
  21. data/motion/core/kvo.rb +11 -13
  22. data/motion/core/persistence.rb +8 -1
  23. data/motion/core/string.rb +3 -3
  24. data/motion/core/time.rb +5 -0
  25. data/motion/http.rb +1 -1
  26. data/motion/http/query.rb +39 -19
  27. data/motion/http/response.rb +4 -4
  28. data/motion/mail/mail.rb +59 -0
  29. data/motion/mail/result.rb +29 -0
  30. data/motion/reactor/eventable.rb +3 -2
  31. data/motion/reactor/periodic_timer.rb +12 -8
  32. data/motion/reactor/timer.rb +11 -7
  33. data/motion/sms/result.rb +24 -0
  34. data/motion/sms/sms.rb +47 -0
  35. data/motion/ui/pollute.rb +4 -1
  36. data/motion/ui/ui_alert_view.rb +15 -8
  37. data/motion/ui/ui_control_wrapper.rb +16 -0
  38. data/motion/ui/ui_view_controller_wrapper.rb +13 -0
  39. data/motion/ui/ui_view_wrapper.rb +55 -0
  40. data/resources/Localizable.strings +1 -0
  41. data/samples/gesture/Gemfile +2 -2
  42. data/samples/location/Gemfile +1 -1
  43. data/samples/osx/Gemfile +2 -2
  44. data/spec/motion/core/app_spec.rb +6 -0
  45. data/spec/motion/core/device/ios/camera_spec.rb +3 -3
  46. data/spec/motion/core/device/ios/screen_spec.rb +45 -0
  47. data/spec/motion/core/ios/app_spec.rb +29 -0
  48. data/spec/motion/core/persistence_spec.rb +25 -0
  49. data/spec/motion/core/string_spec.rb +2 -2
  50. data/spec/motion/core/time_spec.rb +14 -4
  51. data/spec/motion/core_spec.rb +9 -8
  52. data/spec/motion/http/query_spec.rb +92 -15
  53. data/spec/motion/http/response_spec.rb +4 -3
  54. data/spec/motion/location/location_spec.rb +1 -1
  55. data/spec/motion/mail/mail_spec.rb +125 -0
  56. data/spec/motion/mail/result_spec.rb +53 -0
  57. data/spec/motion/reactor/eventable_spec.rb +85 -0
  58. data/spec/motion/reactor_spec.rb +0 -20
  59. data/spec/motion/sms/result_spec.rb +38 -0
  60. data/spec/motion/sms/sms_spec.rb +71 -0
  61. data/spec/motion/ui/pollute_spec.rb +13 -0
  62. data/spec/motion/ui/ui_bar_button_item_spec.rb +8 -8
  63. data/spec/motion/ui/ui_control_wrapper_spec.rb +35 -0
  64. data/spec/motion/ui/ui_view_controller_wrapper_spec.rb +34 -0
  65. data/spec/motion/ui/ui_view_wrapper_spec.rb +62 -0
  66. data/travis.sh +7 -0
  67. metadata +52 -22
  68. data/motion/ui/gestures.rb +0 -57
  69. data/motion/ui/ui_control.rb +0 -14
  70. data/motion/ui/ui_view_controller.rb +0 -11
  71. data/spec/motion/ui/gestures_spec.rb +0 -62
  72. data/spec/motion/ui/ui_control_spec.rb +0 -42
@@ -8,23 +8,27 @@ module BubbleWrap
8
8
 
9
9
  # Create a new timer that fires after a given number of seconds
10
10
  def initialize(interval, *args, &blk)
11
- options = args.last.is_a?(Hash) ? args.last : {}
12
11
  callback = args.first.respond_to?(:call) ? args.first : blk
13
12
  raise ArgumentError, "No callback or block supplied to periodic timer" unless callback
14
-
13
+
14
+ options = args.last.is_a?(Hash) ? args.last : {}
15
+ if options[:common_modes]
16
+ NSLog "[DEPRECATED - Option :common_modes] a Run Loop Mode is no longer needed."
17
+ end
18
+
15
19
  self.interval = interval
16
- fire = proc {
20
+
21
+ leeway = interval
22
+ queue = Dispatch::Queue.current
23
+ @timer = Dispatch::Source.timer(leeway, interval, 0.0, queue) do
17
24
  callback.call
18
25
  trigger(:fired)
19
- }
20
- @timer = NSTimer.timerWithTimeInterval(interval, target: fire, selector: 'call:', userInfo: nil, repeats: true)
21
- runloop_mode = options[:common_modes] ? NSRunLoopCommonModes : NSDefaultRunLoopMode
22
- NSRunLoop.currentRunLoop.addTimer(@timer, forMode: runloop_mode)
26
+ end
23
27
  end
24
28
 
25
29
  # Cancel the timer
26
30
  def cancel
27
- @timer.invalidate
31
+ @timer.cancel!
28
32
  trigger(:cancelled)
29
33
  end
30
34
 
@@ -5,17 +5,21 @@ module BubbleWrap
5
5
  include Eventable
6
6
 
7
7
  # Create a new timer that fires after a given number of seconds
8
- def initialize(interval, callback=nil, &blk)
9
- fire = proc {
10
- (callback || blk).call
11
- trigger(:fired)
12
- }
13
- @timer = NSTimer.scheduledTimerWithTimeInterval(interval,target: fire, selector: 'call:', userInfo: nil, repeats: false)
8
+ def initialize(leeway, callback=nil, &blk)
9
+ queue = Dispatch::Queue.current
10
+ @timer = Dispatch::Source.timer(leeway, Dispatch::TIME_FOREVER, 0.0, queue) do |src|
11
+ begin
12
+ (callback || blk).call
13
+ trigger(:fired)
14
+ ensure
15
+ src.cancel!
16
+ end
17
+ end
14
18
  end
15
19
 
16
20
  # Cancel the timer
17
21
  def cancel
18
- @timer.invalidate
22
+ @timer.cancel! if @timer
19
23
  trigger(:cancelled)
20
24
  true
21
25
  end
@@ -0,0 +1,24 @@
1
+ module BubbleWrap
2
+ module SMS
3
+ class Result
4
+ attr_accessor :result
5
+
6
+ def initialize(result)
7
+ self.result = result
8
+ end
9
+
10
+ def sent?
11
+ self.result == MessageComposeResultSent
12
+ end
13
+
14
+ def canceled?
15
+ self.result == MessageComposeResultCancelled
16
+ end
17
+
18
+ def failed?
19
+ self.result == MessageComposeResultFailed
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,47 @@
1
+ module BubbleWrap
2
+ module SMS
3
+
4
+ module_function
5
+
6
+ # Base method to create your in-app mail
7
+ # ---------------------------------------
8
+ # EX
9
+ # BW::SMS.compose (
10
+ # {
11
+ # delegate: self, # optional, will use root view controller by default
12
+ # to: [ "1(234)567-8910" ],
13
+ # message: "This is my message. It isn't very long.",
14
+ # animated: false
15
+ # }) {|result, error|
16
+ # result.sent? # => boolean
17
+ # result.canceled? # => boolean
18
+ # result.failed? # => boolean
19
+ # error # => NSError
20
+ # }
21
+
22
+ def compose(options={}, &callback)
23
+ @delegate = options[:delegate] || App.window.rootViewController
24
+ @callback = callback
25
+ @message_controller = create_message_controller(options)
26
+ @message_is_animated = options[:animated] == false ? false : true
27
+ @delegate.presentModalViewController(@message_controller, animated: @message_is_animated)
28
+ end
29
+
30
+ def create_message_controller(options={})
31
+ message_controller = MFMessageComposeViewController.alloc.init
32
+ message_controller.messageComposeDelegate = self
33
+ message_controller.body = options[:message]
34
+ message_controller.recipients = Array(options[:to])
35
+ message_controller
36
+ end
37
+
38
+ # Event when the MFMessageComposeViewController is closed
39
+ # -------------------------------------------------------------
40
+ # the callback is fired if it was present in the constructor
41
+
42
+ def messageComposeViewController(controller, didFinishWithResult: result)
43
+ @delegate.dismissModalViewControllerAnimated(@message_is_animated)
44
+ @callback.call Result.new(result) if @callback
45
+ end
46
+ end
47
+ end
@@ -1,5 +1,8 @@
1
+ # Please, no more! It'll hurt BubbleWrap's compatibility with other libraries.
1
2
  [
2
- [UIControl, UIControlWrap]
3
+ [UIControl, BW::UIControlWrapper],
4
+ [UIView, BW::UIViewWrapper],
5
+ [UIViewController, BW::UIViewControllerWrapper],
3
6
  ].each do |base, wrapper|
4
7
  base.send(:include, wrapper)
5
8
  end
@@ -38,23 +38,30 @@ module BW
38
38
  end
39
39
 
40
40
  def default(options = {}, &block)
41
- options = { buttons: "OK" }.merge!(options)
42
- new(options.merge!(style: :default), &block)
41
+ options = {buttons: "OK"}.merge!(options)
42
+ options[:style] = :default
43
+ new(options, &block)
43
44
  end
44
45
 
45
46
  def plain_text_input(options = {}, &block)
46
- options = { buttons: ["Cancel", "OK"], cancel_button_index: 0 }.merge!(options)
47
- new(options.merge!(style: :plain_text_input), &block)
47
+ options = {buttons: ["Cancel", "OK"],
48
+ cancel_button_index: 0}.merge!(options)
49
+ options[:style] = :plain_text_input
50
+ new(options, &block)
48
51
  end
49
52
 
50
53
  def secure_text_input(options = {}, &block)
51
- options = { buttons: ["Cancel", "OK"], cancel_button_index: 0 }.merge!(options)
52
- new(options.merge!(style: :secure_text_input), &block)
54
+ options = {buttons: ["Cancel", "OK"],
55
+ cancel_button_index: 0}.merge!(options)
56
+ options[:style] = :secure_text_input
57
+ new(options, &block)
53
58
  end
54
59
 
55
60
  def login_and_password_input(options = {}, &block)
56
- options = { buttons: ["Cancel", "Log in"], cancel_button_index: 0 }.merge!(options)
57
- new(options.merge!(style: :login_and_password_input), &block)
61
+ options = {buttons: ["Cancel", "Log in"],
62
+ cancel_button_index: 0}.merge!(options)
63
+ options[:style] = :login_and_password_input
64
+ new(options, &block)
58
65
  end
59
66
  end
60
67
 
@@ -0,0 +1,16 @@
1
+ module BubbleWrap
2
+ module UIControlWrapper
3
+ def when(events, options={}, &block)
4
+ @callback ||= {}
5
+ @callback[events] ||= []
6
+
7
+ unless options[:append]
8
+ @callback[events] = []
9
+ removeTarget(nil, action: nil, forControlEvents: events)
10
+ end
11
+
12
+ @callback[events] << block
13
+ addTarget(@callback[events].last, action:'call', forControlEvents: events)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ module BubbleWrap
2
+ module UIViewControllerWrapper
3
+ # Short hand to get the content frame
4
+ #
5
+ # Return content frame: the application frame - navigation bar frame
6
+ def content_frame
7
+ app_frame = App.frame
8
+ navbar_height = self.navigationController.nil? ?
9
+ 0 : self.navigationController.navigationBar.frame.size.height
10
+ CGRectMake(0, 0, app_frame.size.width, app_frame.size.height - navbar_height)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,55 @@
1
+ module BubbleWrap
2
+ module UIViewWrapper
3
+ def when_tapped(enableInteraction=true, &proc)
4
+ add_gesture_recognizer_helper(UITapGestureRecognizer.alloc.initWithTarget(self, action:'handle_gesture:'), enableInteraction, proc)
5
+ end
6
+
7
+ def when_pinched(enableInteraction=true, &proc)
8
+ add_gesture_recognizer_helper(UIPinchGestureRecognizer.alloc.initWithTarget(self, action:'handle_gesture:'), enableInteraction, proc)
9
+ end
10
+
11
+ def when_rotated(enableInteraction=true, &proc)
12
+ add_gesture_recognizer_helper(UIRotationGestureRecognizer.alloc.initWithTarget(self, action:'handle_gesture:'), enableInteraction, proc)
13
+ end
14
+
15
+ def when_swiped(enableInteraction=true, &proc)
16
+ add_gesture_recognizer_helper(UISwipeGestureRecognizer.alloc.initWithTarget(self, action:'handle_gesture:'), enableInteraction, proc)
17
+ end
18
+
19
+ def when_panned(enableInteraction=true, &proc)
20
+ add_gesture_recognizer_helper(UIPanGestureRecognizer.alloc.initWithTarget(self, action:'handle_gesture:'), enableInteraction, proc)
21
+ end
22
+
23
+ def when_pressed(enableInteraction=true, &proc)
24
+ add_gesture_recognizer_helper(UILongPressGestureRecognizer.alloc.initWithTarget(self, action:'handle_gesture:'), enableInteraction, proc)
25
+ end
26
+
27
+ def self.deprecated_methods
28
+ %w(whenTapped whenPinched whenRotated whenSwiped whenPanned whenPressed)
29
+ end
30
+
31
+ deprecated_methods.each do |method|
32
+ define_method(method) do |enableInteraction = true, &proc|
33
+ NSLog "[DEPRECATED - #{method}] please use #{method.underscore} instead."
34
+ send(method.underscore, enableInteraction, &proc)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def handle_gesture(recognizer)
41
+ @recognizers[recognizer].call(recognizer)
42
+ end
43
+
44
+ # Adds the recognizer and keeps a strong reference to the Proc object.
45
+ def add_gesture_recognizer_helper(recognizer, enableInteraction, proc)
46
+ setUserInteractionEnabled true if enableInteraction && !isUserInteractionEnabled
47
+ self.addGestureRecognizer(recognizer)
48
+
49
+ @recognizers = {} unless @recognizers
50
+ @recognizers[recognizer] = proc
51
+
52
+ recognizer
53
+ end
54
+ end
55
+ end
@@ -0,0 +1 @@
1
+ "real_key" = "Real Key";
@@ -1,4 +1,4 @@
1
- source :rubygems
1
+ source "https://rubygems.org"
2
2
 
3
3
  gem 'bubble-wrap', '~> 1.1.0'
4
- gem 'motion-dryer'
4
+ gem 'motion-dryer'
@@ -1,3 +1,3 @@
1
- source :rubygems
1
+ source "https://rubygems.org"
2
2
 
3
3
  gem 'bubble-wrap', '~> 1.1.0'
@@ -1,3 +1,3 @@
1
- source :rubygems
1
+ source "https://rubygems.org"
2
2
 
3
- gem 'bubble-wrap', :path => "../../" # '~> 1.3.0'
3
+ gem 'bubble-wrap', :path => "../../" # '~> 1.3.0'
@@ -36,6 +36,12 @@ describe BubbleWrap::App do
36
36
  end
37
37
  end
38
38
 
39
+ describe '.info_plist' do
40
+ it 'returns the information property list hash' do
41
+ App.info_plist.should == NSBundle.mainBundle.infoDictionary
42
+ end
43
+ end
44
+
39
45
  describe '.name' do
40
46
  it 'returns the application name' do
41
47
  App.name.should == 'testSuite'
@@ -3,9 +3,9 @@ def camera_picker
3
3
  end
4
4
 
5
5
  def example_info
6
- info = { UIImagePickerControllerMediaType => KUTTypeImage,
7
- UIImagePickerControllerOriginalImage => UIImage.alloc.init,
8
- UIImagePickerControllerMediaURL => NSURL.alloc.init}
6
+ { UIImagePickerControllerMediaType => KUTTypeImage,
7
+ UIImagePickerControllerOriginalImage => UIImage.alloc.init,
8
+ UIImagePickerControllerMediaURL => NSURL.alloc.init}
9
9
  end
10
10
 
11
11
  describe BubbleWrap::Device::Camera do
@@ -103,6 +103,51 @@ describe "iOS" do
103
103
  end
104
104
  end
105
105
 
106
+ describe '.interface_orientation' do
107
+
108
+ describe 'portrait' do
109
+ it 'returns :portrait' do
110
+ BW::Device::Screen.interface_orientation(UIInterfaceOrientationPortrait).should == :portrait
111
+ end
112
+ end
113
+
114
+ describe 'portrait upside down' do
115
+ it 'returns :portrait_upside_down' do
116
+ BW::Device::Screen.interface_orientation(UIInterfaceOrientationPortraitUpsideDown).should == :portrait_upside_down
117
+ end
118
+ end
119
+
120
+ describe 'landscape left' do
121
+ it 'returns :landscape_left' do
122
+ BW::Device::Screen.interface_orientation(UIInterfaceOrientationLandscapeLeft).should == :landscape_left
123
+ end
124
+ end
125
+
126
+ describe 'landscape right' do
127
+ it 'returns :landscape_right' do
128
+ BW::Device::Screen.interface_orientation(UIInterfaceOrientationLandscapeRight).should == :landscape_right
129
+ end
130
+ end
131
+
132
+ describe 'unknown' do
133
+ it 'returns :unknown if fallback is false' do
134
+ BW::Device::Screen.interface_orientation(UIDeviceOrientationUnknown, false).should == :unknown
135
+ end
136
+ it 'returns Status bar orientation if fallback not specified' do
137
+ BW::Device::Screen.interface_orientation(UIDeviceOrientationUnknown).should == BW::Device::Screen.interface_orientation(UIApplication.sharedApplication.statusBarOrientation)
138
+ end
139
+ end
140
+
141
+ describe 'any other input' do
142
+ it 'returns :unknown if fallback is false' do
143
+ BW::Device::Screen.interface_orientation('twiggy twiggy twiggy', false).should == :unknown
144
+ end
145
+ it 'returns Status bar orientation if fallback not specified' do
146
+ BW::Device::Screen.interface_orientation('twiggy twiggy twiggy').should == BW::Device::Screen.interface_orientation(UIApplication.sharedApplication.statusBarOrientation)
147
+ end
148
+ end
149
+ end
150
+
106
151
  describe '.width' do
107
152
  it 'returns the current device screen width' do
108
153
  BW::Device::Screen.width.should == 320.0 if BW::Device.iphone?
@@ -3,6 +3,9 @@ describe BubbleWrap::App do
3
3
  describe '.alert' do
4
4
  after do
5
5
  @alert.dismissWithClickedButtonIndex(@alert.cancelButtonIndex, animated: false)
6
+
7
+ wait 0.3 do
8
+ end
6
9
  end
7
10
 
8
11
  describe "with only one string argument" do
@@ -138,10 +141,36 @@ describe BubbleWrap::App do
138
141
  end
139
142
  end
140
143
 
144
+ describe '.windows' do
145
+ it 'returns UIApplication.sharedApplication.windows' do
146
+ App.windows.should == UIApplication.sharedApplication.windows
147
+ end
148
+ end
149
+
141
150
  describe '.window' do
142
151
  it 'returns UIApplication.sharedApplication.keyWindow' do
143
152
  App.window.should == UIApplication.sharedApplication.keyWindow
144
153
  end
154
+
155
+ describe 'with UIActionSheet' do
156
+
157
+ it 'returns the correct window' do
158
+ action_sheet = UIActionSheet.alloc.init
159
+ action_sheet.cancelButtonIndex = (action_sheet.addButtonWithTitle("Cancel"))
160
+
161
+ old_window = App.window
162
+ window_count = App.windows.count
163
+ action_sheet.showInView(App.window)
164
+ wait 1 do
165
+ UIApplication.sharedApplication.windows.count.should > window_count
166
+ App.window.should == old_window
167
+
168
+ action_sheet.dismissWithClickedButtonIndex(action_sheet.cancelButtonIndex, animated: false)
169
+
170
+ App.window.should == old_window
171
+ end
172
+ end
173
+ end
145
174
  end
146
175
 
147
176
  describe '.run_after' do
@@ -65,4 +65,29 @@ describe BubbleWrap::Persistence do
65
65
  BubbleWrap::Persistence[:arbitraryString].methods.should == 'test string'.methods
66
66
  end
67
67
  end
68
+
69
+ describe "deleting object" do
70
+ before do
71
+ BubbleWrap::Persistence['arbitraryString'] = 'foobarbaz'
72
+ end
73
+
74
+ it 'can delete persisted object' do
75
+ BubbleWrap::Persistence.delete(:arbitraryString).should == 'foobarbaz'
76
+ BubbleWrap::Persistence['arbitraryString'].should.equal nil
77
+ end
78
+
79
+ it 'returns nil when the object does not exist' do
80
+ BubbleWrap::Persistence.delete(:wrongKey).should == nil
81
+ end
82
+
83
+ it 'must call synchronize' do
84
+ storage = NSUserDefaults.standardUserDefaults
85
+ def storage.synchronize; @sync_was_called = true; end
86
+
87
+ BubbleWrap::Persistence.delete(:arbitraryString)
88
+
89
+ storage.instance_variable_get(:@sync_was_called).should.equal true
90
+ end
91
+
92
+ end
68
93
  end