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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4f0a470d4ad01eb619b6b2863640c8282c37ed21
4
- data.tar.gz: 8006e9ea160a63f470318b472a4e5d34623517d0
3
+ metadata.gz: 3a3fd78d0dd9d0ee88751c3b0ba15652bd4a15d9
4
+ data.tar.gz: 0629f8dfc7068fa195ce3fc5d556313b59c0a713
5
5
  SHA512:
6
- metadata.gz: 44e55fac18e3e4a4cf8ce9a7093ea10cc87e726dbba63d0943a3fb3bfaebbcf095d5fb9b32fffdc8c7f3055af1c5cdae20e377d199cdb6f1e5be63c5c01fb20e
7
- data.tar.gz: 8468fee414fe7f8ad40f2eebbd4ddeaefc1886eddf239b174cc7d2d686ebf9c835fbcb94eedd9e81f7d5a002d814bb35ca67d5c8c7e97895289be4e1b0c3a432
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
- In case of paginated resources it's important to know the difference between [count vs. length](#count-vs-length)
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
 
@@ -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 :InternalCollection, 'lhs/concerns/collection/internal_collection'
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[items_key]
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[items_key]
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[record.items_key].present?
37
- return false if !record && value[LHS::Record::Configuration::DEFAULT_ITEMS_KEY].present?
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[record.items_key].present?
44
- return true if value.is_a?(Hash) && !record && value[LHS::Record::Configuration::DEFAULT_ITEMS_KEY].present?
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
- (@configuration.try(:[], :items_key) || DEFAULT_ITEMS_KEY).to_sym
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
- (@configuration.try(:[], :limit_key) || :limit).to_sym
30
+ symbolize_unless_complex(
31
+ @configuration.try(:[], :limit_key) || :limit
32
+ )
25
33
  end
26
34
 
27
35
  def total_key
28
- (@configuration.try(:[], :total_key) || :total).to_sym
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
- (@configuration.try(:[], :pagination_key) || :offset).to_sym
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
- (@configuration.try(:[], :pagination_strategy) || :offset).to_sym
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[items_key] ||= []
128
- if target._raw[items_key].empty?
129
- target._raw[items_key] = addition.map(&:_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[items_key].each_with_index do |item, index|
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._raw[items_key].concat batch_data.raw_items
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]
@@ -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
- _raw.merge! data._raw
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[items_key]) ||
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
@@ -17,15 +17,15 @@ module LHS::Pagination
17
17
  end
18
18
 
19
19
  def total
20
- data._raw[_record.total_key.to_sym]
20
+ data._raw.dig(*_record.total_key)
21
21
  end
22
22
 
23
23
  def limit
24
- data._raw[_record.limit_key.to_sym] || DEFAULT_LIMIT
24
+ data._raw.dig(*_record.limit_key) || DEFAULT_LIMIT
25
25
  end
26
26
 
27
27
  def offset
28
- data._raw[_record.pagination_key.to_sym].presence || 0
28
+ data._raw.dig(*_record.pagination_key) || 0
29
29
  end
30
30
  alias current_page offset
31
31
  alias start offset
@@ -1,3 +1,3 @@
1
1
  module LHS
2
- VERSION = "12.0.3"
2
+ VERSION = "12.1.0"
3
3
  end
@@ -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.3
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-26 00:00:00.000000000 Z
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