bubble-wrap 0.3.1 → 0.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 (48) hide show
  1. data/CHANGELOG.md +5 -0
  2. data/README.md +183 -66
  3. data/Rakefile +6 -6
  4. data/bubble-wrap.gemspec +10 -5
  5. data/lib/bubble-wrap.rb +4 -39
  6. data/lib/bubble-wrap/core.rb +7 -0
  7. data/lib/bubble-wrap/ext.rb +2 -0
  8. data/lib/bubble-wrap/ext/motion_project_app.rb +26 -0
  9. data/lib/bubble-wrap/ext/motion_project_config.rb +21 -0
  10. data/lib/bubble-wrap/http.rb +2 -249
  11. data/lib/bubble-wrap/loader.rb +23 -0
  12. data/lib/bubble-wrap/requirement.rb +88 -0
  13. data/lib/bubble-wrap/requirement/path_manipulation.rb +40 -0
  14. data/lib/bubble-wrap/version.rb +1 -1
  15. data/lib_spec/bubble-wrap/requirement/path_manipulation_spec.rb +51 -0
  16. data/lib_spec/bubble-wrap/requirement_spec.rb +72 -0
  17. data/lib_spec/bubble-wrap_spec.rb +17 -0
  18. data/lib_spec/motion_stub.rb +12 -0
  19. data/{lib/bubble-wrap/module.rb → motion/core.rb} +0 -0
  20. data/{lib/bubble-wrap → motion/core}/app.rb +0 -0
  21. data/{lib/bubble-wrap → motion/core}/device.rb +0 -16
  22. data/{lib/bubble-wrap → motion/core}/device/screen.rb +0 -0
  23. data/{lib/bubble-wrap → motion/core}/gestures.rb +0 -0
  24. data/{lib/bubble-wrap → motion/core}/json.rb +0 -0
  25. data/{lib/bubble-wrap → motion/core}/ns_index_path.rb +0 -0
  26. data/{lib/bubble-wrap → motion/core}/ns_notification_center.rb +0 -0
  27. data/{lib/bubble-wrap → motion/core}/ns_user_defaults.rb +0 -0
  28. data/{lib/bubble-wrap → motion/core}/persistence.rb +0 -0
  29. data/{lib → motion/core}/pollute.rb +1 -1
  30. data/motion/core/string.rb +38 -0
  31. data/{lib/bubble-wrap → motion/core}/time.rb +0 -0
  32. data/{lib/bubble-wrap → motion/core}/ui_control.rb +0 -0
  33. data/{lib/bubble-wrap → motion/core}/ui_view_controller.rb +0 -0
  34. data/motion/http.rb +249 -0
  35. data/spec/{app_spec.rb → core/app_spec.rb} +2 -2
  36. data/spec/{device → core/device}/screen_spec.rb +0 -0
  37. data/spec/{device_spec.rb → core/device_spec.rb} +0 -0
  38. data/spec/{gestures_spec.rb → core/gestures_spec.rb} +0 -0
  39. data/spec/{json_spec.rb → core/json_spec.rb} +0 -0
  40. data/spec/{ns_index_path_spec.rb → core/ns_index_path_spec.rb} +0 -0
  41. data/spec/{ns_notification_center_spec.rb → core/ns_notification_center_spec.rb} +0 -0
  42. data/spec/{persistence_spec.rb → core/persistence_spec.rb} +0 -0
  43. data/spec/core/string_spec.rb +69 -0
  44. data/spec/{time_spec.rb → core/time_spec.rb} +0 -0
  45. data/spec/{ui_control_spec.rb → core/ui_control_spec.rb} +0 -0
  46. data/spec/{module_spec.rb → core_spec.rb} +0 -0
  47. data/spec/http_spec.rb +300 -280
  48. metadata +115 -42
@@ -0,0 +1,21 @@
1
+ module Motion
2
+ module Project
3
+ class Config
4
+ # HACK NEEDED since RubyMotion doesn't support full path
5
+ # dependencies.
6
+ def files_dependencies(deps_hash)
7
+ res_path = lambda do |x|
8
+ path = /^\.?\//.match(x) ? x : File.join('.', x)
9
+ unless @files.include?(path)
10
+ App.fail "Can't resolve dependency `#{x}' because #{path} is not in #{@files.inspect}"
11
+ end
12
+ path
13
+ end
14
+ deps_hash.each do |path, deps|
15
+ deps = [deps] unless deps.is_a?(Array)
16
+ @dependencies[res_path.call(path)] = deps.map(&res_path)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,249 +1,2 @@
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.to_s =~ /20\d/ ? true : false
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
- @cachePolicy = options.delete(:cache_policy) || NSURLRequestUseProtocolCachePolicy
122
- @options = options
123
- @response = HTTP::Response.new
124
- initiate_request(url)
125
- connection.start
126
- UIApplication.sharedApplication.networkActivityIndicatorVisible = true
127
- connection
128
- end
129
-
130
- def generate_get_params(payload, prefix=nil)
131
- list = []
132
- payload.each do |k,v|
133
- if v.is_a?(Hash)
134
- new_prefix = prefix ? "#{prefix}[#{k.to_s}]" : k.to_s
135
- param = generate_get_params(v, new_prefix)
136
- else
137
- param = prefix ? "#{prefix}[#{k}]=#{v}" : "#{k}=#{v}"
138
- end
139
- list << param
140
- end
141
- return list.flatten
142
- end
143
-
144
- def initiate_request(url_string)
145
- # http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/nsrunloop_Class/Reference/Reference.html#//apple_ref/doc/constant_group/Run_Loop_Modes
146
- # NSConnectionReplyMode
147
-
148
- unless @payload.nil?
149
- if @payload.is_a?(Hash)
150
- params = generate_get_params(@payload)
151
- @payload = params.join("&")
152
- end
153
- url_string = "#{url_string}?#{@payload}" if @method == "GET"
154
- end
155
-
156
- p "BubbleWrap::HTTP building a NSRequest for #{url_string}" if SETTINGS[:debug]
157
- @url = NSURL.URLWithString(url_string.stringByAddingPercentEscapesUsingEncoding NSUTF8StringEncoding)
158
- @request = NSMutableURLRequest.requestWithURL(@url,
159
- cachePolicy:@cachePolicy,
160
- timeoutInterval:@timeout)
161
- @request.setHTTPMethod @method
162
- @request.setAllHTTPHeaderFields(@headers) if @headers
163
-
164
- # @payload needs to be converted to data
165
- unless @method == "GET" || @payload.nil?
166
- @payload = @payload.to_s.dataUsingEncoding(NSUTF8StringEncoding)
167
- @request.setHTTPBody @payload
168
- end
169
-
170
- # NSHTTPCookieStorage.sharedHTTPCookieStorage
171
-
172
- @connection = create_connection(request, self)
173
- @request.instance_variable_set("@done_loading", false)
174
- def @request.done_loading; @done_loading; end
175
- def @request.done_loading!; @done_loading = true; end
176
- end
177
-
178
- def connection(connection, didReceiveResponse:response)
179
- @status_code = response.statusCode
180
- @response_headers = response.allHeaderFields
181
- @response_size = response.expectedContentLength.to_f
182
- end
183
-
184
- # This delegate method get called every time a chunk of data is being received
185
- def connection(connection, didReceiveData:received_data)
186
- @received_data ||= NSMutableData.new
187
- @received_data.appendData(received_data)
188
- end
189
-
190
- def connection(connection, willSendRequest:request, redirectResponse:redirect_response)
191
- p "HTTP redirected #{request.description}" if SETTINGS[:debug]
192
- new_request = request.mutableCopy
193
- # new_request.setValue(@credentials.inspect, forHTTPHeaderField:'Authorization') # disabled while we figure this one out
194
- new_request.setAllHTTPHeaderFields(@headers) if @headers
195
- @connection.cancel
196
- @connection = create_connection(new_request, self)
197
- new_request
198
- end
199
-
200
- def connection(connection, didFailWithError: error)
201
- UIApplication.sharedApplication.networkActivityIndicatorVisible = false
202
- @request.done_loading!
203
- NSLog"HTTP Connection failed #{error.localizedDescription}" if SETTINGS[:debug]
204
- @response.error_message = error.localizedDescription
205
- call_delegator_with_response
206
- end
207
-
208
-
209
- # The transfer is done and everything went well
210
- def connectionDidFinishLoading(connection)
211
- UIApplication.sharedApplication.networkActivityIndicatorVisible = false
212
- @request.done_loading!
213
- # copy the data in a local var that we will attach to the response object
214
- response_body = NSData.dataWithData(@received_data) if @received_data
215
- @response.update(status_code: status_code, body: response_body, headers: response_headers, url: @url)
216
-
217
- call_delegator_with_response
218
- end
219
-
220
- def connection(connection, didReceiveAuthenticationChallenge:challenge)
221
-
222
- if (challenge.previousFailureCount == 0)
223
- # by default we are keeping the credential for the entire session
224
- # Eventually, it would be good to let the user pick one of the 3 possible credential persistence options:
225
- # NSURLCredentialPersistenceNone,
226
- # NSURLCredentialPersistenceForSession,
227
- # NSURLCredentialPersistencePermanent
228
- p "auth challenged, answered with credentials: #{credentials.inspect}" if SETTINGS[:debug]
229
- new_credential = NSURLCredential.credentialWithUser(credentials[:username], password:credentials[:password], persistence:NSURLCredentialPersistenceForSession)
230
- challenge.sender.useCredential(new_credential, forAuthenticationChallenge:challenge)
231
- else
232
- challenge.sender.cancelAuthenticationChallenge(challenge)
233
- p 'Auth Failed :('
234
- end
235
- end
236
-
237
- def call_delegator_with_response
238
- if @delegator.respond_to?(:call)
239
- @delegator.call( @response, self )
240
- end
241
- end
242
-
243
- # This is a temporary method used for mocking.
244
- def create_connection(request, delegate)
245
- NSURLConnection.connectionWithRequest(request, delegate:delegate)
246
- end
247
- end
248
- end
249
- end
1
+ require File.expand_path('../loader.rb', __FILE__)
2
+ BubbleWrap.require('motion/http.rb')
@@ -0,0 +1,23 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "This file must be required within a RubyMotion project Rakefile."
3
+ end
4
+
5
+ require File.expand_path('../version', __FILE__) unless defined?(BubbleWrap::VERSION)
6
+ require File.expand_path('../ext', __FILE__)
7
+ require File.expand_path('../requirement', __FILE__)
8
+
9
+ module BubbleWrap
10
+
11
+ module_function
12
+
13
+ def root
14
+ File.expand_path('../../../', __FILE__)
15
+ end
16
+
17
+ def require(file_spec, &block)
18
+ Requirement.scan(caller.first, file_spec, &block)
19
+ end
20
+
21
+ end
22
+
23
+ BW = BubbleWrap
@@ -0,0 +1,88 @@
1
+ require File.expand_path "../requirement/path_manipulation", __FILE__
2
+
3
+ module BubbleWrap
4
+ class Requirement
5
+ extend PathManipulation
6
+ include PathManipulation
7
+
8
+ attr_accessor :file, :root
9
+ attr_writer :file_dependencies
10
+
11
+ def initialize(file,root)
12
+ self.file = file
13
+ self.root = root
14
+ end
15
+
16
+ def relative
17
+ convert_to_relative(file, root)
18
+ end
19
+
20
+ def depends_on(file_or_paths)
21
+ paths = file_or_paths.respond_to?(:each) ? file_or_paths : [ file_or_paths ]
22
+ self.file_dependencies += paths.map do |f|
23
+ f = self.class.file(f) unless f.is_a? Requirement
24
+ f unless f.file == file
25
+ end.compact
26
+ self.file_dependencies.uniq!(&:to_s)
27
+ end
28
+
29
+ def uses_framework(framework_name)
30
+ self.frameworks << framework_name
31
+ end
32
+
33
+ def dependencies
34
+ return {} if file_dependencies.empty?
35
+ { file => file_dependencies.map(&:to_s) }
36
+ end
37
+
38
+ def to_s
39
+ file
40
+ end
41
+
42
+ def file_dependencies
43
+ @file_dependencies ||= []
44
+ end
45
+
46
+ def frameworks
47
+ @frameworks ||= []
48
+ end
49
+
50
+ class << self
51
+
52
+ attr_accessor :paths
53
+
54
+ def scan(caller_location, file_spec, &block)
55
+ root = convert_caller_to_root_path caller_location
56
+ self.paths ||= {}
57
+ Dir.glob(File.expand_path(file_spec, root)).each do |file|
58
+ p = new(file,root)
59
+ self.paths[p.relative] = p
60
+ end
61
+ self.class_eval(&block) if block
62
+ end
63
+
64
+ def file(relative)
65
+ paths.fetch(relative)
66
+ end
67
+
68
+ def files
69
+ paths.values.map(&:to_s)
70
+ end
71
+
72
+ def files_dependencies
73
+ deps = {}
74
+ paths.each_value do |file|
75
+ deps.merge! file.dependencies
76
+ end
77
+ deps
78
+ end
79
+
80
+ def frameworks
81
+ frameworks = ['UIKit', 'Foundation', 'CoreGraphics'] +
82
+ paths.values.map(&:frameworks)
83
+ frameworks.flatten.compact.sort.uniq
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,40 @@
1
+ module BubbleWrap
2
+ class Requirement
3
+ module PathManipulation
4
+
5
+ def convert_caller_to_root_path(path)
6
+ path = convert_caller_to_path path
7
+ path = convert_to_absolute_path path
8
+ strip_up_to_last_lib path
9
+ end
10
+
11
+ def convert_caller_to_path(string)
12
+ chunks = string.split(':')
13
+ return chunks[0..-3].join(':') if chunks.size >= 3
14
+ string
15
+ end
16
+
17
+ def convert_to_absolute_path(path)
18
+ File.expand_path(path)
19
+ end
20
+
21
+ def strip_up_to_last_lib(path)
22
+ path = path.split('lib')
23
+ path = if path.size > 1
24
+ path[0..-2].join('lib')
25
+ else
26
+ path[0]
27
+ end
28
+ path = path[0..-2] if path[-1] == '/'
29
+ path
30
+ end
31
+
32
+ def convert_to_relative(path,root)
33
+ path = path.gsub(root,'')
34
+ path = path[1..-1] if path[0] == '/'
35
+ path
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,3 @@
1
1
  module BubbleWrap
2
- VERSION = '0.3.1'
2
+ VERSION = '0.4.0'
3
3
  end
@@ -0,0 +1,51 @@
1
+ require File.expand_path('../../../../lib/bubble-wrap/requirement/path_manipulation', __FILE__)
2
+
3
+ describe BubbleWrap::Requirement::PathManipulation do
4
+
5
+ subject do
6
+ o = Object.new
7
+ o.extend BubbleWrap::Requirement::PathManipulation
8
+ o
9
+ end
10
+
11
+ describe '#convert_caller_to_path' do
12
+ it 'strips off from the second-to-last colon' do
13
+ subject.convert_caller_to_path("/fake/:path:91:in `fake_method'").
14
+ should == '/fake/:path'
15
+ end
16
+
17
+ it 'leaves plain old paths unmolested' do
18
+ subject.convert_caller_to_path("/fake/path").
19
+ should == '/fake/path'
20
+ end
21
+ end
22
+
23
+ describe '#convert_to_absolute_path' do
24
+ it 'converts relative paths to absolute paths' do
25
+ subject.convert_to_absolute_path('foo')[0].should == '/'
26
+ end
27
+
28
+ it "doesn't modify absolute paths" do
29
+ subject.convert_to_absolute_path('/foo').should == '/foo'
30
+ end
31
+ end
32
+
33
+ describe '#strip_up_to_last_lib' do
34
+ it 'strips off from the last lib' do
35
+ subject.strip_up_to_last_lib('/fake/lib/dir/lib/foo').
36
+ should == '/fake/lib/dir'
37
+ end
38
+
39
+ it "doesn't modify the path otherwise" do
40
+ subject.strip_up_to_last_lib('/fake/path').
41
+ should == '/fake/path'
42
+ end
43
+ end
44
+
45
+ describe "#convert_to_relative" do
46
+ it 'strips off the root portion' do
47
+ subject.convert_to_relative('/foo/bar/baz', '/foo').
48
+ should == 'bar/baz'
49
+ end
50
+ end
51
+ end