bubble-wrap 1.3.0 → 1.4.0

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