rest-in-peace 1.4.0 → 2.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 +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
|