ddy_remote_resource 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +182 -0
- data/Rakefile +7 -0
- data/lib/extensions/ethon/easy/queryable.rb +36 -0
- data/lib/remote_resource.rb +64 -0
- data/lib/remote_resource/base.rb +126 -0
- data/lib/remote_resource/builder.rb +53 -0
- data/lib/remote_resource/collection.rb +31 -0
- data/lib/remote_resource/connection.rb +24 -0
- data/lib/remote_resource/connection_options.rb +41 -0
- data/lib/remote_resource/http_errors.rb +33 -0
- data/lib/remote_resource/querying/finder_methods.rb +34 -0
- data/lib/remote_resource/querying/persistence_methods.rb +38 -0
- data/lib/remote_resource/request.rb +106 -0
- data/lib/remote_resource/response.rb +69 -0
- data/lib/remote_resource/response_handeling.rb +48 -0
- data/lib/remote_resource/rest.rb +29 -0
- data/lib/remote_resource/url_naming.rb +34 -0
- data/lib/remote_resource/url_naming_determination.rb +39 -0
- data/lib/remote_resource/version.rb +3 -0
- data/remote_resource.gemspec +32 -0
- data/spec/lib/extensions/ethon/easy/queryable_spec.rb +135 -0
- data/spec/lib/remote_resource/base_spec.rb +388 -0
- data/spec/lib/remote_resource/builder_spec.rb +245 -0
- data/spec/lib/remote_resource/collection_spec.rb +148 -0
- data/spec/lib/remote_resource/connection_options_spec.rb +124 -0
- data/spec/lib/remote_resource/connection_spec.rb +61 -0
- data/spec/lib/remote_resource/querying/finder_methods_spec.rb +105 -0
- data/spec/lib/remote_resource/querying/persistence_methods_spec.rb +174 -0
- data/spec/lib/remote_resource/request_spec.rb +594 -0
- data/spec/lib/remote_resource/response_spec.rb +196 -0
- data/spec/lib/remote_resource/rest_spec.rb +98 -0
- data/spec/lib/remote_resource/url_naming_determination_spec.rb +225 -0
- data/spec/lib/remote_resource/url_naming_spec.rb +72 -0
- data/spec/lib/remote_resource/version_spec.rb +8 -0
- data/spec/spec_helper.rb +4 -0
- metadata +242 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: eb2c03a904db96aa05327826f442deb7b2852228
|
4
|
+
data.tar.gz: 4cd7f8229e8a6c7d43d47bc1e4d45c1f3cdb832c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7390456c69a1ca82d053f44e7966f6f160e0b361af64f4670e2000bb5fe5160feafb2499809be857538c8d1b8e03dc96db218da497c3f73be6e97e5401cec7f3
|
7
|
+
data.tar.gz: aa727cdb44ed2933313c2c3681f6fa7e400c66d607be48194c6ade900add787f6754e107885c1fc4a1549a1504641ffa787b12158593b7d634f4ff51418dc0fd
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
remote_resource
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.1
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Jan van der Pas
|
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,182 @@
|
|
1
|
+
# RemoteResource
|
2
|
+
|
3
|
+
RemoteResource is a gem to use resources with REST services.
|
4
|
+
|
5
|
+
## Goal of RemoteResource
|
6
|
+
|
7
|
+
To replace `ActiveResource` by providing a dynamic and customizable API interface for REST services.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'remote_resource', git: 'git@lab.digidentity.eu:jvanderpas/remote_resource.git'
|
15
|
+
```
|
16
|
+
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
Simply include the `RemoteResource::Base` module in the class you want to enable for the REST services.
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
class ContactPerson
|
24
|
+
include RemoteResource::Base
|
25
|
+
|
26
|
+
self.site = "https://www.myapp.com"
|
27
|
+
self.version = '/v2'
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
### Options
|
32
|
+
|
33
|
+
You can set a few options for the `RemoteResource` enabled class.
|
34
|
+
|
35
|
+
|
36
|
+
#### Base URL options (`base_url`)
|
37
|
+
|
38
|
+
The `base_url` is constructed from the `.site`, `.version`, `.path_prefix`, `.path_postfix`, `.collection`, and `.collection_name` options. The `.collection_name` is automatically constructed from the relative class name.
|
39
|
+
|
40
|
+
We will use the `ContactPerson` class for these examples, with the `.collection_name` of `'contact_person'`:
|
41
|
+
|
42
|
+
* `.site`: This sets the URL which should be used to construct the `base_url`.
|
43
|
+
* *Example:* `.site = "https://www.myapp.com"`
|
44
|
+
* *`base_url`:* `https://www.myapp.com/contact_person`
|
45
|
+
* `.version`: This sets the API version for the path, after the `.site` and before the `.path_prefix` that is used to construct the `base_url`.
|
46
|
+
* *Example:* `.version = "/api/v2"`
|
47
|
+
* *`base_url`:* `https://www.myapp.com/api/v2/contact_person`
|
48
|
+
* `.path_prefix`: This sets the prefix for the path, after the `.version` and before the `.collection_name` that is used to construct the `base_url`.
|
49
|
+
* *Example:* `.path_prefix = "/registration"`
|
50
|
+
* *`base_url`:* `https://www.myapp.com/registration/contact_person`
|
51
|
+
* `.path_postfix`: This sets the postfix for the path, after the `.collection_name` that is used to construct the `base_url`.
|
52
|
+
* *Example:* `.path_postfix = "/new"`
|
53
|
+
* *`base_url`:* `https://www.myapp.com/contact_person/new`
|
54
|
+
* `.collection`: This toggles the pluralization of the `collection_name` that is used to construct the `base_url`.
|
55
|
+
* *Default:* `false`
|
56
|
+
* *Example:* `.collection = true`
|
57
|
+
* *`base_url`:* `https://www.myapp.com/contact_persons`
|
58
|
+
* `.collection_name`: This sets the `collection_name` that is used to construct the `base_url`.
|
59
|
+
* *Example:* `.collection_name = "company"`
|
60
|
+
* *`base_url`:* `https://www.myapp.com/company`
|
61
|
+
|
62
|
+
**override**
|
63
|
+
|
64
|
+
To override the `base_url` completely, you can use the `base_url` option. This option should be passed into the `connection_options` hash when making a request:
|
65
|
+
|
66
|
+
* `base_url`: This sets the `base_url`. *note: this does not override the `.content_type` option*
|
67
|
+
* *Example:* `{ base_url: "https://api.foo.com/v1" }`
|
68
|
+
* *`base_url`:* `https://api.foo.com/v1`
|
69
|
+
|
70
|
+
|
71
|
+
#### Request options
|
72
|
+
|
73
|
+
Apart from the options which manipulate the `base_url`, there are some more:
|
74
|
+
|
75
|
+
* `.extra_headers`: This sets the extra headers which are merged with the `.default_headers` and should be used for the request. *note: you can't set the `.default_headers`*
|
76
|
+
* *Default:* `.default_headers`: `{ "Content-Type" => "application/json" }`
|
77
|
+
* *Example:* `.extra_headers = { "X-Locale" => "en" }`
|
78
|
+
* `.headers`: `{ "Content-Type" => "application/json", "X-Locale" => "en" }`
|
79
|
+
* `.content_type`: This sets the content-type which should be used for the request URL. *note: this is appended to the `base_url`*
|
80
|
+
* *Default:* `".json"`
|
81
|
+
* *`base_url`:* `https://www.myapp.com/contact_person`
|
82
|
+
* *Example:* `.content-type = ".json"`
|
83
|
+
* *Request URL:* `https://www.myapp.com/contact_person.json`
|
84
|
+
|
85
|
+
#### Body and params options
|
86
|
+
|
87
|
+
Last but not least, you can pack the request body or params in a `root_element`:
|
88
|
+
|
89
|
+
* `.root_element`: This sets the `root_element` in which the request body or params should be 'packed' for the request.
|
90
|
+
* *Params:* `{ email_address: "foo@bar.com", phone_number: "0031701234567" }`
|
91
|
+
* *Example:* `.root_element = :contact_person`
|
92
|
+
* *Packed params:* `{ "contact_person" => { email_address: "foo@bar.com", phone_number: "0031701234567" } }`
|
93
|
+
|
94
|
+
|
95
|
+
### Querying
|
96
|
+
|
97
|
+
#### Finder methods
|
98
|
+
|
99
|
+
You can use the `.find`, `.find_by` and `.all` class methods:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
# use the `id` as argument
|
103
|
+
ContactPerson.find(12)
|
104
|
+
|
105
|
+
# use a conditions `Hash` as argument
|
106
|
+
ContactPerson.find_by(username: 'foobar')
|
107
|
+
|
108
|
+
# just the whole collection
|
109
|
+
ContactPerson.all
|
110
|
+
```
|
111
|
+
|
112
|
+
To override the given `options`, you can pass in a `connection_options` hash:
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
connection_options: { root_element: :contact_person, headers: { "X-Locale" => "nl" } }
|
116
|
+
|
117
|
+
# use the `id` as argument
|
118
|
+
ContactPerson.find(12, connection_options)
|
119
|
+
|
120
|
+
# use a conditions `Hash` as argument
|
121
|
+
ContactPerson.find_by((username: 'foobar'), connection_options)
|
122
|
+
```
|
123
|
+
|
124
|
+
#### Persistence methods
|
125
|
+
|
126
|
+
You can use the `.create` class method and the `#save` instance method:
|
127
|
+
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
# .create
|
131
|
+
ContactPerson.create(username: 'aapmies', first_name: 'Mies')
|
132
|
+
|
133
|
+
# #save
|
134
|
+
contact_person = ContactPerson.new(id: 12)
|
135
|
+
contact_person.username = 'aapmies'
|
136
|
+
contact_person.save
|
137
|
+
```
|
138
|
+
To override the given `options`, you can pass in a `connection_options` hash:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
connection_options: { root_element: :contact_person, headers: { "X-Locale" => "nl" } }
|
142
|
+
|
143
|
+
contact_person = ContactPerson.new(id: 12)
|
144
|
+
contact_person.username = 'aapmies'
|
145
|
+
contact_person.save(connection_options)
|
146
|
+
```
|
147
|
+
|
148
|
+
#### REST methods
|
149
|
+
|
150
|
+
You can use the `.get`, `.put`, `.patch` and `.post` class methods and the `
|
151
|
+
#get`, `#put`, `#patch` and `#post` instance methods.
|
152
|
+
|
153
|
+
|
154
|
+
#### With a `connection_options` block
|
155
|
+
|
156
|
+
You can make your requests in a `connection_options` block. All the requests in the block will use the passed in `connection_options`.
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
ContactPerson.with_connection_options(headers: { "X-Locale" => "en" }) do
|
160
|
+
ContactPerson.find_by(username: 'foobar')
|
161
|
+
ContactPerson.find_by(username: 'aapmies', (content-type: '.xml'))
|
162
|
+
ContactPerson.find_by((username: 'viking'), (headers: { "X-Locale" => "nl" }))
|
163
|
+
end
|
164
|
+
```
|
165
|
+
|
166
|
+
This will result in two request which use the `{ headers: { "X-Locale" => "en" } }` as `connection_options`, one which will use the `{ headers: { "X-Locale" => "nl" } }` as `connection_options`. And one that will append `.xml` to the request URL.
|
167
|
+
|
168
|
+
### Responses
|
169
|
+
|
170
|
+
The response body of the request will be 'unpacked' from the `root_element` if necessary and parsed. The resulting `Hash` will be used to assign the attributes of the resource.
|
171
|
+
|
172
|
+
However if you want to access the response of the request, you can use the `#_response` method. This returns a `RemoteResource::Response` object with the `#response_body` and `#response_code` methods.
|
173
|
+
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
contact_person = ContactPerson.find_by((username: 'foobar'), connection_options)
|
177
|
+
contact_person._response #=> RemoteResource::Response
|
178
|
+
contact_person._response.response_code #=> 200
|
179
|
+
contact_person._response.response_body #=> '{"username":"foobar", "name":"Foo", "surname":"Bar"}'
|
180
|
+
```
|
181
|
+
|
182
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# This is a monkey patch to pass Array
|
2
|
+
# params without an index.
|
3
|
+
#
|
4
|
+
# The problem is described in typhoeus/typhoeus issue #320:
|
5
|
+
# https://github.com/typhoeus/typhoeus/issues/320
|
6
|
+
#
|
7
|
+
# The fix is described in dylanfareed/ethon commit 548033a:
|
8
|
+
# https://github.com/dylanfareed/ethon/commit/548033a8557a48203b7d49f3f98812bd79bc05e4
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'ethon'
|
12
|
+
|
13
|
+
module Ethon
|
14
|
+
class Easy
|
15
|
+
module Queryable
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def recursively_generate_pairs(h, prefix, pairs)
|
20
|
+
case h
|
21
|
+
when Hash
|
22
|
+
h.each_pair do |k,v|
|
23
|
+
key = prefix.nil? ? k : "#{prefix}[#{k}]"
|
24
|
+
pairs_for(v, key, pairs)
|
25
|
+
end
|
26
|
+
when Array
|
27
|
+
h.each_with_index do |v, i|
|
28
|
+
key = "#{prefix}[]"
|
29
|
+
pairs_for(v, key, pairs)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
require 'active_model'
|
3
|
+
require 'virtus'
|
4
|
+
require 'typhoeus'
|
5
|
+
|
6
|
+
require_relative 'extensions/ethon/easy/queryable'
|
7
|
+
|
8
|
+
require 'remote_resource/version'
|
9
|
+
require 'remote_resource/base'
|
10
|
+
require 'remote_resource/collection'
|
11
|
+
require 'remote_resource/url_naming_determination'
|
12
|
+
require 'remote_resource/url_naming'
|
13
|
+
require 'remote_resource/connection'
|
14
|
+
require 'remote_resource/builder'
|
15
|
+
require 'remote_resource/connection_options'
|
16
|
+
require 'remote_resource/rest'
|
17
|
+
require 'remote_resource/response'
|
18
|
+
require 'remote_resource/querying/finder_methods'
|
19
|
+
require 'remote_resource/querying/persistence_methods'
|
20
|
+
require 'remote_resource/http_errors'
|
21
|
+
require 'remote_resource/request'
|
22
|
+
|
23
|
+
|
24
|
+
module RemoteResource
|
25
|
+
RemoteResourceError = Class.new StandardError
|
26
|
+
|
27
|
+
RESTActionUnknown = Class.new RemoteResourceError # REST action
|
28
|
+
|
29
|
+
class HTTPError < RemoteResourceError # HTTP errors
|
30
|
+
|
31
|
+
def initialize(response)
|
32
|
+
if response.try :response_code
|
33
|
+
super "with HTTP response status: #{response.response_code} and response: #{response}"
|
34
|
+
else
|
35
|
+
super "with HTTP response: #{response}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
HTTPRedirectionError = Class.new HTTPError # HTTP 3xx
|
41
|
+
HTTPClientError = Class.new HTTPError # HTTP 4xx
|
42
|
+
HTTPServerError = Class.new HTTPError # HTTP 5xx
|
43
|
+
|
44
|
+
HTTPBadRequest = Class.new HTTPClientError # HTTP 400
|
45
|
+
HTTPUnauthorized = Class.new HTTPClientError # HTTP 401
|
46
|
+
HTTPForbidden = Class.new HTTPClientError # HTTP 403
|
47
|
+
HTTPNotFound = Class.new HTTPClientError # HTTP 404
|
48
|
+
HTTPMethodNotAllowed = Class.new HTTPClientError # HTTP 405
|
49
|
+
HTTPNotAcceptable = Class.new HTTPClientError # HTTP 406
|
50
|
+
HTTPRequestTimeout = Class.new HTTPClientError # HTTP 408
|
51
|
+
HTTPConflict = Class.new HTTPClientError # HTTP 409
|
52
|
+
HTTPGone = Class.new HTTPClientError # HTTP 410
|
53
|
+
HTTPTeapot = Class.new HTTPClientError # HTTP 418
|
54
|
+
|
55
|
+
NginxClientError = Class.new HTTPClientError # HTTP errors used in Nginx
|
56
|
+
|
57
|
+
HTTPNoResponse = Class.new NginxClientError # HTTP 444
|
58
|
+
HTTPRequestHeaderTooLarge = Class.new NginxClientError # HTTP 494
|
59
|
+
HTTPCertError = Class.new NginxClientError # HTTP 495
|
60
|
+
HTTPNoCert = Class.new NginxClientError # HTTP 496
|
61
|
+
HTTPToHTTPS = Class.new NginxClientError # HTTP 497
|
62
|
+
HTTPClientClosedRequest = Class.new NginxClientError # HTTP 499
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module RemoteResource
|
2
|
+
module Base
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
OPTIONS = [:base_url, :site, :headers, :version, :path_prefix, :path_postfix, :content_type, :collection, :collection_name, :root_element]
|
6
|
+
|
7
|
+
included do
|
8
|
+
include Virtus.model
|
9
|
+
extend ActiveModel::Naming
|
10
|
+
extend ActiveModel::Translation
|
11
|
+
include ActiveModel::Conversion
|
12
|
+
include ActiveModel::Validations
|
13
|
+
|
14
|
+
include RemoteResource::Builder
|
15
|
+
include RemoteResource::UrlNaming
|
16
|
+
include RemoteResource::Connection
|
17
|
+
include RemoteResource::REST
|
18
|
+
|
19
|
+
include RemoteResource::Querying::FinderMethods
|
20
|
+
include RemoteResource::Querying::PersistenceMethods
|
21
|
+
|
22
|
+
attr_accessor :_response
|
23
|
+
|
24
|
+
attribute :id
|
25
|
+
class_attribute :root_element, instance_accessor: false
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.global_headers=(headers)
|
29
|
+
Thread.current[:global_headers] = headers
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.global_headers
|
33
|
+
Thread.current[:global_headers] ||= {}
|
34
|
+
end
|
35
|
+
|
36
|
+
module ClassMethods
|
37
|
+
|
38
|
+
def connection_options
|
39
|
+
Thread.current[connection_options_thread_name] ||= RemoteResource::ConnectionOptions.new(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
def threaded_connection_options
|
43
|
+
Thread.current[threaded_connection_options_thread_name] ||= {}
|
44
|
+
end
|
45
|
+
|
46
|
+
def with_connection_options(connection_options = {})
|
47
|
+
begin
|
48
|
+
threaded_connection_options
|
49
|
+
Thread.current[threaded_connection_options_thread_name].merge! connection_options
|
50
|
+
yield
|
51
|
+
ensure
|
52
|
+
Thread.current[threaded_connection_options_thread_name] = nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def threaded_connection_options_thread_name
|
59
|
+
"remote_resource.#{_module_name}.threaded_connection_options"
|
60
|
+
end
|
61
|
+
|
62
|
+
def connection_options_thread_name
|
63
|
+
"remote_resource.#{_module_name}.connection_options"
|
64
|
+
end
|
65
|
+
|
66
|
+
def _module_name
|
67
|
+
self.name.to_s.demodulize.underscore.downcase
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def connection_options
|
72
|
+
@connection_options ||= RemoteResource::ConnectionOptions.new(self.class)
|
73
|
+
end
|
74
|
+
|
75
|
+
def empty?
|
76
|
+
_response.try(:sanitized_response_body).blank?
|
77
|
+
end
|
78
|
+
|
79
|
+
def persisted?
|
80
|
+
id.present?
|
81
|
+
end
|
82
|
+
|
83
|
+
def new_record?
|
84
|
+
!persisted?
|
85
|
+
end
|
86
|
+
|
87
|
+
def success?
|
88
|
+
_response.success? && !errors?
|
89
|
+
end
|
90
|
+
|
91
|
+
def errors?
|
92
|
+
errors.present?
|
93
|
+
end
|
94
|
+
|
95
|
+
def handle_response(response)
|
96
|
+
if response.unprocessable_entity?
|
97
|
+
rebuild_resource_from_response(response).tap do |resource|
|
98
|
+
resource.assign_errors_from_response response
|
99
|
+
end
|
100
|
+
else
|
101
|
+
rebuild_resource_from_response(response)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def assign_response(response)
|
106
|
+
@_response = response
|
107
|
+
end
|
108
|
+
|
109
|
+
def assign_errors_from_response(response)
|
110
|
+
assign_errors response.error_messages_response_body
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def assign_errors(error_messages)
|
116
|
+
return unless error_messages.respond_to? :each
|
117
|
+
|
118
|
+
error_messages.each do |attribute, attribute_errors|
|
119
|
+
attribute_errors.each do |error|
|
120
|
+
self.errors.add attribute, error
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|