bubble-wrap 1.0.0 → 1.1.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 (65) hide show
  1. data/.gitignore +0 -1
  2. data/.yardopts +2 -0
  3. data/CHANGELOG.md +15 -0
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +24 -0
  6. data/HACKING.md +2 -2
  7. data/README.md +363 -21
  8. data/Rakefile +6 -2
  9. data/lib/bubble-wrap.rb +0 -1
  10. data/lib/bubble-wrap/all.rb +4 -0
  11. data/lib/bubble-wrap/camera.rb +7 -0
  12. data/lib/bubble-wrap/core.rb +3 -2
  13. data/lib/bubble-wrap/ext/motion_project_app.rb +2 -2
  14. data/lib/bubble-wrap/http.rb +1 -0
  15. data/lib/bubble-wrap/loader.rb +19 -12
  16. data/lib/bubble-wrap/location.rb +6 -0
  17. data/lib/bubble-wrap/reactor.rb +10 -0
  18. data/lib/bubble-wrap/requirement.rb +11 -3
  19. data/lib/bubble-wrap/rss_parser.rb +2 -0
  20. data/lib/bubble-wrap/ui.rb +4 -0
  21. data/lib/bubble-wrap/version.rb +1 -1
  22. data/motion/core.rb +8 -2
  23. data/motion/core/app.rb +34 -6
  24. data/motion/core/device.rb +10 -1
  25. data/motion/core/device/camera.rb +219 -0
  26. data/motion/core/device/camera_wrapper.rb +45 -0
  27. data/motion/core/ns_url_request.rb +10 -0
  28. data/motion/core/persistence.rb +7 -0
  29. data/motion/core/pollute.rb +1 -2
  30. data/motion/core/string.rb +23 -0
  31. data/motion/http.rb +142 -83
  32. data/motion/location/location.rb +152 -0
  33. data/motion/location/pollute.rb +5 -0
  34. data/motion/reactor.rb +105 -0
  35. data/motion/reactor/default_deferrable.rb +9 -0
  36. data/motion/reactor/deferrable.rb +126 -0
  37. data/motion/reactor/eventable.rb +24 -0
  38. data/motion/reactor/future.rb +22 -0
  39. data/motion/reactor/periodic_timer.rb +27 -0
  40. data/motion/reactor/queue.rb +60 -0
  41. data/motion/reactor/timer.rb +25 -0
  42. data/motion/rss_parser.rb +131 -0
  43. data/motion/shortcut.rb +18 -0
  44. data/motion/{core → ui}/gestures.rb +15 -9
  45. data/motion/ui/pollute.rb +5 -0
  46. data/motion/{core → ui}/ui_control.rb +0 -0
  47. data/motion/{core → ui}/ui_view_controller.rb +0 -0
  48. data/resources/atom.xml +1705 -0
  49. data/spec/lib/bubble-wrap/ext/motion_project_app_spec.rb +7 -11
  50. data/spec/lib/bubble-wrap/requirement_spec.rb +4 -4
  51. data/spec/lib/bubble-wrap_spec.rb +2 -2
  52. data/spec/motion/core/app_spec.rb +118 -14
  53. data/spec/motion/core/device/camera_spec.rb +130 -0
  54. data/spec/motion/core/device/camera_wrapper_spec.rb +45 -0
  55. data/spec/motion/core/device_spec.rb +0 -50
  56. data/spec/motion/core/gestures_spec.rb +5 -0
  57. data/spec/motion/core/persistence_spec.rb +24 -2
  58. data/spec/motion/core/string_spec.rb +55 -0
  59. data/spec/motion/core_spec.rb +72 -1
  60. data/spec/motion/http_spec.rb +100 -65
  61. data/spec/motion/location/location_spec.rb +152 -0
  62. data/spec/motion/reactor/eventable_spec.rb +40 -0
  63. data/spec/motion/reactor_spec.rb +163 -0
  64. data/spec/motion/rss_parser_spec.rb +36 -0
  65. metadata +75 -16
@@ -0,0 +1,45 @@
1
+ module BubbleWrap
2
+ module Device
3
+ module CameraWrapper
4
+
5
+ module_function
6
+
7
+ # The front-facing camera used to capture media
8
+ # @return [Device::Camera, NilClass] a Camera will be returned if there is a front camera, nil otherwise
9
+ def front
10
+ BubbleWrap::Device::Camera.front
11
+ end
12
+
13
+ # Verifies that the device running has a front facing camera.
14
+ # @return [TrueClass, FalseClass] true will be returned if the device has a front facing camera, false otherwise.
15
+ def front?
16
+ !!front
17
+ end
18
+
19
+ # The rear-facing camera used to capture media
20
+ # @return [Device::Camera, NilClass] a Camera will be returned if there is a rear camera, nil otherwise
21
+ def rear
22
+ BubbleWrap::Device::Camera.rear
23
+ end
24
+
25
+ # Verifies that the device running has a rear facing camera.
26
+ # @return [TrueClass, FalseClass] true will be returned if the device has a rear facing camera, false otherwise.
27
+ def rear?
28
+ !!rear
29
+ end
30
+
31
+ # A Device::Camera used to capture media; by default it will use the :photo_library source type
32
+ # See Device::Camera docs for more source type options.
33
+ # @return [Device::Camera] a Camera will always be returned.
34
+ def any
35
+ BubbleWrap::Device::Camera.any
36
+ end
37
+
38
+ # Should always return true, since picking images from *some* source is always possible
39
+ # @return [TrueClass]
40
+ def any?
41
+ !!any
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,10 @@
1
+ class NSURLRequest
2
+
3
+ # Provides a to_s method so we can use inspect in instances and get
4
+ # valuable information.
5
+ def to_s
6
+ "#<#{self.class}:#{self.object_id} - url: #{self.URL.description}, cache policy: #{self.cachePolicy}, Pipelining: #{self.HTTPShouldUsePipelining}, main doc url: #{mainDocumentURL},\
7
+ timeout: #{self.timeoutInterval}, network service type: #{self.networkServiceType} >"
8
+ end
9
+
10
+ end
@@ -16,6 +16,13 @@ module BubbleWrap
16
16
  storage.objectForKey storage_key(key.to_s)
17
17
  end
18
18
 
19
+ def merge(values)
20
+ values.each do |key, value|
21
+ storage.setObject(value, forKey: storage_key(key.to_s))
22
+ end
23
+ storage.synchronize
24
+ end
25
+
19
26
  def storage
20
27
  NSUserDefaults.standardUserDefaults
21
28
  end
@@ -1,6 +1,5 @@
1
1
  [
2
- [NSIndexPath, NSIndexPathWrap],
3
- [UIControl, UIControlWrap]
2
+ [NSIndexPath, NSIndexPathWrap]
4
3
  ].each do |base, wrapper|
5
4
  base.send(:include, wrapper)
6
5
  end
@@ -32,6 +32,29 @@ module BubbleWrap
32
32
  word.downcase!
33
33
  word
34
34
  end
35
+
36
+ def to_color
37
+ # First check if it is a color keyword
38
+ keyword_selector = "#{self.camelize(false)}Color"
39
+ return UIColor.send(keyword_selector) if UIColor.respond_to? keyword_selector
40
+
41
+ # Next attempt to convert from hex
42
+ hex_color = self.gsub("#", "")
43
+ case hex_color.size
44
+ when 3
45
+ colors = hex_color.scan(%r{[0-9A-Fa-f]}).map{ |el| (el * 2).to_i(16) }
46
+ when 6
47
+ colors = hex_color.scan(%r<[0-9A-Fa-f]{2}>).map{ |el| el.to_i(16) }
48
+ else
49
+ raise ArgumentError
50
+ end
51
+ if colors.size == 3
52
+ UIColor.colorWithRed((colors[0]/255.0), green:(colors[1]/255.0), blue:(colors[2]/255.0), alpha:1)
53
+ else
54
+ raise ArgumentError
55
+ end
56
+ end
57
+
35
58
  end
36
59
  end
37
60
 
data/motion/http.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  module BubbleWrap
2
2
 
3
- SETTINGS = {}
4
-
5
3
  # The HTTP module provides a simple interface to make HTTP requests.
6
4
  #
7
5
  # TODO: preflight support, easier/better cookie support, better error handling
@@ -21,38 +19,38 @@ module BubbleWrap
21
19
  # end
22
20
  #
23
21
  def self.get(url, options={}, &block)
24
- create_query url, :get, options, block
22
+ options[:action] = block if block_given?
23
+ HTTP::Query.new(url, :get, options)
25
24
  end
26
25
 
27
26
  # Make a POST request
28
27
  def self.post(url, options={}, &block)
29
- create_query url, :post, options, block
28
+ options[:action] = block if block_given?
29
+ HTTP::Query.new(url, :post, options)
30
30
  end
31
31
 
32
32
  # Make a PUT request
33
33
  def self.put(url, options={}, &block)
34
- create_query url, :put, options, block
34
+ options[:action] = block if block_given?
35
+ HTTP::Query.new(url, :put, options)
35
36
  end
36
37
 
37
38
  # Make a DELETE request
38
39
  def self.delete(url, options={}, &block)
39
- create_query url, :delete, options, block
40
+ options[:action] = block if block_given?
41
+ HTTP::Query.new(url, :delete, options)
40
42
  end
41
43
 
42
44
  # Make a HEAD request
43
45
  def self.head(url, options={}, &block)
44
- create_query url, :head, options, block
46
+ options[:action] = block if block_given?
47
+ HTTP::Query.new(url, :head, options)
45
48
  end
46
49
 
47
50
  # Make a PATCH request
48
51
  def self.patch(url, options={}, &block)
49
- create_query url, :patch, options, block
50
- end
51
-
52
- private
53
- def self.create_query(url, method, options, passed_block)
54
- options[:action] = passed_block if passed_block
55
- HTTP::Query.new( url, method, options )
52
+ options[:action] = block if block_given?
53
+ HTTP::Query.new(url, :patch, options)
56
54
  end
57
55
 
58
56
  # Response class wrapping the results of a Query's response
@@ -76,6 +74,11 @@ module BubbleWrap
76
74
  status_code.to_s =~ /20\d/ ? true : false
77
75
  end
78
76
 
77
+ def to_s
78
+ "#<#{self.class}:#{self.object_id} - url: #{self.url}, body: #{self.body}, headers: #{self.headers}, status code: #{self.status_code}, error message: #{self.error_message} >"
79
+ end
80
+ alias description to_s
81
+
79
82
  end
80
83
 
81
84
  # Class wrapping NSConnection and often used indirectly by the BubbleWrap::HTTP module methods.
@@ -106,10 +109,12 @@ module BubbleWrap
106
109
  # :headers<Hash> - headers send with the request
107
110
  # Anything else will be available via the options attribute reader.
108
111
  #
109
- def initialize(url, http_method = :get, options={})
112
+ def initialize(url_string, http_method = :get, options={})
110
113
  @method = http_method.upcase.to_s
111
114
  @delegator = options.delete(:action) || self
112
115
  @payload = options.delete(:payload)
116
+ @files = options.delete(:files)
117
+ @boundary = options.delete(:boundary) || BW.create_uuid
113
118
  @credentials = options.delete(:credentials) || {}
114
119
  @credentials = {:username => '', :password => ''}.merge(@credentials)
115
120
  @timeout = options.delete(:timeout) || 30.0
@@ -117,62 +122,21 @@ module BubbleWrap
117
122
  @cache_policy = options.delete(:cache_policy) || NSURLRequestUseProtocolCachePolicy
118
123
  @options = options
119
124
  @response = HTTP::Response.new
120
- initiate_request(url)
121
- connection.start
122
- UIApplication.sharedApplication.networkActivityIndicatorVisible = true
123
- connection
124
- end
125
125
 
126
- def generate_params(payload, prefix=nil)
127
- list = []
128
- payload.each do |k,v|
129
- if v.is_a?(Hash)
130
- new_prefix = prefix ? "#{prefix}[#{k.to_s}]" : k.to_s
131
- param = generate_params(v, new_prefix)
132
- list << param
133
- elsif v.is_a?(Array)
134
- v.each do |val|
135
- param = prefix ? "#{prefix}[#{k}][]=#{val}" : "#{k}[]=#{val}"
136
- list << param
137
- end
138
- else
139
- param = prefix ? "#{prefix}[#{k}]=#{v}" : "#{k}=#{v}"
140
- list << param
141
- end
142
- end
143
- return list.flatten
144
- end
145
-
146
- def initiate_request(url_string)
147
- # http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/nsrunloop_Class/Reference/Reference.html#//apple_ref/doc/constant_group/Run_Loop_Modes
148
- # NSConnectionReplyMode
149
-
150
- unless @payload.nil?
151
- if @payload.is_a?(Hash)
152
- params = generate_params(@payload)
153
- @payload = params.join("&")
154
- end
155
- url_string = "#{url_string}?#{@payload}" if @method == "GET"
156
- end
157
- #this method needs a refactor when the specs are done. (especially this utf8 escaping part)
158
- log "BubbleWrap::HTTP building a NSRequest for #{url_string}"
159
- @url = NSURL.URLWithString(url_string.stringByAddingPercentEscapesUsingEncoding NSUTF8StringEncoding)
160
- @request = NSMutableURLRequest.requestWithURL(@url,
161
- cachePolicy:@cache_policy,
162
- timeoutInterval:@timeout)
163
- @request.setHTTPMethod @method
164
- @request.setAllHTTPHeaderFields(@headers) if @headers
126
+ @url = create_url(url_string)
127
+ @body = create_request_body
128
+ @request = create_request
129
+ @connection = create_connection(request, self)
130
+ @connection.start
165
131
 
166
- # @payload needs to be converted to data
167
- unless @method == "GET" || @payload.nil?
168
- @payload = @payload.to_s.dataUsingEncoding(NSUTF8StringEncoding)
169
- @request.setHTTPBody @payload
170
- end
132
+ UIApplication.sharedApplication.networkActivityIndicatorVisible = true
133
+ end
171
134
 
172
- # NSHTTPCookieStorage.sharedHTTPCookieStorage
173
- @connection = create_connection(request, self)
174
- patch_nsurl_request
135
+ def to_s
136
+ "#<#{self.class}:#{self.object_id} - Method: #{@method}, url: #{@url.description}, body: #{@body.description}, Payload: #{@payload}, Headers: #{@headers} Credentials: #{@credentials}, Timeout: #{@timeout}, \
137
+ Cache policy: #{@cache_policy}, response: #{@response.inspect} >"
175
138
  end
139
+ alias description to_s
176
140
 
177
141
  def connection(connection, didReceiveResponse:response)
178
142
  @status_code = response.statusCode
@@ -184,10 +148,14 @@ module BubbleWrap
184
148
  def connection(connection, didReceiveData:received_data)
185
149
  @received_data ||= NSMutableData.new
186
150
  @received_data.appendData(received_data)
151
+
152
+ if download_progress = options[:download_progress]
153
+ download_progress.call(@received_data.length.to_f, response_size)
154
+ end
187
155
  end
188
156
 
189
157
  def connection(connection, willSendRequest:request, redirectResponse:redirect_response)
190
- log "HTTP redirected #{request.description}"
158
+ log "HTTP redirected info: #{request} - #{self.description}"
191
159
  new_request = request.mutableCopy
192
160
  # new_request.setValue(@credentials.inspect, forHTTPHeaderField:'Authorization') # disabled while we figure this one out
193
161
  new_request.setAllHTTPHeaderFields(@headers) if @headers
@@ -197,19 +165,22 @@ module BubbleWrap
197
165
  end
198
166
 
199
167
  def connection(connection, didFailWithError: error)
168
+ log "HTTP Connection failed #{error.localizedDescription}"
200
169
  UIApplication.sharedApplication.networkActivityIndicatorVisible = false
201
170
  @request.done_loading!
202
- log "HTTP Connection failed #{error.localizedDescription}"
203
171
  @response.error_message = error.localizedDescription
204
172
  call_delegator_with_response
205
173
  end
206
174
 
175
+ def connection(connection, didSendBodyData:sending, totalBytesWritten:written, totalBytesExpectedToWrite:expected)
176
+ if upload_progress = options[:upload_progress]
177
+ upload_progress.call(sending, written, expected)
178
+ end
179
+ end
207
180
 
208
- # The transfer is done and everything went well
209
181
  def connectionDidFinishLoading(connection)
210
182
  UIApplication.sharedApplication.networkActivityIndicatorVisible = false
211
183
  @request.done_loading!
212
- # copy the data in a local var that we will attach to the response object
213
184
  response_body = NSData.dataWithData(@received_data) if @received_data
214
185
  @response.update(status_code: status_code, body: response_body, headers: response_headers, url: @url)
215
186
 
@@ -217,16 +188,10 @@ module BubbleWrap
217
188
  end
218
189
 
219
190
  def connection(connection, didReceiveAuthenticationChallenge:challenge)
220
-
221
191
  if (challenge.previousFailureCount == 0)
222
- # by default we are keeping the credential for the entire session
223
- # Eventually, it would be good to let the user pick one of the 3 possible credential persistence options:
224
- # NSURLCredentialPersistenceNone,
225
- # NSURLCredentialPersistenceForSession,
226
- # NSURLCredentialPersistencePermanent
227
- log "auth challenged, answered with credentials: #{credentials.inspect}"
228
192
  new_credential = NSURLCredential.credentialWithUser(credentials[:username], password:credentials[:password], persistence:NSURLCredentialPersistenceForSession)
229
193
  challenge.sender.useCredential(new_credential, forAuthenticationChallenge:challenge)
194
+ log "auth challenged, answered with credentials: #{credentials.inspect}"
230
195
  else
231
196
  challenge.sender.cancelAuthenticationChallenge(challenge)
232
197
  log 'Auth Failed :('
@@ -236,8 +201,101 @@ module BubbleWrap
236
201
 
237
202
  private
238
203
 
204
+ def create_request
205
+ log "BubbleWrap::HTTP building a NSRequest for #{@url.description}"
206
+
207
+ request = NSMutableURLRequest.requestWithURL(@url,
208
+ cachePolicy:@cache_policy,
209
+ timeoutInterval:@timeout)
210
+ request.setHTTPMethod(@method)
211
+ request.setAllHTTPHeaderFields(@headers)
212
+ request.setHTTPBody(@body)
213
+ patch_nsurl_request(request)
214
+
215
+ request
216
+ end
217
+
218
+ def create_request_body
219
+ return nil if (@method == "GET" || @method == "HEAD")
220
+ return nil unless (@payload || @files)
221
+ @headers = {"Content-Type" => "multipart/form-data; boundary=#{@boundary}"} if @headers.nil?
222
+
223
+ body = NSMutableData.data
224
+
225
+ append_payload(body) if @payload
226
+ append_files(body) if @files
227
+ body.appendData("\r\n--#{@boundary}--\r\n".dataUsingEncoding NSUTF8StringEncoding)
228
+
229
+ log "Built HTTP body: \n #{body.to_str}"
230
+ body
231
+ end
232
+
233
+ def append_payload(body)
234
+ if @payload.is_a?(NSData)
235
+ body.appendData(@payload)
236
+ else
237
+ append_form_params(body)
238
+ end
239
+ end
240
+
241
+ def append_form_params(body)
242
+ @payload.each do |key, value|
243
+ form_data = NSMutableData.new
244
+ s = "\r\n--#{@boundary}\r\n"
245
+ s += "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n"
246
+ s += value.to_s
247
+ form_data.appendData(s.dataUsingEncoding NSUTF8StringEncoding)
248
+ body.appendData(form_data)
249
+ end
250
+ end
251
+
252
+ def append_files(body)
253
+ @files.each do |key, value|
254
+ file_data = NSMutableData.new
255
+ s = "\r\n--#{@boundary}\r\n"
256
+ s += "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{key}\"\r\n"
257
+ s += "Content-Type: application/octet-stream\r\n\r\n"
258
+ file_data.appendData(s.dataUsingEncoding NSUTF8StringEncoding)
259
+ file_data.appendData(value)
260
+ body.appendData(file_data)
261
+ end
262
+ end
263
+
264
+ def create_url(url_string)
265
+ if (@method == "GET" || @method == "HEAD") && @payload
266
+ convert_payload_to_url if @payload.is_a?(Hash)
267
+ url_string += "?#{@payload}"
268
+ end
269
+ NSURL.URLWithString(url_string.stringByAddingPercentEscapesUsingEncoding NSUTF8StringEncoding)
270
+ end
271
+
272
+ def convert_payload_to_url
273
+ params_array = generate_get_params(@payload)
274
+ @payload = params_array.join("&")
275
+ end
276
+
277
+ def generate_get_params(payload, prefix=nil)
278
+ list = []
279
+ payload.each do |k,v|
280
+ if v.is_a?(Hash)
281
+ new_prefix = prefix ? "#{prefix}[#{k.to_s}]" : k.to_s
282
+ param = generate_get_params(v, new_prefix)
283
+ list << param
284
+ elsif v.is_a?(Array)
285
+ v.each do |val|
286
+ param = prefix ? "#{prefix}[#{k}][]=#{val}" : "#{k}[]=#{val}"
287
+ list << param
288
+ end
289
+ else
290
+ param = prefix ? "#{prefix}[#{k}]=#{v}" : "#{k}=#{v}"
291
+ list << param
292
+ end
293
+ end
294
+ return list.flatten
295
+ end
296
+
239
297
  def log(message)
240
- NSLog message if SETTINGS[:debug]
298
+ NSLog message if BubbleWrap.debug?
241
299
  end
242
300
 
243
301
  def escape_line_feeds(hash)
@@ -248,11 +306,11 @@ module BubbleWrap
248
306
  escaped_hash
249
307
  end
250
308
 
251
- def patch_nsurl_request
252
- @request.instance_variable_set("@done_loading", false)
309
+ def patch_nsurl_request(request)
310
+ request.instance_variable_set("@done_loading", false)
253
311
 
254
- def @request.done_loading; @done_loading; end
255
- def @request.done_loading!; @done_loading = true; end
312
+ def request.done_loading; @done_loading; end
313
+ def request.done_loading!; @done_loading = true; end
256
314
  end
257
315
 
258
316
  def call_delegator_with_response
@@ -265,6 +323,7 @@ module BubbleWrap
265
323
  def create_connection(request, delegate)
266
324
  NSURLConnection.connectionWithRequest(request, delegate:delegate)
267
325
  end
326
+
268
327
  end
269
328
  end
270
329
  end