lhs 5.2.0 → 5.3.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: 42f49d74b6cae3f84139b46895b5defb5454c184
4
- data.tar.gz: 9232b8df9672d9caa11e3f0a3a36f2474ba3386e
3
+ metadata.gz: b5e2d8c23fc37ece5d7b13aebd02f43df60aec96
4
+ data.tar.gz: 650560241439a1cb5b97c86894ef4a5e133d5bd4
5
5
  SHA512:
6
- metadata.gz: 7fbeecfceee4e4c6ebb5f125d427d98cbc1ece337469fb3ae8070de018616e33e5c8923df319863c835367cfcbda6581cc24f22512d3d3b11899bd8531aa801a
7
- data.tar.gz: 36520b86b942ac32f0c01191a7e6f418100122f18724b760a0d7f4c7ef630b8c32c3974fa49c714bf6631dc044b5e8448bf8a87ef6be26e7a6f1bb552537d059
6
+ metadata.gz: 7d086164639d3fd4b3a40a772c2c98360cf212f6b58e6d55a5debe7317daecb341e42aec62948783e79f3eb18f398a607a9cffc99a7828d73f9fc02c894cc36a
7
+ data.tar.gz: 329fbdb7a8edc677a156588061af8e99e9a15db6bec9684f50e057157f6c4489fd1eb71d5c58c8378bb7e194682b2679e77b58c0ada1f3fc2b012bdfc70c2e9d
data/README.md CHANGED
@@ -293,7 +293,7 @@ feedback.ratings # {:quality=>3}
293
293
 
294
294
  ## Include linked resources
295
295
 
296
- When fetching records, you can specify in advance all the linked resources that you want to include in the results. With `includes`, LHS ensures that all matching and explicitly linked resources are loaded and merged.
296
+ When fetching records, you can specify in advance all the linked resources that you want to include in the results. With `includes`, LHS ensures that all matching and explicitly linked resources are loaded and merged, if they're included in the server response.
297
297
 
298
298
  The implementation is heavily influenced by [http://guides.rubyonrails.org/active_record_class_querying](http://guides.rubyonrails.org/active_record_class_querying.html#eager-loading-associations) and you should read it to understand this feature in all its glory.
299
299
 
@@ -26,6 +26,7 @@ class LHS::Record
26
26
  end
27
27
 
28
28
  def convert_option_to_endpoints(option)
29
+ return unless option.present?
29
30
  new_options = option.dup
30
31
  url = option[:url]
31
32
  endpoint = LHS::Endpoint.for_url(url)
@@ -38,10 +39,12 @@ class LHS::Record
38
39
 
39
40
  # Extends existing raw data with additionaly fetched data
40
41
  def extend_raw_data(data, addition, key)
41
- if data._proxy.is_a? LHS::Collection
42
+ return if addition.empty?
43
+ if data.collection?
42
44
  data.each_with_index do |item, i|
43
45
  item = item[i] if item.is_a? LHS::Collection
44
- item._raw[key.to_sym].merge!(addition[i]._raw)
46
+ link = item[key.to_sym]
47
+ link.merge_raw!(addition[i]) if link.present?
45
48
  end
46
49
  elsif data._proxy.is_a? LHS::Item
47
50
  data._raw[key.to_sym].merge!(addition._raw)
@@ -59,19 +62,25 @@ class LHS::Record
59
62
  end
60
63
 
61
64
  def handle_include(included, data, sub_includes = nil)
62
- return unless data.present?
65
+ return if data.blank? || skip_loading_includes?(data, included)
63
66
  options =
64
- if data._proxy.is_a? LHS::Collection
65
- options_for_multiple(data, included)
67
+ if data.collection?
66
68
  options_for_multiple(data, included)
67
69
  else
68
70
  url_option_for(data, included)
69
- url_option_for(data, included)
70
71
  end
71
72
  addition = load_include(options, data, sub_includes)
72
73
  extend_raw_data(data, addition, included)
73
74
  end
74
75
 
76
+ def skip_loading_includes?(data, included)
77
+ if data.collection?
78
+ data.to_a.none? { |item| item[included].present? }
79
+ else
80
+ !data._raw.key?(included)
81
+ end
82
+ end
83
+
75
84
  # Load additional resources that are requested with include
76
85
  def load_include(options, data, sub_includes)
77
86
  record = record_for_options(options) || self
@@ -92,14 +101,31 @@ class LHS::Record
92
101
  end
93
102
 
94
103
  def multiple_requests(options)
95
- options = options.map { |option| process_options(option, find_endpoint(option[:params])) }
96
- responses = LHC.request(options)
97
- data = responses.map { |response| LHS::Data.new(response.body, nil, self, response.request) }
98
- data = LHS::Data.new(data, nil, self)
99
- handle_includes(including, data) if including
104
+ options = options.map do |option|
105
+ next unless option.present?
106
+ process_options(option, find_endpoint(option[:params]))
107
+ end
108
+ data = LHC.request(options.compact).map { |response| LHS::Data.new(response.body, nil, self, response.request) }
109
+ data = restore_with_nils(data, locate_nils(options)) # nil objects in data provide location information for mapping
110
+ unless data.empty?
111
+ data = LHS::Data.new(data, nil, self)
112
+ handle_includes(including, data) if including
113
+ end
100
114
  data
101
115
  end
102
116
 
117
+ def locate_nils(array)
118
+ nils = []
119
+ array.each_with_index { |value, index| nils << index if value.nil? }
120
+ nils
121
+ end
122
+
123
+ def restore_with_nils(array, nils)
124
+ array = array.dup
125
+ nils.sort.each { |index| array.insert(index, nil) }
126
+ array
127
+ end
128
+
103
129
  def options_for_multiple(data, key)
104
130
  data.map do |item|
105
131
  url_option_for(item, key)
@@ -119,7 +145,7 @@ class LHS::Record
119
145
  def record_for_options(options)
120
146
  records = []
121
147
  if options.is_a?(Array)
122
- options.each do |option|
148
+ options.compact.each do |option|
123
149
  record = LHS::Record.for_url(option[:url])
124
150
  next unless record
125
151
  records.push(record)
@@ -143,7 +169,7 @@ class LHS::Record
143
169
 
144
170
  def url_option_for(item, key)
145
171
  link = item[key]
146
- { url: link.href }
172
+ return { url: link.href } if link.present? && link.href.present?
147
173
  end
148
174
  end
149
175
  end
@@ -23,7 +23,7 @@ class LHS::Data
23
23
  # merging data
24
24
  # e.g. when loading remote data via link
25
25
  def merge_raw!(data)
26
- return false unless data._raw.is_a?(Hash)
26
+ return false if data.blank? || !data._raw.is_a?(Hash)
27
27
  _raw.merge! data._raw
28
28
  end
29
29
 
@@ -18,9 +18,7 @@ class LHS::Record
18
18
  include Request
19
19
  include Scope
20
20
 
21
- delegate :_proxy, to: :_data
22
- delegate :_endpoint, to: :_data
23
- delegate :select, to: :_data
21
+ delegate :_proxy, :_endpoint, :merge_raw!, :select, to: :_data
24
22
 
25
23
  def initialize(data = nil)
26
24
  data = LHS::Data.new({}, nil, self.class) unless data
@@ -1,3 +1,3 @@
1
1
  module LHS
2
- VERSION = "5.2.0"
2
+ VERSION = "5.3.0"
3
3
  end
@@ -170,6 +170,59 @@ describe LHS::Record do
170
170
  # rubocop:enable RSpec/InstanceVariable
171
171
  end
172
172
  end
173
+
174
+ context 'includes not present in response' do
175
+ before :each do
176
+ class Parent < LHS::Record
177
+ endpoint ':datastore/local-parents'
178
+ endpoint ':datastore/local-parents/:id'
179
+ end
180
+
181
+ class OptionalChild < LHS::Record
182
+ endpoint ':datastore/local-children/:id'
183
+ end
184
+ end
185
+
186
+ it 'handles missing but included fields in single object response' do
187
+ stub_request(:get, "#{datastore}/local-parents/1")
188
+ .to_return(status: 200, body: {
189
+ 'href' => "#{datastore}/local-parents/1",
190
+ 'name' => 'RspecName'
191
+ }.to_json)
192
+
193
+ parent = Parent.includes(:optional_children).find(1)
194
+ expect(parent).not_to be nil
195
+ expect(parent.name).to eq 'RspecName'
196
+ expect(parent.optional_children).to be nil
197
+ end
198
+
199
+ it 'handles missing but included fields in collection response' do
200
+ stub_request(:get, "#{datastore}/local-parents")
201
+ .to_return(status: 200, body: {
202
+ items: [
203
+ {
204
+ 'href' => "#{datastore}/local-parents/1",
205
+ 'name' => 'RspecParent'
206
+ }, {
207
+ 'href' => "#{datastore}/local-parents/2",
208
+ 'name' => 'RspecParent2',
209
+ 'optional_child' => {
210
+ 'href' => "#{datastore}/local-children/1"
211
+ }
212
+ }]
213
+ }.to_json)
214
+
215
+ stub_request(:get, "#{datastore}/local-children/1")
216
+ .to_return(status: 200, body: {
217
+ href: "#{datastore}/local_children/1",
218
+ name: 'RspecOptionalChild1'
219
+ }.to_json)
220
+
221
+ child = Parent.includes(:optional_child).where[1].optional_child
222
+ expect(child).not_to be nil
223
+ expect(child.name).to eq 'RspecOptionalChild1'
224
+ end
225
+ end
173
226
  end
174
227
 
175
228
  context 'links pointing to nowhere' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lhs
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - https://github.com/local-ch/lhs/graphs/contributors
@@ -343,7 +343,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
343
343
  requirements:
344
344
  - Ruby >= 1.9.2
345
345
  rubyforge_project:
346
- rubygems_version: 2.2.2
346
+ rubygems_version: 2.5.1
347
347
  signing_key:
348
348
  specification_version: 4
349
349
  summary: Rails gem providing an easy, active-record-like interface for http json services