ddy_remote_resource 0.4.2
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 +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
|