bubble-wrap 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .rake_tasks~
2
+ build/
3
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bubble-wrap.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ LICENCE
2
+
3
+ MIT: http://mattaimonetti.mit-license.org
4
+
5
+ ------------------------------------------------
6
+
7
+ The MIT License (MIT)
8
+ Copyright © 2012 Matt Aimonetti <matt.aimonetti@gmail.com>
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the “Software”), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in
18
+ all copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # BubbleWrap for RubyMotion
2
+
3
+ A collection of helpers and wrappers used to wrap CocoaTouch code and provide more Ruby like APIs.
4
+
5
+ ## Installation
6
+
7
+ ```ruby
8
+ gem install bubble-wrap
9
+ ```
10
+
11
+ ### Setup
12
+
13
+ 1. Edit the Rakefile of your RubyMotion project and add the following require line.
14
+ ```ruby
15
+ require 'bubble-wrap'
16
+ ```
17
+
18
+ 2. Now, you can use BubbleWrap extension in your app:
19
+ ````ruby
20
+ class AppDelegate
21
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
22
+ puts "#{App.name} (#{documents_path})"
23
+ true
24
+ end
25
+ end
26
+ ````
27
+
28
+ For a more complete list of helper/wrapper descriptions and more details, see the [wiki](https://github.com/mattetti/BubbleWrap/wiki).
29
+
30
+ ## HTTP
31
+
32
+ `BubbleWrap::HTTP` wraps `NSURLRequest`, `NSURLConnection` and friends to provide Ruby developers with a more familiar and easier to use API.
33
+ The API uses async calls and blocks to stay as simple as possible.
34
+
35
+ Usage example:
36
+
37
+ ```ruby
38
+ BubbleWrap::HTTP.get("https://api.github.com/users/mattetti") do |response|
39
+ p response.body.to_str
40
+ end
41
+ ```
42
+
43
+ ```ruby
44
+ BubbleWrap::HTTP.get("https://api.github.com/users/mattetti", {credentials: {username: 'matt', password: 'aimonetti'}}) do |response|
45
+ p response.body.to_str # prints the response's body
46
+ end
47
+ ```
48
+
49
+ ```ruby
50
+ data = {first_name: 'Matt', last_name: 'Aimonetti'}
51
+ BubbleWrap::HTTP.post("http://foo.bar.com/", {payload: data}) do |response|
52
+ if response.ok?
53
+ json = BubbleWrap::JSON.parse(response.body.to_str)
54
+ p json['id']
55
+ elsif response.status_code.to_s =~ /40\d/
56
+ alert("Login failed") # helper provided by the kernel file in this repo.
57
+ else
58
+ alert(response.error_message)
59
+ end
60
+ end
61
+ ```
62
+
63
+ ## JSON
64
+
65
+ `BubbleWrap::JSON` wraps `NSJSONSerialization` available in iOS5 and offers the same API as Ruby's JSON std lib.
66
+
67
+ ## Kernel
68
+
69
+ A collection of useful methods used often in my RubyMotion apps.
70
+
71
+ Examples:
72
+ ```ruby
73
+ > iphone?
74
+ # true
75
+ > ipad?
76
+ # false
77
+ > orientation
78
+ # :portrait
79
+ > simulator?
80
+ # true
81
+ > documents_path
82
+ # "/Users/mattetti/Library/Application Support/iPhone Simulator/5.0/Applications/EEC6454E-1816-451E-BB9A-EE18222E1A8F/Documents"
83
+ ```
84
+
85
+ ## App
86
+
87
+ A module allowing developers to store global states and also provides a
88
+ persistence layer.
89
+
90
+ ## NSUserDefaults
91
+
92
+ Helper methods added to the class repsonsible for user preferences.
93
+
94
+ ## NSIndexPath
95
+
96
+ Helper methods added to give `NSIndexPath` a bit more or a Ruby
97
+ interface.
98
+
99
+ ## Gestures
100
+
101
+ Extra methods on `UIView` for working with gesture recognizers. A gesture recognizer can be added using a normal Ruby block, like so:
102
+
103
+ ```ruby
104
+ view.whenTapped do
105
+ UIView.animateWithDuration(1,
106
+ animations:lambda {
107
+ # animate
108
+ # @view.transform = ...
109
+ })
110
+ end
111
+ ```
112
+
113
+ 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.
114
+
115
+ ## UIButton
116
+
117
+ Helper methods to give `UIButton` a Ruby-like interface. Ex:
118
+
119
+ ```ruby
120
+ button.when(UIControlEventTouchUpInside) do
121
+ self.view.backgroundColor = UIColor.redColor
122
+ end
123
+ ```
124
+
125
+ ## NSNotificationCenter
126
+
127
+ Helper methods to give NSNotificationCenter a Ruby-like interface:
128
+
129
+ ```ruby
130
+ def viewWillAppear(animated)
131
+ notification_center.observe self, UIApplicationWillEnterForegroundNotification do
132
+ loadAndRefresh
133
+ end
134
+
135
+ notification_center.observe self, ReloadNotification do
136
+ loadAndRefresh
137
+ end
138
+ end
139
+
140
+ def viewWillDisappear(animated)
141
+ notification_center.unobserve self
142
+ end
143
+
144
+ def reload
145
+ notification_center.post ReloadNotification
146
+ end
147
+ ```
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,16 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/bubble-wrap/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Matt Aimonetti", "Francis Chong"]
6
+ gem.email = ["mattaimonetti@gmail.com", "francis@ignition.hk"]
7
+ gem.description = "RubyMotion wrappers and helpers (Ruby for iOS) - Making Cocoa APIs more Ruby like, one API at a time. Fork away and send your pull request."
8
+ gem.summary = "RubyMotion wrappers and helpers (Ruby for iOS) - Making Cocoa APIs more Ruby like, one API at a time. Fork away and send your pull request."
9
+ gem.homepage = "https://github.com/mattetti/BubbleWrap"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
13
+ gem.name = "bubble-wrap"
14
+ gem.require_paths = ["lib"]
15
+ gem.version = BubbleWrap::VERSION
16
+ end
@@ -0,0 +1,7 @@
1
+ require "bubble-wrap/version"
2
+
3
+ Motion::Project::App.setup do |app|
4
+ Dir.glob(File.join(File.dirname(__FILE__), 'bubble-wrap/*.rb')).each do |file|
5
+ app.files.unshift(file)
6
+ end
7
+ end
@@ -0,0 +1,50 @@
1
+ # Provides a module to store global states and a persistence layer.
2
+ #
3
+ module App
4
+ module_function
5
+
6
+ @states = {}
7
+
8
+ def states
9
+ @states
10
+ end
11
+
12
+ def name
13
+ NSBundle.mainBundle.bundleIdentifier
14
+ end
15
+
16
+ # Return application frame
17
+ def frame
18
+ UIScreen.mainScreen.applicationFrame
19
+ end
20
+
21
+ # Application Delegate
22
+ def delegate
23
+ UIApplication.sharedApplication.delegate
24
+ end
25
+
26
+ # Persistence module built on top of NSUserDefaults
27
+ module Persistence
28
+ def self.app_key
29
+ @app_key ||= App.name
30
+ end
31
+
32
+ def self.[]=(key, value)
33
+ defaults = NSUserDefaults.standardUserDefaults
34
+ defaults.setObject(value, forKey: storage_key(key))
35
+ defaults.synchronize
36
+ end
37
+
38
+ def self.[](key)
39
+ defaults = NSUserDefaults.standardUserDefaults
40
+ defaults.objectForKey storage_key(key)
41
+ end
42
+
43
+ private
44
+
45
+ def self.storage_key(key)
46
+ app_key + '_' + key
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,39 @@
1
+ # Opens UIView to add methods for working with gesture recognizers.
2
+
3
+ class UIView
4
+
5
+ def whenTapped(&proc)
6
+ addGestureRecognizerHelper(proc, UITapGestureRecognizer.alloc.initWithTarget(proc, action:'call'))
7
+ end
8
+
9
+ def whenPinched(&proc)
10
+ addGestureRecognizerHelper(proc, UIPinchGestureRecognizer.alloc.initWithTarget(proc, action:'call'))
11
+ end
12
+
13
+ def whenRotated(&proc)
14
+ addGestureRecognizerHelper(proc, UIRotationGestureRecognizer.alloc.initWithTarget(proc, action:'call'))
15
+ end
16
+
17
+ def whenSwiped(&proc)
18
+ addGestureRecognizerHelper(proc, UISwipeGestureRecognizer.alloc.initWithTarget(proc, action:'call'))
19
+ end
20
+
21
+ def whenPanned(&proc)
22
+ addGestureRecognizerHelper(proc, UIPanGestureRecognizer.alloc.initWithTarget(proc, action:'call'))
23
+ end
24
+
25
+ def whenPressed(&proc)
26
+ addGestureRecognizerHelper(proc, UILongPressGestureRecognizer.alloc.initWithTarget(proc, action:'call'))
27
+ end
28
+
29
+ private
30
+
31
+ # Adds the recognizer and keeps a strong reference to the Proc object.
32
+ def addGestureRecognizerHelper(proc, recognizer)
33
+ self.addGestureRecognizer(recognizer)
34
+ @recognizers = {} unless @recognizers
35
+ @recognizers["#{proc}"] = proc
36
+ end
37
+
38
+ end
39
+
@@ -0,0 +1,240 @@
1
+ module BubbleWrap
2
+
3
+ SETTINGS = {}
4
+
5
+ # The HTTP module provides a simple interface to make HTTP requests.
6
+ #
7
+ # TODO: preflight support, easier/better cookie support, better error handling
8
+ module HTTP
9
+
10
+ # Make a GET request and process the response asynchronously via a block.
11
+ #
12
+ # @examples
13
+ # # Simple GET request printing the body
14
+ # BubbleWrap::HTTP.get("https://api.github.com/users/mattetti") do |response|
15
+ # p response.body.to_str
16
+ # end
17
+ #
18
+ # # GET request with basic auth credentials
19
+ # BubbleWrap::HTTP.get("https://api.github.com/users/mattetti", {credentials: {username: 'matt', password: 'aimonetti'}}) do |response|
20
+ # p response.body.to_str # prints the response's body
21
+ # end
22
+ #
23
+ def self.get(url, options={}, &block)
24
+ delegator = block_given? ? block : options.delete(:action)
25
+ HTTP::Query.new( url, :get, options.merge({:action => delegator}) )
26
+ end
27
+
28
+ # Make a POST request
29
+ def self.post(url, options={}, &block)
30
+ delegator = block_given? ? block : options.delete(:action)
31
+ HTTP::Query.new( url, :post, options.merge({:action => delegator}) )
32
+ end
33
+
34
+ # Make a PUT request
35
+ def self.put(url, options={}, &block)
36
+ delegator = block_given? ? block : options.delete(:action)
37
+ HTTP::Query.new( url, :put, options.merge({:action => delegator}) )
38
+ end
39
+
40
+ # Make a DELETE request
41
+ def self.delete(url, options={}, &block)
42
+ delegator = block_given? ? block : options.delete(:action)
43
+ HTTP::Query.new( url, :delete, options.merge({:action => delegator}) )
44
+ end
45
+
46
+ # Make a HEAD request
47
+ def self.head(url, options={}, &block)
48
+ delegator = block_given? ? block : options.delete(:action)
49
+ HTTP::Query.new( url, :head, options.merge({:action => delegator}) )
50
+ end
51
+
52
+ # Make a PATCH request
53
+ def self.patch(url, options={}, &block)
54
+ delegator = block_given? ? block : options.delete(:action)
55
+ HTTP::Query.new( url, :patch, options.merge({:action => delegator}) )
56
+ end
57
+
58
+ # Response class wrapping the results of a Query's response
59
+ class Response
60
+ attr_reader :body
61
+ attr_reader :headers
62
+ attr_accessor :status_code, :error_message
63
+ attr_reader :url
64
+
65
+ def initialize(values={})
66
+ self.update(values)
67
+ end
68
+
69
+ def update(values)
70
+ values.each do |k,v|
71
+ self.instance_variable_set("@#{k}", v)
72
+ end
73
+ end
74
+
75
+ def ok?
76
+ status_code == 200
77
+ end
78
+
79
+ end
80
+
81
+ # Class wrapping NSConnection and often used indirectly by the BubbleWrap::HTTP module methods.
82
+ class Query
83
+ attr_accessor :request
84
+ attr_accessor :connection
85
+ attr_accessor :credentials # username & password has a hash
86
+ attr_accessor :proxy_credential # credential supplied to proxy servers
87
+ attr_accessor :post_data
88
+ attr_reader :method
89
+
90
+ attr_reader :response
91
+ attr_reader :status_code
92
+ attr_reader :response_headers
93
+ attr_reader :response_size
94
+ attr_reader :options
95
+
96
+ # ==== Parameters
97
+ # url<String>:: url of the resource to download
98
+ # http_method<Symbol>:: Value representing the HTTP method to use
99
+ # options<Hash>:: optional options used for the query
100
+ #
101
+ # ==== Options
102
+ # :payload<String> - data to pass to a POST, PUT, DELETE query.
103
+ # :delegator - Proc, class or object to call when the file is downloaded.
104
+ # a proc will receive a Response object while the passed object
105
+ # will receive the handle_query_response method
106
+ # :headers<Hash> - headers send with the request
107
+ # Anything else will be available via the options attribute reader.
108
+ #
109
+ def initialize(url, http_method = :get, options={})
110
+ @method = http_method.upcase.to_s
111
+ @delegator = options.delete(:action) || self
112
+ @payload = options.delete(:payload)
113
+ @credentials = options.delete(:credentials) || {}
114
+ @credentials = {:username => '', :password => ''}.merge(@credentials)
115
+ @timeout = options.delete(:timeout) || 30.0
116
+ headers = options.delete(:headers)
117
+ if headers
118
+ @headers = {}
119
+ headers.each{|k,v| @headers[k] = v.gsub("\n", '\\n') } # escaping LFs
120
+ end
121
+ @options = options
122
+ @response = HTTP::Response.new
123
+ initiate_request(url)
124
+ connection.start
125
+ connection
126
+ end
127
+
128
+ def generate_get_params(payload, prefix=nil)
129
+ list = []
130
+ payload.each do |k,v|
131
+ if v.is_a?(Hash)
132
+ new_prefix = prefix ? "#{prefix}[#{k.to_s}]" : k.to_s
133
+ param = generate_get_params(v, new_prefix)
134
+ else
135
+ param = prefix ? "#{prefix}[#{k}]=#{v}" : "#{k}=#{v}"
136
+ end
137
+ list << param
138
+ end
139
+ return list.flatten
140
+ end
141
+
142
+ def initiate_request(url_string)
143
+ # http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/nsrunloop_Class/Reference/Reference.html#//apple_ref/doc/constant_group/Run_Loop_Modes
144
+ # NSConnectionReplyMode
145
+
146
+ unless @payload.nil?
147
+ if @payload.is_a?(Hash)
148
+ params = generate_get_params(@payload)
149
+ @payload = params.join("&")
150
+ end
151
+ url_string = "#{url_string}?#{@payload}" if @method == "GET"
152
+ end
153
+
154
+ p "HTTP building a NSRequest for #{url_string}"# if SETTINGS[:debug]
155
+ @url = NSURL.URLWithString(url_string)
156
+ @request = NSMutableURLRequest.requestWithURL(@url,
157
+ cachePolicy:NSURLRequestUseProtocolCachePolicy,
158
+ timeoutInterval:@timeout)
159
+ @request.setHTTPMethod @method
160
+ @request.setAllHTTPHeaderFields(@headers) if @headers
161
+
162
+ # @payload needs to be converted to data
163
+ unless @method == "GET" || @payload.nil?
164
+ @payload = @payload.to_s.dataUsingEncoding(NSUTF8StringEncoding)
165
+ @request.setHTTPBody @payload
166
+ end
167
+
168
+ # NSHTTPCookieStorage.sharedHTTPCookieStorage
169
+
170
+ @connection = NSURLConnection.connectionWithRequest(request, delegate:self)
171
+ @request.instance_variable_set("@done_loading", false)
172
+ def @request.done_loading; @done_loading; end
173
+ def @request.done_loading!; @done_loading = true; end
174
+ end
175
+
176
+ def connection(connection, didReceiveResponse:response)
177
+ @status_code = response.statusCode
178
+ @response_headers = response.allHeaderFields
179
+ @response_size = response.expectedContentLength.to_f
180
+ # p "HTTP status code: #{@status_code}, content length: #{@response_size}, headers: #{@response_headers}" if SETTINGS[:debug]
181
+ end
182
+
183
+ # This delegate method get called every time a chunk of data is being received
184
+ def connection(connection, didReceiveData:received_data)
185
+ @received_data ||= NSMutableData.new
186
+ @received_data.appendData(received_data)
187
+ end
188
+
189
+ def connection(connection, willSendRequest:request, redirectResponse:redirect_response)
190
+ puts "HTTP redirected #{request.description}" #if SETTINGS[:debug]
191
+ new_request = request.mutableCopy
192
+ # new_request.setValue(@credentials.inspect, forHTTPHeaderField:'Authorization') # disabled while we figure this one out
193
+ new_request.setAllHTTPHeaderFields(@headers) if @headers
194
+ @connection.cancel
195
+ @connection = NSURLConnection.connectionWithRequest(new_request, delegate:self)
196
+ new_request
197
+ end
198
+
199
+ def connection(connection, didFailWithError: error)
200
+ @request.done_loading!
201
+ p "HTTP Connection failed #{error.localizedDescription}"
202
+ @response.error_message = error.localizedDescription
203
+ if @delegator.respond_to?(:call)
204
+ @delegator.call( @response, self )
205
+ end
206
+ end
207
+
208
+ # The transfer is done and everything went well
209
+ def connectionDidFinishLoading(connection)
210
+ @request.done_loading!
211
+
212
+ # copy the data in a local var that we will attach to the response object
213
+ response_body = NSData.dataWithData(@received_data) if @received_data
214
+ @response.update(status_code: status_code, body: response_body, headers: response_headers, url: @url)
215
+ # Don't reset the received data since this method can be called multiple times if the headers can report the wrong length.
216
+ # @received_data = nil
217
+ if @delegator.respond_to?(:call)
218
+ @delegator.call( @response, self )
219
+ end
220
+ end
221
+
222
+ def connection(connection, didReceiveAuthenticationChallenge:challenge)
223
+ # p "HTTP auth required" if SETTINGS[:debug]
224
+ if (challenge.previousFailureCount == 0)
225
+ # by default we are keeping the credential for the entire session
226
+ # Eventually, it would be good to let the user pick one of the 3 possible credential persistence options:
227
+ # NSURLCredentialPersistenceNone,
228
+ # NSURLCredentialPersistenceForSession,
229
+ # NSURLCredentialPersistencePermanent
230
+ p "auth challenged, answered with credentials: #{credentials.inspect}"
231
+ new_credential = NSURLCredential.credentialWithUser(credentials[:username], password:credentials[:password], persistence:NSURLCredentialPersistenceForSession)
232
+ challenge.sender.useCredential(new_credential, forAuthenticationChallenge:challenge)
233
+ else
234
+ challenge.sender.cancelAuthenticationChallenge(challenge)
235
+ p 'Auth Failed :('
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,36 @@
1
+ module BubbleWrap
2
+
3
+ # Handles JSON encoding and decoding in a similar way Ruby 1.9 does.
4
+ module JSON
5
+
6
+ class ParserError < StandardError; end
7
+
8
+ # Parses a string or data object and converts it in data structure.
9
+ #
10
+ # @param [String, NSData] str_data the string or data to serialize.
11
+ # @raise [ParserError] If the parsing of the passed string/data isn't valid.
12
+ # @return [Hash, Array, NilClass] the converted data structure, nil if the incoming string isn't valid.
13
+ #
14
+ # TODO: support options like the C Ruby module does
15
+ def self.parse(str_data, &block)
16
+ data = str_data.respond_to?(:to_data) ? str_data.to_data : str_data
17
+ opts = NSJSONReadingMutableContainers & NSJSONReadingMutableLeaves & NSJSONReadingAllowFragments
18
+ error = Pointer.new(:id)
19
+ obj = NSJSONSerialization.JSONObjectWithData(data, options:opts, error:error)
20
+ raise ParserError, error[0].description if error[0]
21
+ if block_given?
22
+ yield obj
23
+ else
24
+ obj
25
+ end
26
+
27
+ end
28
+
29
+ def self.generate(obj)
30
+ # opts = NSJSONWritingPrettyPrinted
31
+ data = NSJSONSerialization.dataWithJSONObject(obj, options:0, error:nil)
32
+ data.to_str
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,95 @@
1
+ module Kernel
2
+
3
+ # Verifies that the device running the app is an iPhone.
4
+ # @return [TrueClass, FalseClass] true will be returned if the device is an iPhone, false otherwise.
5
+ def iphone?
6
+ UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone
7
+ end
8
+
9
+ # Verifies that the device running the app is an iPad.
10
+ # @return [TrueClass, FalseClass] true will be returned if the device is an iPad, false otherwise.
11
+ def ipad?
12
+ UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad
13
+ end
14
+
15
+ # Verifies that the device running has a front facing camera.
16
+ # @return [TrueClass, FalseClass] true will be returned if the device has a front facing camera, false otherwise.
17
+ def front_camera?
18
+ UIImagePickerController.isCameraDeviceAvailable(UIImagePickerControllerCameraDeviceFront)
19
+ end
20
+
21
+ # Verifies that the device running has a rear facing camera.
22
+ # @return [TrueClass, FalseClass] true will be returned if the device has a rear facing camera, false otherwise.
23
+ def rear_camera?
24
+ UIImagePickerController.isCameraDeviceAvailable(UIImagePickerControllerCameraDeviceRear)
25
+ end
26
+
27
+ # Returns the application's document directory path where users might be able to upload content.
28
+ # @return [String] the path to the document directory
29
+ def documents_path
30
+ NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true)[0]
31
+ end
32
+
33
+ # Returns the application resource path where resource located
34
+ # @return [String] the application main bundle resource path
35
+ def resources_path
36
+ NSBundle.mainBundle.resourcePath
37
+ end
38
+
39
+ # Returns the default notification center
40
+ # @return [NSNotificationCenter] the default notification center
41
+ def notification_center
42
+ NSNotificationCenter.defaultCenter
43
+ end
44
+
45
+ def orientation
46
+ case UIDevice.currentDevice.orientation
47
+ when UIDeviceOrientationUnknown then :unknown
48
+ when UIDeviceOrientationPortrait then :portrait
49
+ when UIDeviceOrientationPortraitUpsideDown then :portrait_upside_down
50
+ when UIDeviceOrientationLandscapeLeft then :landscape_left
51
+ when UIDeviceOrientationLandscapeRight then :landscape_right
52
+ when UIDeviceOrientationFaceUp then :face_up
53
+ when UIDeviceOrientationFaceDown then :face_down
54
+ else
55
+ :unknown
56
+ end
57
+ end
58
+
59
+ # @return [UIcolor]
60
+ def rgb_color(r,g,b)
61
+ rgba_color(r,g,b,1)
62
+ end
63
+
64
+ # @return [UIcolor]
65
+ def rgba_color(r,g,b,a)
66
+ UIColor.colorWithRed((r/255.0), green:(g/255.0), blue:(b/255.0), alpha:a)
67
+ end
68
+
69
+ def NSLocalizedString(key, value)
70
+ NSBundle.mainBundle.localizedStringForKey(key, value:value, table:nil)
71
+ end
72
+
73
+ def user_cache
74
+ NSUserDefaults.standardUserDefaults
75
+ end
76
+
77
+ def alert(msg)
78
+ alert = UIAlertView.alloc.initWithTitle msg,
79
+ message: nil,
80
+ delegate: nil,
81
+ cancelButtonTitle: "OK",
82
+ otherButtonTitles: nil
83
+ alert.show
84
+ end
85
+
86
+ def simulator?
87
+ @simulator_state ||= !(UIDevice.currentDevice.model =~ /simulator/i).nil?
88
+ end
89
+
90
+ # I had issues with #p on the device, this is a temporary workaround
91
+ def p(arg)
92
+ NSLog arg.inspect
93
+ end
94
+
95
+ end
@@ -0,0 +1,23 @@
1
+ class NSIndexPath
2
+
3
+ # Gives access to an index at a given position.
4
+ # @param [Integer] position to use to fetch the index
5
+ # @return [Integer] the index for the given position
6
+ def [](position)
7
+ raise ArgumentError unless position.is_a?(Integer)
8
+ indexAtPosition(position)
9
+ end
10
+
11
+ # Provides an iterator taking a block following the common Ruby idiom.
12
+ # @param [Block]
13
+ # @return [NSIndexPath] the iterated object itself
14
+ def each
15
+ i = 0
16
+ until i == self.length
17
+ yield self.indexAtPosition(i)
18
+ i += 1
19
+ end
20
+ self
21
+ end
22
+
23
+ end
@@ -0,0 +1,22 @@
1
+ class NSNotificationCenter
2
+ attr_reader :observers
3
+
4
+ def observe(observer, name, object=nil, &proc)
5
+ @observers ||= {}
6
+ @observers[observer] ||= []
7
+ @observers[observer] << proc
8
+ self.addObserver(proc, selector:'call', name:name, object:object)
9
+ end
10
+
11
+ def unobserve(observer)
12
+ return unless @observers[observer]
13
+ @observers[observer].each do |proc|
14
+ removeObserver(proc)
15
+ end
16
+ @observers.delete(observer)
17
+ end
18
+
19
+ def post(name, object=nil, info=nil)
20
+ self.postNotificationName(name, object: object, userInfo:info)
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ # Reopens the NSUserDefaults class to add Array like accessors
2
+ # @see https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/nsuserdefaults_Class/Reference/Reference.html
3
+ class NSUserDefaults
4
+
5
+ # Retrieves the object for the passed key
6
+ def [](key)
7
+ self.objectForKey(key)
8
+ end
9
+
10
+ # Sets the value for a given key and save it right away.
11
+ def []=(key, val)
12
+ self.setObject(val, forKey: key)
13
+ self.synchronize
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ class UIButton
2
+ def when(events, &block)
3
+ @callback ||= {}
4
+ @callback[events] = block
5
+ addTarget(@callback[events], action:'call', forControlEvents: events)
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ class UIViewController
2
+ # Short hand to get the content frame
3
+ #
4
+ # Return content frame: the application frame - navigation bar frame
5
+ def content_frame
6
+ app_frame = App.frame
7
+ navbar_height = self.navigationController.nil? ?
8
+ 0 : self.navigationController.navigationBar.frame.size.height
9
+ CGRectMake(0, 0, app_frame.size.width, app_frame.size.height - navbar_height)
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module BubbleWrap
2
+ VERSION = '0.1.0'
3
+ end
data/spec/http_spec.rb ADDED
File without changes
data/spec/json_spec.rb ADDED
@@ -0,0 +1,116 @@
1
+ describe "JSON" do
2
+
3
+ before do
4
+ @json_string = <<-EOS
5
+ {
6
+ "public_gists": 248,
7
+ "type": "User",
8
+ "blog": "http://merbist.com",
9
+ "location": "San Diego, CA",
10
+ "followers": 303,
11
+ "company": "LivingSocial",
12
+ "html_url": "https://github.com/mattetti",
13
+ "created_at": "2008-01-31T22:56:31Z",
14
+ "email": "mattaimonetti@gmail.com",
15
+ "hireable": true,
16
+ "gravatar_id": "c69521d6e22fc0bbd69337ec8b1698df",
17
+ "bio": "",
18
+ "public_repos": 137,
19
+ "following": 6,
20
+ "name": "Matt Aimonetti",
21
+ "login": "mattetti",
22
+ "url": "https://api.github.com/users/mattetti",
23
+ "id": 113,
24
+ "avatar_url": "https://secure.gravatar.com/avatar/c69521d6e22fc0bbd69337ec8b1698df?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png"
25
+ }
26
+ EOS
27
+ end
28
+
29
+ describe "parsing a basic JSON string without block" do
30
+
31
+ before do
32
+ @parsed = BubbleWrap::JSON.parse(@json_string)
33
+ end
34
+
35
+ it "should convert a top object into a Ruby hash" do
36
+ obj = @parsed
37
+ obj.class.should == Hash
38
+ obj.keys.size.should == 19
39
+ end
40
+
41
+ it "should properly convert integers values" do
42
+ @parsed["id"].is_a?(Integer).should == true
43
+ end
44
+
45
+ it "should properly convert string values" do
46
+ @parsed["login"].is_a?(String).should == true
47
+ end
48
+
49
+ it "should convert an array into a Ruby array" do
50
+ p Bacon::Counter.inspect
51
+ obj = BubbleWrap::JSON.parse("[1,2,3]")
52
+ obj.class.should == Array
53
+ obj.size.should == 3
54
+ end
55
+
56
+ end
57
+
58
+ describe "parsing a basic JSON string with block" do
59
+
60
+ before do
61
+ BubbleWrap::JSON.parse(@json_string) do |parsed|
62
+ @parsed = parsed
63
+ end
64
+ end
65
+
66
+ it "should convert a top object into a Ruby hash" do
67
+ obj = @parsed
68
+ obj.class.should == Hash
69
+ obj.keys.size.should == 19
70
+ end
71
+
72
+ it "should properly convert integers values" do
73
+ @parsed["id"].is_a?(Integer).should == true
74
+ end
75
+
76
+ it "should properly convert string values" do
77
+ @parsed["login"].is_a?(String).should == true
78
+ end
79
+
80
+ it "should convert an array into a Ruby array" do
81
+ p Bacon::Counter.inspect
82
+ obj = BubbleWrap::JSON.parse("[1,2,3]")
83
+ obj.class.should == Array
84
+ obj.size.should == 3
85
+ end
86
+
87
+ end
88
+
89
+ describe "generating a JSON string from an object" do
90
+
91
+ before do
92
+ @obj = { foo: 'bar',
93
+ 'bar' => 'baz',
94
+ baz: 123,
95
+ foobar: [1,2,3],
96
+ foobaz: {a: 1, b: 2}
97
+ }
98
+ end
99
+
100
+ it "should generate from a hash" do
101
+ json = BubbleWrap::JSON.generate(@obj)
102
+ json.class == String
103
+ json.should == "{\"foo\":\"bar\",\"bar\":\"baz\",\"baz\":123,\"foobar\":[1,2,3],\"foobaz\":{\"a\":1,\"b\":2}}"
104
+ end
105
+
106
+ it "should encode and decode and object losslessly" do
107
+ json = BubbleWrap::JSON.generate(@obj)
108
+ obj = BubbleWrap::JSON.parse(json)
109
+ obj.keys.sort.should == @obj.keys.sort
110
+ obj.values.sort.should == @obj.values.sort
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+
@@ -0,0 +1,32 @@
1
+ describe "NSNotificationCenter" do
2
+ SampleNotification = "SampleNotification"
3
+ after do
4
+ @observer = Object.new
5
+ end
6
+
7
+ after do
8
+ notification_center.unobserve(@observer)
9
+ end
10
+
11
+ it "return notification center" do
12
+ notification_center.should.not.be.nil
13
+ end
14
+
15
+ it "add observer" do
16
+ notified = false
17
+ notification_center.observe(@observer, SampleNotification) do
18
+ notified = true
19
+ end
20
+
21
+ lambda {
22
+ notification_center.post SampleNotification
23
+ }.should.change { notified }
24
+ end
25
+
26
+ it "remove observer" do
27
+ lambda {
28
+ notification_center.observe(@observer, SampleNotification) {}
29
+ notification_center.unobserve(@observer)
30
+ }.should.not.change { notification_center.observers.keys.size }
31
+ end
32
+ end
@@ -0,0 +1,7 @@
1
+ class TestSuiteDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
4
+ @window.rootViewController = UIViewController.alloc.init
5
+ true
6
+ end
7
+ end
@@ -0,0 +1,31 @@
1
+ diff --git a/lib/motion/project/builder.rb b/lib/motion/project/builder.rb
2
+ index 39956a8..3e5a646 100644
3
+ --- a/lib/motion/project/builder.rb
4
+ +++ b/lib/motion/project/builder.rb
5
+ @@ -143,6 +143,7 @@ module Motion; module Project;
6
+ if config.spec_mode
7
+ # Build spec files too, but sequentially.
8
+ objs << build_file.call(File.expand_path(File.join(File.dirname(__FILE__), '../spec.rb')))
9
+ + objs << build_file.call(config.spec_helper) if File.exist?(config.spec_helper)
10
+ spec_objs = config.spec_files.map { |path| build_file.call(path) }
11
+ objs += spec_objs
12
+ end
13
+ diff --git a/lib/motion/project/config.rb b/lib/motion/project/config.rb
14
+ index 8c7ba43..d43d9a3 100644
15
+ --- a/lib/motion/project/config.rb
16
+ +++ b/lib/motion/project/config.rb
17
+ @@ -253,7 +253,13 @@ module Motion; module Project
18
+ end
19
+
20
+ def spec_files
21
+ - Dir.glob(File.join(specs_dir, '**', '*.rb'))
22
+ + files = Dir.glob(File.join(specs_dir, '**', '*.rb'))
23
+ + files.delete(spec_helper)
24
+ + files
25
+ + end
26
+ +
27
+ + def spec_helper
28
+ + File.join(specs_dir, 'spec_helper.rb')
29
+ end
30
+
31
+ def motiondir
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bubble-wrap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Matt Aimonetti
9
+ - Francis Chong
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-05-22 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description: RubyMotion wrappers and helpers (Ruby for iOS) - Making Cocoa APIs more
16
+ Ruby like, one API at a time. Fork away and send your pull request.
17
+ email:
18
+ - mattaimonetti@gmail.com
19
+ - francis@ignition.hk
20
+ executables: []
21
+ extensions: []
22
+ extra_rdoc_files: []
23
+ files:
24
+ - .gitignore
25
+ - Gemfile
26
+ - LICENSE
27
+ - README.md
28
+ - Rakefile
29
+ - bubble-wrap.gemspec
30
+ - lib/bubble-wrap.rb
31
+ - lib/bubble-wrap/app.rb
32
+ - lib/bubble-wrap/gestures.rb
33
+ - lib/bubble-wrap/http.rb
34
+ - lib/bubble-wrap/json.rb
35
+ - lib/bubble-wrap/kernel.rb
36
+ - lib/bubble-wrap/ns_index_path.rb
37
+ - lib/bubble-wrap/ns_notification_center.rb
38
+ - lib/bubble-wrap/ns_user_defaults.rb
39
+ - lib/bubble-wrap/ui_button.rb
40
+ - lib/bubble-wrap/ui_view_controller.rb
41
+ - lib/bubble-wrap/version.rb
42
+ - spec/http_spec.rb
43
+ - spec/json_spec.rb
44
+ - spec/ns_notification_center_spec.rb
45
+ - spec/spec_helper.rb
46
+ - spec_helper_patch.diff
47
+ homepage: https://github.com/mattetti/BubbleWrap
48
+ licenses: []
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 1.8.15
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: RubyMotion wrappers and helpers (Ruby for iOS) - Making Cocoa APIs more Ruby
71
+ like, one API at a time. Fork away and send your pull request.
72
+ test_files:
73
+ - spec/http_spec.rb
74
+ - spec/json_spec.rb
75
+ - spec/ns_notification_center_spec.rb
76
+ - spec/spec_helper.rb