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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e63300dc507979f0b7591f11c0bae0b8f51fc8d9
4
- data.tar.gz: 1c22f0baf42e3ab2f1a130c17e4b6e77c5e23728
3
+ metadata.gz: a6ba83068b0d92b968d8ab4edd3f4d4daca21f90
4
+ data.tar.gz: 53051631d82e0bddf15c2854d14382893a0448cd
5
5
  SHA512:
6
- metadata.gz: 6b76b52b3b65ca59508ba529045f3a2730e85e7780de81762a7f4f2f1a4a618938564e71f5485e3f347534d4e5d63bb72577f5b00de5745b2645bd92f8fa228a
7
- data.tar.gz: cdab71113c09bc1db9e2c734c0ab613f4452e7d7590b8c1f6d5387504a444b6ebdfef2296c76239299e4227632ed2aed8ef0b72cc40c38e4bf6b7d3e19068320
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).
@@ -13,6 +13,8 @@ module JsonApiClient
13
13
 
14
14
  def attributes=(attrs = {})
15
15
  @attributes ||= {}.with_indifferent_access
16
+
17
+ return @attributes unless attrs.present?
16
18
  @attributes.merge!(attrs)
17
19
  end
18
20
 
@@ -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(:links)
11
- obj.links = links if links
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,11 @@
1
+ module JsonApiClient
2
+ class Link
3
+ attr_accessor :type
4
+
5
+ def initialize(type, spec)
6
+ @type = type
7
+ @spec = spec
8
+ end
9
+
10
+ end
11
+ 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
@@ -6,5 +6,6 @@ module JsonApiClient
6
6
  autoload :Destroy, 'json_api_client/query/destroy'
7
7
  autoload :Find, 'json_api_client/query/find'
8
8
  autoload :Update, 'json_api_client/query/update'
9
+ autoload :Linked, 'json_api_client/query/linked'
9
10
  end
10
11
  end
@@ -4,6 +4,7 @@ require 'active_support/inflector'
4
4
  require 'active_support/core_ext/hash'
5
5
  require 'active_support/core_ext/module'
6
6
  require 'active_support/core_ext/class/attribute'
7
+ require 'active_support/core_ext/enumerable'
7
8
 
8
9
  module JsonApiClient
9
10
  class Resource
@@ -1,12 +1,19 @@
1
1
  module JsonApiClient
2
2
  class ResultSet < Array
3
3
 
4
- attr_accessor :total_pages, :total_entries, :offset, :per_page, :current_page, :errors
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
@@ -1,3 +1,3 @@
1
1
  module JsonApiClient
2
- VERSION = "0.3.1"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -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 :Links, 'json_api_client/links'
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.3.1
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-06-23 00:00:00.000000000 Z
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