json_api_client 0.3.1 → 0.4.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 +4 -4
- data/README.md +7 -1
- data/lib/json_api_client/helpers/attributable.rb +2 -0
- data/lib/json_api_client/helpers/linkable.rb +25 -3
- data/lib/json_api_client/link.rb +11 -0
- data/lib/json_api_client/link_definition.rb +27 -0
- data/lib/json_api_client/linked_data.rb +69 -0
- data/lib/json_api_client/parser.rb +15 -0
- data/lib/json_api_client/query/linked.rb +24 -0
- data/lib/json_api_client/query.rb +1 -0
- data/lib/json_api_client/resource.rb +1 -0
- data/lib/json_api_client/result_set.rb +8 -1
- data/lib/json_api_client/version.rb +1 -1
- data/lib/json_api_client.rb +2 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6ba83068b0d92b968d8ab4edd3f4d4daca21f90
|
4
|
+
data.tar.gz: 53051631d82e0bddf15c2854d14382893a0448cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fab60fb4108bd15e9c7164370623c616153d52d5ca6c77df8b70a94ca0340a8a25b3dd8c18fbd73eb487616abf7857438950980a86b80261bda88e9cec0f918
|
7
|
+
data.tar.gz: 8d1998191c06f27e45208832a934b408214673a1a54c9318c875af1ccb9a0e293c1b159d6c83b5ebafbdba4a44ee55a348ea377fe88952cba6b256475ca0671a
|
data/README.md
CHANGED
@@ -153,4 +153,10 @@ end
|
|
153
153
|
|
154
154
|
In the above scenario, you can call the class method `MyApi::User.search`. The results will be parsed like any other query. If the response returns users, you will get back a `ResultSet` of `MyApi::User` instances.
|
155
155
|
|
156
|
-
You can also call the instance method `verify` on a `MyApi::User` instance.
|
156
|
+
You can also call the instance method `verify` on a `MyApi::User` instance.
|
157
|
+
|
158
|
+
## Links
|
159
|
+
|
160
|
+
We also respect the [links specification](http://jsonapi.org/format/#document-structure-resource-relationships). The client can fetch linked resources based on the defined endpoint from the link specification as well as load data from any `linked` data provided in the response. Additionally, it will still fetch missing data if not all linked resources are provided in the `linked` data response.
|
161
|
+
|
162
|
+
See the [tests](https://github.com/chingor13/json_api_client/blob/master/test/unit/links_test.rb).
|
@@ -4,13 +4,35 @@ module JsonApiClient
|
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
6
|
included do
|
7
|
-
attr_accessor :links
|
7
|
+
attr_accessor :links,
|
8
|
+
:linked_data
|
8
9
|
|
9
10
|
initializer do |obj, params|
|
10
|
-
links = params.delete(
|
11
|
-
|
11
|
+
if params && links = params.delete("links")
|
12
|
+
obj.links = links
|
13
|
+
end
|
12
14
|
end
|
13
15
|
end
|
16
|
+
|
17
|
+
def method_missing(method, *args)
|
18
|
+
return super unless has_link?(method)
|
19
|
+
|
20
|
+
linked_data.data_for(method, links[method.to_s])
|
21
|
+
end
|
22
|
+
|
23
|
+
def respond_to?(symbol, include_all = false)
|
24
|
+
return true if has_link?(symbol)
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def has_link?(symbol)
|
31
|
+
links &&
|
32
|
+
links.has_key?(symbol.to_s) &&
|
33
|
+
linked_data &&
|
34
|
+
linked_data.has_link?(symbol.to_s)
|
35
|
+
end
|
14
36
|
end
|
15
37
|
end
|
16
38
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module JsonApiClient
|
2
|
+
class LinkDefinition
|
3
|
+
|
4
|
+
def initialize(spec)
|
5
|
+
@spec = {}.with_indifferent_access
|
6
|
+
spec.each do |type, definition|
|
7
|
+
@spec[type.split(".").last] = definition.merge({slurp: type})
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_link?(type)
|
12
|
+
@spec.has_key?(type)
|
13
|
+
end
|
14
|
+
|
15
|
+
def attribute_name_for(type)
|
16
|
+
@spec.fetch(type).fetch("type")
|
17
|
+
end
|
18
|
+
|
19
|
+
def url_for(type, ids)
|
20
|
+
definition = @spec.fetch(type)
|
21
|
+
href = definition.fetch("href")
|
22
|
+
slurp = definition.fetch("slurp")
|
23
|
+
href.gsub("{#{slurp}}", Array(ids).join(","))
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# This object holds the preloaded data from the json response - essentially
|
2
|
+
# the preloaded foreign keys
|
3
|
+
module JsonApiClient
|
4
|
+
class LinkedData
|
5
|
+
attr_reader :link_definition,
|
6
|
+
:record_class
|
7
|
+
|
8
|
+
extend Forwardable
|
9
|
+
def_delegators :link_definition, :has_link?
|
10
|
+
|
11
|
+
def initialize(data, link_definition, record_class)
|
12
|
+
@link_definition = link_definition
|
13
|
+
@record_class = record_class
|
14
|
+
@results_by_type_by_id = {}
|
15
|
+
|
16
|
+
data.each do |type, results|
|
17
|
+
klass = klass_for(type)
|
18
|
+
add_data(type, results.map{|result| klass.new(result)})
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def data_for(type, ids)
|
23
|
+
ids = Array(ids)
|
24
|
+
|
25
|
+
# the name of the linked data is provided by the link definition from the result
|
26
|
+
attr_name = link_definition.attribute_name_for(type)
|
27
|
+
|
28
|
+
# get any preloaded data from the result
|
29
|
+
type_data = @results_by_type_by_id.fetch(attr_name, {})
|
30
|
+
|
31
|
+
# find the associated class for the data
|
32
|
+
klass = klass_for(type)
|
33
|
+
|
34
|
+
# return all the found records
|
35
|
+
found, missing = ids.partition { |id| type_data[id].present? }
|
36
|
+
|
37
|
+
# make another api request if there are missing records
|
38
|
+
fetch_data(klass, type, missing) if missing.present?
|
39
|
+
|
40
|
+
# reload data
|
41
|
+
type_data = @results_by_type_by_id.fetch(attr_name, {})
|
42
|
+
|
43
|
+
ids.map do |id|
|
44
|
+
type_data[id]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# make an api request to fetch the missing data
|
49
|
+
def fetch_data(klass, type, missing_ids)
|
50
|
+
path = URI(link_definition.url_for(type, missing_ids)).path
|
51
|
+
|
52
|
+
query = Query::Linked.new(path)
|
53
|
+
results = klass.run_request(query)
|
54
|
+
|
55
|
+
key = link_definition.attribute_name_for(type).to_s
|
56
|
+
add_data(key, results)
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_data(key, data)
|
60
|
+
@results_by_type_by_id[key] ||= {}
|
61
|
+
@results_by_type_by_id[key].merge!(data.index_by{|datum| datum["id"]})
|
62
|
+
end
|
63
|
+
|
64
|
+
def klass_for(type)
|
65
|
+
Utils.compute_type(record_class, type.to_s.pluralize.classify)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -6,6 +6,7 @@ module JsonApiClient
|
|
6
6
|
data = response.body
|
7
7
|
ResultSet.build(klass, data) do |result_set|
|
8
8
|
handle_pagination(result_set, data)
|
9
|
+
handle_links(result_set, data)
|
9
10
|
handle_errors(result_set, data)
|
10
11
|
end
|
11
12
|
end
|
@@ -33,6 +34,20 @@ module JsonApiClient
|
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
37
|
+
def handle_links(result_set, data)
|
38
|
+
return if result_set.empty?
|
39
|
+
|
40
|
+
linked_data = LinkedData.new(
|
41
|
+
data.fetch("linked", {}),
|
42
|
+
LinkDefinition.new(data.fetch("links", {})),
|
43
|
+
result_set.record_class
|
44
|
+
)
|
45
|
+
|
46
|
+
result_set.each do |resource|
|
47
|
+
resource.linked_data = linked_data
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
36
51
|
def handle_errors(result_set, data)
|
37
52
|
result_set.errors = data.fetch("meta", {}).fetch("errors", [])
|
38
53
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module JsonApiClient
|
2
|
+
module Query
|
3
|
+
class Linked
|
4
|
+
attr_accessor :path
|
5
|
+
|
6
|
+
def initialize(path)
|
7
|
+
@path = path
|
8
|
+
end
|
9
|
+
|
10
|
+
def request_method
|
11
|
+
:get
|
12
|
+
end
|
13
|
+
|
14
|
+
def headers
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
|
18
|
+
def params
|
19
|
+
{}
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,12 +1,19 @@
|
|
1
1
|
module JsonApiClient
|
2
2
|
class ResultSet < Array
|
3
3
|
|
4
|
-
attr_accessor :total_pages,
|
4
|
+
attr_accessor :total_pages,
|
5
|
+
:total_entries,
|
6
|
+
:offset,
|
7
|
+
:per_page,
|
8
|
+
:current_page,
|
9
|
+
:errors,
|
10
|
+
:record_class
|
5
11
|
alias_attribute :limit_value, :per_page
|
6
12
|
|
7
13
|
def self.build(klass, data)
|
8
14
|
result_data = data.fetch(klass.table_name, [])
|
9
15
|
new(result_data.map {|attributes| klass.new(attributes) }).tap do |result_set|
|
16
|
+
result_set.record_class = klass
|
10
17
|
yield(result_set) if block_given?
|
11
18
|
end
|
12
19
|
end
|
data/lib/json_api_client.rb
CHANGED
@@ -7,7 +7,8 @@ module JsonApiClient
|
|
7
7
|
autoload :Connection, 'json_api_client/connection'
|
8
8
|
autoload :Errors, 'json_api_client/errors'
|
9
9
|
autoload :Helpers, 'json_api_client/helpers'
|
10
|
-
autoload :
|
10
|
+
autoload :LinkDefinition, 'json_api_client/link_definition'
|
11
|
+
autoload :LinkedData, 'json_api_client/linked_data'
|
11
12
|
autoload :Middleware, 'json_api_client/middleware'
|
12
13
|
autoload :Parser, 'json_api_client/parser'
|
13
14
|
autoload :Query, 'json_api_client/query'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json_api_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Ching
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -92,6 +92,9 @@ files:
|
|
92
92
|
- lib/json_api_client/helpers/parsable.rb
|
93
93
|
- lib/json_api_client/helpers/queryable.rb
|
94
94
|
- lib/json_api_client/helpers/serializable.rb
|
95
|
+
- lib/json_api_client/link.rb
|
96
|
+
- lib/json_api_client/link_definition.rb
|
97
|
+
- lib/json_api_client/linked_data.rb
|
95
98
|
- lib/json_api_client/middleware.rb
|
96
99
|
- lib/json_api_client/middleware/json_request.rb
|
97
100
|
- lib/json_api_client/middleware/parse_json.rb
|
@@ -103,6 +106,7 @@ files:
|
|
103
106
|
- lib/json_api_client/query/custom.rb
|
104
107
|
- lib/json_api_client/query/destroy.rb
|
105
108
|
- lib/json_api_client/query/find.rb
|
109
|
+
- lib/json_api_client/query/linked.rb
|
106
110
|
- lib/json_api_client/query/update.rb
|
107
111
|
- lib/json_api_client/resource.rb
|
108
112
|
- lib/json_api_client/result_set.rb
|