simple_jsonapi_client 0.1.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 +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Dockerfile +9 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +264 -0
- data/Rakefile +8 -0
- data/bin/console +3 -0
- data/bin/development_start +12 -0
- data/bin/rails +3 -0
- data/bin/setup +7 -0
- data/bin/wait_for_it +178 -0
- data/docker-compose.yml +50 -0
- data/lib/simple_jsonapi_client.rb +5 -0
- data/lib/simple_jsonapi_client/base.rb +325 -0
- data/lib/simple_jsonapi_client/error.rb +6 -0
- data/lib/simple_jsonapi_client/errors/api_error.rb +84 -0
- data/lib/simple_jsonapi_client/redirection/fetch_all.rb +43 -0
- data/lib/simple_jsonapi_client/redirection/proxy.rb +54 -0
- data/lib/simple_jsonapi_client/relationships/array_data_relationship.rb +19 -0
- data/lib/simple_jsonapi_client/relationships/array_link_relationship.rb +15 -0
- data/lib/simple_jsonapi_client/relationships/data_relationship_proxy.rb +30 -0
- data/lib/simple_jsonapi_client/relationships/has_many_relationship.rb +16 -0
- data/lib/simple_jsonapi_client/relationships/has_one_relationship.rb +16 -0
- data/lib/simple_jsonapi_client/relationships/link_relationship_proxy.rb +19 -0
- data/lib/simple_jsonapi_client/relationships/relationship.rb +34 -0
- data/lib/simple_jsonapi_client/relationships/singular_data_relationship.rb +17 -0
- data/lib/simple_jsonapi_client/relationships/singular_link_relationship.rb +13 -0
- data/lib/simple_jsonapi_client/version.rb +3 -0
- data/simple_jsonapi_client.gemspec +31 -0
- metadata +180 -0
data/bin/rails
ADDED
data/bin/setup
ADDED
data/bin/wait_for_it
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
# Use this script to test if a given TCP host/port are available
|
3
|
+
# Credit: https://github.com/vishnubob/wait-for-it/blob/db049716e42767d39961e95dd9696103dca813f1/wait-for-it.sh
|
4
|
+
|
5
|
+
cmdname=$(basename $0)
|
6
|
+
|
7
|
+
echoerr() { if [[ $QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
|
8
|
+
|
9
|
+
usage()
|
10
|
+
{
|
11
|
+
cat << USAGE >&2
|
12
|
+
Usage:
|
13
|
+
$cmdname host:port [-s] [-t timeout] [-- command args]
|
14
|
+
-h HOST | --host=HOST Host or IP under test
|
15
|
+
-p PORT | --port=PORT TCP port under test
|
16
|
+
Alternatively, you specify the host and port as host:port
|
17
|
+
-s | --strict Only execute subcommand if the test succeeds
|
18
|
+
-q | --quiet Don't output any status messages
|
19
|
+
-t TIMEOUT | --timeout=TIMEOUT
|
20
|
+
Timeout in seconds, zero for no timeout
|
21
|
+
-- COMMAND ARGS Execute command with args after the test finishes
|
22
|
+
USAGE
|
23
|
+
exit 1
|
24
|
+
}
|
25
|
+
|
26
|
+
wait_for()
|
27
|
+
{
|
28
|
+
if [[ $TIMEOUT -gt 0 ]]; then
|
29
|
+
echoerr "$cmdname: waiting $TIMEOUT seconds for $HOST:$PORT"
|
30
|
+
else
|
31
|
+
echoerr "$cmdname: waiting for $HOST:$PORT without a timeout"
|
32
|
+
fi
|
33
|
+
start_ts=$(date +%s)
|
34
|
+
while :
|
35
|
+
do
|
36
|
+
if [[ $ISBUSY -eq 1 ]]; then
|
37
|
+
nc -z $HOST $PORT
|
38
|
+
result=$?
|
39
|
+
else
|
40
|
+
(echo > /dev/tcp/$HOST/$PORT) >/dev/null 2>&1
|
41
|
+
result=$?
|
42
|
+
fi
|
43
|
+
if [[ $result -eq 0 ]]; then
|
44
|
+
end_ts=$(date +%s)
|
45
|
+
echoerr "$cmdname: $HOST:$PORT is available after $((end_ts - start_ts)) seconds"
|
46
|
+
break
|
47
|
+
fi
|
48
|
+
sleep 1
|
49
|
+
done
|
50
|
+
return $result
|
51
|
+
}
|
52
|
+
|
53
|
+
wait_for_wrapper()
|
54
|
+
{
|
55
|
+
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
|
56
|
+
if [[ $QUIET -eq 1 ]]; then
|
57
|
+
timeout $BUSYTIMEFLAG $TIMEOUT $0 --quiet --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
|
58
|
+
else
|
59
|
+
timeout $BUSYTIMEFLAG $TIMEOUT $0 --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
|
60
|
+
fi
|
61
|
+
PID=$!
|
62
|
+
trap "kill -INT -$PID" INT
|
63
|
+
wait $PID
|
64
|
+
RESULT=$?
|
65
|
+
if [[ $RESULT -ne 0 ]]; then
|
66
|
+
echoerr "$cmdname: timeout occurred after waiting $TIMEOUT seconds for $HOST:$PORT"
|
67
|
+
fi
|
68
|
+
return $RESULT
|
69
|
+
}
|
70
|
+
|
71
|
+
# process arguments
|
72
|
+
while [[ $# -gt 0 ]]
|
73
|
+
do
|
74
|
+
case "$1" in
|
75
|
+
*:* )
|
76
|
+
hostport=(${1//:/ })
|
77
|
+
HOST=${hostport[0]}
|
78
|
+
PORT=${hostport[1]}
|
79
|
+
shift 1
|
80
|
+
;;
|
81
|
+
--child)
|
82
|
+
CHILD=1
|
83
|
+
shift 1
|
84
|
+
;;
|
85
|
+
-q | --quiet)
|
86
|
+
QUIET=1
|
87
|
+
shift 1
|
88
|
+
;;
|
89
|
+
-s | --strict)
|
90
|
+
STRICT=1
|
91
|
+
shift 1
|
92
|
+
;;
|
93
|
+
-h)
|
94
|
+
HOST="$2"
|
95
|
+
if [[ $HOST == "" ]]; then break; fi
|
96
|
+
shift 2
|
97
|
+
;;
|
98
|
+
--host=*)
|
99
|
+
HOST="${1#*=}"
|
100
|
+
shift 1
|
101
|
+
;;
|
102
|
+
-p)
|
103
|
+
PORT="$2"
|
104
|
+
if [[ $PORT == "" ]]; then break; fi
|
105
|
+
shift 2
|
106
|
+
;;
|
107
|
+
--port=*)
|
108
|
+
PORT="${1#*=}"
|
109
|
+
shift 1
|
110
|
+
;;
|
111
|
+
-t)
|
112
|
+
TIMEOUT="$2"
|
113
|
+
if [[ $TIMEOUT == "" ]]; then break; fi
|
114
|
+
shift 2
|
115
|
+
;;
|
116
|
+
--timeout=*)
|
117
|
+
TIMEOUT="${1#*=}"
|
118
|
+
shift 1
|
119
|
+
;;
|
120
|
+
--)
|
121
|
+
shift
|
122
|
+
CLI=("$@")
|
123
|
+
break
|
124
|
+
;;
|
125
|
+
--help)
|
126
|
+
usage
|
127
|
+
;;
|
128
|
+
*)
|
129
|
+
echoerr "Unknown argument: $1"
|
130
|
+
usage
|
131
|
+
;;
|
132
|
+
esac
|
133
|
+
done
|
134
|
+
|
135
|
+
if [[ "$HOST" == "" || "$PORT" == "" ]]; then
|
136
|
+
echoerr "Error: you need to provide a host and port to test."
|
137
|
+
usage
|
138
|
+
fi
|
139
|
+
|
140
|
+
TIMEOUT=${TIMEOUT:-15}
|
141
|
+
STRICT=${STRICT:-0}
|
142
|
+
CHILD=${CHILD:-0}
|
143
|
+
QUIET=${QUIET:-0}
|
144
|
+
|
145
|
+
# check to see if timeout is from busybox?
|
146
|
+
# check to see if timeout is from busybox?
|
147
|
+
TIMEOUT_PATH=$(realpath $(which timeout))
|
148
|
+
if [[ $TIMEOUT_PATH =~ "busybox" ]]; then
|
149
|
+
ISBUSY=1
|
150
|
+
BUSYTIMEFLAG="-t"
|
151
|
+
else
|
152
|
+
ISBUSY=0
|
153
|
+
BUSYTIMEFLAG=""
|
154
|
+
fi
|
155
|
+
|
156
|
+
if [[ $CHILD -gt 0 ]]; then
|
157
|
+
wait_for
|
158
|
+
RESULT=$?
|
159
|
+
exit $RESULT
|
160
|
+
else
|
161
|
+
if [[ $TIMEOUT -gt 0 ]]; then
|
162
|
+
wait_for_wrapper
|
163
|
+
RESULT=$?
|
164
|
+
else
|
165
|
+
wait_for
|
166
|
+
RESULT=$?
|
167
|
+
fi
|
168
|
+
fi
|
169
|
+
|
170
|
+
if [[ $CLI != "" ]]; then
|
171
|
+
if [[ $RESULT -ne 0 && $STRICT -eq 1 ]]; then
|
172
|
+
echoerr "$cmdname: strict mode, refusing to execute subprocess"
|
173
|
+
exit $RESULT
|
174
|
+
fi
|
175
|
+
exec "${CLI[@]}"
|
176
|
+
else
|
177
|
+
exit $RESULT
|
178
|
+
fi
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
version: '3'
|
2
|
+
services:
|
3
|
+
db:
|
4
|
+
image: postgres
|
5
|
+
spec:
|
6
|
+
build: .
|
7
|
+
entrypoint: bin/wait_for_it jsonapi_app_spec:3001 -t 30 --
|
8
|
+
command: bundle exec rspec
|
9
|
+
volumes:
|
10
|
+
- .:/simple_jsonapi_client
|
11
|
+
depends_on:
|
12
|
+
- jsonapi_app_spec
|
13
|
+
environment:
|
14
|
+
API_URL: jsonapi_app_spec
|
15
|
+
API_PORT: 3001
|
16
|
+
jsonapi_app_spec:
|
17
|
+
build: ./spec/jsonapi_app
|
18
|
+
entrypoint: bin/wait_for_it db:5432 -t 30 --
|
19
|
+
volumes:
|
20
|
+
- ./spec/jsonapi_app:/jsonapi_app
|
21
|
+
- /jsonapi_app/tmp/
|
22
|
+
command: bundle exec rails s -p 3001 -b '0.0.0.0'
|
23
|
+
ports:
|
24
|
+
- "3001:3001"
|
25
|
+
environment:
|
26
|
+
API_PORT: 3001
|
27
|
+
depends_on:
|
28
|
+
- db
|
29
|
+
console:
|
30
|
+
build: .
|
31
|
+
entrypoint: bin/wait_for_it jsonapi_app_console:3002 -t 30 --
|
32
|
+
command: bin/development_start
|
33
|
+
volumes:
|
34
|
+
- .:/simple_jsonapi_client
|
35
|
+
depends_on:
|
36
|
+
- jsonapi_app_console
|
37
|
+
environment:
|
38
|
+
API_URL: jsonapi_app_console
|
39
|
+
API_PORT: 3002
|
40
|
+
jsonapi_app_console:
|
41
|
+
build: ./spec/jsonapi_app
|
42
|
+
entrypoint: bin/wait_for_it db:5432 -t 30 --
|
43
|
+
volumes:
|
44
|
+
- ./spec/jsonapi_app:/jsonapi_app
|
45
|
+
- /jsonapi_app/tmp/
|
46
|
+
command: bundle exec rails s -p 3002 -b '0.0.0.0'
|
47
|
+
ports:
|
48
|
+
- "3002:3002"
|
49
|
+
depends_on:
|
50
|
+
- db
|
@@ -0,0 +1,325 @@
|
|
1
|
+
require 'active_support/core_ext/hash/keys'
|
2
|
+
require 'active_support/core_ext/hash/transform_values'
|
3
|
+
require 'simple_jsonapi_client/error'
|
4
|
+
require 'simple_jsonapi_client/relationships/relationship'
|
5
|
+
require 'simple_jsonapi_client/redirection/fetch_all'
|
6
|
+
|
7
|
+
module SimpleJSONAPIClient
|
8
|
+
class Base
|
9
|
+
class << self
|
10
|
+
def relationships
|
11
|
+
@relationships ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def _attributes
|
15
|
+
@_attributes ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def attributes(*attrs)
|
19
|
+
attrs.each do |attr|
|
20
|
+
define_method(attr) { attributes[attr] }
|
21
|
+
define_method("#{attr}=") { |x| attributes[attr] = x }
|
22
|
+
_attributes[attr] = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def meta(*attrs)
|
27
|
+
attrs.each do |attr|
|
28
|
+
define_method(attr) { meta[attr] }
|
29
|
+
define_method("#{attr}=") { |x| meta[attr] = x }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_many(relationship_name, opts)
|
34
|
+
model_class = opts.fetch(:class) { opts.fetch(:class_name) }
|
35
|
+
define_relationship_methods!(relationship_name)
|
36
|
+
relationships[relationship_name.to_sym] =
|
37
|
+
Relationships::HasManyRelationship.new(model_class)
|
38
|
+
end
|
39
|
+
|
40
|
+
def has_one(relationship_name, opts)
|
41
|
+
define_relationship_methods!(relationship_name)
|
42
|
+
model_class = opts.fetch(:class) { opts.fetch(:class_name) }
|
43
|
+
relationships[relationship_name.to_sym] =
|
44
|
+
Relationships::HasOneRelationship.new(model_class)
|
45
|
+
end
|
46
|
+
|
47
|
+
def fetch(opts)
|
48
|
+
operation(:fetch_request, :singular, opts)
|
49
|
+
end
|
50
|
+
|
51
|
+
def fetch_all(opts)
|
52
|
+
Redirection::FetchAll.new(opts) do |request_opts|
|
53
|
+
operation(:fetch_all_request, :plural, request_opts)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def create(opts)
|
58
|
+
operation(:create_request, :singular, opts)
|
59
|
+
end
|
60
|
+
|
61
|
+
def update(opts)
|
62
|
+
operation(:update_request, :singular, opts)
|
63
|
+
end
|
64
|
+
|
65
|
+
def delete(opts)
|
66
|
+
operation(:delete_request, :empty, opts)
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
def model_from(record, included, connection, context = nil)
|
71
|
+
return unless record
|
72
|
+
new(
|
73
|
+
meta: record['meta'],
|
74
|
+
id: record['id'],
|
75
|
+
attributes: record.fetch('attributes', {}),
|
76
|
+
relationships: record.fetch('relationships', {}),
|
77
|
+
context: context,
|
78
|
+
included: included,
|
79
|
+
connection: connection
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
def interpreted_included(records, included)
|
84
|
+
{}.tap do |included_hash|
|
85
|
+
include_records(included_hash, records)
|
86
|
+
include_records(included_hash, included)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def include_records(included_hash, records)
|
91
|
+
records.to_a.each do |record|
|
92
|
+
included_hash[{ 'id' => record['id'], 'type' => record['type'] }] = record
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def template(id: nil, attributes:, relationships: {})
|
97
|
+
data = {
|
98
|
+
type: self::TYPE,
|
99
|
+
attributes: attributes,
|
100
|
+
relationships: interpreted_relationships(relationships)
|
101
|
+
}
|
102
|
+
data[:id] = id if id
|
103
|
+
{ data: data }
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def define_relationship_methods!(relationship_name)
|
109
|
+
define_method(relationship_name) { relationships[relationship_name] }
|
110
|
+
define_method("#{relationship_name}=") { |x| relationships[relationship_name] = x }
|
111
|
+
end
|
112
|
+
|
113
|
+
def operation(request_method, response_type, opts)
|
114
|
+
response = send(request_method, opts)
|
115
|
+
handling_error(response) do
|
116
|
+
send(:"interpret_#{response_type}_response", response, opts[:connection])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def create_request(connection:,
|
121
|
+
url_opts: {},
|
122
|
+
url: self::COLLECTION_URL % url_opts,
|
123
|
+
attributes: {},
|
124
|
+
relationships: {},
|
125
|
+
**attrs)
|
126
|
+
attributes, relationships = extract_attrs(attrs, attributes, relationships)
|
127
|
+
body = template(attributes: attributes, relationships: relationships)
|
128
|
+
connection.post(url, body)
|
129
|
+
end
|
130
|
+
|
131
|
+
def update_request(connection:,
|
132
|
+
id:,
|
133
|
+
url_opts: {},
|
134
|
+
attributes: {},
|
135
|
+
relationships: {},
|
136
|
+
**attrs)
|
137
|
+
attributes, relationships = extract_attrs(attrs, attributes, relationships)
|
138
|
+
connection.patch(self::INDIVIDUAL_URL % url_opts) do |request|
|
139
|
+
request.body = template(
|
140
|
+
id: id,
|
141
|
+
attributes: attributes,
|
142
|
+
relationships: relationships
|
143
|
+
)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def delete_request(connection:, url_opts: {})
|
148
|
+
connection.delete(self::INDIVIDUAL_URL % url_opts)
|
149
|
+
end
|
150
|
+
|
151
|
+
def fetch_request(connection:,
|
152
|
+
url_opts: {},
|
153
|
+
filter_opts: {},
|
154
|
+
url: self::INDIVIDUAL_URL % url_opts,
|
155
|
+
includes: [])
|
156
|
+
params = {}
|
157
|
+
params[:include] = includes.join(',') unless includes.empty?
|
158
|
+
params[:filter] = filter_opts unless filter_opts.empty?
|
159
|
+
connection.get(url, params)
|
160
|
+
end
|
161
|
+
|
162
|
+
def fetch_all_request(connection:,
|
163
|
+
url_opts: {},
|
164
|
+
url: self::COLLECTION_URL % url_opts,
|
165
|
+
filter_opts: {},
|
166
|
+
includes: [])
|
167
|
+
params = {}
|
168
|
+
params[:include] = includes.join(',') unless includes.empty?
|
169
|
+
params[:filter] = filter_opts unless filter_opts.empty?
|
170
|
+
connection.get(url, params)
|
171
|
+
end
|
172
|
+
|
173
|
+
def extract_attrs(attrs, attributes, relationships)
|
174
|
+
attrs.each do |attr, value|
|
175
|
+
if _attributes.key?(attr)
|
176
|
+
attributes[attr] = value
|
177
|
+
elsif self.relationships.key?(attr)
|
178
|
+
relationships[attr] = value
|
179
|
+
else
|
180
|
+
raise ArgumentError, %{Invalid attribute "#{attr}"}
|
181
|
+
end
|
182
|
+
end
|
183
|
+
[attributes, relationships]
|
184
|
+
end
|
185
|
+
|
186
|
+
def interpret_singular_response(response, connection)
|
187
|
+
body = response.body
|
188
|
+
record = body['data']
|
189
|
+
records = [record].compact
|
190
|
+
included = interpreted_included(records, body['included'])
|
191
|
+
model_from(record, included, connection, response)
|
192
|
+
end
|
193
|
+
|
194
|
+
def interpret_plural_response(response, connection)
|
195
|
+
body = response.body
|
196
|
+
records = body['data']
|
197
|
+
included = interpreted_included(records, body['included'])
|
198
|
+
{
|
199
|
+
'links' => body['links'],
|
200
|
+
'data' => records.map { |record|
|
201
|
+
model_from(record, included, connection, response)
|
202
|
+
}
|
203
|
+
}
|
204
|
+
end
|
205
|
+
|
206
|
+
def interpret_empty_response(response, connection)
|
207
|
+
end
|
208
|
+
|
209
|
+
def interpreted_relationships(relationships)
|
210
|
+
relationships.transform_values { |value|
|
211
|
+
if (relationship = relationship_from(value))
|
212
|
+
{ data: relationship }
|
213
|
+
end
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
def relationship_from(value)
|
218
|
+
if value.respond_to?(:to_relationship)
|
219
|
+
value.to_relationship
|
220
|
+
elsif value.respond_to?(:map)
|
221
|
+
value.map(&:to_relationship)
|
222
|
+
elsif !value.nil?
|
223
|
+
raise ArgumentError, "#{value} cannot be converted to relationship!"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def handling_error(response)
|
228
|
+
if response.success?
|
229
|
+
yield
|
230
|
+
else
|
231
|
+
raise ::SimpleJSONAPIClient::Errors::APIError.generate(response)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
attr_reader :id, :context
|
237
|
+
|
238
|
+
def initialize(meta: nil, id:, attributes: nil, relationships: nil, included: {}, connection:, context: nil)
|
239
|
+
@meta = meta.symbolize_keys if meta
|
240
|
+
@id = id
|
241
|
+
@included = included
|
242
|
+
@connection = connection
|
243
|
+
@context = context
|
244
|
+
@attributes = attributes.symbolize_keys if attributes
|
245
|
+
@input_relationships = relationships
|
246
|
+
end
|
247
|
+
|
248
|
+
def to_relationship
|
249
|
+
{ type: self.class::TYPE, id: id }
|
250
|
+
end
|
251
|
+
|
252
|
+
def same_record_as?(other)
|
253
|
+
to_relationship == other.to_relationship
|
254
|
+
end
|
255
|
+
|
256
|
+
def attributes
|
257
|
+
@attributes ||= loaded_record.attributes
|
258
|
+
end
|
259
|
+
|
260
|
+
def meta
|
261
|
+
@meta ||= loaded_record.meta
|
262
|
+
end
|
263
|
+
|
264
|
+
def relationships
|
265
|
+
@relationships ||=
|
266
|
+
begin
|
267
|
+
if input_relationships
|
268
|
+
relationships_to_models(input_relationships.symbolize_keys)
|
269
|
+
else
|
270
|
+
loaded_record.relationships
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def update(**attrs)
|
276
|
+
self.class.update(
|
277
|
+
connection: connection,
|
278
|
+
id: id,
|
279
|
+
url_opts: { id: id },
|
280
|
+
**attrs
|
281
|
+
)
|
282
|
+
end
|
283
|
+
|
284
|
+
def delete
|
285
|
+
self.class.delete(
|
286
|
+
connection: connection,
|
287
|
+
url_opts: { id: id }
|
288
|
+
)
|
289
|
+
end
|
290
|
+
|
291
|
+
def as_json
|
292
|
+
self.class.template(
|
293
|
+
id: id,
|
294
|
+
attributes: attributes,
|
295
|
+
relationships: relationships
|
296
|
+
)
|
297
|
+
end
|
298
|
+
|
299
|
+
def to_json(*args)
|
300
|
+
as_json.to_json(*args)
|
301
|
+
end
|
302
|
+
|
303
|
+
def inspect
|
304
|
+
parsed_attributes = attributes.map { |key, value| "#{key}=#{value.inspect}" }.join(' ')
|
305
|
+
parsed_attributes = " #{parsed_attributes}" unless parsed_attributes.empty?
|
306
|
+
parsed_relationships = relationships.map { |key, value| "#{key}=#{value.inspect}" }.join(' ')
|
307
|
+
parsed_relationships = " #{parsed_relationships}" unless parsed_relationships.empty?
|
308
|
+
"#<#{self.class.name} id=#{id}#{parsed_attributes}#{parsed_relationships}>"
|
309
|
+
end
|
310
|
+
|
311
|
+
private
|
312
|
+
attr_reader :input_relationships, :included, :connection
|
313
|
+
|
314
|
+
def relationships_to_models(relationships)
|
315
|
+
relationships.each_with_object({}) do |(relationship, info), memo|
|
316
|
+
next unless implementation = self.class.relationships[relationship]
|
317
|
+
memo[relationship] = implementation.call(info, included, connection)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def loaded_record
|
322
|
+
@loaded_record ||= self.class.fetch(connection: connection, url_opts: { id: id })
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|