bubble-wrap 1.0.0 → 1.1.0

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