rest_api 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +13 -0
- data/LICENSE +22 -0
- data/README.md +367 -0
- data/Rakefile +2 -0
- data/lib/rest_api/api/request_parser.rb +96 -0
- data/lib/rest_api/api.rb +19 -0
- data/lib/rest_api/custom_api_method_call.rb +77 -0
- data/lib/rest_api/exceptions.rb +17 -0
- data/lib/rest_api/request_handler/client.rb +37 -0
- data/lib/rest_api/request_handler.rb +77 -0
- data/lib/rest_api/version.rb +3 -0
- data/lib/rest_api.rb +46 -0
- data/rest_api.gemspec +23 -0
- data/spec/rest_api/api/request_parser_spec.rb +199 -0
- data/spec/rest_api/api_spec.rb +13 -0
- data/spec/rest_api/custom_api_method_spec.rb +90 -0
- data/spec/rest_api/request_handler/client_spec.rb +93 -0
- data/spec/rest_api/request_handler_spec.rb +204 -0
- data/spec/rest_api/rest_api_spec.rb +32 -0
- data/spec/spec_helper.rb +7 -0
- metadata +123 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.3@gems-restapi --create
|
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in RestApi.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :test,:development do
|
7
|
+
gem 'rspec-rails', '2.11.0'
|
8
|
+
gem 'fakeweb', '1.3.0'
|
9
|
+
end
|
10
|
+
|
11
|
+
gem 'activesupport', "3.2.8"
|
12
|
+
gem "rest-client", "1.6.7"
|
13
|
+
gem 'json', "1.7.5"
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 TODO: Write your name
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,367 @@
|
|
1
|
+
# RestApi - RESTful client for everything
|
2
|
+
|
3
|
+
**RestApi** aims to be a generic RESTful client for ruby. The goal is to build a friendly API client that you can use right away with minimum configuration. But, if you want, you can bend the client as you wish
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'rest_api'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle install
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install rest_api
|
18
|
+
|
19
|
+
## Configuration
|
20
|
+
|
21
|
+
* **In ruby**
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
RestApi.setup do |config|
|
25
|
+
config.api_url = "http://www.myapiurl.com/"
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
or
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
RestApi.api_url = "http://www.myapiurl.com/"
|
33
|
+
```
|
34
|
+
|
35
|
+
* **In Rails**
|
36
|
+
|
37
|
+
Just put the above code in /config/initializers/rest_api.rb
|
38
|
+
|
39
|
+
## Usage
|
40
|
+
|
41
|
+
Every request must be made through the RestApi.request object.
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
RestApi.request.my_request_method(arguments)
|
45
|
+
```
|
46
|
+
|
47
|
+
### my_request_method
|
48
|
+
|
49
|
+
This is the name of your RESTful url. It defines the **request type** and the **resources names** that will be used in the request URL.
|
50
|
+
|
51
|
+
The method name must start with *get_*, *post_*, *put_* or *delete_* and the resources must be separated by *underscores*.
|
52
|
+
|
53
|
+
These are reserved words that the resources cannot use as name:
|
54
|
+
|
55
|
+
- in
|
56
|
+
- from
|
57
|
+
- of
|
58
|
+
|
59
|
+
**EXAMPLES**
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
RestApi.request.get_users
|
63
|
+
# GET to "http://www.myapiurl.com/users"
|
64
|
+
|
65
|
+
RestApi.request.get_cars_from_users
|
66
|
+
# GET to "http://www.myapiurl.com/users/cars"
|
67
|
+
|
68
|
+
RestApi.request.get_cars_users
|
69
|
+
# GET to "http://www.myapiurl.com/users/cars"
|
70
|
+
|
71
|
+
RestApi.request.post_cars
|
72
|
+
# POST to "http://www.myapiurl.com/cars"
|
73
|
+
|
74
|
+
RestApi.request.put_cars_users
|
75
|
+
# PUT to "http://www.myapiurl.com/users/cars"
|
76
|
+
|
77
|
+
RestApi.request.delete_users
|
78
|
+
# DELETE to "http://www.myapiurl.com/users"
|
79
|
+
|
80
|
+
RestApi.request.delete_houses_from_users
|
81
|
+
# DELETE to "http://www.myapiurl.com/users/houses"
|
82
|
+
```
|
83
|
+
|
84
|
+
### arguments
|
85
|
+
|
86
|
+
|
87
|
+
The arguments of the api resource method call has two goals:
|
88
|
+
|
89
|
+
1. Modify the url adding some value after a resource using the **:resource_params** value.
|
90
|
+
2. Modify the request HEADER (*post*, *put*) or QUERYSTRING (*get*, *delete*) using the **:request_params** value .
|
91
|
+
|
92
|
+
**:resource_params**
|
93
|
+
****
|
94
|
+
|
95
|
+
You can pass a hash in the :resource_params where, for each pair, the key is the resource name in the method called and the value is the what will be put after the resource in the URL call to the API.
|
96
|
+
|
97
|
+
**EXAMPLES**
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
RestApi.request.delete_users(:resources_params => { :users => 7})
|
101
|
+
# DELETE to "http://www.myapiurl.com/users/7"
|
102
|
+
|
103
|
+
RestApi.request.put_cars_in_users(:resources_params => { :users => 7})
|
104
|
+
# PUT to "http://www.myapiurl.com/users/7/cars/"
|
105
|
+
|
106
|
+
RestApi.request.put_cars_in_users(:resources_params => { :cars => 18})
|
107
|
+
# PUT to "http://www.myapiurl.com/users/cars/18"
|
108
|
+
|
109
|
+
RestApi.request.get_users(:resources_params => { :users => 18})
|
110
|
+
# GET to "http://www.myapiurl.com/users/18"
|
111
|
+
```
|
112
|
+
|
113
|
+
**:request_params**
|
114
|
+
****
|
115
|
+
|
116
|
+
You can pass a hash in the :request_params where, for each pair, the key is the param name and the value is the param value. Then the hash will be sent in the header if the request type is a *POST* or *PUT*. If the request type is *GET* or *DELETE* it will be parsed to a querystring and will be append to the end of the url.
|
117
|
+
|
118
|
+
**EXAMPLE**
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
RestApi.request.post_users(:request_params => { :user => {:name => "myname"})
|
122
|
+
# POST to "http://www.myapiurl.com/users/" with "{ :user => {:name => "name"}" in the header
|
123
|
+
|
124
|
+
RestApi.request.get_users(:request_params => { :user => {:name => "name"})
|
125
|
+
# GET to "http://www.myapiurl.com/users?name=myname"
|
126
|
+
```
|
127
|
+
|
128
|
+
**Using both :resource_params and :request_params**
|
129
|
+
****
|
130
|
+
|
131
|
+
Obviously you can use them together.
|
132
|
+
|
133
|
+
**EXAMPLES**
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
RestApi.request.get_cars_from_users(:request_params => { :page => 5, :resource_params => { :users => 8})
|
137
|
+
# GET to "http://www.myapiurl.com/users/8/cars?page=5"
|
138
|
+
|
139
|
+
RestApi.request.post_cars_in_users(:request_params => { :car => {:model => "ferrari"}, :resource_params => { :users => 8})
|
140
|
+
# POST to "http://www.myapiurl.com/users/8/cars" with "{ :car => {:model => "ferrari"}" in the header
|
141
|
+
```
|
142
|
+
|
143
|
+
**Short Syntax**
|
144
|
+
****
|
145
|
+
You can pass as many arguments you want. They will be considered as a **:resource_param** following the order in the URL.
|
146
|
+
|
147
|
+
**EXAMPLES**
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
RestApi.request.get_users(18)
|
151
|
+
# GET to "http://www.myapiurl.com/users/18"
|
152
|
+
|
153
|
+
RestApi.request.get_cars_from_users(18)
|
154
|
+
# GET to "http://www.myapiurl.com/users/18/cars"
|
155
|
+
|
156
|
+
RestApi.request.get_cars_from_users(18, 6)
|
157
|
+
# GET to "http://www.myapiurl.com/users/18/cars/6"
|
158
|
+
|
159
|
+
RestApi.request.put_cars_in_users(18, 6)
|
160
|
+
# PUT to "http://www.myapiurl.com/users/18/cars/6"
|
161
|
+
```
|
162
|
+
|
163
|
+
**IF** the last argument is a hash then it will be considered the **:request_params**.
|
164
|
+
|
165
|
+
**EXAMPLES**
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
RestApi.request.get_cars_from_users(8, :page => 5)
|
169
|
+
# GET to "http://www.myapiurl.com/users/8/cars?page=5"
|
170
|
+
|
171
|
+
RestApi.request.put_users(18, :user => {:name => "myname"})
|
172
|
+
# PUT to "http://www.myapiurl.com/users/18/" with { :user => {:name => "myname"} } in the header
|
173
|
+
|
174
|
+
RestApi.request.put_cars_in_users(18, 6, :car => {:model => "mercedes"})
|
175
|
+
# PUT to "http://www.myapiurl.com/users/18/cars/6" with { :car => {:model => "mercedes"} } in the header
|
176
|
+
```
|
177
|
+
|
178
|
+
If there is only one argument and it is a hash then it will be considered as the **:request_params**.
|
179
|
+
|
180
|
+
**EXAMPLES**
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
RestApi.request.get_users(:page => 5)
|
184
|
+
# GET to "http://www.myapiurl.com/users/?page=8
|
185
|
+
|
186
|
+
RestApi.request.post_cars_in_users(:car => {:model => "mercedes"})
|
187
|
+
# POST to "http://www.myapiurl.com/users/18/cars/6" with {:car => {:model => "mercedes"} in the header
|
188
|
+
```
|
189
|
+
|
190
|
+
## Advanced Configuration
|
191
|
+
|
192
|
+
There are some considerations in the current implementation that you must be aware of to make RestApi to work properly. Here we will show them to you and the configuration for every case.
|
193
|
+
|
194
|
+
#### RestApi.request_parser#ensure_resource_name
|
195
|
+
****
|
196
|
+
|
197
|
+
Every word separated by a "_" will be considered as resource.
|
198
|
+
|
199
|
+
So if you have a resource called *public_users* and make a request like this:
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
RestApi.request.get_public_users
|
203
|
+
```
|
204
|
+
|
205
|
+
It will make a GET to:
|
206
|
+
|
207
|
+
*http://www.myapiurl.com/users/public_users*
|
208
|
+
|
209
|
+
To make things work properly you must ensure the name of the resource like this:
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
RestApi.request_parser.ensure_resource_name :public_users
|
213
|
+
```
|
214
|
+
|
215
|
+
Then when you call:
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
RestApi.request.get_public_users
|
219
|
+
```
|
220
|
+
|
221
|
+
It will make a GET to:
|
222
|
+
|
223
|
+
*http://www.myapiurl.com/public_users*
|
224
|
+
|
225
|
+
And you can do something like this:
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
RestApi.request.get_public_users :resources_params => {:public_users => 2}
|
229
|
+
```
|
230
|
+
|
231
|
+
It will make a GET to:
|
232
|
+
|
233
|
+
*http://www.myapiurl.com/public_users/2
|
234
|
+
|
235
|
+
You can ensure more than one resource name at once:
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
RestApi.request_parser.ensure_resource_name :my_resource1, :my_resource2, :my_resource3 ....
|
239
|
+
```
|
240
|
+
|
241
|
+
#### RestApi.request_parser#reset_ensure_resource_name
|
242
|
+
****
|
243
|
+
|
244
|
+
If you need you can reset the ensured resources names with:
|
245
|
+
|
246
|
+
```ruby
|
247
|
+
RestApi.request_parser.reset_ensure_resource_name
|
248
|
+
```
|
249
|
+
|
250
|
+
#### RestApi#map_custom_api_method#
|
251
|
+
****
|
252
|
+
Every resource name in the method call must be *unique*.
|
253
|
+
|
254
|
+
For instance, let's say you have a resource called categories. And each category:
|
255
|
+
|
256
|
+
* *belongs_to* a category
|
257
|
+
* *has_many/has_one* category
|
258
|
+
|
259
|
+
And you have a RESTful url like this:
|
260
|
+
|
261
|
+
*http://www.myapiurl.com/categories/2/categories*
|
262
|
+
|
263
|
+
That allows to get/post/put/delete the child categories of category 2. But if you try to do:
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
RestApi.request.get_categories_in_categories(2)
|
267
|
+
```
|
268
|
+
|
269
|
+
or
|
270
|
+
|
271
|
+
```ruby
|
272
|
+
RestApi.request.get_categories_in_categories(:resources_params => {:categories => 2})
|
273
|
+
```
|
274
|
+
|
275
|
+
It will make a GET to:
|
276
|
+
|
277
|
+
*http://www.myapiurl.com/categories/2/categories/2*
|
278
|
+
|
279
|
+
Because the resources params arguments are linked to a resource name defined in the method call.
|
280
|
+
|
281
|
+
You can turn this aroud by defining a request method and mapping the resources like this:
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
RestApi.map_custom_api_method :get, :subcategories_in_categories do |map|
|
285
|
+
map.subcategories = "categories"
|
286
|
+
end
|
287
|
+
```
|
288
|
+
|
289
|
+
Now when you do:
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
RestApi.request.get_subcategories_in_categories :resources_params => {:subcategories => 2}
|
293
|
+
```
|
294
|
+
|
295
|
+
Will make a GET to:
|
296
|
+
|
297
|
+
*http://www.myapiurl.com/categories/2/categories/*
|
298
|
+
|
299
|
+
If you don't define a map to some resource name in the method, it will be parsed to the RESTful URL as itself.
|
300
|
+
|
301
|
+
#### RestApi#add_restful_api_methods
|
302
|
+
****
|
303
|
+
|
304
|
+
Note that *map_custom_api_method* only define one request type per time. If you try:
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
RestApi.map_custom_api_method :get, :subcategories_in_categories do |map|
|
308
|
+
map.subcategories = "categories"
|
309
|
+
end
|
310
|
+
|
311
|
+
RestApi.request.put_subcategories_in_categories :resources_params => {:categories => 2}
|
312
|
+
```
|
313
|
+
|
314
|
+
The custom *GET* mapping will be ignored and will be made a *PUT* request with the default map to:
|
315
|
+
|
316
|
+
*http://www.myapiurl.com/categories/2/subcategories/*
|
317
|
+
|
318
|
+
You can define a custom method for every HTTP verb **or** you can use the *add_restful_api_methods* method. This method automatically create the *GET*, *POST*, *PUT* and *DELETE*.
|
319
|
+
|
320
|
+
**EXAMPLE**
|
321
|
+
|
322
|
+
```ruby
|
323
|
+
RestApi.add_restful_api_methods :subcategories_in_categories do |map|
|
324
|
+
map.subcategories = "categories"
|
325
|
+
end
|
326
|
+
```
|
327
|
+
|
328
|
+
Now you can do:
|
329
|
+
|
330
|
+
```ruby
|
331
|
+
RestApi.request.put_subcategories_in_categories :resources_params => {:subcategories => 5, :categories => 2}
|
332
|
+
```
|
333
|
+
That a PUT will be made to:
|
334
|
+
|
335
|
+
*http://www.myapiurl.com/categories/2/categories/5*
|
336
|
+
|
337
|
+
#### RestApi#unmap_resources
|
338
|
+
****
|
339
|
+
|
340
|
+
If yout need to reset the mapped resources you can call:
|
341
|
+
|
342
|
+
```ruby
|
343
|
+
RestApi.unmap_resources
|
344
|
+
```
|
345
|
+
|
346
|
+
## TODO
|
347
|
+
|
348
|
+
Here we will put the features that will be implemented in the future.
|
349
|
+
|
350
|
+
* API autentication
|
351
|
+
* Extend the client to accept others formats than JSON
|
352
|
+
* Create the RestApi::Model to work like an ActiveRecord model (ok. we have a long way to go)
|
353
|
+
* ...
|
354
|
+
*
|
355
|
+
## Contact
|
356
|
+
|
357
|
+
If you have any suggestions or a bug fix feel free to create a new issue.
|
358
|
+
|
359
|
+
You can visit our page on <http://www.codus.com.br>
|
360
|
+
|
361
|
+
## Contributing
|
362
|
+
|
363
|
+
1. Fork it
|
364
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
365
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
366
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
367
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module RestApi
|
4
|
+
module API
|
5
|
+
module RequestParser
|
6
|
+
class << self
|
7
|
+
def get_url_from_method method_id, resources_params = nil
|
8
|
+
tokens_array = get_url_tokens_from_method method_id
|
9
|
+
tokens_array = insert_resources_params_in_tokens_array tokens_array, resources_params unless (resources_params.nil? || resources_params.empty?)
|
10
|
+
API.url + tokens_array.join("/")
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_url_from_map array_resources_names, resources_map, resources_params = nil
|
14
|
+
# insert the params before translate the url with a map so the rousources_params
|
15
|
+
# must be relative the array resrouces_names
|
16
|
+
# tokens_array = array_resources_names
|
17
|
+
|
18
|
+
tokens_array = insert_resources_params_in_tokens_array array_resources_names, resources_params unless (resources_params.nil? || resources_params.empty?)
|
19
|
+
tokens_array ||= array_resources_names
|
20
|
+
tokens_array_mapped = tokens_array.map do |resource_name|
|
21
|
+
if resources_map.respond_to?(resource_name.to_sym)
|
22
|
+
resources_map.send(resource_name.to_sym)
|
23
|
+
else
|
24
|
+
resource_name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
API.url + tokens_array_mapped.join("/")
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_request_type_from_method method_id
|
32
|
+
request_type = method_id.to_s.match(/^get|put|delete|post/i)
|
33
|
+
request_type.nil? ? request_type : request_type[0].to_sym
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_url_tokens_from_method method_id
|
37
|
+
method_name_without_pronouns = method_id.to_s.gsub(/_(in|of|from)_/,"_").gsub(/(put|get|post|delete)_/, "")
|
38
|
+
method_name_with_reserved_mask = method_name_without_pronouns
|
39
|
+
ensured_resources_names.each { |ensured_resource_name|
|
40
|
+
method_name_with_reserved_mask.gsub!(ensured_resource_name, ensured_resource_name.split("_").join("+"))
|
41
|
+
}
|
42
|
+
url_tokens = method_name_with_reserved_mask.split("_").map { |resource_name_masked| resource_name_masked.gsub("+", "_")}
|
43
|
+
url_tokens.reverse
|
44
|
+
end
|
45
|
+
|
46
|
+
def insert_resources_params_in_tokens_array tokens_array, resources_params
|
47
|
+
resources_params_hash = {}
|
48
|
+
tokens_array_copy = Array.new tokens_array #copy so the original remains intact
|
49
|
+
if resources_params.is_a? Hash
|
50
|
+
resources_params_hash = resources_params
|
51
|
+
else
|
52
|
+
tokens_array_copy.each_with_index { |token, index|
|
53
|
+
resources_params_hash[token.to_sym] = resources_params[index] unless resources_params[index].nil?
|
54
|
+
}
|
55
|
+
end
|
56
|
+
params_insert_positions = Array.new
|
57
|
+
tokens_array_copy.each_with_index do |token, index|
|
58
|
+
params_insert_positions << { :param => resources_params_hash[token.to_sym], :position => (index + 1)} if resources_params_hash.has_key? token.to_sym
|
59
|
+
end
|
60
|
+
|
61
|
+
params_insert_positions.inject(0) do |insert_offset,insert_position_hash|
|
62
|
+
insert_position = insert_position_hash[:position] + insert_offset
|
63
|
+
tokens_array_copy.insert(insert_position, insert_position_hash[:param].to_s)
|
64
|
+
insert_offset = insert_offset + 1
|
65
|
+
end
|
66
|
+
|
67
|
+
tokens_array_copy
|
68
|
+
end
|
69
|
+
|
70
|
+
def pluralize_resource resource_name
|
71
|
+
(resource_name.match(/(\d+|s)$/).nil?) ? (resource_name + "s") : (resource_name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def ensure_resource_name *resources_names
|
75
|
+
if resources_names.length == 0
|
76
|
+
raise ArgumentError.new("wrong number of arguments (0 for N)")
|
77
|
+
else
|
78
|
+
resources_names.each do |resource_name|
|
79
|
+
ensured_resources_names << resource_name.to_s
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def reset_ensure_resource_name
|
85
|
+
@ensured_resources_names = Set.new
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def ensured_resources_names
|
90
|
+
@ensured_resources_names ||= Set.new
|
91
|
+
@ensured_resources_names
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/rest_api/api.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "rest_api/api/request_parser"
|
2
|
+
|
3
|
+
module RestApi
|
4
|
+
module API
|
5
|
+
@@custom_calls = []
|
6
|
+
|
7
|
+
def self.url
|
8
|
+
@@url
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.url=(value)
|
12
|
+
if value.match(/.+\/$/)
|
13
|
+
@@url = value
|
14
|
+
else
|
15
|
+
@@url = value + "/"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module RestApi
|
5
|
+
@@mapped_methods = Set.new
|
6
|
+
# custom map to api call
|
7
|
+
# Example:
|
8
|
+
# RestApi.map_custom_api_method :put, :casas_in_usuarios do |map|
|
9
|
+
# map.casas = "casinha"
|
10
|
+
# map.usuarios = "usuarionsinhos"
|
11
|
+
# end
|
12
|
+
# RestApi.request.put_casas_in_usuarios -> PUT "<APIURL>/usuarionsinhos/casinha"
|
13
|
+
# RestApi.request.put_casas_in_usuarios(5) -> PUT "<APIURL>/usuarionsinhos/5/casinha"
|
14
|
+
# RestApi.map_custom_api_method :get, :boss_from_offices
|
15
|
+
# RestApi.request.get_boss_from_offices -> GET "<APIURL>/offices/boss"
|
16
|
+
# RestApi.request.get_boss_from_offices(:resources_params => { :offices => 5} ) -> GET "<APIURL>/offices/5/boss"
|
17
|
+
|
18
|
+
def self.map_custom_api_method request_type, request_resources
|
19
|
+
array_resources_names = API::RequestParser.get_url_tokens_from_method request_resources
|
20
|
+
|
21
|
+
# initialize the resource map and pass to the yield
|
22
|
+
resources_map = OpenStruct.new
|
23
|
+
if block_given?
|
24
|
+
yield resources_map
|
25
|
+
end
|
26
|
+
|
27
|
+
# complete the resource map is necessary
|
28
|
+
array_resources_names.each do |resource_name|
|
29
|
+
resources_map.send("#{resource_name}=", resource_name) unless resources_map.respond_to?(resource_name.to_sym)
|
30
|
+
end
|
31
|
+
|
32
|
+
# create the method
|
33
|
+
method_name = "#{request_type}_#{request_resources}"
|
34
|
+
|
35
|
+
self.mapped_methods << method_name.to_sym
|
36
|
+
|
37
|
+
RequestHandler.class_eval do
|
38
|
+
eigenclass = class << self
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
eigenclass.class_eval do
|
43
|
+
# self = RestApi::RequestHandler
|
44
|
+
define_method method_name do |*arguments|
|
45
|
+
params = get_params_from_array_arguments arguments
|
46
|
+
request_url = API::RequestParser.get_url_from_map(array_resources_names, resources_map, params[:resources_params])
|
47
|
+
make_request request_type, request_url, params[:request_params]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.unmap_resources
|
54
|
+
self.mapped_methods.each do |mapped_method_id|
|
55
|
+
RequestHandler.class_eval do
|
56
|
+
eigenclass = class << self
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
eigenclass.class_eval do
|
61
|
+
undef_method mapped_method_id
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
@@mapped_methods = Set.new
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.mapped_methods
|
69
|
+
@@mapped_methods
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.add_restful_api_methods request_resources, &block
|
73
|
+
[:get, :put, :post, :delete].each do |request_type|
|
74
|
+
self.map_custom_api_method request_type, request_resources, &block
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RestApi
|
2
|
+
module Exceptions
|
3
|
+
class ParseResponseException < Exception
|
4
|
+
|
5
|
+
attr_accessor :response_body
|
6
|
+
|
7
|
+
def initialize(args = {})
|
8
|
+
@response_body = args[:response_body]
|
9
|
+
@error = args[:error]
|
10
|
+
super(@error)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
class ApiConnectionException < Exception;end;
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module RestApi
|
5
|
+
module RequestHandler
|
6
|
+
module Client
|
7
|
+
class << self
|
8
|
+
def get url, params = {}
|
9
|
+
parse_response(RestClient.get(url, :params => params, :accept => :json))
|
10
|
+
end
|
11
|
+
|
12
|
+
def post url, params = {}
|
13
|
+
parse_response(RestClient.post(url, params, :accept => :json))
|
14
|
+
end
|
15
|
+
|
16
|
+
def put url, params = {}
|
17
|
+
parse_response(RestClient.put(url, params, :accept => :json))
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete url, params = {}
|
21
|
+
parse_response(RestClient.delete(url, :params => params, :accept => :json))
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_response string_response
|
25
|
+
begin
|
26
|
+
return JSON.parse(string_response)
|
27
|
+
rescue Exception => e
|
28
|
+
raise RestApi::Exceptions::ParseResponseException.new(
|
29
|
+
:response_body => string_response,
|
30
|
+
:error => e )
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|