lhs 12.0.3 → 12.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 +4 -4
- data/README.md +55 -1
- data/lib/lhs/collection.rb +8 -8
- data/lib/lhs/concerns/collection/handle_nested.rb +41 -0
- data/lib/lhs/concerns/proxy/accessors.rb +4 -4
- data/lib/lhs/concerns/record/configuration.rb +29 -7
- data/lib/lhs/concerns/record/request.rb +5 -5
- data/lib/lhs/data.rb +6 -2
- data/lib/lhs/pagination/base.rb +3 -3
- data/lib/lhs/version.rb +1 -1
- data/spec/record/dig_configuration_spec.rb +67 -0
- data/spec/record/items_created_key_configuration_spec.rb +35 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a3fd78d0dd9d0ee88751c3b0ba15652bd4a15d9
|
4
|
+
data.tar.gz: 0629f8dfc7068fa195ce3fc5d556313b59c0a713
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9042c4d3e3d7b70be32018087fef6c1b472de591048b19866aad7f2fa9098e223490f318b5527a3e53708a0e591419bb0f77f47650df5c1c1f26f5ffd5b38b1e
|
7
|
+
data.tar.gz: 8c0571a35bcacaa09ccd58fa729b083e84d1613cee295cfec64208c1d130d9615c1e9b32cc92ab36978e8e82722316d063baf4baa806c8c5b8e0df6d6b6b018e
|
data/README.md
CHANGED
@@ -855,8 +855,21 @@ class Results < LHS::Record
|
|
855
855
|
end
|
856
856
|
```
|
857
857
|
|
858
|
+
In case of paginated resources it's important to know the difference between [count vs. length](#count-vs-length)
|
859
|
+
|
860
|
+
## Configuration of Records
|
861
|
+
|
862
|
+
```ruby
|
863
|
+
class Search < LHS::Record
|
864
|
+
configuration items_key: 'searchResults', total_key: 'total', limit_key: 'limit', pagination_key: 'offset', pagination_strategy: 'offset'
|
865
|
+
endpoint 'https://search'
|
866
|
+
end
|
867
|
+
```
|
868
|
+
|
858
869
|
`items_key` key used to determine items of the current page (e.g. `docs`, `items`, etc.).
|
859
870
|
|
871
|
+
`item_created_key` key used to merge record data thats nested in the creation response body.
|
872
|
+
|
860
873
|
`limit_key` key used to work with page limits (e.g. `size`, `limit`, etc.)
|
861
874
|
|
862
875
|
`pagination_key` key used to paginate multiple pages (e.g. `offset`, `page`, `startAt` etc.).
|
@@ -865,7 +878,48 @@ end
|
|
865
878
|
|
866
879
|
`total_key` key used to determine the total amount of items (e.g. `total`, `totalResults`, etc.).
|
867
880
|
|
868
|
-
|
881
|
+
### Configure complex accessors for nested data (EXPERIMENTAL)
|
882
|
+
|
883
|
+
If items, limit, pagination, total etc. is nested in the responding objects, use complex data structures for configuring a record.
|
884
|
+
|
885
|
+
```
|
886
|
+
response: {
|
887
|
+
offset: 0,
|
888
|
+
max: 50,
|
889
|
+
count: 1,
|
890
|
+
businesses: [
|
891
|
+
{}
|
892
|
+
]
|
893
|
+
}
|
894
|
+
```
|
895
|
+
|
896
|
+
```ruby
|
897
|
+
class Business < LHS::Record
|
898
|
+
configuration items_key: [:response, :businesses], limit_key: [:response, :max], pagination_key: [:response, :offset], total_key: [:response, :count], pagination_strategy: :offset
|
899
|
+
endpoint 'http://uberall/businesses'
|
900
|
+
end
|
901
|
+
```
|
902
|
+
|
903
|
+
If record data after creation is nested in the response body, configure the record, so that it gets properl merged with the your record instance:
|
904
|
+
|
905
|
+
```
|
906
|
+
POST /businesses
|
907
|
+
response: {
|
908
|
+
business: {
|
909
|
+
id: 123
|
910
|
+
}
|
911
|
+
}
|
912
|
+
```
|
913
|
+
|
914
|
+
```ruby
|
915
|
+
class Business < LHS::Record
|
916
|
+
configuration item_created_key: [:response, :business]
|
917
|
+
endpoint 'http://uberall/businesses'
|
918
|
+
end
|
919
|
+
|
920
|
+
business = Business.create(name: 'localsearch')
|
921
|
+
business.id # 123
|
922
|
+
```
|
869
923
|
|
870
924
|
### Pagination Chains
|
871
925
|
|
data/lib/lhs/collection.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
# A collection is a special type of data
|
2
2
|
# that contains multiple items
|
3
3
|
class LHS::Collection < LHS::Proxy
|
4
|
-
autoload :
|
4
|
+
autoload :HandleNested,
|
5
|
+
'lhs/concerns/collection/handle_nested'
|
6
|
+
autoload :InternalCollection,
|
7
|
+
'lhs/concerns/collection/internal_collection'
|
8
|
+
|
9
|
+
include HandleNested
|
5
10
|
include InternalCollection
|
6
11
|
include Create
|
7
12
|
|
@@ -23,7 +28,7 @@ class LHS::Collection < LHS::Proxy
|
|
23
28
|
|
24
29
|
def _collection
|
25
30
|
raw = _data._raw if _data._raw.is_a?(Array)
|
26
|
-
raw ||= _data._raw
|
31
|
+
raw ||= _data.access(input: _data._raw, record: _record)
|
27
32
|
Collection.new(raw, _data, _record)
|
28
33
|
end
|
29
34
|
|
@@ -39,7 +44,7 @@ class LHS::Collection < LHS::Proxy
|
|
39
44
|
if _raw.is_a?(Array)
|
40
45
|
_raw
|
41
46
|
else
|
42
|
-
_raw
|
47
|
+
access(input: _raw, record: _record)
|
43
48
|
end
|
44
49
|
end
|
45
50
|
|
@@ -64,11 +69,6 @@ class LHS::Collection < LHS::Proxy
|
|
64
69
|
|
65
70
|
private
|
66
71
|
|
67
|
-
def items_key
|
68
|
-
return _record.items_key if _record
|
69
|
-
:items
|
70
|
-
end
|
71
|
-
|
72
72
|
# Encloses accessed collection item
|
73
73
|
# by wrapping it in an LHS::Item
|
74
74
|
def enclose_item_in_data(value)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
class LHS::Collection < LHS::Proxy
|
4
|
+
|
5
|
+
# Handles pontentially (deep-)nested collections
|
6
|
+
# Examples:
|
7
|
+
# [ { name: 'Steve '} ]
|
8
|
+
# { items: [ { name: 'Steve' } ] }
|
9
|
+
# { response: { business: [ { name: 'Steve' } ] } }
|
10
|
+
module HandleNested
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
|
13
|
+
delegate :access, :nest, :concat, to: :class
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
# Access potentially nested collection of items
|
17
|
+
def access(input:, record: nil)
|
18
|
+
input.dig(*items_key(record))
|
19
|
+
end
|
20
|
+
|
21
|
+
# Initializes nested collection
|
22
|
+
def nest(input:, value: nil, record: nil)
|
23
|
+
input[items_key(record)] = value
|
24
|
+
end
|
25
|
+
|
26
|
+
# Concats existing nested collection of items
|
27
|
+
# with given items
|
28
|
+
def concat(input:, items:, record: nil)
|
29
|
+
input.dig(*items_key(record)).concat(items)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Takes configured items key to access collection of items
|
35
|
+
# of falls back to the default key
|
36
|
+
def items_key(record)
|
37
|
+
record && record.items_key || LHS::Record.items_key
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -33,15 +33,15 @@ class LHS::Proxy
|
|
33
33
|
|
34
34
|
def accessing_item?(value, record)
|
35
35
|
return false unless value.is_a?(Hash)
|
36
|
-
return false if record && value
|
37
|
-
return false if !record && value[
|
36
|
+
return false if record && access(input: value, record: record).present?
|
37
|
+
return false if !record && value[:items].present?
|
38
38
|
true
|
39
39
|
end
|
40
40
|
|
41
41
|
def accessing_collection?(value, record)
|
42
42
|
return true if value.is_a?(Array)
|
43
|
-
return true if value.is_a?(Hash) && record && value
|
44
|
-
return true if value.is_a?(Hash) && !record && value[
|
43
|
+
return true if value.is_a?(Hash) && record && access(input: value, record: record).present?
|
44
|
+
return true if value.is_a?(Hash) && !record && value[:items].present?
|
45
45
|
end
|
46
46
|
|
47
47
|
def convert(value)
|
@@ -7,8 +7,6 @@ class LHS::Record
|
|
7
7
|
module Configuration
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
-
DEFAULT_ITEMS_KEY = :items
|
11
|
-
|
12
10
|
mattr_accessor :configuration
|
13
11
|
|
14
12
|
module ClassMethods
|
@@ -17,25 +15,49 @@ class LHS::Record
|
|
17
15
|
end
|
18
16
|
|
19
17
|
def items_key
|
20
|
-
(
|
18
|
+
symbolize_unless_complex(
|
19
|
+
@configuration.try(:[], :items_key) || :items
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def item_created_key
|
24
|
+
symbolize_unless_complex(
|
25
|
+
@configuration.try(:[], :item_created_key)
|
26
|
+
)
|
21
27
|
end
|
22
28
|
|
23
29
|
def limit_key
|
24
|
-
(
|
30
|
+
symbolize_unless_complex(
|
31
|
+
@configuration.try(:[], :limit_key) || :limit
|
32
|
+
)
|
25
33
|
end
|
26
34
|
|
27
35
|
def total_key
|
28
|
-
(
|
36
|
+
symbolize_unless_complex(
|
37
|
+
@configuration.try(:[], :total_key) || :total
|
38
|
+
)
|
29
39
|
end
|
30
40
|
|
31
41
|
# Key used for determine current page
|
32
42
|
def pagination_key
|
33
|
-
(
|
43
|
+
symbolize_unless_complex(
|
44
|
+
@configuration.try(:[], :pagination_key) || :offset
|
45
|
+
)
|
34
46
|
end
|
35
47
|
|
36
48
|
# Strategy used for calculationg next pages and navigate pages
|
37
49
|
def pagination_strategy
|
38
|
-
(
|
50
|
+
symbolize_unless_complex(
|
51
|
+
@configuration.try(:[], :pagination_strategy) || :offset
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def symbolize_unless_complex(value)
|
58
|
+
return if value.blank?
|
59
|
+
return value.to_sym unless value.is_a?(Array)
|
60
|
+
value
|
39
61
|
end
|
40
62
|
end
|
41
63
|
end
|
@@ -124,11 +124,11 @@ class LHS::Record
|
|
124
124
|
end
|
125
125
|
|
126
126
|
def extend_base_item_with_hash_of_items!(target, addition)
|
127
|
-
target._raw[
|
128
|
-
if target._raw
|
129
|
-
target._raw
|
127
|
+
LHS::Collection.nest(input: target._raw, value: [], record: self)
|
128
|
+
if LHS::Collection.access(input: target._raw, record: self).empty?
|
129
|
+
LHS::Collection.nest(input: target._raw, value: addition.map(&:_raw), record: self)
|
130
130
|
else
|
131
|
-
target._raw
|
131
|
+
LHS::Collection.access(input: target._raw, record: self).each_with_index do |item, index|
|
132
132
|
item.merge!(addition[index])
|
133
133
|
end
|
134
134
|
end
|
@@ -326,7 +326,7 @@ class LHS::Record
|
|
326
326
|
end
|
327
327
|
|
328
328
|
def merge_batch_data_with_parent!(batch_data, parent_data)
|
329
|
-
parent_data.
|
329
|
+
parent_data.concat(input: parent_data._raw, items: batch_data.raw_items, record: self)
|
330
330
|
parent_data._raw[limit_key] = batch_data._raw[limit_key]
|
331
331
|
parent_data._raw[total_key] = batch_data._raw[total_key]
|
332
332
|
parent_data._raw[pagination_key] = batch_data._raw[pagination_key]
|
data/lib/lhs/data.rb
CHANGED
@@ -30,7 +30,11 @@ class LHS::Data
|
|
30
30
|
# e.g. when loading remote data via link
|
31
31
|
def merge_raw!(data)
|
32
32
|
return false if data.blank? || !data._raw.is_a?(Hash)
|
33
|
-
|
33
|
+
if _record && _record.item_created_key
|
34
|
+
_raw.merge! data._raw.dig(*_record.item_created_key)
|
35
|
+
else
|
36
|
+
_raw.merge! data._raw
|
37
|
+
end
|
34
38
|
end
|
35
39
|
|
36
40
|
def _root
|
@@ -83,7 +87,7 @@ class LHS::Data
|
|
83
87
|
private
|
84
88
|
|
85
89
|
def collection_proxy?(input)
|
86
|
-
(input.is_a?(Hash) && input
|
90
|
+
(input.is_a?(Hash) && LHS::Collection.access(input: input, record: _record)) ||
|
87
91
|
input.is_a?(Array) ||
|
88
92
|
_raw.is_a?(Array)
|
89
93
|
end
|
data/lib/lhs/pagination/base.rb
CHANGED
@@ -17,15 +17,15 @@ module LHS::Pagination
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def total
|
20
|
-
data._raw
|
20
|
+
data._raw.dig(*_record.total_key)
|
21
21
|
end
|
22
22
|
|
23
23
|
def limit
|
24
|
-
data._raw
|
24
|
+
data._raw.dig(*_record.limit_key) || DEFAULT_LIMIT
|
25
25
|
end
|
26
26
|
|
27
27
|
def offset
|
28
|
-
data._raw
|
28
|
+
data._raw.dig(*_record.pagination_key) || 0
|
29
29
|
end
|
30
30
|
alias current_page offset
|
31
31
|
alias start offset
|
data/lib/lhs/version.rb
CHANGED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe LHS::Record do
|
4
|
+
before(:each) do
|
5
|
+
class Business < LHS::Record
|
6
|
+
configuration items_key: [:response, :businesses], limit_key: [:response, :max], pagination_key: [:response, :offset], total_key: [:response, :count], pagination_strategy: :offset
|
7
|
+
endpoint 'http://uberall/businesses'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:stub_single_business_request) do
|
12
|
+
stub_request(:get, "http://uberall/businesses?identifier=ABC123&limit=1")
|
13
|
+
.to_return(body: {
|
14
|
+
status: "SUCCESS",
|
15
|
+
response: {
|
16
|
+
offset: 0,
|
17
|
+
max: 50,
|
18
|
+
count: 1,
|
19
|
+
businesses: [
|
20
|
+
{
|
21
|
+
identifier: 'ABC123',
|
22
|
+
name: 'localsearch'
|
23
|
+
}
|
24
|
+
]
|
25
|
+
}
|
26
|
+
}.to_json)
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:stub_multiple_businesses_request) do
|
30
|
+
stub_request(:get, "http://uberall/businesses?name=localsearch")
|
31
|
+
.to_return(body: {
|
32
|
+
status: "SUCCESS",
|
33
|
+
response: {
|
34
|
+
offset: 0,
|
35
|
+
max: 50,
|
36
|
+
count: 2,
|
37
|
+
businesses: [
|
38
|
+
{
|
39
|
+
identifier: 'ABC123',
|
40
|
+
name: 'localsearch'
|
41
|
+
},
|
42
|
+
{
|
43
|
+
identifier: 'ABC121',
|
44
|
+
name: 'Swisscom'
|
45
|
+
}
|
46
|
+
]
|
47
|
+
}
|
48
|
+
}.to_json)
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'access nested keys for configuration' do
|
52
|
+
it 'uses paths from configuration to access nested values' do
|
53
|
+
stub_single_business_request
|
54
|
+
business = Business.find_by(identifier: 'ABC123')
|
55
|
+
expect(business.identifier).to eq 'ABC123'
|
56
|
+
expect(business.name).to eq 'localsearch'
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'digs for meta data when meta information is nested' do
|
60
|
+
stub_multiple_businesses_request
|
61
|
+
businesses = Business.where(name: 'localsearch')
|
62
|
+
expect(businesses.length).to eq 2
|
63
|
+
expect(businesses.count).to eq 2
|
64
|
+
expect(businesses.offset).to eq 0
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe LHS::Record do
|
4
|
+
before(:each) do
|
5
|
+
class Business < LHS::Record
|
6
|
+
configuration item_created_key: [:response, :business], limit_key: [:response, :max], pagination_key: [:response, :offset], total_key: [:response, :count], pagination_strategy: :offset
|
7
|
+
endpoint 'http://uberall/businesses'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:stub_create_business_request) do
|
12
|
+
stub_request(:post, "http://uberall/businesses")
|
13
|
+
.to_return(body: {
|
14
|
+
status: "SUCCESS",
|
15
|
+
response: {
|
16
|
+
business: {
|
17
|
+
identifier: 'ABC123',
|
18
|
+
name: 'localsearch',
|
19
|
+
id: 239650
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}.to_json)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'uses paths from configuration to access nested values' do
|
26
|
+
stub_create_business_request
|
27
|
+
business = Business.create!(
|
28
|
+
identifier: 'ABC123',
|
29
|
+
name: 'localsearch'
|
30
|
+
)
|
31
|
+
expect(business.identifier).to eq 'ABC123'
|
32
|
+
expect(business.name).to eq 'localsearch'
|
33
|
+
expect(business.id).to eq 239650
|
34
|
+
end
|
35
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lhs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 12.0
|
4
|
+
version: 12.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- https://github.com/local-ch/lhs/graphs/contributors
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-07-
|
11
|
+
date: 2017-07-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lhc
|
@@ -198,6 +198,7 @@ files:
|
|
198
198
|
- lib/lhs/collection.rb
|
199
199
|
- lib/lhs/complex.rb
|
200
200
|
- lib/lhs/concerns/autoload_records.rb
|
201
|
+
- lib/lhs/concerns/collection/handle_nested.rb
|
201
202
|
- lib/lhs/concerns/collection/internal_collection.rb
|
202
203
|
- lib/lhs/concerns/configuration.rb
|
203
204
|
- lib/lhs/concerns/data/equality.rb
|
@@ -340,6 +341,7 @@ files:
|
|
340
341
|
- spec/record/creation_failed_spec.rb
|
341
342
|
- spec/record/definitions_spec.rb
|
342
343
|
- spec/record/destroy_spec.rb
|
344
|
+
- spec/record/dig_configuration_spec.rb
|
343
345
|
- spec/record/endpoint_inheritance_spec.rb
|
344
346
|
- spec/record/endpoint_misconfiguration_spec.rb
|
345
347
|
- spec/record/endpoint_options_spec.rb
|
@@ -357,6 +359,7 @@ files:
|
|
357
359
|
- spec/record/includes_all_spec.rb
|
358
360
|
- spec/record/includes_spec.rb
|
359
361
|
- spec/record/includes_warning_spec.rb
|
362
|
+
- spec/record/items_created_key_configuration_spec.rb
|
360
363
|
- spec/record/loading_twice_spec.rb
|
361
364
|
- spec/record/mapping_spec.rb
|
362
365
|
- spec/record/model_name_spec.rb
|
@@ -507,6 +510,7 @@ test_files:
|
|
507
510
|
- spec/record/creation_failed_spec.rb
|
508
511
|
- spec/record/definitions_spec.rb
|
509
512
|
- spec/record/destroy_spec.rb
|
513
|
+
- spec/record/dig_configuration_spec.rb
|
510
514
|
- spec/record/endpoint_inheritance_spec.rb
|
511
515
|
- spec/record/endpoint_misconfiguration_spec.rb
|
512
516
|
- spec/record/endpoint_options_spec.rb
|
@@ -524,6 +528,7 @@ test_files:
|
|
524
528
|
- spec/record/includes_all_spec.rb
|
525
529
|
- spec/record/includes_spec.rb
|
526
530
|
- spec/record/includes_warning_spec.rb
|
531
|
+
- spec/record/items_created_key_configuration_spec.rb
|
527
532
|
- spec/record/loading_twice_spec.rb
|
528
533
|
- spec/record/mapping_spec.rb
|
529
534
|
- spec/record/model_name_spec.rb
|