rest-in-peace 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.
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +3 -0
- data/Guardfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +163 -0
- data/Rakefile +9 -0
- data/VERSION +1 -0
- data/examples/pagination_with_headers.rb +26 -0
- data/images/rest_in_peace.gif +0 -0
- data/lib/rest-in-peace.rb +1 -0
- data/lib/rest_in_peace/api_call.rb +53 -0
- data/lib/rest_in_peace/definition_proxy/collection_method_definitions.rb +31 -0
- data/lib/rest_in_peace/definition_proxy/resource_method_definitions.rb +39 -0
- data/lib/rest_in_peace/definition_proxy.rb +24 -0
- data/lib/rest_in_peace/errors.rb +3 -0
- data/lib/rest_in_peace/response_converter.rb +33 -0
- data/lib/rest_in_peace/ssl_config_creator.rb +53 -0
- data/lib/rest_in_peace/template_sanitizer.rb +32 -0
- data/lib/rest_in_peace.rb +38 -0
- data/rest-in-peace.gemspec +25 -0
- data/spec/rest_in_peace/api_call_spec.rb +54 -0
- data/spec/rest_in_peace/definition_proxy/collection_method_definitions_spec.rb +80 -0
- data/spec/rest_in_peace/definition_proxy/resource_method_definitions_spec.rb +77 -0
- data/spec/rest_in_peace/definition_proxy_spec.rb +36 -0
- data/spec/rest_in_peace/response_converter_spec.rb +47 -0
- data/spec/rest_in_peace/template_sanitizer_spec.rb +64 -0
- data/spec/rest_in_peace_spec.rb +55 -0
- data/spec/spec_helper.rb +61 -0
- metadata +166 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rest-in-peace
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3-p547
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard :rspec do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
9
|
+
end
|
10
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Nine Internet Solutions AG (nine.ch)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
# REST in Peace [](https://travis-ci.org/ninech/REST-in-Peace) [](https://codeclimate.com/github/ninech/REST-in-Peace)
|
2
|
+
|
3
|
+
A ruby REST client that lets you feel like in heaven when consuming APIs.
|
4
|
+
|
5
|
+

|
6
|
+
|
7
|
+
## Getting Started
|
8
|
+
|
9
|
+
1. Add `REST-in-Peace` to your dependencies
|
10
|
+
|
11
|
+
gem 'rest-in-peace'
|
12
|
+
|
13
|
+
2. Choose which http adapter you want to use
|
14
|
+
|
15
|
+
gem 'faraday'
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
### HTTP Client Library
|
20
|
+
|
21
|
+
There is no dependency on a specific HTTP client library but the client has been tested with [Faraday](https://github.com/lostisland/faraday) only. You can use any other client library as long as it has the same API as Faraday.
|
22
|
+
|
23
|
+
### Configuration
|
24
|
+
|
25
|
+
You need to define all the API endpoints you want to consume with `RESTinPeace`. Currently the four HTTP Verbs `GET`, `POST`, `PATCH` and `DELETE` are supported.
|
26
|
+
|
27
|
+
There are two sections where you can specify endpoints: `resource` and `collection`:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
rest_in_peace do
|
31
|
+
resource do
|
32
|
+
get :reload, '/rip/:id'
|
33
|
+
end
|
34
|
+
collection do
|
35
|
+
get :find, '/rip/:id'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
#### HTTP Client Configuration
|
41
|
+
|
42
|
+
You need to specify the HTTP client library to use. You can either specify a block (for lazy loading) or a client instance directly.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
class Resource
|
46
|
+
rest_in_peace do
|
47
|
+
use_api ->() { Faraday.new(url: 'http://rip.dev') }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class ResourceTwo
|
52
|
+
rest_in_peace do
|
53
|
+
use_api Faraday.new(url: 'http://rip.dev')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
#### Resource
|
59
|
+
|
60
|
+
If you define anything inside the `resource` block, it will define a method on the instances of the class:
|
61
|
+
```ruby
|
62
|
+
class Resource
|
63
|
+
rest_in_peace do
|
64
|
+
resource do
|
65
|
+
get :reload, '/rip/:id'
|
66
|
+
post :create, '/rip'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
resource = Resource.new(id: 1)
|
72
|
+
resource.create # calls "POST /rip"
|
73
|
+
resource.reload # calls "GET /rip/1"
|
74
|
+
```
|
75
|
+
|
76
|
+
#### Collection
|
77
|
+
|
78
|
+
If you define anything inside the `collection` block, it will define a method on the class:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
class Resource
|
82
|
+
rest_in_peace do
|
83
|
+
collection do
|
84
|
+
get :find, '/rip/:id'
|
85
|
+
get :find_on_other, '/other/:other_id/rip/:id'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
resource = Resource.find(1) # calls "GET /rip/1"
|
91
|
+
resource = Resource.find_on_other(42, 1337) # calls "GET /other/42/rip/1337"
|
92
|
+
```
|
93
|
+
|
94
|
+
#### Pagination
|
95
|
+
|
96
|
+
You can define your own pagination module which will be mixed in when calling the API:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
class Resource
|
100
|
+
rest_in_peace do
|
101
|
+
collection do
|
102
|
+
get :all, '/rips', paginate_with: MyClient::Paginator
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
An example pagination mixin with HTTP headers can be found in the [examples directory](https://github.com/ninech/REST-in-Peace/blob/master/examples) of this repo.
|
109
|
+
|
110
|
+
#### Complete Configuration
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
require 'my_client/paginator'
|
114
|
+
require 'rest_in_peace'
|
115
|
+
|
116
|
+
module MyClient
|
117
|
+
class Fabric < Struct.new(:id, :name, :ip)
|
118
|
+
include RESTinPeace
|
119
|
+
|
120
|
+
rest_in_peace do
|
121
|
+
use_api ->() { MyClient.api }
|
122
|
+
|
123
|
+
resource do
|
124
|
+
patch :save, '/fabrics/:id'
|
125
|
+
post :create, '/fabrics'
|
126
|
+
delete :destroy, '/fabrics/:id'
|
127
|
+
get :reload, '/fabrics/:id'
|
128
|
+
end
|
129
|
+
|
130
|
+
collection do
|
131
|
+
get :all, '/fabrics', paginate_with: MyClient::Paginator
|
132
|
+
get :find, '/fabrics/:id'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
## Helpers
|
140
|
+
|
141
|
+
### SSL Configuration for Faraday
|
142
|
+
|
143
|
+
There is a helper class which can be used to create a Faraday compatible SSL configuration hash (with support for client certificates).
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
ssl_config = {
|
147
|
+
"api_url" => "https://api-backend.dev:3443",
|
148
|
+
"use_cert" => true,
|
149
|
+
"ssl_cert_client" => "/etc/ssl/private/client.crt",
|
150
|
+
"ssl_key_client" => "/etc/ssl/private/client.key",
|
151
|
+
"ssl_ca" => "/etc/ssl/certs/ca-chain.crt"
|
152
|
+
}
|
153
|
+
|
154
|
+
ssl_config_creator = RESTinPeace::SSLConfigCreator.new(ssl_config, :peer)
|
155
|
+
ssl_config_creator.faraday_options.inspect
|
156
|
+
# =>
|
157
|
+
{
|
158
|
+
:client_cert => #<OpenSSL::X509::Certificate>,
|
159
|
+
:client_key => Long key is long,
|
160
|
+
:ca_file => "/etc/ssl/certs/ca-chain.crt",
|
161
|
+
:verify_mode => 1
|
162
|
+
}
|
163
|
+
```
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module MyClient
|
2
|
+
module Paginator
|
3
|
+
def get
|
4
|
+
Enumerator.new do |yielder|
|
5
|
+
@params.merge!(page: 1)
|
6
|
+
result = api.get(url, @params)
|
7
|
+
current_page = result.env.response_headers['X-Page'].to_i
|
8
|
+
total_pages = result.env.response_headers['X-Total-Pages'].to_i
|
9
|
+
|
10
|
+
loop do
|
11
|
+
# Yield the results we got in the body.
|
12
|
+
result.body.each do |item|
|
13
|
+
yielder << @klass.new(item)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Only make a request to get the next page if we have not
|
17
|
+
# reached the last page yet.
|
18
|
+
raise StopIteration if current_page == total_pages
|
19
|
+
@params.merge!(page: current_page + 1)
|
20
|
+
result = api.get(url, @params)
|
21
|
+
current_page = result.env.response_headers['X-Page'].to_i
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'rest_in_peace'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rest_in_peace/template_sanitizer'
|
2
|
+
require 'rest_in_peace/response_converter'
|
3
|
+
|
4
|
+
module RESTinPeace
|
5
|
+
class ApiCall
|
6
|
+
def initialize(api, url_template, klass, params)
|
7
|
+
@api = api
|
8
|
+
@url_template = url_template
|
9
|
+
@klass = klass
|
10
|
+
@params = params
|
11
|
+
end
|
12
|
+
|
13
|
+
def get
|
14
|
+
response = api.get(url, params)
|
15
|
+
convert_response(response)
|
16
|
+
end
|
17
|
+
|
18
|
+
def post
|
19
|
+
response = api.post(url, params)
|
20
|
+
convert_response(response)
|
21
|
+
end
|
22
|
+
|
23
|
+
def patch
|
24
|
+
response = api.patch(url, params)
|
25
|
+
convert_response(response)
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete
|
29
|
+
response = api.delete(url, params)
|
30
|
+
convert_response(response)
|
31
|
+
end
|
32
|
+
|
33
|
+
def url
|
34
|
+
sanitizer.url
|
35
|
+
end
|
36
|
+
|
37
|
+
def params
|
38
|
+
sanitizer.leftover_params
|
39
|
+
end
|
40
|
+
|
41
|
+
def sanitizer
|
42
|
+
@sanitizer ||= RESTinPeace::TemplateSanitizer.new(@url_template, @params)
|
43
|
+
end
|
44
|
+
|
45
|
+
def convert_response(response)
|
46
|
+
RESTinPeace::ResponseConverter.new(response, @klass).result
|
47
|
+
end
|
48
|
+
|
49
|
+
def api
|
50
|
+
@api.respond_to?(:call) ? @api.call : @api
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rest_in_peace/template_sanitizer'
|
2
|
+
require 'rest_in_peace/api_call'
|
3
|
+
|
4
|
+
module RESTinPeace
|
5
|
+
class DefinitionProxy
|
6
|
+
class CollectionMethodDefinitions
|
7
|
+
def initialize(target)
|
8
|
+
@target = target
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(method_name, url_template, default_params = {})
|
12
|
+
@target.send(:define_singleton_method, method_name) do |*args|
|
13
|
+
if args.last.is_a?(Hash)
|
14
|
+
params = default_params.merge(args.pop)
|
15
|
+
else
|
16
|
+
params = default_params.dup
|
17
|
+
tokens = RESTinPeace::TemplateSanitizer.new(url_template, {}).tokens
|
18
|
+
tokens.each do |token|
|
19
|
+
params.merge!(token.to_sym => args.shift)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
call = RESTinPeace::ApiCall.new(api, url_template, self, params)
|
24
|
+
call.extend(params.delete(:paginate_with)) if params[:paginate_with]
|
25
|
+
call.get
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module RESTinPeace
|
2
|
+
class DefinitionProxy
|
3
|
+
class ResourceMethodDefinitions
|
4
|
+
def initialize(target)
|
5
|
+
@target = target
|
6
|
+
end
|
7
|
+
|
8
|
+
def get(method_name, url_template, default_params = {})
|
9
|
+
@target.send(:define_method, method_name) do
|
10
|
+
call = RESTinPeace::ApiCall.new(api, url_template, self, to_h)
|
11
|
+
call.get
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def patch(method_name, url_template)
|
16
|
+
@target.send(:define_method, method_name) do
|
17
|
+
call = RESTinPeace::ApiCall.new(api, url_template, self, to_h)
|
18
|
+
call.patch
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def post(method_name, url_template)
|
23
|
+
@target.send(:define_method, method_name) do
|
24
|
+
call = RESTinPeace::ApiCall.new(api, url_template, self, to_h)
|
25
|
+
call.post
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete(method_name, url_template, default_params = {})
|
30
|
+
@target.send(:define_method, method_name) do |params = {}|
|
31
|
+
merged_params = default_params.merge(to_h).merge(params)
|
32
|
+
call = RESTinPeace::ApiCall.new(api, url_template, self, merged_params)
|
33
|
+
call.delete
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rest_in_peace/definition_proxy/resource_method_definitions'
|
2
|
+
require 'rest_in_peace/definition_proxy/collection_method_definitions'
|
3
|
+
|
4
|
+
module RESTinPeace
|
5
|
+
class DefinitionProxy
|
6
|
+
def initialize(target)
|
7
|
+
@target = target
|
8
|
+
end
|
9
|
+
|
10
|
+
def resource(&block)
|
11
|
+
method_definitions = RESTinPeace::DefinitionProxy::ResourceMethodDefinitions.new(@target)
|
12
|
+
method_definitions.instance_eval(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def collection(&block)
|
16
|
+
method_definitions = RESTinPeace::DefinitionProxy::CollectionMethodDefinitions.new(@target)
|
17
|
+
method_definitions.instance_eval(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def use_api(api)
|
21
|
+
@target.api = api
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module RESTinPeace
|
2
|
+
class ResponseConverter
|
3
|
+
def initialize(response, klass)
|
4
|
+
@response = response
|
5
|
+
@klass = klass
|
6
|
+
end
|
7
|
+
|
8
|
+
def result
|
9
|
+
case @response.body.class.to_s
|
10
|
+
when 'Array'
|
11
|
+
convert_from_array
|
12
|
+
when 'Hash'
|
13
|
+
convert_from_hash
|
14
|
+
else
|
15
|
+
raise "Don't know how to convert #{@response.body.class}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def convert_from_array
|
20
|
+
@response.body.map do |entity|
|
21
|
+
convert_from_hash(entity)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def convert_from_hash(entity = @response.body)
|
26
|
+
klass.new entity
|
27
|
+
end
|
28
|
+
|
29
|
+
def klass
|
30
|
+
@klass.respond_to?(:new) ? @klass : @klass.class
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module RESTinPeace
|
4
|
+
class SSLConfigCreator
|
5
|
+
def initialize(config, verify = :peer)
|
6
|
+
@config = config
|
7
|
+
@verify = verify
|
8
|
+
end
|
9
|
+
|
10
|
+
def faraday_options
|
11
|
+
{client_cert: client_cert, client_key: client_key, ca_file: ca_cert_path, verify_mode: verify_mode}
|
12
|
+
end
|
13
|
+
|
14
|
+
def client_cert
|
15
|
+
OpenSSL::X509::Certificate.new(open_file(client_cert_path))
|
16
|
+
end
|
17
|
+
|
18
|
+
def client_cert_path
|
19
|
+
path(@config[:ssl_cert_client])
|
20
|
+
end
|
21
|
+
|
22
|
+
def client_key
|
23
|
+
OpenSSL::PKey::RSA.new(open_file(client_key_path))
|
24
|
+
end
|
25
|
+
|
26
|
+
def client_key_path
|
27
|
+
path(@config[:ssl_key_client])
|
28
|
+
end
|
29
|
+
|
30
|
+
def ca_cert_path
|
31
|
+
path(@config[:ssl_ca])
|
32
|
+
end
|
33
|
+
|
34
|
+
def verify_mode
|
35
|
+
case @verify
|
36
|
+
when :peer
|
37
|
+
OpenSSL::SSL::VERIFY_PEER
|
38
|
+
else
|
39
|
+
raise "Unknown verify variant '#{@verify}'"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def open_file(file)
|
46
|
+
File.open(file)
|
47
|
+
end
|
48
|
+
|
49
|
+
def path(file)
|
50
|
+
File.join(file)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rest_in_peace/errors'
|
2
|
+
|
3
|
+
module RESTinPeace
|
4
|
+
class TemplateSanitizer
|
5
|
+
|
6
|
+
class IncompleteParams < RESTinPeace::DefaultError; end
|
7
|
+
|
8
|
+
def initialize(url_template, params)
|
9
|
+
@url_template = url_template
|
10
|
+
@params = params.dup
|
11
|
+
end
|
12
|
+
|
13
|
+
def url
|
14
|
+
return @url if @url
|
15
|
+
@url = @url_template.dup
|
16
|
+
tokens.each do |token|
|
17
|
+
param = @params.delete(token.to_sym)
|
18
|
+
raise IncompleteParams, "Unknown parameter for token :#{token} found" unless param
|
19
|
+
@url.gsub!(%r{:#{token}}, param.to_s)
|
20
|
+
end
|
21
|
+
@url
|
22
|
+
end
|
23
|
+
|
24
|
+
def tokens
|
25
|
+
@url_template.scan(%r{:([a-z_]+)}).flatten
|
26
|
+
end
|
27
|
+
|
28
|
+
def leftover_params
|
29
|
+
@params.delete_if { |param| tokens.include?(param.to_s) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rest_in_peace/definition_proxy'
|
2
|
+
|
3
|
+
module RESTinPeace
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.send :extend, ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
def api
|
10
|
+
self.class.api
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(attributes = {})
|
14
|
+
update_from_hash(attributes)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_h
|
18
|
+
Hash[each_pair.to_a]
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def update_from_hash(hash)
|
24
|
+
hash.each do |key, value|
|
25
|
+
next unless self.class.members.map(&:to_s).include?(key.to_s)
|
26
|
+
send("#{key}=", value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module ClassMethods
|
31
|
+
attr_accessor :api
|
32
|
+
|
33
|
+
def rest_in_peace(&block)
|
34
|
+
definition_proxy = RESTinPeace::DefinitionProxy.new(self)
|
35
|
+
definition_proxy.instance_eval(&block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
$:.push File.expand_path('../lib', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'rest-in-peace'
|
7
|
+
s.version = File.read(File.expand_path('../VERSION', __FILE__)).strip
|
8
|
+
s.authors = ['Raffael Schmid']
|
9
|
+
s.email = ['raffael.schmid@nine.ch']
|
10
|
+
s.homepage = 'http://github.com/ninech/'
|
11
|
+
s.license = 'MIT'
|
12
|
+
s.summary = 'REST in peace'
|
13
|
+
s.description = 'Let your api REST in peace.'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ['lib']
|
19
|
+
|
20
|
+
s.add_development_dependency 'rake', '~> 10.0'
|
21
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
22
|
+
s.add_development_dependency 'guard', '~> 2.6.1'
|
23
|
+
s.add_development_dependency 'guard-rspec', '~> 4.2.0'
|
24
|
+
s.add_development_dependency 'simplecov', '~> 0.8.2'
|
25
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rest_in_peace/api_call'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe RESTinPeace::ApiCall do
|
5
|
+
let(:api) { double }
|
6
|
+
let(:url_template) { '/rip/:id' }
|
7
|
+
let(:klass) { OpenStruct }
|
8
|
+
let(:params) { {id: 1} }
|
9
|
+
|
10
|
+
let(:api_call) { RESTinPeace::ApiCall.new(api, url_template, klass, params) }
|
11
|
+
|
12
|
+
let(:response) { OpenStruct.new(body: []) }
|
13
|
+
|
14
|
+
describe '#get' do
|
15
|
+
context 'with enough parameters for the template' do
|
16
|
+
it 'calls the api with the parameters' do
|
17
|
+
expect(api).to receive(:get).with('/rip/1', {}).and_return(response)
|
18
|
+
api_call.get
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'with more parameters than needed' do
|
23
|
+
let(:params) { { id: 1, name: 'test' } }
|
24
|
+
it 'uses also the additional parameters' do
|
25
|
+
expect(api).to receive(:get).with('/rip/1', { name: 'test' }).and_return(response)
|
26
|
+
api_call.get
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#post' do
|
32
|
+
let(:url_template) { '/rip' }
|
33
|
+
let(:params) { { name: 'test' } }
|
34
|
+
it 'calls the api with the parameters' do
|
35
|
+
expect(api).to receive(:post).with('/rip', { name: 'test' }).and_return(response)
|
36
|
+
api_call.post
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#patch' do
|
41
|
+
let(:params) { { id: 1, name: 'test' } }
|
42
|
+
it 'calls the api with the parameters' do
|
43
|
+
expect(api).to receive(:patch).with('/rip/1', { name: 'test' }).and_return(response)
|
44
|
+
api_call.patch
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#delete' do
|
49
|
+
it 'calls the api with the parameters' do
|
50
|
+
expect(api).to receive(:delete).with('/rip/1', {}).and_return(response)
|
51
|
+
api_call.delete
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'rest_in_peace'
|
2
|
+
require 'rest_in_peace/definition_proxy/collection_method_definitions'
|
3
|
+
|
4
|
+
describe RESTinPeace::DefinitionProxy::CollectionMethodDefinitions do
|
5
|
+
let(:method_name) { :find }
|
6
|
+
let(:url_template) { '/a/:id' }
|
7
|
+
let(:default_params) { {} }
|
8
|
+
let(:struct) { Struct.new(:id, :name) }
|
9
|
+
let(:target) do
|
10
|
+
Class.new(struct) do
|
11
|
+
include RESTinPeace
|
12
|
+
end
|
13
|
+
end
|
14
|
+
let(:definitions) { described_class.new(target) }
|
15
|
+
let(:api_call_double) { object_double(RESTinPeace::ApiCall.new(target.api, url_template, definitions, default_params)) }
|
16
|
+
|
17
|
+
subject { definitions }
|
18
|
+
|
19
|
+
before do
|
20
|
+
allow(RESTinPeace::ApiCall).to receive(:new).and_return(api_call_double)
|
21
|
+
end
|
22
|
+
|
23
|
+
context '#get' do
|
24
|
+
it 'defines a singleton method on the target' do
|
25
|
+
expect { subject.get(:find, '/a/:id', default_params) }.
|
26
|
+
to change { target.respond_to?(:find) }.from(false).to(true)
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'the created method' do
|
30
|
+
before do
|
31
|
+
allow(api_call_double).to receive(:get)
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'without a paginator' do
|
35
|
+
it 'does not extend api call' do
|
36
|
+
expect(api_call_double).to_not receive(:extend)
|
37
|
+
|
38
|
+
subject.get(:find, '/a/:id', default_params)
|
39
|
+
target.find(1)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with a paginator' do
|
44
|
+
it 'extends api call' do
|
45
|
+
expect(api_call_double).to receive(:extend)
|
46
|
+
|
47
|
+
subject.get(:find, '/a/:id', default_params.merge(paginate_with: Module))
|
48
|
+
target.find(1)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe 'parameter and arguments handling' do
|
53
|
+
it 'converts the parameters' do
|
54
|
+
expect(RESTinPeace::ApiCall).to receive(:new).
|
55
|
+
with(target.api, '/a/:id', target, {id: 1}).
|
56
|
+
and_return(api_call_double)
|
57
|
+
|
58
|
+
subject.get(:find, '/a/:id', default_params)
|
59
|
+
target.find(1)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'appends the given attributes' do
|
63
|
+
expect(RESTinPeace::ApiCall).to receive(:new).
|
64
|
+
with(target.api, '/a', target, {name: 'daniele', last_name: 'in der o'}).
|
65
|
+
and_return(api_call_double)
|
66
|
+
|
67
|
+
subject.get(:all, '/a', {last_name: 'in der o'})
|
68
|
+
target.all(name: 'daniele')
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'does not modify the default params' do
|
72
|
+
default_params = { per_page: 250 }
|
73
|
+
subject.get(:find, '/a/:id', default_params)
|
74
|
+
target.find(1)
|
75
|
+
expect(default_params).to eq({ per_page: 250 })
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'rest_in_peace'
|
2
|
+
require 'rest_in_peace/definition_proxy/resource_method_definitions'
|
3
|
+
|
4
|
+
describe RESTinPeace::DefinitionProxy::ResourceMethodDefinitions do
|
5
|
+
let(:struct) { Struct.new(:id, :name) }
|
6
|
+
let(:target) do
|
7
|
+
Class.new(struct) do
|
8
|
+
include RESTinPeace
|
9
|
+
end
|
10
|
+
end
|
11
|
+
let(:instance) { target.new }
|
12
|
+
let(:definitions) { described_class.new(target) }
|
13
|
+
let(:api_call_double) { object_double(RESTinPeace::ApiCall.new(target.api, '/a/:id', definitions, {})) }
|
14
|
+
|
15
|
+
subject { definitions }
|
16
|
+
|
17
|
+
before do
|
18
|
+
allow(RESTinPeace::ApiCall).to receive(:new).and_return(api_call_double)
|
19
|
+
end
|
20
|
+
|
21
|
+
shared_examples_for 'an instance method' do
|
22
|
+
it 'defines a singleton method on the target' do
|
23
|
+
expect { subject.send(http_verb, method_name, url_template) }.
|
24
|
+
to change { instance.respond_to?(method_name) }.from(false).to(true)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'the created method' do
|
28
|
+
before do
|
29
|
+
allow(api_call_double).to receive(http_verb)
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'parameter and arguments handling' do
|
33
|
+
it 'uses the attributes of the class' do
|
34
|
+
expect(RESTinPeace::ApiCall).to receive(:new).
|
35
|
+
with(target.api, url_template, instance, instance.to_h).
|
36
|
+
and_return(api_call_double)
|
37
|
+
|
38
|
+
subject.send(http_verb, method_name, url_template)
|
39
|
+
instance.send(method_name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context '#get' do
|
46
|
+
it_behaves_like 'an instance method' do
|
47
|
+
let(:http_verb) { :get }
|
48
|
+
let(:method_name) { :reload }
|
49
|
+
let(:url_template) { '/a/:id' }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context '#patch' do
|
54
|
+
it_behaves_like 'an instance method' do
|
55
|
+
let(:http_verb) { :patch }
|
56
|
+
let(:method_name) { :save }
|
57
|
+
let(:url_template) { '/a/:id' }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context '#post' do
|
62
|
+
it_behaves_like 'an instance method' do
|
63
|
+
let(:http_verb) { :post }
|
64
|
+
let(:method_name) { :create }
|
65
|
+
let(:url_template) { '/a/:id' }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context '#delete' do
|
70
|
+
it_behaves_like 'an instance method' do
|
71
|
+
let(:http_verb) { :delete }
|
72
|
+
let(:method_name) { :destroy }
|
73
|
+
let(:url_template) { '/a/:id' }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rest_in_peace/definition_proxy'
|
2
|
+
|
3
|
+
describe RESTinPeace::DefinitionProxy do
|
4
|
+
let(:resource_definitions) { object_double(RESTinPeace::DefinitionProxy::ResourceMethodDefinitions) }
|
5
|
+
let(:collection_definitions) { object_double(RESTinPeace::DefinitionProxy::CollectionMethodDefinitions) }
|
6
|
+
let(:target) { }
|
7
|
+
let(:proxy) { RESTinPeace::DefinitionProxy.new(target) }
|
8
|
+
let(:test_proc) { ->() {} }
|
9
|
+
|
10
|
+
subject { proxy }
|
11
|
+
|
12
|
+
before do
|
13
|
+
allow(RESTinPeace::DefinitionProxy::ResourceMethodDefinitions).
|
14
|
+
to receive(:new).with(target).and_return(resource_definitions)
|
15
|
+
allow(RESTinPeace::DefinitionProxy::CollectionMethodDefinitions).
|
16
|
+
to receive(:new).with(target).and_return(collection_definitions)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#resource' do
|
20
|
+
it 'forwards the given block to a resource method definition' do
|
21
|
+
expect(resource_definitions).to receive(:instance_eval) do |&block|
|
22
|
+
expect(block).to be_instance_of(Proc)
|
23
|
+
end
|
24
|
+
subject.resource(&test_proc)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#collection' do
|
29
|
+
it 'forwards the given block to a collection method definition' do
|
30
|
+
expect(collection_definitions).to receive(:instance_eval) do |&block|
|
31
|
+
expect(block).to be_instance_of(Proc)
|
32
|
+
end
|
33
|
+
subject.collection(&test_proc)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rest_in_peace/response_converter'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe RESTinPeace::ResponseConverter do
|
5
|
+
let(:element1) { { name: 'test1' } }
|
6
|
+
let(:element2) { { name: 'test2' } }
|
7
|
+
let(:response) { OpenStruct.new(body: response_body) }
|
8
|
+
let(:converter) { RESTinPeace::ResponseConverter.new(response, klass) }
|
9
|
+
|
10
|
+
describe '#result' do
|
11
|
+
subject { converter.result }
|
12
|
+
|
13
|
+
shared_examples_for 'an array input' do
|
14
|
+
let(:response_body) { [element1, element2] }
|
15
|
+
specify { expect(subject).to be_instance_of(Array) }
|
16
|
+
specify { expect(subject).to eq([OpenStruct.new(name: 'test1'), OpenStruct.new(name: 'test2')]) }
|
17
|
+
end
|
18
|
+
|
19
|
+
shared_examples_for 'a hash input' do
|
20
|
+
let(:response_body) { element1 }
|
21
|
+
specify { expect(subject).to be_instance_of(OpenStruct) }
|
22
|
+
specify { expect(subject).to eq(OpenStruct.new(name: 'test1')) }
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'given type is a class' do
|
26
|
+
let(:klass) { OpenStruct }
|
27
|
+
context 'input is an array' do
|
28
|
+
it_behaves_like 'an array input'
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'input is a hash' do
|
32
|
+
it_behaves_like 'a hash input'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'given type is an instance' do
|
37
|
+
let(:klass) { OpenStruct.new }
|
38
|
+
context 'input is an array' do
|
39
|
+
it_behaves_like 'an array input'
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'input is a hash' do
|
43
|
+
it_behaves_like 'a hash input'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rest_in_peace/template_sanitizer'
|
2
|
+
|
3
|
+
describe RESTinPeace::TemplateSanitizer do
|
4
|
+
|
5
|
+
let(:template_sanitizer) { RESTinPeace::TemplateSanitizer.new(url_template, params) }
|
6
|
+
|
7
|
+
describe '#url' do
|
8
|
+
subject { template_sanitizer.url }
|
9
|
+
|
10
|
+
context 'single token' do
|
11
|
+
let(:params) { { id: 1 } }
|
12
|
+
let(:url_template) { '/a/:id' }
|
13
|
+
specify { expect(subject).to eq('/a/1') }
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'multiple token' do
|
17
|
+
let(:params) { { id: 2, a_id: 1 } }
|
18
|
+
let(:url_template) { '/a/:a_id/b/:id' }
|
19
|
+
specify { expect(subject).to eq('/a/1/b/2') }
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'incomplete params' do
|
23
|
+
let(:params) { {} }
|
24
|
+
let(:url_template) { '/a/:id' }
|
25
|
+
specify { expect { subject }.to raise_error(RESTinPeace::TemplateSanitizer::IncompleteParams) }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'immutability of the url template' do
|
29
|
+
let(:params) { { id: 1 } }
|
30
|
+
let(:url_template) { '/a/:id' }
|
31
|
+
specify { expect { subject }.to_not change { url_template } }
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'immutability of the params' do
|
35
|
+
let(:params) { { id: 1 } }
|
36
|
+
let(:url_template) { '/a/:id' }
|
37
|
+
specify { expect { subject }.to_not change { params } }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#tokens' do
|
42
|
+
let(:params) { {} }
|
43
|
+
|
44
|
+
context 'single token' do
|
45
|
+
let(:url_template) { '/a/:id' }
|
46
|
+
subject { template_sanitizer.tokens }
|
47
|
+
specify { expect(subject).to eq(%w(id)) }
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'multiple tokens' do
|
51
|
+
let(:url_template) { '/a/:a_id/b/:id' }
|
52
|
+
subject { template_sanitizer.tokens }
|
53
|
+
specify { expect(subject).to eq(%w(a_id id)) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#leftover_params' do
|
58
|
+
let(:params) { { id: 1, name: 'test' } }
|
59
|
+
let(:url_template) { '/a/:id' }
|
60
|
+
subject { template_sanitizer.leftover_params }
|
61
|
+
|
62
|
+
specify { expect(subject).to eq({name: 'test'}) }
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rest_in_peace'
|
2
|
+
|
3
|
+
describe RESTinPeace do
|
4
|
+
|
5
|
+
let(:struct) { Struct.new(:name) }
|
6
|
+
let(:extended_class) do
|
7
|
+
Class.new(struct) do
|
8
|
+
include RESTinPeace
|
9
|
+
end
|
10
|
+
end
|
11
|
+
let(:attributes) { { name: 'test' } }
|
12
|
+
let(:instance) { extended_class.new(attributes) }
|
13
|
+
|
14
|
+
describe '::api' do
|
15
|
+
subject { extended_class }
|
16
|
+
specify { expect(subject).to respond_to(:api).with(0).arguments }
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '::rest_in_peace' do
|
20
|
+
subject { extended_class }
|
21
|
+
specify { expect(subject).to respond_to(:rest_in_peace).with(0).arguments }
|
22
|
+
let(:definition_proxy) { object_double(RESTinPeace::DefinitionProxy) }
|
23
|
+
|
24
|
+
|
25
|
+
it 'evaluates the given block inside the definition proxy' do
|
26
|
+
allow(RESTinPeace::DefinitionProxy).to receive(:new).with(subject).and_return(definition_proxy)
|
27
|
+
expect(definition_proxy).to receive(:instance_eval) do |&block|
|
28
|
+
expect(block).to be_instance_of(Proc)
|
29
|
+
end
|
30
|
+
subject.rest_in_peace { }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#api' do
|
35
|
+
subject { instance }
|
36
|
+
specify { expect(subject).to respond_to(:api).with(0).arguments }
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#to_h' do
|
40
|
+
subject { instance }
|
41
|
+
specify { expect(subject).to respond_to(:to_h).with(0).arguments }
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#initialize' do
|
45
|
+
subject { instance }
|
46
|
+
specify { expect(subject.name).to eq('test') }
|
47
|
+
|
48
|
+
context 'unknown params' do
|
49
|
+
let(:attributes) { { name: 'test42', email: 'yolo@example.org' } }
|
50
|
+
specify { expect(subject.name).to eq('test42') }
|
51
|
+
specify { expect { subject.email }.to raise_error(NoMethodError) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
# These two settings work together to allow you to limit a spec run
|
6
|
+
# to individual examples or groups you care about by tagging them with
|
7
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
8
|
+
# get run.
|
9
|
+
config.filter_run :focus
|
10
|
+
config.run_all_when_everything_filtered = true
|
11
|
+
|
12
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
13
|
+
# file, and it's useful to allow more verbose output when running an
|
14
|
+
# individual spec file.
|
15
|
+
if config.files_to_run.one?
|
16
|
+
# Use the documentation formatter for detailed output,
|
17
|
+
# unless a formatter has already been configured
|
18
|
+
# (e.g. via a command-line flag).
|
19
|
+
config.default_formatter = 'doc'
|
20
|
+
end
|
21
|
+
|
22
|
+
# Print the 10 slowest examples and example groups at the
|
23
|
+
# end of the spec run, to help surface which specs are running
|
24
|
+
# particularly slow.
|
25
|
+
config.profile_examples = 10
|
26
|
+
|
27
|
+
# Run specs in random order to surface order dependencies. If you find an
|
28
|
+
# order dependency and want to debug it, you can fix the order by providing
|
29
|
+
# the seed, which is printed after each run.
|
30
|
+
# --seed 1234
|
31
|
+
config.order = :random
|
32
|
+
|
33
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
34
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
35
|
+
# test failures related to randomization by passing the same `--seed` value
|
36
|
+
# as the one that triggered the failure.
|
37
|
+
Kernel.srand config.seed
|
38
|
+
|
39
|
+
# rspec-expectations config goes here. You can use an alternate
|
40
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
41
|
+
# assertions if you prefer.
|
42
|
+
config.expect_with :rspec do |expectations|
|
43
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
44
|
+
# For more details, see:
|
45
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
46
|
+
expectations.syntax = :expect
|
47
|
+
end
|
48
|
+
|
49
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
50
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
51
|
+
config.mock_with :rspec do |mocks|
|
52
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
53
|
+
# For more details, see:
|
54
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
55
|
+
mocks.syntax = :expect
|
56
|
+
|
57
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
58
|
+
# a real object. This is generally recommended.
|
59
|
+
mocks.verify_partial_doubles = true
|
60
|
+
end
|
61
|
+
end
|
metadata
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rest-in-peace
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Raffael Schmid
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-07-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '10.0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '10.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '3.0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '3.0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: guard
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.6.1
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.6.1
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: guard-rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 4.2.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 4.2.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: simplecov
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 0.8.2
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.8.2
|
94
|
+
description: Let your api REST in peace.
|
95
|
+
email:
|
96
|
+
- raffael.schmid@nine.ch
|
97
|
+
executables: []
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files: []
|
100
|
+
files:
|
101
|
+
- .gitignore
|
102
|
+
- .rspec
|
103
|
+
- .ruby-gemset
|
104
|
+
- .ruby-version
|
105
|
+
- .travis.yml
|
106
|
+
- Gemfile
|
107
|
+
- Guardfile
|
108
|
+
- LICENSE.txt
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- VERSION
|
112
|
+
- examples/pagination_with_headers.rb
|
113
|
+
- images/rest_in_peace.gif
|
114
|
+
- lib/rest-in-peace.rb
|
115
|
+
- lib/rest_in_peace.rb
|
116
|
+
- lib/rest_in_peace/api_call.rb
|
117
|
+
- lib/rest_in_peace/definition_proxy.rb
|
118
|
+
- lib/rest_in_peace/definition_proxy/collection_method_definitions.rb
|
119
|
+
- lib/rest_in_peace/definition_proxy/resource_method_definitions.rb
|
120
|
+
- lib/rest_in_peace/errors.rb
|
121
|
+
- lib/rest_in_peace/response_converter.rb
|
122
|
+
- lib/rest_in_peace/ssl_config_creator.rb
|
123
|
+
- lib/rest_in_peace/template_sanitizer.rb
|
124
|
+
- rest-in-peace.gemspec
|
125
|
+
- spec/rest_in_peace/api_call_spec.rb
|
126
|
+
- spec/rest_in_peace/definition_proxy/collection_method_definitions_spec.rb
|
127
|
+
- spec/rest_in_peace/definition_proxy/resource_method_definitions_spec.rb
|
128
|
+
- spec/rest_in_peace/definition_proxy_spec.rb
|
129
|
+
- spec/rest_in_peace/response_converter_spec.rb
|
130
|
+
- spec/rest_in_peace/template_sanitizer_spec.rb
|
131
|
+
- spec/rest_in_peace_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
homepage: http://github.com/ninech/
|
134
|
+
licenses:
|
135
|
+
- MIT
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
none: false
|
142
|
+
requirements:
|
143
|
+
- - ! '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
|
+
none: false
|
148
|
+
requirements:
|
149
|
+
- - ! '>='
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
requirements: []
|
153
|
+
rubyforge_project:
|
154
|
+
rubygems_version: 1.8.23.2
|
155
|
+
signing_key:
|
156
|
+
specification_version: 3
|
157
|
+
summary: REST in peace
|
158
|
+
test_files:
|
159
|
+
- spec/rest_in_peace/api_call_spec.rb
|
160
|
+
- spec/rest_in_peace/definition_proxy/collection_method_definitions_spec.rb
|
161
|
+
- spec/rest_in_peace/definition_proxy/resource_method_definitions_spec.rb
|
162
|
+
- spec/rest_in_peace/definition_proxy_spec.rb
|
163
|
+
- spec/rest_in_peace/response_converter_spec.rb
|
164
|
+
- spec/rest_in_peace/template_sanitizer_spec.rb
|
165
|
+
- spec/rest_in_peace_spec.rb
|
166
|
+
- spec/spec_helper.rb
|