bubble-wrap 0.3.1 → 0.4.0

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