lhs 12.0.3 → 12.1.0

Sign up to get free protection for your applications and to get access to all the features.
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