kontent-delivery-sdk-ruby 2.0.22 → 2.0.24

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +21 -21
  3. data/README.md +603 -602
  4. data/bin/console +14 -14
  5. data/bin/setup +8 -8
  6. data/lib/delivery/builders/image_transformation_builder.rb +272 -272
  7. data/lib/delivery/builders/url_builder.rb +123 -123
  8. data/lib/delivery/client/delivery_client.rb +184 -184
  9. data/lib/delivery/client/delivery_query.rb +302 -302
  10. data/lib/delivery/client/request_manager.rb +126 -127
  11. data/lib/delivery/models/content_item.rb +153 -153
  12. data/lib/delivery/models/content_type.rb +41 -41
  13. data/lib/delivery/models/language.rb +29 -29
  14. data/lib/delivery/models/pagination.rb +22 -22
  15. data/lib/delivery/models/taxonomy_group.rb +39 -39
  16. data/lib/delivery/query_parameters/filters.rb +201 -201
  17. data/lib/delivery/query_parameters/parameter_base.rb +56 -56
  18. data/lib/delivery/query_parameters/query_string.rb +78 -78
  19. data/lib/delivery/resolvers/content_link_resolver.rb +102 -102
  20. data/lib/delivery/resolvers/inline_content_item_resolver.rb +75 -75
  21. data/lib/delivery/resolvers/linked_item_resolver.rb +43 -37
  22. data/lib/delivery/responses/delivery_element_response.rb +34 -34
  23. data/lib/delivery/responses/delivery_item_listing_response.rb +54 -54
  24. data/lib/delivery/responses/delivery_item_response.rb +40 -40
  25. data/lib/delivery/responses/delivery_items_feed_response.rb +58 -58
  26. data/lib/delivery/responses/delivery_language_listing_response.rb +44 -44
  27. data/lib/delivery/responses/delivery_taxonomy_listing_response.rb +47 -47
  28. data/lib/delivery/responses/delivery_taxonomy_response.rb +33 -33
  29. data/lib/delivery/responses/delivery_type_listing_response.rb +46 -46
  30. data/lib/delivery/responses/delivery_type_response.rb +32 -32
  31. data/lib/delivery/responses/response_base.rb +39 -39
  32. data/lib/delivery/tests/401.json +5 -5
  33. data/lib/delivery/tests/429.json +4 -4
  34. data/lib/delivery/tests/fake_responder.rb +99 -105
  35. data/lib/delivery/tests/filtering/items_with_count.json +5384 -5384
  36. data/lib/delivery/tests/filtering/pagination.json +761 -761
  37. data/lib/delivery/tests/generic/items.json +5383 -5383
  38. data/lib/delivery/tests/generic/items/about_us.json +276 -276
  39. data/lib/delivery/tests/generic/items/aeropress_filters.json +155 -155
  40. data/lib/delivery/tests/generic/items/coffee_processing_techniques.json +565 -565
  41. data/lib/delivery/tests/generic/items/where_does_coffee_come_from_.json +598 -598
  42. data/lib/delivery/tests/generic/languages.json +23 -23
  43. data/lib/delivery/tests/generic/taxonomies.json +203 -203
  44. data/lib/delivery/tests/generic/taxonomies/manufacturer.json +29 -29
  45. data/lib/delivery/tests/generic/types.json +835 -835
  46. data/lib/delivery/tests/generic/types/brewer.json +88 -88
  47. data/lib/delivery/tests/generic/types/brewer/elements/product_status.json +5 -5
  48. data/lib/delivery/tests/items_feed/articles_feed_1.json +39 -39
  49. data/lib/delivery/tests/items_feed/articles_feed_2.json +78 -78
  50. data/lib/delivery/tests/items_feed/articles_feed_3.json +104 -104
  51. data/lib/kontent-delivery-sdk-ruby.rb +22 -22
  52. metadata +13 -32
@@ -1,128 +1,127 @@
1
- require 'rest-client'
2
- require 'dotenv/load'
3
-
4
- module Kentico
5
- module Kontent
6
- module Delivery
7
- class RequestManager
8
- class << self
9
- MAX_ATTEMPTS = 6
10
- MAX_DELAY_SECONDS = 30
11
- INITIAL_DELAY = 1
12
- RETRY_WHEN_CODE = [408, 429, 500, 502, 503, 504].freeze
13
- CODES_WITH_POSSIBLE_RETRY_HEADER = [429, 503].freeze
14
-
15
- def start(query, headers)
16
- @query = query
17
- @headers = headers
18
- @times_run = 1
19
- @delay = INITIAL_DELAY
20
- @url = @query.provide_url
21
- @total_delay = 0
22
- continue
23
- end
24
-
25
- private
26
-
27
- def should_retry(potential_response)
28
- return potential_response if @times_run == MAX_ATTEMPTS ||
29
- !RETRY_WHEN_CODE.include?(potential_response.http_code) ||
30
- !@query.with_retry_policy ||
31
- @total_delay >= MAX_DELAY_SECONDS
32
-
33
- next_delay
34
- sleep(@delay)
35
- @total_delay += @delay
36
- continue
37
- end
38
-
39
- # Generates a random delay based on times_run, then increases times_run
40
- def next_delay
41
- min = 0.8 * INITIAL_DELAY
42
- max = (1.2 * INITIAL_DELAY) * (2**@times_run)
43
- @delay = rand(min..max)
44
- @times_run += 1
45
- end
46
-
47
- def continue
48
- if ENV['TEST'] == '1'
49
- resp = Kentico::Kontent::Delivery::Tests::FakeResponder.get_response @query, @url, @headers
50
- return should_retry(resp) if resp.is_a? Kentico::Kontent::Delivery::Responses::ResponseBase
51
-
52
- make_response resp # resp is pure JSON
53
- else
54
- begin
55
- resp = RestClient.get @url, @headers
56
- rescue RestClient::ExceptionWithResponse => err
57
- should_retry Kentico::Kontent::Delivery::Responses::ResponseBase.new err.http_code, err.response
58
- rescue RestClient::SSLCertificateNotVerified => err
59
- should_retry Kentico::Kontent::Delivery::Responses::ResponseBase.new 500, err
60
- rescue SocketError => err
61
- should_retry Kentico::Kontent::Delivery::Responses::ResponseBase.new 500, err.message
62
- else
63
- make_response resp
64
- end
65
- end
66
- end
67
-
68
- # Converts a standard REST response based on the type of query.
69
- #
70
- # * *Returns*:
71
- # - An object derived from the Kentico::Kontent::Delivery::Responses::ResponseBase class
72
- def make_response(response)
73
- case @query.query_type
74
- when Kentico::Kontent::Delivery::QUERY_TYPE_ITEMS_FEED
75
- Kentico::Kontent::Delivery::Responses::DeliveryItemsFeedResponse.new(
76
- response.headers,
77
- response.body,
78
- @query
79
- )
80
- when Kentico::Kontent::Delivery::QUERY_TYPE_ITEMS
81
- respond_item response
82
- when Kentico::Kontent::Delivery::QUERY_TYPE_TYPES
83
- respond_type response
84
- when Kentico::Kontent::Delivery::QUERY_TYPE_TAXONOMIES
85
- respond_taxonomy response
86
- when Kentico::Kontent::Delivery::QUERY_TYPE_ELEMENT
87
- Kentico::Kontent::Delivery::Responses::DeliveryElementResponse.new response.headers, response.body
88
- when Kentico::Kontent::Delivery::QUERY_TYPE_LANGUAGES
89
- Kentico::Kontent::Delivery::Responses::DeliveryLanguageListingResponse.new response.headers, response.body
90
- end
91
- end
92
-
93
- def respond_type(response)
94
- if @query.code_name.nil?
95
- Kentico::Kontent::Delivery::Responses::DeliveryTypeListingResponse.new response.headers, response.body
96
- else
97
- Kentico::Kontent::Delivery::Responses::DeliveryTypeResponse.new response.headers, response.body
98
- end
99
- end
100
-
101
- def respond_taxonomy(response)
102
- if @query.code_name.nil?
103
- Kentico::Kontent::Delivery::Responses::DeliveryTaxonomyListingResponse.new response.headers, response.body
104
- else
105
- Kentico::Kontent::Delivery::Responses::DeliveryTaxonomyResponse.new response.headers, response.body
106
- end
107
- end
108
-
109
- def respond_item(response)
110
- if @query.code_name.nil?
111
- Kentico::Kontent::Delivery::Responses::DeliveryItemListingResponse.new(
112
- response.headers,
113
- response.body,
114
- @query
115
- )
116
- else
117
- Kentico::Kontent::Delivery::Responses::DeliveryItemResponse.new(
118
- response.headers,
119
- response.body,
120
- @query
121
- )
122
- end
123
- end
124
- end
125
- end
126
- end
127
- end
1
+ require 'rest-client'
2
+
3
+ module Kentico
4
+ module Kontent
5
+ module Delivery
6
+ class RequestManager
7
+ class << self
8
+ MAX_ATTEMPTS = 6
9
+ MAX_DELAY_SECONDS = 30
10
+ INITIAL_DELAY = 1
11
+ RETRY_WHEN_CODE = [408, 429, 500, 502, 503, 504].freeze
12
+ CODES_WITH_POSSIBLE_RETRY_HEADER = [429, 503].freeze
13
+
14
+ def start(query, headers)
15
+ @query = query
16
+ @headers = headers
17
+ @times_run = 1
18
+ @delay = INITIAL_DELAY
19
+ @url = @query.provide_url
20
+ @total_delay = 0
21
+ continue
22
+ end
23
+
24
+ private
25
+
26
+ def should_retry(potential_response)
27
+ return potential_response if @times_run == MAX_ATTEMPTS ||
28
+ !RETRY_WHEN_CODE.include?(potential_response.http_code) ||
29
+ !@query.with_retry_policy ||
30
+ @total_delay >= MAX_DELAY_SECONDS
31
+
32
+ next_delay
33
+ sleep(@delay)
34
+ @total_delay += @delay
35
+ continue
36
+ end
37
+
38
+ # Generates a random delay based on times_run, then increases times_run
39
+ def next_delay
40
+ min = 0.8 * INITIAL_DELAY
41
+ max = (1.2 * INITIAL_DELAY) * (2**@times_run)
42
+ @delay = rand(min..max)
43
+ @times_run += 1
44
+ end
45
+
46
+ def continue
47
+ if ENV['TEST'] == '1'
48
+ resp = Kentico::Kontent::Delivery::Tests::FakeResponder.get_response @query, @url, @headers
49
+ return should_retry(resp) if resp.is_a? Kentico::Kontent::Delivery::Responses::ResponseBase
50
+
51
+ make_response resp # resp is pure JSON
52
+ else
53
+ begin
54
+ resp = RestClient.get @url, @headers
55
+ rescue RestClient::ExceptionWithResponse => err
56
+ should_retry Kentico::Kontent::Delivery::Responses::ResponseBase.new err.http_code, err.response
57
+ rescue RestClient::SSLCertificateNotVerified => err
58
+ should_retry Kentico::Kontent::Delivery::Responses::ResponseBase.new 500, err
59
+ rescue SocketError => err
60
+ should_retry Kentico::Kontent::Delivery::Responses::ResponseBase.new 500, err.message
61
+ else
62
+ make_response resp
63
+ end
64
+ end
65
+ end
66
+
67
+ # Converts a standard REST response based on the type of query.
68
+ #
69
+ # * *Returns*:
70
+ # - An object derived from the Kentico::Kontent::Delivery::Responses::ResponseBase class
71
+ def make_response(response)
72
+ case @query.query_type
73
+ when Kentico::Kontent::Delivery::QUERY_TYPE_ITEMS_FEED
74
+ Kentico::Kontent::Delivery::Responses::DeliveryItemsFeedResponse.new(
75
+ response.headers,
76
+ response.body,
77
+ @query
78
+ )
79
+ when Kentico::Kontent::Delivery::QUERY_TYPE_ITEMS
80
+ respond_item response
81
+ when Kentico::Kontent::Delivery::QUERY_TYPE_TYPES
82
+ respond_type response
83
+ when Kentico::Kontent::Delivery::QUERY_TYPE_TAXONOMIES
84
+ respond_taxonomy response
85
+ when Kentico::Kontent::Delivery::QUERY_TYPE_ELEMENT
86
+ Kentico::Kontent::Delivery::Responses::DeliveryElementResponse.new response.headers, response.body
87
+ when Kentico::Kontent::Delivery::QUERY_TYPE_LANGUAGES
88
+ Kentico::Kontent::Delivery::Responses::DeliveryLanguageListingResponse.new response.headers, response.body
89
+ end
90
+ end
91
+
92
+ def respond_type(response)
93
+ if @query.code_name.nil?
94
+ Kentico::Kontent::Delivery::Responses::DeliveryTypeListingResponse.new response.headers, response.body
95
+ else
96
+ Kentico::Kontent::Delivery::Responses::DeliveryTypeResponse.new response.headers, response.body
97
+ end
98
+ end
99
+
100
+ def respond_taxonomy(response)
101
+ if @query.code_name.nil?
102
+ Kentico::Kontent::Delivery::Responses::DeliveryTaxonomyListingResponse.new response.headers, response.body
103
+ else
104
+ Kentico::Kontent::Delivery::Responses::DeliveryTaxonomyResponse.new response.headers, response.body
105
+ end
106
+ end
107
+
108
+ def respond_item(response)
109
+ if @query.code_name.nil?
110
+ Kentico::Kontent::Delivery::Responses::DeliveryItemListingResponse.new(
111
+ response.headers,
112
+ response.body,
113
+ @query
114
+ )
115
+ else
116
+ Kentico::Kontent::Delivery::Responses::DeliveryItemResponse.new(
117
+ response.headers,
118
+ response.body,
119
+ @query
120
+ )
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
128
127
  end
@@ -1,153 +1,153 @@
1
- require 'ostruct'
2
- require 'nokogiri'
3
-
4
- module Kentico
5
- module Kontent
6
- module Delivery
7
- class ContentItem
8
- attr_accessor :content_link_url_resolver,
9
- :inline_content_item_resolver
10
-
11
- # Parses the 'elements' JSON object as a dynamic OpenStruct object.
12
- #
13
- # * *Returns*:
14
- # - +OpenStruct+ The elements of the content item
15
- def elements
16
- @elements unless @elements.nil?
17
- @elements = JSON.parse(
18
- JSON.generate(@source['elements']),
19
- object_class: OpenStruct
20
- )
21
- end
22
-
23
- # Parses the 'system' JSON object as a dynamic OpenStruct object.
24
- #
25
- # * *Returns*:
26
- # - +OpenStruct+ The system properties of the content item
27
- def system
28
- @system unless @system.nil?
29
- @system = JSON.parse(
30
- JSON.generate(@source['system']),
31
- object_class: OpenStruct
32
- )
33
- end
34
-
35
- # Constructor.
36
- #
37
- # * *Args*:
38
- # - *source* (+JSON+) The response from a REST request for content items. The item may be on the root or under the 'item' node
39
- # - *content_link_url_resolver* ( Kentico::Kontent::Delivery::Resolvers::ContentLinkResolver )
40
- # - *inline_content_item_resolver* ( Kentico::Kontent::Delivery::Resolvers::InlineContentItemResolver )
41
- # - *linked_items_resolver* ( Kentico::Kontent::Delivery::Resolvers::LinkedItemResolver )
42
- def initialize(source, content_link_url_resolver, inline_content_item_resolver, linked_items_resolver)
43
- @source =
44
- if source['item'].nil?
45
- source
46
- else
47
- source['item']
48
- end
49
- @linked_items_resolver = linked_items_resolver
50
- self.content_link_url_resolver = content_link_url_resolver
51
- self.inline_content_item_resolver = inline_content_item_resolver
52
- end
53
-
54
- # Gets a string representation of the data stored in the element. Using this
55
- # method instead of directly accessing the +elements+ collection causes
56
- # the content to be resolved using the resolvers passed during instantiation.
57
- # See https://github.com/Kentico/kontent-delivery-sdk-ruby#resolving-links
58
- #
59
- # * *Args*:
60
- # - *code_name* (+string+) The code name of the desired element
61
- #
62
- # * *Returns*:
63
- # - +string+ The data converted to a string, resolved if the element is a 'rich_text' element
64
- def get_string(code_name)
65
- element = get_element code_name
66
- content = element['value']
67
-
68
- if element['type'] == 'rich_text'
69
- content = content_link_url_resolver.resolve content, element['links'] if should_resolve_links element
70
- inline_items = get_inline_items code_name
71
- content = inline_content_item_resolver.resolve content, inline_items if should_resolve_inline_content element
72
- end
73
- content.to_s
74
- end
75
-
76
- # Returns an array of assets inserted into the specified element of the
77
- # 'asset' type.
78
- #
79
- # * *Args*:
80
- # - *code_name* (+string+) The code name of the desired element
81
- #
82
- # * *Returns*:
83
- # - +Array+ The element's assets parsed as +OpenStruct+ objects
84
- def get_assets(code_name)
85
- element = get_element code_name
86
- element['value'].map { |n| OpenStruct.new(n) }
87
- end
88
-
89
- # Returns an array of ContentItems that are linked in a 'modular_content'
90
- # element.
91
- #
92
- # * *Args*:
93
- # - *code_name* (+string+) The code name of the desired element
94
- #
95
- # * *Returns*:
96
- # - +Array+ The element's linked items parsed as +ContentItem+ objects
97
- def get_links(code_name)
98
- element = get_element code_name
99
- get_linked_items element['value']
100
- end
101
-
102
- # Returns an array of ContentItems that are inserted as inline content
103
- # items or componenets of a 'rich_text' element.
104
- #
105
- # * *Args*:
106
- # - *code_name* (+string+) The code name of the desired element
107
- #
108
- # * *Returns*:
109
- # - +Array+ The element's inline content items parsed as +ContentItem+ objects
110
- def get_inline_items(code_name)
111
- element = get_element code_name
112
- get_linked_items element['modular_content']
113
- end
114
-
115
- private
116
-
117
- def should_resolve_links(element)
118
- !element['links'].nil? && !content_link_url_resolver.nil?
119
- end
120
-
121
- def should_resolve_inline_content(element)
122
- !element['modular_content'].nil? && !inline_content_item_resolver.nil?
123
- end
124
-
125
- # Gets the JSON object from the 'elements' collection with the specified key
126
- #
127
- # * *Args*:
128
- # - *code_name* (+string+, +symbol+) The code name or symbol of the desired element
129
- #
130
- # * *Returns*:
131
- # - +JSON+ The element as a JSON object
132
- #
133
- # * *Raises*:
134
- # - +ArgumentError+ if +code_name+ is +nil+
135
- def get_element(code_name)
136
- raise ArgumentError, "Argument 'code_name' cannot be null" if code_name.nil?
137
-
138
- code_name = code_name.to_s if code_name.is_a? Symbol
139
- @source['elements'][code_name]
140
- end
141
-
142
- def get_linked_items(codenames)
143
- return [] unless codenames.class == Array
144
-
145
- codenames.each_with_object([]) do |codename, items|
146
- item = @linked_items_resolver.resolve codename
147
- items << item if item
148
- end
149
- end
150
- end
151
- end
152
- end
153
- end
1
+ require 'ostruct'
2
+ require 'nokogiri'
3
+
4
+ module Kentico
5
+ module Kontent
6
+ module Delivery
7
+ class ContentItem
8
+ attr_accessor :content_link_url_resolver,
9
+ :inline_content_item_resolver
10
+
11
+ # Parses the 'elements' JSON object as a dynamic OpenStruct object.
12
+ #
13
+ # * *Returns*:
14
+ # - +OpenStruct+ The elements of the content item
15
+ def elements
16
+ @elements unless @elements.nil?
17
+ @elements = JSON.parse(
18
+ JSON.generate(@source['elements']),
19
+ object_class: OpenStruct
20
+ )
21
+ end
22
+
23
+ # Parses the 'system' JSON object as a dynamic OpenStruct object.
24
+ #
25
+ # * *Returns*:
26
+ # - +OpenStruct+ The system properties of the content item
27
+ def system
28
+ @system unless @system.nil?
29
+ @system = JSON.parse(
30
+ JSON.generate(@source['system']),
31
+ object_class: OpenStruct
32
+ )
33
+ end
34
+
35
+ # Constructor.
36
+ #
37
+ # * *Args*:
38
+ # - *source* (+JSON+) The response from a REST request for content items. The item may be on the root or under the 'item' node
39
+ # - *content_link_url_resolver* ( Kentico::Kontent::Delivery::Resolvers::ContentLinkResolver )
40
+ # - *inline_content_item_resolver* ( Kentico::Kontent::Delivery::Resolvers::InlineContentItemResolver )
41
+ # - *linked_items_resolver* ( Kentico::Kontent::Delivery::Resolvers::LinkedItemResolver )
42
+ def initialize(source, content_link_url_resolver, inline_content_item_resolver, linked_items_resolver)
43
+ @source =
44
+ if source['item'].nil?
45
+ source
46
+ else
47
+ source['item']
48
+ end
49
+ @linked_items_resolver = linked_items_resolver
50
+ self.content_link_url_resolver = content_link_url_resolver
51
+ self.inline_content_item_resolver = inline_content_item_resolver
52
+ end
53
+
54
+ # Gets a string representation of the data stored in the element. Using this
55
+ # method instead of directly accessing the +elements+ collection causes
56
+ # the content to be resolved using the resolvers passed during instantiation.
57
+ # See https://github.com/Kentico/kontent-delivery-sdk-ruby#resolving-links
58
+ #
59
+ # * *Args*:
60
+ # - *code_name* (+string+) The code name of the desired element
61
+ #
62
+ # * *Returns*:
63
+ # - +string+ The data converted to a string, resolved if the element is a 'rich_text' element
64
+ def get_string(code_name)
65
+ element = get_element code_name
66
+ content = element['value']
67
+
68
+ if element['type'] == 'rich_text'
69
+ content = content_link_url_resolver.resolve content, element['links'] if should_resolve_links element
70
+ inline_items = get_inline_items code_name
71
+ content = inline_content_item_resolver.resolve content, inline_items if should_resolve_inline_content element
72
+ end
73
+ content.to_s
74
+ end
75
+
76
+ # Returns an array of assets inserted into the specified element of the
77
+ # 'asset' type.
78
+ #
79
+ # * *Args*:
80
+ # - *code_name* (+string+) The code name of the desired element
81
+ #
82
+ # * *Returns*:
83
+ # - +Array+ The element's assets parsed as +OpenStruct+ objects
84
+ def get_assets(code_name)
85
+ element = get_element code_name
86
+ element['value'].map { |n| OpenStruct.new(n) }
87
+ end
88
+
89
+ # Returns an array of ContentItems that are linked in a 'modular_content'
90
+ # element.
91
+ #
92
+ # * *Args*:
93
+ # - *code_name* (+string+) The code name of the desired element
94
+ #
95
+ # * *Returns*:
96
+ # - +Array+ The element's linked items parsed as +ContentItem+ objects
97
+ def get_links(code_name)
98
+ element = get_element code_name
99
+ get_linked_items element['value']
100
+ end
101
+
102
+ # Returns an array of ContentItems that are inserted as inline content
103
+ # items or componenets of a 'rich_text' element.
104
+ #
105
+ # * *Args*:
106
+ # - *code_name* (+string+) The code name of the desired element
107
+ #
108
+ # * *Returns*:
109
+ # - +Array+ The element's inline content items parsed as +ContentItem+ objects
110
+ def get_inline_items(code_name)
111
+ element = get_element code_name
112
+ get_linked_items element['modular_content']
113
+ end
114
+
115
+ private
116
+
117
+ def should_resolve_links(element)
118
+ !element['links'].nil? && !content_link_url_resolver.nil?
119
+ end
120
+
121
+ def should_resolve_inline_content(element)
122
+ !element['modular_content'].nil? && !inline_content_item_resolver.nil?
123
+ end
124
+
125
+ # Gets the JSON object from the 'elements' collection with the specified key
126
+ #
127
+ # * *Args*:
128
+ # - *code_name* (+string+, +symbol+) The code name or symbol of the desired element
129
+ #
130
+ # * *Returns*:
131
+ # - +JSON+ The element as a JSON object
132
+ #
133
+ # * *Raises*:
134
+ # - +ArgumentError+ if +code_name+ is +nil+
135
+ def get_element(code_name)
136
+ raise ArgumentError, "Argument 'code_name' cannot be null" if code_name.nil?
137
+
138
+ code_name = code_name.to_s if code_name.is_a? Symbol
139
+ @source['elements'][code_name]
140
+ end
141
+
142
+ def get_linked_items(codenames)
143
+ return [] unless codenames.class == Array
144
+
145
+ codenames.each_with_object([]) do |codename, items|
146
+ item = @linked_items_resolver.resolve codename
147
+ items << item if item
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end