motion-http 0.2.0 → 1.0.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.
- checksums.yaml +4 -4
- data/README.md +74 -15
- data/lib/android/adapter.rb +4 -52
- data/lib/android/base64.rb +13 -0
- data/lib/android/json.rb +1 -1
- data/lib/android/params_encoder.rb +5 -0
- data/lib/android/volley_request.rb +52 -0
- data/lib/cocoa/adapter.rb +16 -10
- data/lib/cocoa/base64.rb +12 -0
- data/lib/cocoa/params_encoder.rb +8 -0
- data/lib/common/http.rb +33 -18
- data/lib/common/http/client.rb +45 -18
- data/lib/common/http/headers.rb +27 -3
- data/lib/common/http/logger.rb +57 -17
- data/lib/common/http/request.rb +46 -5
- data/lib/common/http/response.rb +13 -2
- data/lib/motion-http.rb +2 -3
- metadata +7 -3
- data/lib/cocoa/form_data_serializer.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38301fffb680c2ff73c68cc0cafef260be772a4c09892c8f6706bb415014bdc6
|
4
|
+
data.tar.gz: 562ac8347a504aa3c061beeb6555ec54b73d0ec5c6d27266349150c1e4239c45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d34d53372cd85ff50915ca586a36fdba710852a62e9f60c511139d0d72887084e974cc7a39faedc02a5737c96d858900e66ef661960572c22970aebccc613e32
|
7
|
+
data.tar.gz: 01d2e62e89ed670128c423c4f005383cc2ff46e351c3dc2163cf567a88ee7a5507257c3321cac4ba151f1bb91f2865af9ebd4ce1aae9f0c06b369d46ba63117a
|
data/README.md
CHANGED
@@ -35,11 +35,11 @@ Using `motion-http` is quick and easy. You can use the simple approach for makin
|
|
35
35
|
|
36
36
|
The basic syntax for a request looks like this:
|
37
37
|
```ruby
|
38
|
-
HTTP.method(url,
|
39
|
-
# this
|
38
|
+
HTTP.method(url, options) do |response|
|
39
|
+
# this will be called asynchronously
|
40
40
|
end
|
41
41
|
```
|
42
|
-
Where `method` can be `get`, `post`, `put`, `patch`, or `
|
42
|
+
Where `method` can be either `get`, `post`, `put`, `patch`, `delete`, `head`, `options`, or `trace`.
|
43
43
|
|
44
44
|
For example, to make a simple `GET` request:
|
45
45
|
```ruby
|
@@ -52,23 +52,27 @@ HTTP.get("http://www.example.com") do |response|
|
|
52
52
|
end
|
53
53
|
```
|
54
54
|
|
55
|
-
|
55
|
+
If you need to specify query params:
|
56
56
|
```ruby
|
57
|
-
HTTP.get("http://www.example.com/search", term: "my search term") do |response|
|
57
|
+
HTTP.get("http://www.example.com/search", params: { term: "my search term" }) do |response|
|
58
58
|
# ...
|
59
59
|
end
|
60
60
|
```
|
61
61
|
|
62
|
-
The response object contains the status code, headers, and
|
62
|
+
The response object contains the status code, headers, body, and shortcut methods for checking the response status:
|
63
63
|
```ruby
|
64
64
|
HTTP.get("http://example.com") do |response|
|
65
|
-
puts response.status_code
|
65
|
+
puts response.status_code.to_s
|
66
66
|
puts response.headers.inspect
|
67
67
|
puts response.body
|
68
|
+
response.success? # 2xx status
|
69
|
+
response.redirect? # 3xx status
|
70
|
+
response.client_error? # 4xx status
|
71
|
+
response.server_error? # 5xx status
|
68
72
|
end
|
69
73
|
```
|
70
74
|
|
71
|
-
JSON
|
75
|
+
If the response body has a JSON content type it will automatically be parsed when requesting the `response.object`:
|
72
76
|
```ruby
|
73
77
|
HTTP.get("http://api.example.com/people.json") do |response|
|
74
78
|
if response.success?
|
@@ -81,17 +85,29 @@ HTTP.get("http://api.example.com/people.json") do |response|
|
|
81
85
|
end
|
82
86
|
```
|
83
87
|
|
84
|
-
|
88
|
+
Use the `follow_redirects` option to specify whether or not to follow redirects. It defaults to true:
|
85
89
|
```ruby
|
86
|
-
HTTP.get("http://example.com/redirect",
|
90
|
+
HTTP.get("http://example.com/redirect", follow_redirects: false) do |response|
|
87
91
|
# ...
|
88
92
|
end
|
89
93
|
```
|
90
94
|
|
91
|
-
|
95
|
+
When making a `POST` request, specify the `:form` option and it will automatically be encoded as `application/x-www-form-urlencoded` request body:
|
92
96
|
```ruby
|
93
|
-
|
94
|
-
|
97
|
+
HTTP.post("http://www.example.com/login", form: { user: 'andrew', pass: 'secret'}) do |response|
|
98
|
+
if response.success?
|
99
|
+
puts "Authenticated!"
|
100
|
+
elsif response.client_error?
|
101
|
+
puts "Bad username or password"
|
102
|
+
else
|
103
|
+
puts "Oops! Something went wrong."
|
104
|
+
end
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
Likewise, to send a JSON encoded request body, use the `:json` option:
|
109
|
+
```ruby
|
110
|
+
HTTP.post("http://www.example.com/widgets", json: { widget: { name: "Foobar" } }) do |response|
|
95
111
|
if response.success?
|
96
112
|
puts "Widget created!"
|
97
113
|
elsif response.status_code == 422
|
@@ -102,11 +118,24 @@ HTTP.post("http://www.example.com/widgets", json) do |response|
|
|
102
118
|
end
|
103
119
|
```
|
104
120
|
|
105
|
-
|
121
|
+
Request specific headers can also be specified with the `:headers` option (overriding any previously set headers):
|
122
|
+
```ruby
|
123
|
+
HTTP.post("http://www.example.com/widgets",
|
124
|
+
headers: { 'Content-Type' => 'application/vnd.api+json' },
|
125
|
+
json: { widget: { name: "Foobar" } }
|
126
|
+
) do |response|
|
127
|
+
# ...
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
All other HTTP method requests work the same way:
|
106
132
|
```ruby
|
107
133
|
HTTP.put(url, params) { ... }
|
108
134
|
HTTP.patch(url, params) { ... }
|
109
135
|
HTTP.delete(url, params) { ... }
|
136
|
+
HTTP.head(url, params) { ... }
|
137
|
+
HTTP.options(url, params) { ... }
|
138
|
+
HTTP.trace(url, params) { ... }
|
110
139
|
```
|
111
140
|
|
112
141
|
### Advanced Usage
|
@@ -117,6 +146,7 @@ A common use case is to create a reusable HTTP client that uses a common base UR
|
|
117
146
|
client = HTTP::Client.new("http://api.example.com")
|
118
147
|
# Set or replace a single header:
|
119
148
|
client.header "X-API-TOKEN", "abc123xyz"
|
149
|
+
client.header["X-API-TOKEN"] = "abc123xyz"
|
120
150
|
|
121
151
|
# To set or replace multiple headers:
|
122
152
|
client.headers "X-API-TOKEN" => "abc123xyz",
|
@@ -125,7 +155,7 @@ client.headers "X-API-TOKEN" => "abc123xyz",
|
|
125
155
|
# Note that it is valid for some headers to appear multiple times (Accept, Vary, etc).
|
126
156
|
# To append multiple headers of the same key:
|
127
157
|
client.add_header "Accept", "application/json"
|
128
|
-
client.
|
158
|
+
client.headers.add "Accept", "application/json"
|
129
159
|
```
|
130
160
|
|
131
161
|
Then you can make your requests relative to the base URL that you specified when creating your client.
|
@@ -135,6 +165,35 @@ client.get("/people") do |response|
|
|
135
165
|
end
|
136
166
|
```
|
137
167
|
|
168
|
+
### Basic Auth / Token Auth
|
169
|
+
|
170
|
+
To make Basic Auth requests, either set the credentials before the request, or set it on your client:
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
HTTP.basic_auth('username', 'password').get('https://example.com/protected')
|
174
|
+
# or
|
175
|
+
client.basic_auth('username', 'password')
|
176
|
+
client.get('/protected')
|
177
|
+
```
|
178
|
+
|
179
|
+
The `auth` method is another shortcut for setting any value of the Authorization header:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
HTTP.auth("Token token=#{my_token}")
|
183
|
+
# or
|
184
|
+
client.auth("Token token=#{my_token}")
|
185
|
+
# same as
|
186
|
+
client.headers['Authorization'] = "Token token=#{my_token}"
|
187
|
+
```
|
188
|
+
|
189
|
+
### Logging
|
190
|
+
|
191
|
+
By default, requests and responses will be logged. If you would like to disable this:
|
192
|
+
|
193
|
+
```
|
194
|
+
HTTP.logger.disable!
|
195
|
+
```
|
196
|
+
|
138
197
|
## Contributing
|
139
198
|
|
140
199
|
1. Fork it
|
data/lib/android/adapter.rb
CHANGED
@@ -1,61 +1,13 @@
|
|
1
1
|
class Motion
|
2
2
|
class HTTP
|
3
3
|
class Adapter
|
4
|
-
JSONMediaType = Okhttp3::MediaType.parse("application/json; charset=utf-8")
|
5
|
-
|
6
|
-
def self.client
|
7
|
-
@client ||= Okhttp3::OkHttpClient.new
|
8
|
-
end
|
9
|
-
|
10
4
|
def self.perform(request, &callback)
|
11
|
-
|
12
|
-
|
13
|
-
headers = request.headers
|
14
|
-
params = request.params
|
15
|
-
|
16
|
-
request = OkHttp3::Request::Builder.new
|
17
|
-
request.url(url) # TODO: encode GET params and append to URL prior to calling this method
|
18
|
-
headers.each do |key, value|
|
19
|
-
if value.is_a? Array
|
20
|
-
value.each {|val| request.addHeader(key, val) }
|
21
|
-
else
|
22
|
-
request.header(key, value)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
if http_method != :get
|
26
|
-
puts "would have set body for #{http_method.to_s.upcase} #{url}"
|
27
|
-
# body = OkHttp3::RequestBody.create(JSONMediaType, params) # TODO: allow other content types
|
28
|
-
# request.method(http_method.to_s, body)
|
29
|
-
end
|
30
|
-
client.newCall(request.build).enqueue(OkhttpCallback.new(request, callback))
|
5
|
+
volley_request = VolleyRequest.create(request, callback)
|
6
|
+
queue.add(volley_request)
|
31
7
|
end
|
32
8
|
|
33
|
-
|
34
|
-
|
35
|
-
@request = request
|
36
|
-
@callback = callback
|
37
|
-
end
|
38
|
-
|
39
|
-
def onFailure(call, e)
|
40
|
-
puts "Error: #{e.getMessage}"
|
41
|
-
@callback.call(Response.new(@request, nil, Headers.new, e.getMessage))
|
42
|
-
end
|
43
|
-
|
44
|
-
def onResponse(call, response)
|
45
|
-
@callback.call(parse_response(response))
|
46
|
-
end
|
47
|
-
|
48
|
-
def parse_response(response)
|
49
|
-
headers = Headers.new
|
50
|
-
i = 0
|
51
|
-
while i < response.headers.size
|
52
|
-
key = response.headers.name(i)
|
53
|
-
value = response.headers.value(i)
|
54
|
-
headers.add(key, value)
|
55
|
-
i += 1
|
56
|
-
end
|
57
|
-
Response.new(@request, response.code, headers, response.body.string)
|
58
|
-
end
|
9
|
+
def self.queue
|
10
|
+
@queue ||= Com::Android::Volley::Toolbox::Volley.newRequestQueue(Motion::HTTP.application_context)
|
59
11
|
end
|
60
12
|
end
|
61
13
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Copied from https://github.com/HipByte/Flow/blob/44283b31a63bc826d2c068557b6357dc1195680b/flow/base64/android/base64.rb
|
2
|
+
class Base64
|
3
|
+
def self.encode(string)
|
4
|
+
bytes = Java::Lang::String.new(string).getBytes("UTF-8")
|
5
|
+
Android::Util::Base64.encodeToString(bytes, Android::Util::Base64::NO_WRAP)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.decode(string)
|
9
|
+
java_string = Java::Lang::String.new(string)
|
10
|
+
bytes = Android::Util::Base64.decode(java_string, Android::Util::Base64::NO_WRAP)
|
11
|
+
Java::Lang::String.new(bytes, "UTF-8")
|
12
|
+
end
|
13
|
+
end
|
data/lib/android/json.rb
CHANGED
@@ -0,0 +1,52 @@
|
|
1
|
+
class VolleyRequest < Com::Android::Volley::Request
|
2
|
+
attr_accessor :original_request, :callback
|
3
|
+
|
4
|
+
METHOD_CODES = {
|
5
|
+
get: 0,
|
6
|
+
post: 1,
|
7
|
+
put: 2,
|
8
|
+
delete: 3,
|
9
|
+
head: 4,
|
10
|
+
options: 5,
|
11
|
+
trace: 6,
|
12
|
+
patch: 7,
|
13
|
+
}
|
14
|
+
|
15
|
+
def self.create(request, callback)
|
16
|
+
volley_request = new(METHOD_CODES[request.http_method], request.url, nil)
|
17
|
+
volley_request.original_request = request
|
18
|
+
volley_request.headers = request.headers.to_hash
|
19
|
+
volley_request.body = request.body
|
20
|
+
volley_request.callback = callback
|
21
|
+
volley_request
|
22
|
+
end
|
23
|
+
|
24
|
+
def parseNetworkResponse(networkResponse)
|
25
|
+
response = build_response(networkResponse)
|
26
|
+
Com::Android::Volley::Response.success(response, Com::Android::Volley::Toolbox::HttpHeaderParser.parseCacheHeaders(networkResponse))
|
27
|
+
end
|
28
|
+
|
29
|
+
def deliverResponse(response)
|
30
|
+
Motion::HTTP.logger.log_response(response)
|
31
|
+
callback.call(response) if callback
|
32
|
+
end
|
33
|
+
|
34
|
+
def deliverError(error)
|
35
|
+
if error.networkResponse
|
36
|
+
response = build_response(error.networkResponse)
|
37
|
+
deliverResponse(response)
|
38
|
+
else
|
39
|
+
Motion::HTTP.logger.error("Error while requesting #{original_request.url}: #{error.getMessage}")
|
40
|
+
error.getStackTrace.each do |line|
|
41
|
+
puts line.toString
|
42
|
+
end
|
43
|
+
response = Motion::HTTP::Response.new(original_request, nil, nil, error.getMessage)
|
44
|
+
callback.call(response) if callback
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def build_response(networkResponse)
|
49
|
+
body = parse_body_from_response(networkResponse)
|
50
|
+
Motion::HTTP::Response.new(original_request, networkResponse.statusCode, Motion::HTTP::Headers.new(networkResponse.headers), body)
|
51
|
+
end
|
52
|
+
end
|
data/lib/cocoa/adapter.rb
CHANGED
@@ -16,15 +16,14 @@ class Motion
|
|
16
16
|
ns_url_request = build_ns_url_request
|
17
17
|
task = @session.dataTaskWithRequest(ns_url_request, completionHandler: -> (data, response, error) {
|
18
18
|
if error
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
response = Response.new(@request, response.statusCode, Headers.new(response.allHeaderFields), error_message)
|
19
|
+
error_message = "#{error.localizedDescription} #{error.userInfo['NSLocalizedDescriptionKey']}"
|
20
|
+
Motion::HTTP.logger.error("Error while requesting #{@request.url}: #{error_message}")
|
21
|
+
response = Response.new(@request, response&.statusCode, Headers.new(response&.allHeaderFields), error_message)
|
23
22
|
else
|
24
23
|
response = Response.new(@request, response.statusCode, Headers.new(response.allHeaderFields), data.to_s)
|
24
|
+
Motion::HTTP.logger.log_response(response)
|
25
25
|
end
|
26
|
-
|
27
|
-
callback.call(response)
|
26
|
+
callback.call(response) if callback
|
28
27
|
})
|
29
28
|
task.resume
|
30
29
|
end
|
@@ -32,11 +31,18 @@ class Motion
|
|
32
31
|
def build_ns_url_request
|
33
32
|
ns_url_request = NSMutableURLRequest.alloc.initWithURL(NSURL.URLWithString(@request.url))
|
34
33
|
ns_url_request.HTTPMethod = @request.http_method.to_s.upcase
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
@request.headers.each do |key, value|
|
35
|
+
if value.is_a? Array
|
36
|
+
value.each {|v2| ns_url_request.addValue(v2, forHTTPHeaderField: key) }
|
37
|
+
else
|
38
|
+
ns_url_request.setValue(value, forHTTPHeaderField: key)
|
39
|
+
end
|
39
40
|
end
|
41
|
+
|
42
|
+
if @request.body
|
43
|
+
ns_url_request.HTTPBody = NSString.alloc.initWithString(@request.body).dataUsingEncoding(NSUTF8StringEncoding)
|
44
|
+
end
|
45
|
+
|
40
46
|
# TODO: add other headers
|
41
47
|
ns_url_request
|
42
48
|
end
|
data/lib/cocoa/base64.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Copied from https://github.com/HipByte/Flow/blob/44283b31a63bc826d2c068557b6357dc1195680b/flow/base64/cocoa/base64.rb
|
2
|
+
class Base64
|
3
|
+
def self.encode(string)
|
4
|
+
data = string.dataUsingEncoding(NSUTF8StringEncoding)
|
5
|
+
data.base64EncodedStringWithOptions(0)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.decode(string)
|
9
|
+
data = NSData.alloc.initWithBase64EncodedString(string, options: 0)
|
10
|
+
NSString.alloc.initWithData(data, encoding: NSUTF8StringEncoding)
|
11
|
+
end
|
12
|
+
end
|
data/lib/common/http.rb
CHANGED
@@ -1,39 +1,54 @@
|
|
1
1
|
class Motion
|
2
2
|
class HTTP
|
3
3
|
class << self
|
4
|
+
attr_accessor :application_context # Android
|
5
|
+
|
4
6
|
def logger
|
5
7
|
@logger ||= Logger.new
|
6
8
|
end
|
7
9
|
|
8
|
-
def client
|
9
|
-
|
10
|
+
def client(*args)
|
11
|
+
Client.new(*args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def basic_auth(username, password)
|
15
|
+
client.basic_auth(username, password)
|
16
|
+
end
|
17
|
+
|
18
|
+
def auth(header_value)
|
19
|
+
client.auth(header_value)
|
10
20
|
end
|
11
21
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
22
|
+
def get(url, options = nil, &callback)
|
23
|
+
client.get(url, options, &callback)
|
24
|
+
end
|
25
|
+
|
26
|
+
def post(url, options = nil, &callback)
|
27
|
+
client.post(url, options, &callback)
|
28
|
+
end
|
29
|
+
|
30
|
+
def put(url, options = nil, &callback)
|
31
|
+
client.put(url, options, &callback)
|
32
|
+
end
|
18
33
|
|
19
|
-
def
|
20
|
-
client.
|
34
|
+
def patch(url, options = nil, &callback)
|
35
|
+
client.patch(url, options, &callback)
|
21
36
|
end
|
22
37
|
|
23
|
-
def
|
24
|
-
client.
|
38
|
+
def delete(url, options = nil, &callback)
|
39
|
+
client.delete(url, options, &callback)
|
25
40
|
end
|
26
41
|
|
27
|
-
def
|
28
|
-
client.
|
42
|
+
def head(url, options = nil, &callback)
|
43
|
+
client.head(url, options, &callback)
|
29
44
|
end
|
30
45
|
|
31
|
-
def
|
32
|
-
client.
|
46
|
+
def options(url, options = nil, &callback)
|
47
|
+
client.options(url, options, &callback)
|
33
48
|
end
|
34
49
|
|
35
|
-
def
|
36
|
-
client.
|
50
|
+
def trace(url, options = nil, &callback)
|
51
|
+
client.trace(url, options, &callback)
|
37
52
|
end
|
38
53
|
end
|
39
54
|
end
|
data/lib/common/http/client.rb
CHANGED
@@ -3,9 +3,10 @@ class Motion
|
|
3
3
|
class Client
|
4
4
|
attr_reader :base_url
|
5
5
|
|
6
|
-
def initialize(base_url = nil)
|
6
|
+
def initialize(base_url = nil, options = nil)
|
7
7
|
@base_url = base_url || ''
|
8
|
-
|
8
|
+
options ||= {}
|
9
|
+
@headers = Headers.new(options.delete(:headers))
|
9
10
|
end
|
10
11
|
|
11
12
|
def header(key, value)
|
@@ -25,31 +26,57 @@ class Motion
|
|
25
26
|
@headers
|
26
27
|
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
def basic_auth(username, password)
|
30
|
+
header_value = 'Basic ' + Base64.encode("#{username}:#{password}")
|
31
|
+
auth(header_value)
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def auth(header_value)
|
36
|
+
@headers.set 'Authorization', header_value
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def get(path, options = nil, &callback)
|
41
|
+
request(:get, path, options, &callback)
|
42
|
+
end
|
43
|
+
|
44
|
+
def post(path, options = nil, &callback)
|
45
|
+
request(:post, path, options, &callback)
|
46
|
+
end
|
47
|
+
|
48
|
+
def put(path, options = nil, &callback)
|
49
|
+
request(:put, path, options, &callback)
|
50
|
+
end
|
34
51
|
|
35
|
-
def
|
36
|
-
|
52
|
+
def patch(path, options = nil, &callback)
|
53
|
+
request(:patch, path, options, &callback)
|
37
54
|
end
|
38
55
|
|
39
|
-
def
|
40
|
-
|
56
|
+
def delete(path, options = nil, &callback)
|
57
|
+
request(:delete, path, options, &callback)
|
41
58
|
end
|
42
59
|
|
43
|
-
def
|
44
|
-
|
60
|
+
def head(path, options = nil, &callback)
|
61
|
+
request(:head, path, options, &callback)
|
45
62
|
end
|
46
63
|
|
47
|
-
def
|
48
|
-
|
64
|
+
def options(path, options = nil, &callback)
|
65
|
+
request(:options, path, options, &callback)
|
49
66
|
end
|
50
67
|
|
51
|
-
def
|
52
|
-
|
68
|
+
def trace(path, options = nil, &callback)
|
69
|
+
request(:trace, path, options, &callback)
|
70
|
+
end
|
71
|
+
|
72
|
+
def request(http_method, path, options = nil, &callback)
|
73
|
+
options ||= {}
|
74
|
+
headers_dup = headers.dup
|
75
|
+
if options[:headers]
|
76
|
+
options.delete(:headers).each {|key, value| headers_dup.set(key, value) }
|
77
|
+
end
|
78
|
+
options[:headers] = headers_dup
|
79
|
+
Request.new(http_method, base_url + path, options).perform(&callback)
|
53
80
|
end
|
54
81
|
end
|
55
82
|
end
|
data/lib/common/http/headers.rb
CHANGED
@@ -2,12 +2,27 @@ class Motion
|
|
2
2
|
class HTTP
|
3
3
|
class Headers
|
4
4
|
def initialize(headers = {})
|
5
|
-
@headers =
|
5
|
+
@headers = {}
|
6
|
+
if headers
|
7
|
+
headers.each {|key, value| set(key, value) }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(key)
|
12
|
+
@headers[key.downcase]
|
13
|
+
end
|
14
|
+
# alias :[] :get # FIXME: doesn't work in Android
|
15
|
+
def [](key)
|
16
|
+
get(key)
|
6
17
|
end
|
7
18
|
|
8
19
|
def set(key, value)
|
9
20
|
@headers[key.downcase] = value
|
10
21
|
end
|
22
|
+
# alias :[]= :set # FIXME: doesn't work in Android
|
23
|
+
def []=(key, value)
|
24
|
+
set(key, value)
|
25
|
+
end
|
11
26
|
|
12
27
|
def add(key, value)
|
13
28
|
key = key.downcase
|
@@ -17,13 +32,22 @@ class Motion
|
|
17
32
|
end
|
18
33
|
@headers[key] << value
|
19
34
|
end
|
35
|
+
# alias :<< :add # FIXME: doesn't work in Android
|
36
|
+
def <<(key, value)
|
37
|
+
add(key, value)
|
38
|
+
end
|
20
39
|
|
21
40
|
def each(&block)
|
22
41
|
@headers.each(&block)
|
23
42
|
end
|
24
43
|
|
25
|
-
def
|
26
|
-
@headers
|
44
|
+
def to_hash
|
45
|
+
@headers # TODO: flatten array values
|
46
|
+
end
|
47
|
+
|
48
|
+
# FIXME: Android doesn't support dup (Java exception raised: java.lang.CloneNotSupportedException: Class com.yourcompany.motion_http.Headers doesn't implement Cloneable)
|
49
|
+
def dup
|
50
|
+
Headers.new(@headers)
|
27
51
|
end
|
28
52
|
end
|
29
53
|
end
|
data/lib/common/http/logger.rb
CHANGED
@@ -1,36 +1,76 @@
|
|
1
1
|
class Motion
|
2
2
|
class HTTP
|
3
3
|
class Logger
|
4
|
-
|
5
|
-
|
4
|
+
attr_reader :enabled
|
5
|
+
|
6
|
+
def initialize(enabled = true)
|
7
|
+
@enabled = enabled
|
8
|
+
end
|
9
|
+
|
10
|
+
def enable!
|
11
|
+
@enabled = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def disable!
|
15
|
+
@enabled = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def log(message, color = :white)
|
19
|
+
puts colorize(color, message) if enabled
|
20
|
+
end
|
21
|
+
|
22
|
+
def error(message)
|
23
|
+
puts colorize(:red, message)
|
6
24
|
end
|
7
25
|
|
8
26
|
def log_request(request)
|
9
|
-
log "
|
27
|
+
log "\nRequest:\n#{request.http_method.to_s.upcase} #{request.url}", :dark_gray
|
10
28
|
|
11
29
|
if request.headers
|
12
30
|
request.headers.each do |k,v|
|
13
|
-
log "#{k}: #{v
|
31
|
+
log "#{k}: #{v}", :dark_gray
|
14
32
|
end
|
15
33
|
end
|
16
34
|
|
17
|
-
if request.
|
18
|
-
# log serialized_params
|
19
|
-
request.params.each do |k,v|
|
20
|
-
log "\t#{k}=#{v.inspect}"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
log "\n"
|
35
|
+
log(request.body, :dark_gray) if request.body
|
24
36
|
end
|
25
37
|
|
26
38
|
def log_response(response)
|
27
|
-
log "
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
39
|
+
log "\nResponse:", :dark_gray
|
40
|
+
if response.original_request
|
41
|
+
log "URL: #{response.original_request.url}", :dark_gray
|
42
|
+
end
|
43
|
+
log "Status: #{response.status_code}", :dark_gray
|
44
|
+
response.headers.each do |k,v|
|
45
|
+
log "#{k}: #{v}", :dark_gray
|
46
|
+
end
|
47
|
+
log "\n#{response.body}", :dark_gray
|
48
|
+
end
|
49
|
+
|
50
|
+
def colorize(color, string)
|
51
|
+
return string unless simulator? # Only colorize in the simulator
|
52
|
+
|
53
|
+
code = {
|
54
|
+
red: 31,
|
55
|
+
dark_gray: 90,
|
56
|
+
}[color]
|
57
|
+
|
58
|
+
if code
|
59
|
+
"\e[#{code}m#{string}\e[0m"
|
60
|
+
else
|
61
|
+
string
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Copied from https://github.com/rubymotion/BubbleWrap/blob/8eaf99a0966f2b375e774f5940279a704c10ad29/motion/core/ios/device.rb#L46
|
66
|
+
def simulator?
|
67
|
+
@simulator_state ||= begin
|
68
|
+
if defined?(NSObject) # iOS
|
69
|
+
!NSBundle.mainBundle.bundlePath.start_with?('/var/')
|
70
|
+
else # android
|
71
|
+
false
|
72
|
+
end
|
32
73
|
end
|
33
|
-
log "\n#{response.body}"
|
34
74
|
end
|
35
75
|
end
|
36
76
|
end
|
data/lib/common/http/request.rb
CHANGED
@@ -1,14 +1,55 @@
|
|
1
1
|
class Motion
|
2
2
|
class HTTP
|
3
3
|
class Request
|
4
|
-
attr_reader :http_method, :url, :headers, :
|
4
|
+
attr_reader :http_method, :url, :headers, :body, :options
|
5
5
|
|
6
|
-
def initialize(http_method, url,
|
6
|
+
def initialize(http_method, url, options = nil)
|
7
7
|
@http_method = http_method
|
8
8
|
@url = url
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@
|
9
|
+
@options = options ||= {}
|
10
|
+
@headers = @options.delete(:headers) || Headers.new
|
11
|
+
@body = @options.delete(:body)
|
12
|
+
|
13
|
+
if @options[:params]
|
14
|
+
@params = @options.delete(:params)
|
15
|
+
flatten_params!
|
16
|
+
encode_params!
|
17
|
+
@url = "#{url}?#{@params.map{|k,v|"#{k}=#{v}"}.join('&')}"
|
18
|
+
|
19
|
+
elsif @options[:form]
|
20
|
+
@headers['Content-Type'] ||= 'application/x-www-form-urlencoded'
|
21
|
+
@params = @options.delete(:form)
|
22
|
+
flatten_params!
|
23
|
+
encode_params!
|
24
|
+
@body = @params.map{|k,v|"#{k}=#{v}"}.join('&')
|
25
|
+
|
26
|
+
elsif @options[:json]
|
27
|
+
@headers['Content-Type'] ||= 'application/json; charset=utf-8'
|
28
|
+
@body = @options.delete(:json).to_json
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def flatten_params!
|
33
|
+
new_params = {}
|
34
|
+
@params.each do |k,v|
|
35
|
+
if v.is_a? Hash
|
36
|
+
v.each do |nested_k, nested_v|
|
37
|
+
new_params["#{k}[#{nested_k}]"] = nested_v
|
38
|
+
end
|
39
|
+
else
|
40
|
+
new_params[k] = v
|
41
|
+
end
|
42
|
+
end
|
43
|
+
@params = new_params
|
44
|
+
flatten_params! if @params.any? {|k,v| v.is_a? Hash }
|
45
|
+
end
|
46
|
+
|
47
|
+
def encode_params!
|
48
|
+
new_params = {}
|
49
|
+
@params.each do |k,v|
|
50
|
+
new_params[ParamsEncoder.encode(k)] = ParamsEncoder.encode(v)
|
51
|
+
end
|
52
|
+
@params = new_params
|
12
53
|
end
|
13
54
|
|
14
55
|
def perform(&callback)
|
data/lib/common/http/response.rb
CHANGED
@@ -11,8 +11,19 @@ class Motion
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def success?
|
14
|
-
|
15
|
-
|
14
|
+
status_code && (200..299) === status_code
|
15
|
+
end
|
16
|
+
|
17
|
+
def redirect?
|
18
|
+
status_code && (300..399) === status_code
|
19
|
+
end
|
20
|
+
|
21
|
+
def client_error?
|
22
|
+
status_code && (400..499) === status_code
|
23
|
+
end
|
24
|
+
|
25
|
+
def server_error?
|
26
|
+
status_code && (500..599) === status_code
|
16
27
|
end
|
17
28
|
|
18
29
|
def object
|
data/lib/motion-http.rb
CHANGED
@@ -12,9 +12,8 @@ Motion::Project::App.setup do |app|
|
|
12
12
|
when :android
|
13
13
|
require "motion-gradle"
|
14
14
|
app.files.unshift(*Dir.glob(File.join(lib_dir_path, "android/**/*.rb")))
|
15
|
-
app.
|
16
|
-
|
17
|
-
end
|
15
|
+
app.permissions << :internet
|
16
|
+
app.gradle { dependency 'com.android.volley:volley:1.1.1' }
|
18
17
|
when :ios, :tvos, :osx, :watchos, :'ios-extension'
|
19
18
|
app.files.unshift(*Dir.glob(File.join(lib_dir_path, "cocoa/**/*.rb")))
|
20
19
|
else
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motion-http
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Havens
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A cross-platform HTTP client for RubyMotion that's quick and easy to
|
14
14
|
use.
|
@@ -20,10 +20,14 @@ extra_rdoc_files: []
|
|
20
20
|
files:
|
21
21
|
- README.md
|
22
22
|
- lib/android/adapter.rb
|
23
|
+
- lib/android/base64.rb
|
23
24
|
- lib/android/json.rb
|
25
|
+
- lib/android/params_encoder.rb
|
26
|
+
- lib/android/volley_request.rb
|
24
27
|
- lib/cocoa/adapter.rb
|
25
|
-
- lib/cocoa/
|
28
|
+
- lib/cocoa/base64.rb
|
26
29
|
- lib/cocoa/json.rb
|
30
|
+
- lib/cocoa/params_encoder.rb
|
27
31
|
- lib/common/http.rb
|
28
32
|
- lib/common/http/client.rb
|
29
33
|
- lib/common/http/headers.rb
|
@@ -1,29 +0,0 @@
|
|
1
|
-
class FormDataSerializer
|
2
|
-
def self.serialize(params)
|
3
|
-
flattened_params = {}
|
4
|
-
params.each do |k, v|
|
5
|
-
add_param(flattened_params, k, v)
|
6
|
-
end
|
7
|
-
serialized_params = []
|
8
|
-
flattened_params.each do |k, v|
|
9
|
-
serialized_params << "#{k}=#{serialize_value(v)}"
|
10
|
-
end
|
11
|
-
serialized_params.join('&')
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.add_param(hash, k, v)
|
15
|
-
if v.is_a? Hash
|
16
|
-
v.each do |sub_k, sub_v|
|
17
|
-
add_param(hash, "#{k}[#{sub_k}]", sub_v)
|
18
|
-
end
|
19
|
-
else
|
20
|
-
hash[k] = v
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.serialize_value(v)
|
25
|
-
allowed_characters = NSCharacterSet.URLQueryAllowedCharacterSet.mutableCopy
|
26
|
-
allowed_characters.removeCharactersInString(":#[]@!$&'()*+,;=")
|
27
|
-
NSString.alloc.initWithString(v.to_s).stringByAddingPercentEncodingWithAllowedCharacters(allowed_characters)
|
28
|
-
end
|
29
|
-
end
|