rest-in-peace 1.4.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/README.md +75 -2
- data/VERSION +1 -1
- data/lib/rest_in_peace/active_model_api.rb +72 -0
- data/lib/rest_in_peace/definition_proxy/attributes_definitions.rb +20 -0
- data/lib/rest_in_peace/definition_proxy/collection_method_definitions.rb +1 -0
- data/lib/rest_in_peace/definition_proxy/resource_method_definitions.rb +7 -8
- data/lib/rest_in_peace/definition_proxy.rb +21 -0
- data/lib/rest_in_peace/faraday/raise_errors_middleware.rb +34 -0
- data/lib/rest_in_peace/faraday/ssl_config_creator.rb +66 -0
- data/lib/rest_in_peace/response_converter.rb +28 -12
- data/lib/rest_in_peace/template_sanitizer.rb +1 -1
- data/lib/rest_in_peace.rb +45 -9
- data/rest-in-peace.gemspec +5 -3
- data/spec/rest_in_peace/active_model_api_spec.rb +201 -0
- data/spec/rest_in_peace/definition_proxy/attributes_definitions_spec.rb +36 -0
- data/spec/rest_in_peace/definition_proxy/collection_method_definitions_spec.rb +7 -2
- data/spec/rest_in_peace/definition_proxy/resource_method_definitions_spec.rb +69 -13
- data/spec/rest_in_peace/definition_proxy_spec.rb +34 -1
- data/spec/rest_in_peace/response_converter_spec.rb +22 -5
- data/spec/rest_in_peace/template_sanitizer_spec.rb +12 -0
- data/spec/rest_in_peace_spec.rb +140 -7
- data/spec/spec_helper.rb +4 -1
- metadata +51 -18
- data/lib/rest_in_peace/ssl_config_creator.rb +0 -59
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZmYzY2NhN2JjMjNiZWRlOTZkY2ZiMjY0MzA1MDIzNTI5ZWFjMWYwOQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YjA3MTliNWE4MDA3MDhlYzAzM2M1NzRlMWNhYzVjNzExYmRhNTE1MA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NTU2NWQ2NTc4NGMzMzgxMmY1OTVlYTc1NDE5ODkxMDEyODJkNTliMDhhZTYx
|
10
|
+
NzFlZjRmMDdkNTEwODZiMTA1YjJlYmFkMWZlMmIyNmIwNzZmNjdmZTFlNTVi
|
11
|
+
ZjE2MWIzZDE1YTI3NWI3ZGM5MDNkOWUxMWEwOWNjYjg3YjM2MjE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZWFlOTFkNTNkMTQ3MTFhMTA2YTBlYjczMzdiZTlmNTE4ZTM5ODhhM2E0ZjY0
|
14
|
+
ZDMwYjhhOWMyMTc5YzdjMDUxNWEyOWMzNDE3ZjdmMDU0ODFkMjRlNmNlZjRj
|
15
|
+
NzQ0OWUxNmUzYTg4MjQ1M2Y2NTliMjAwNTIwNDcxMTk2ZWRjYTg=
|
data/README.md
CHANGED
@@ -22,6 +22,22 @@ There is no dependency on a specific HTTP client library but the client has been
|
|
22
22
|
|
23
23
|
### Configuration
|
24
24
|
|
25
|
+
#### Attributes
|
26
|
+
|
27
|
+
You need to specify all the attributes which should be read out of the parsed JSON. You have to specify whether an attribute
|
28
|
+
is readonly or writeable:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
rest_in_peace do
|
32
|
+
attributes do
|
33
|
+
read :id
|
34
|
+
write :name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
#### API Endpoints
|
40
|
+
|
25
41
|
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
42
|
|
27
43
|
There are two sections where you can specify endpoints: `resource` and `collection`:
|
@@ -73,6 +89,8 @@ resource.create # calls "POST /rip"
|
|
73
89
|
resource.reload # calls "GET /rip/1"
|
74
90
|
```
|
75
91
|
|
92
|
+
**For any writing action (`:post`, `:put`, `:patch`) RESTinPeace will include the writable attributes in the body and `id`.**
|
93
|
+
|
76
94
|
#### Collection
|
77
95
|
|
78
96
|
If you define anything inside the `collection` block, it will define a method on the class:
|
@@ -107,6 +125,42 @@ end
|
|
107
125
|
|
108
126
|
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
127
|
|
128
|
+
#### ActiveModel Support
|
129
|
+
|
130
|
+
For easy interoperability with Rails, there is the ability to include ActiveModel into your class. To enable this support, follow these steps:
|
131
|
+
|
132
|
+
* Define a `create` method (To be called for saving new objects)
|
133
|
+
* Define a `save` method (To be called for updates)
|
134
|
+
* Call `acts_as_active_model` **after** your *API endpoints* and *attribute* definitions
|
135
|
+
|
136
|
+
##### Example
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
require 'rest_in_peace'
|
140
|
+
|
141
|
+
module MyClient
|
142
|
+
class Fabric < Struct.new(:id, :name, :ip)
|
143
|
+
include RESTinPeace
|
144
|
+
|
145
|
+
rest_in_peace do
|
146
|
+
use_api ->() { MyClient.api }
|
147
|
+
|
148
|
+
attributes do
|
149
|
+
read :id
|
150
|
+
write :name
|
151
|
+
end
|
152
|
+
|
153
|
+
resource do
|
154
|
+
post :create, '/fabrics'
|
155
|
+
patch :save, '/fabrics/:id'
|
156
|
+
end
|
157
|
+
|
158
|
+
acts_as_active_model
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
```
|
163
|
+
|
110
164
|
#### Complete Configuration
|
111
165
|
|
112
166
|
```ruby
|
@@ -114,12 +168,17 @@ require 'my_client/paginator'
|
|
114
168
|
require 'rest_in_peace'
|
115
169
|
|
116
170
|
module MyClient
|
117
|
-
class Fabric
|
171
|
+
class Fabric
|
118
172
|
include RESTinPeace
|
119
173
|
|
120
174
|
rest_in_peace do
|
121
175
|
use_api ->() { MyClient.api }
|
122
176
|
|
177
|
+
attributes do
|
178
|
+
read :id
|
179
|
+
write :name
|
180
|
+
end
|
181
|
+
|
123
182
|
resource do
|
124
183
|
patch :save, '/fabrics/:id'
|
125
184
|
post :create, '/fabrics'
|
@@ -131,6 +190,8 @@ module MyClient
|
|
131
190
|
get :all, '/fabrics', paginate_with: MyClient::Paginator
|
132
191
|
get :find, '/fabrics/:id'
|
133
192
|
end
|
193
|
+
|
194
|
+
acts_as_active_model
|
134
195
|
end
|
135
196
|
end
|
136
197
|
end
|
@@ -149,7 +210,7 @@ ssl_config = {
|
|
149
210
|
"ca_cert" => "/etc/ssl/certs/ca-chain.crt"
|
150
211
|
}
|
151
212
|
|
152
|
-
ssl_config_creator = RESTinPeace::SSLConfigCreator.new(ssl_config, :peer)
|
213
|
+
ssl_config_creator = RESTinPeace::Faraday::SSLConfigCreator.new(ssl_config, :peer)
|
153
214
|
ssl_config_creator.faraday_options.inspect
|
154
215
|
# =>
|
155
216
|
{
|
@@ -158,4 +219,16 @@ ssl_config_creator.faraday_options.inspect
|
|
158
219
|
:ca_file => "/etc/ssl/certs/ca-chain.crt",
|
159
220
|
:verify_mode => 1
|
160
221
|
}
|
222
|
+
```
|
223
|
+
|
224
|
+
### Faraday Middleware: RIP Raise Errors
|
225
|
+
|
226
|
+
This middleware is mostly equivalent to [this one](https://github.com/lostisland/faraday/blob/cf549f4d883a3cae15db0d835628daa33f6f3a2b/lib/faraday/response/raise_error.rb) but it does not raise an error when the HTTP status code is `422` as this code is used to return validation errors.
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
Faraday.new do |faraday|
|
230
|
+
# ...
|
231
|
+
faraday.response :rip_raise_errors
|
232
|
+
# ...
|
233
|
+
end
|
161
234
|
```
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.0.0
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
|
3
|
+
module RESTinPeace
|
4
|
+
module ActiveModelAPI
|
5
|
+
class MissingMethod < RESTinPeace::DefaultError
|
6
|
+
def initialize(method)
|
7
|
+
super "No #{method} method has been defined. "\
|
8
|
+
'Maybe you called acts_as_active_model before defining the api endpoints?'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.included(base)
|
13
|
+
check_for_missing_methods(base)
|
14
|
+
|
15
|
+
base.send(:include, ActiveModel::Dirty)
|
16
|
+
base.send(:include, ActiveModel::Conversion)
|
17
|
+
base.extend ActiveModel::Naming
|
18
|
+
|
19
|
+
base.send(:alias_method, :save_without_dirty_tracking, :save)
|
20
|
+
base.send(:alias_method, :save, :save_with_dirty_tracking)
|
21
|
+
|
22
|
+
base.send :define_attribute_methods, base.rip_attributes[:write]
|
23
|
+
|
24
|
+
base.rip_attributes[:write].each do |attribute|
|
25
|
+
base.send(:define_method, "#{attribute}_with_dirty_tracking=") do |value|
|
26
|
+
attribute_will_change!(attribute) unless send(attribute) == value
|
27
|
+
send("#{attribute}_without_dirty_tracking=", value)
|
28
|
+
end
|
29
|
+
|
30
|
+
base.send(:alias_method, "#{attribute}_without_dirty_tracking=", "#{attribute}=")
|
31
|
+
base.send(:alias_method, "#{attribute}=", "#{attribute}_with_dirty_tracking=")
|
32
|
+
end
|
33
|
+
|
34
|
+
def base.human_attribute_name(attr, options = {})
|
35
|
+
attr.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
def base.lookup_ancestors
|
39
|
+
[self]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.check_for_missing_methods(base)
|
44
|
+
raise MissingMethod, :save unless base.instance_methods.include?(:save)
|
45
|
+
raise MissingMethod, :create unless base.instance_methods.include?(:create)
|
46
|
+
end
|
47
|
+
|
48
|
+
def save_with_dirty_tracking
|
49
|
+
save_without_dirty_tracking.tap do
|
50
|
+
@changed_attributes.clear if @changed_attributes
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def persisted?
|
55
|
+
!!id
|
56
|
+
end
|
57
|
+
|
58
|
+
def read_attribute_for_validation(attr)
|
59
|
+
send(attr)
|
60
|
+
end
|
61
|
+
|
62
|
+
def errors
|
63
|
+
@errors ||= ActiveModel::Errors.new(self)
|
64
|
+
end
|
65
|
+
|
66
|
+
def errors=(new_errors)
|
67
|
+
new_errors.each do |key, value|
|
68
|
+
errors.set(key.to_sym, [value].flatten)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module RESTinPeace
|
2
|
+
class DefinitionProxy
|
3
|
+
class AttributesDefinitions
|
4
|
+
def initialize(target)
|
5
|
+
@target = target
|
6
|
+
end
|
7
|
+
|
8
|
+
def read(*attributes)
|
9
|
+
@target.send(:attr_reader, *attributes)
|
10
|
+
@target.rip_attributes[:read].concat(attributes)
|
11
|
+
end
|
12
|
+
|
13
|
+
def write(*attributes)
|
14
|
+
read(*attributes)
|
15
|
+
@target.send(:attr_writer, *attributes)
|
16
|
+
@target.rip_attributes[:write].concat(attributes)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -11,6 +11,7 @@ module RESTinPeace
|
|
11
11
|
def get(method_name, url_template, default_params = {})
|
12
12
|
@target.rip_registry[:collection] << { method: :get, name: method_name, url: url_template }
|
13
13
|
@target.send(:define_singleton_method, method_name) do |given_params = {}|
|
14
|
+
raise RESTinPeace::DefinitionProxy::InvalidArgument unless given_params.respond_to?(:merge)
|
14
15
|
params = default_params.merge(given_params)
|
15
16
|
|
16
17
|
call = RESTinPeace::ApiCall.new(api, url_template, self, params)
|
@@ -8,7 +8,7 @@ module RESTinPeace
|
|
8
8
|
def get(method_name, url_template, default_params = {})
|
9
9
|
@target.rip_registry[:resource] << { method: :get, name: method_name, url: url_template }
|
10
10
|
@target.send(:define_method, method_name) do
|
11
|
-
call = RESTinPeace::ApiCall.new(api, url_template, self,
|
11
|
+
call = RESTinPeace::ApiCall.new(api, url_template, self, hash_for_updates)
|
12
12
|
call.get
|
13
13
|
end
|
14
14
|
end
|
@@ -16,7 +16,7 @@ module RESTinPeace
|
|
16
16
|
def patch(method_name, url_template)
|
17
17
|
@target.rip_registry[:resource] << { method: :patch, name: method_name, url: url_template }
|
18
18
|
@target.send(:define_method, method_name) do
|
19
|
-
call = RESTinPeace::ApiCall.new(api, url_template, self,
|
19
|
+
call = RESTinPeace::ApiCall.new(api, url_template, self, hash_for_updates)
|
20
20
|
call.patch
|
21
21
|
end
|
22
22
|
end
|
@@ -24,7 +24,7 @@ module RESTinPeace
|
|
24
24
|
def post(method_name, url_template)
|
25
25
|
@target.rip_registry[:resource] << { method: :post, name: method_name, url: url_template }
|
26
26
|
@target.send(:define_method, method_name) do
|
27
|
-
call = RESTinPeace::ApiCall.new(api, url_template, self,
|
27
|
+
call = RESTinPeace::ApiCall.new(api, url_template, self, hash_for_updates)
|
28
28
|
call.post
|
29
29
|
end
|
30
30
|
end
|
@@ -32,16 +32,15 @@ module RESTinPeace
|
|
32
32
|
def put(method_name, url_template)
|
33
33
|
@target.rip_registry[:resource] << { method: :put, name: method_name, url: url_template }
|
34
34
|
@target.send(:define_method, method_name) do
|
35
|
-
call = RESTinPeace::ApiCall.new(api, url_template, self,
|
35
|
+
call = RESTinPeace::ApiCall.new(api, url_template, self, hash_for_updates)
|
36
36
|
call.put
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
def delete(method_name, url_template
|
40
|
+
def delete(method_name, url_template)
|
41
41
|
@target.rip_registry[:resource] << { method: :delete, name: method_name, url: url_template }
|
42
|
-
@target.send(:define_method, method_name) do
|
43
|
-
|
44
|
-
call = RESTinPeace::ApiCall.new(api, url_template, self, merged_params)
|
42
|
+
@target.send(:define_method, method_name) do
|
43
|
+
call = RESTinPeace::ApiCall.new(api, url_template, self, id: id)
|
45
44
|
call.delete
|
46
45
|
end
|
47
46
|
end
|
@@ -1,8 +1,16 @@
|
|
1
1
|
require 'rest_in_peace/definition_proxy/resource_method_definitions'
|
2
2
|
require 'rest_in_peace/definition_proxy/collection_method_definitions'
|
3
|
+
require 'rest_in_peace/definition_proxy/attributes_definitions'
|
4
|
+
require 'rest_in_peace/active_model_api'
|
3
5
|
|
4
6
|
module RESTinPeace
|
5
7
|
class DefinitionProxy
|
8
|
+
class InvalidArgument < RESTinPeace::DefaultError
|
9
|
+
def initialize
|
10
|
+
super('Given parameter must respond to `merge`.')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
6
14
|
def initialize(target)
|
7
15
|
@target = target
|
8
16
|
end
|
@@ -17,6 +25,19 @@ module RESTinPeace
|
|
17
25
|
method_definitions.instance_eval(&block)
|
18
26
|
end
|
19
27
|
|
28
|
+
def attributes(&block)
|
29
|
+
method_definitions = RESTinPeace::DefinitionProxy::AttributesDefinitions.new(@target)
|
30
|
+
method_definitions.instance_eval(&block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def acts_as_active_model
|
34
|
+
@target.send(:include, RESTinPeace::ActiveModelAPI)
|
35
|
+
end
|
36
|
+
|
37
|
+
def namespace_attributes_with(namespace)
|
38
|
+
@target.rip_namespace = namespace
|
39
|
+
end
|
40
|
+
|
20
41
|
def use_api(api)
|
21
42
|
@target.api = api
|
22
43
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module RESTinPeace
|
4
|
+
module Faraday
|
5
|
+
class RaiseErrorsMiddleware < ::Faraday::Response::Middleware
|
6
|
+
CLIENT_ERROR_STATUSES = 400...600
|
7
|
+
|
8
|
+
def on_complete(env)
|
9
|
+
case env[:status]
|
10
|
+
when 404
|
11
|
+
raise Faraday::Error::ResourceNotFound, response_values(env)
|
12
|
+
when 407
|
13
|
+
# mimic the behavior that we get with proxy requests with HTTPS
|
14
|
+
raise Faraday::Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
|
15
|
+
when 422
|
16
|
+
# do not raise an error as 422 from a rails app means validation errors
|
17
|
+
# and response body contains the validation errors
|
18
|
+
when CLIENT_ERROR_STATUSES
|
19
|
+
raise Faraday::Error::ClientError, response_values(env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def response_values(env)
|
24
|
+
{
|
25
|
+
status: env.status,
|
26
|
+
headers: env.response_headers,
|
27
|
+
body: env.body,
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
::Faraday::Response.register_middleware rip_raise_errors: RaiseErrorsMiddleware
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module RESTinPeace
|
4
|
+
module Faraday
|
5
|
+
class SSLConfigCreator
|
6
|
+
class MissingParam < Exception; end
|
7
|
+
|
8
|
+
def initialize(config, verify = :peer)
|
9
|
+
@config = config
|
10
|
+
@verify = verify
|
11
|
+
|
12
|
+
raise MissingParam, 'Specify :ca_cert in ssl options' unless @config[:ca_cert]
|
13
|
+
raise MissingParam, 'Specify :client_key in ssl options' unless @config[:client_key]
|
14
|
+
raise MissingParam, 'Specify :client_cert in ssl options' unless @config[:client_cert]
|
15
|
+
end
|
16
|
+
|
17
|
+
def faraday_options
|
18
|
+
{
|
19
|
+
client_cert: client_cert,
|
20
|
+
client_key: client_key,
|
21
|
+
ca_file: ca_cert_path,
|
22
|
+
verify_mode: verify_mode,
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def client_cert
|
27
|
+
OpenSSL::X509::Certificate.new(open_file(client_cert_path))
|
28
|
+
end
|
29
|
+
|
30
|
+
def client_cert_path
|
31
|
+
path(@config[:client_cert])
|
32
|
+
end
|
33
|
+
|
34
|
+
def client_key
|
35
|
+
OpenSSL::PKey::RSA.new(open_file(client_key_path))
|
36
|
+
end
|
37
|
+
|
38
|
+
def client_key_path
|
39
|
+
path(@config[:client_key])
|
40
|
+
end
|
41
|
+
|
42
|
+
def ca_cert_path
|
43
|
+
path(@config[:ca_cert])
|
44
|
+
end
|
45
|
+
|
46
|
+
def verify_mode
|
47
|
+
case @verify
|
48
|
+
when :peer
|
49
|
+
OpenSSL::SSL::VERIFY_PEER
|
50
|
+
else
|
51
|
+
raise "Unknown verify variant '#{@verify}'"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def open_file(file)
|
58
|
+
File.open(file)
|
59
|
+
end
|
60
|
+
|
61
|
+
def path(file)
|
62
|
+
File.join(file)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -1,35 +1,51 @@
|
|
1
1
|
module RESTinPeace
|
2
2
|
class ResponseConverter
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
class UnknownConvertStrategy < RESTinPeace::DefaultError
|
4
|
+
def initialize(klass)
|
5
|
+
super("Don't know how to convert #{klass}")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :body, :klass, :existing_instance
|
10
|
+
|
11
|
+
def initialize(response, instance_or_class)
|
12
|
+
self.body = response.body
|
13
|
+
|
14
|
+
if instance_or_class.respond_to?(:new)
|
15
|
+
self.klass = instance_or_class
|
16
|
+
self.existing_instance = new_instance
|
17
|
+
else
|
18
|
+
self.klass = instance_or_class.class
|
19
|
+
self.existing_instance = instance_or_class
|
20
|
+
end
|
6
21
|
end
|
7
22
|
|
8
23
|
def result
|
9
|
-
case
|
24
|
+
case body.class.to_s
|
10
25
|
when 'Array'
|
11
26
|
convert_from_array
|
12
27
|
when 'Hash'
|
13
28
|
convert_from_hash
|
14
29
|
when 'String'
|
15
|
-
|
30
|
+
body
|
16
31
|
else
|
17
|
-
raise
|
32
|
+
raise UnknownConvertStrategy, body.class
|
18
33
|
end
|
19
34
|
end
|
20
35
|
|
21
36
|
def convert_from_array
|
22
|
-
|
23
|
-
convert_from_hash(entity)
|
37
|
+
body.map do |entity|
|
38
|
+
convert_from_hash(entity, new_instance)
|
24
39
|
end
|
25
40
|
end
|
26
41
|
|
27
|
-
def convert_from_hash(entity =
|
28
|
-
|
42
|
+
def convert_from_hash(entity = body, instance = existing_instance)
|
43
|
+
instance.force_attributes_from_hash entity
|
44
|
+
instance
|
29
45
|
end
|
30
46
|
|
31
|
-
def
|
32
|
-
|
47
|
+
def new_instance
|
48
|
+
klass.new
|
33
49
|
end
|
34
50
|
end
|
35
51
|
end
|
@@ -16,7 +16,7 @@ module RESTinPeace
|
|
16
16
|
tokens.each do |token|
|
17
17
|
param = @params.delete(token.to_sym)
|
18
18
|
raise IncompleteParams, "Unknown parameter for token :#{token} found" unless param
|
19
|
-
@url.
|
19
|
+
@url.sub!(%r{:#{token}}, param.to_s)
|
20
20
|
end
|
21
21
|
@url
|
22
22
|
end
|
data/lib/rest_in_peace.rb
CHANGED
@@ -11,28 +11,57 @@ module RESTinPeace
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def initialize(attributes = {})
|
14
|
-
|
14
|
+
force_attributes_from_hash(attributes)
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
|
17
|
+
def hash_for_updates
|
18
|
+
hash_representation = { id: id }
|
19
|
+
self.class.rip_attributes[:write].map do |key|
|
20
|
+
value = send(key)
|
21
|
+
hash_representation[key] = hash_representation_of_object(value)
|
22
|
+
end
|
23
|
+
if self.class.rip_namespace
|
24
|
+
{ id: id, self.class.rip_namespace => hash_representation }
|
25
|
+
else
|
26
|
+
hash_representation
|
27
|
+
end
|
19
28
|
end
|
20
29
|
|
21
30
|
def update_attributes(attributes)
|
22
|
-
|
31
|
+
attributes.each do |key, value|
|
32
|
+
next unless respond_to?("#{key}=")
|
33
|
+
send("#{key}=", value)
|
34
|
+
end
|
23
35
|
end
|
24
36
|
|
25
|
-
|
37
|
+
def to_h
|
38
|
+
hash_representation = {}
|
39
|
+
self.class.rip_attributes.values.flatten.each do |attr|
|
40
|
+
hash_representation[attr] = send(attr)
|
41
|
+
end
|
42
|
+
hash_representation
|
43
|
+
end
|
26
44
|
|
27
|
-
def
|
28
|
-
|
29
|
-
next unless
|
30
|
-
|
45
|
+
def force_attributes_from_hash(attributes)
|
46
|
+
attributes.each do |key, value|
|
47
|
+
next unless respond_to?(key)
|
48
|
+
if respond_to?("#{key}=")
|
49
|
+
send("#{key}=", value)
|
50
|
+
else
|
51
|
+
instance_variable_set("@#{key}", value)
|
52
|
+
end
|
31
53
|
end
|
32
54
|
end
|
33
55
|
|
56
|
+
def hash_representation_of_object(object)
|
57
|
+
return object.hash_for_updates if object.respond_to?(:hash_for_updates)
|
58
|
+
return object.map { |element| hash_representation_of_object(element) } if object.is_a?(Array)
|
59
|
+
object
|
60
|
+
end
|
61
|
+
|
34
62
|
module ClassMethods
|
35
63
|
attr_accessor :api
|
64
|
+
attr_accessor :rip_namespace
|
36
65
|
|
37
66
|
def rest_in_peace(&block)
|
38
67
|
definition_proxy = RESTinPeace::DefinitionProxy.new(self)
|
@@ -45,5 +74,12 @@ module RESTinPeace
|
|
45
74
|
collection: [],
|
46
75
|
}
|
47
76
|
end
|
77
|
+
|
78
|
+
def rip_attributes
|
79
|
+
@rip_attributes ||= {
|
80
|
+
read: [],
|
81
|
+
write: [],
|
82
|
+
}
|
83
|
+
end
|
48
84
|
end
|
49
85
|
end
|
data/rest-in-peace.gemspec
CHANGED
@@ -17,9 +17,11 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ['lib']
|
19
19
|
|
20
|
+
s.add_runtime_dependency 'activemodel', '> 3.2', '<= 4.1'
|
21
|
+
|
20
22
|
s.add_development_dependency 'rake', '~> 10.0'
|
21
23
|
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'
|
24
|
+
s.add_development_dependency 'guard', '~> 2.6', '>= 2.6.1'
|
25
|
+
s.add_development_dependency 'guard-rspec', '~> 4.2', '>= 4.2.0'
|
26
|
+
s.add_development_dependency 'simplecov', '~> 0.8', '>= 0.8.2'
|
25
27
|
end
|