berkeley_library-tind 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +1 -1
  3. data/.idea/inspectionProfiles/Project_Default.xml +18 -0
  4. data/.idea/tind.iml +91 -91
  5. data/.ruby-version +1 -1
  6. data/CHANGES.md +33 -1
  7. data/README.md +15 -1
  8. data/berkeley_library-tind.gemspec +3 -2
  9. data/lib/berkeley_library/tind/api/api.rb +17 -11
  10. data/lib/berkeley_library/tind/api/collection.rb +1 -1
  11. data/lib/berkeley_library/tind/api/search.rb +2 -2
  12. data/lib/berkeley_library/tind/export/exporter.rb +1 -1
  13. data/lib/berkeley_library/tind/export/table.rb +1 -1
  14. data/lib/berkeley_library/tind/export/table_metrics.rb +1 -1
  15. data/lib/berkeley_library/tind/marc/xml_builder.rb +62 -0
  16. data/lib/berkeley_library/tind/marc/xml_reader.rb +32 -19
  17. data/lib/berkeley_library/tind/marc/xml_writer.rb +152 -0
  18. data/lib/berkeley_library/tind/module_info.rb +1 -1
  19. data/lib/berkeley_library/util/files.rb +39 -0
  20. data/lib/berkeley_library/util/ods/spreadsheet.rb +1 -1
  21. data/lib/berkeley_library/util/ods/xml/element_node.rb +1 -1
  22. data/spec/berkeley_library/tind/export/export_spec.rb +3 -1
  23. data/spec/berkeley_library/tind/export/table_spec.rb +2 -0
  24. data/spec/berkeley_library/tind/marc/xml_reader_spec.rb +42 -1
  25. data/spec/berkeley_library/tind/marc/xml_writer_spec.rb +156 -0
  26. data/spec/data/new-records.xml +46 -0
  27. metadata +36 -39
  28. data/Jenkinsfile +0 -18
  29. data/lib/berkeley_library/util/arrays.rb +0 -178
  30. data/lib/berkeley_library/util/logging.rb +0 -1
  31. data/lib/berkeley_library/util/paths.rb +0 -111
  32. data/lib/berkeley_library/util/stringios.rb +0 -30
  33. data/lib/berkeley_library/util/strings.rb +0 -42
  34. data/lib/berkeley_library/util/sys_exits.rb +0 -15
  35. data/lib/berkeley_library/util/times.rb +0 -22
  36. data/lib/berkeley_library/util/uris/appender.rb +0 -162
  37. data/lib/berkeley_library/util/uris/requester.rb +0 -62
  38. data/lib/berkeley_library/util/uris/validator.rb +0 -32
  39. data/lib/berkeley_library/util/uris.rb +0 -44
  40. data/spec/berkeley_library/util/arrays_spec.rb +0 -340
  41. data/spec/berkeley_library/util/paths_spec.rb +0 -90
  42. data/spec/berkeley_library/util/stringios_spec.rb +0 -34
  43. data/spec/berkeley_library/util/strings_spec.rb +0 -27
  44. data/spec/berkeley_library/util/times_spec.rb +0 -39
  45. data/spec/berkeley_library/util/uris_spec.rb +0 -118
@@ -1,162 +0,0 @@
1
- require 'berkeley_library/util/paths'
2
- require 'uri'
3
- require 'typesafe_enum'
4
-
5
- module BerkeleyLibrary
6
- module Util
7
- module URIs
8
-
9
- # Appends the specified paths to the path of the specified URI, removing any extraneous slashes,
10
- # and builds a new URI with that path and the same scheme, host, query, fragment, etc.
11
- # as the original.
12
- class Appender
13
- attr_reader :original_uri, :elements
14
-
15
- # Creates and invokes a new {Appender}.
16
- #
17
- # @param uri [URI, String] the original URI
18
- # @param elements [Array<String, Symbol>] the URI elements to join.
19
- # @raise URI::InvalidComponentError if appending the specified elements would create an invalid URI
20
- def initialize(uri, *elements)
21
- raise ArgumentError, 'uri cannot be nil' unless (@original_uri = URIs.uri_or_nil(uri))
22
-
23
- @elements = elements.map(&:to_s)
24
- @elements.each_with_index do |element, elem_index|
25
- next start_query_at(elem_index) if element.include?('?')
26
- next start_fragment_at(elem_index) if element.include?('#')
27
-
28
- add_element(element)
29
- end
30
- end
31
-
32
- # Returns the new URI.
33
- #
34
- # @return [URI] a new URI appending the joined path elements.
35
- # @raise URI::InvalidComponentError if appending the specified elements would create an invalid URI
36
- def to_uri
37
- original_uri.dup.tap do |new_uri|
38
- new_uri.path = Paths.join(original_uri.path, *path_elements)
39
- new_uri.query = query unless query_elements.empty?
40
- new_uri.fragment = fragment unless fragment_elements.empty?
41
- end
42
- end
43
-
44
- private
45
-
46
- def state
47
- @state ||= :path
48
- end
49
-
50
- def in_query?
51
- state == :query
52
- end
53
-
54
- def in_fragment?
55
- state == :fragment
56
- end
57
-
58
- def query
59
- query_elements.join
60
- end
61
-
62
- def fragment
63
- fragment_elements.join
64
- end
65
-
66
- def path_elements
67
- @path_elements ||= []
68
- end
69
-
70
- def query_elements
71
- @query_elements ||= [].tap { |e| e << original_uri.query if original_uri.query }
72
- end
73
-
74
- def fragment_elements
75
- @fragment_elements ||= [].tap { |e| e << original_uri.fragment if original_uri.fragment }
76
- end
77
-
78
- def start_query_at(elem_index)
79
- raise URI::InvalidComponentError, err_query_after_fragment(elem_index) if in_fragment?
80
- raise URI::InvalidComponentError, err_too_many_queries(elem_index) unless query_elements.empty?
81
-
82
- handle_query_start(elem_index)
83
- @state = :query
84
- end
85
-
86
- def start_fragment_at(elem_index)
87
- raise URI::InvalidComponentError, err_too_many_fragments(elem_index) unless fragment_elements.empty?
88
- raise URI::InvalidComponentError, err_query_after_fragment(elem_index) if query_after_fragment?(elem_index)
89
-
90
- handle_fragment_start(elem_index)
91
- @state = :fragment
92
- end
93
-
94
- def query_after_fragment?(elem_index)
95
- e = elements[elem_index]
96
- e.index('?', e.index('#'))
97
- end
98
-
99
- def add_element(e)
100
- return fragment_elements << e if in_fragment?
101
- return query_elements << e if in_query? || (e.include?('&') && !query_elements.empty?)
102
-
103
- path_elements << e
104
- end
105
-
106
- def handle_query_start(elem_index)
107
- element = elements[elem_index]
108
-
109
- # if there's anything before the '?', we treat that excess as a path element
110
- excess, q_start = split_around(element, element.index('?'))
111
- q_start = push_fragment_start(elem_index, q_start)
112
-
113
- query_elements << q_start
114
- path_elements << excess
115
- end
116
-
117
- # if the fragment starts in the middle of this element, we keep the part before
118
- # the fragment delimiter '#', and push the rest (w/'#') back onto the next element
119
- # to be parsed in the next iteration
120
- def push_fragment_start(elem_index, q_start)
121
- return q_start unless (f_index = q_start.index('#'))
122
-
123
- next_index = elem_index + 1
124
- q_start, q_next = split_around(q_start, f_index) # NOTE: this doesn't return the '#'
125
- elements[next_index] = "##{q_next}#{elements[next_index]}" # so we prepend one here
126
- q_start
127
- end
128
-
129
- def handle_fragment_start(elem_index)
130
- element = elements[elem_index]
131
-
132
- # if there's anything before the '#', we treat that excess as a path element,
133
- # or as a query element if there's a query
134
- excess, f_start = split_around(element, element.index('#'))
135
-
136
- fragment_elements << f_start
137
- if in_query?
138
- query_elements << excess
139
- else
140
- path_elements << excess
141
- end
142
- end
143
-
144
- def split_around(s, i)
145
- [s[0...i], s[(i + 1)..]]
146
- end
147
-
148
- def err_too_many_queries(elem_index)
149
- "#{elements[elem_index].inspect}: URI already has a query string: #{query.inspect}"
150
- end
151
-
152
- def err_query_after_fragment(elem_index)
153
- "#{elements[elem_index].inspect}: Query delimiter '?' cannot follow fragment delimeter '#'"
154
- end
155
-
156
- def err_too_many_fragments(elem_index)
157
- "#{elements[elem_index].inspect}: URI already has a fragment: #{fragment.inspect}"
158
- end
159
- end
160
- end
161
- end
162
- end
@@ -1,62 +0,0 @@
1
- require 'rest-client'
2
- require 'berkeley_library/util/uris/appender'
3
- require 'berkeley_library/util/uris/validator'
4
- require 'berkeley_library/logging'
5
-
6
- module BerkeleyLibrary
7
- module Util
8
- module URIs
9
- module Requester
10
- class << self
11
- include BerkeleyLibrary::Logging
12
-
13
- # Performs a GET request.
14
- #
15
- # @param uri [URI, String] the URI to GET
16
- # @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
17
- # @param headers [Hash] the request headers.
18
- # @return [String] the body as a string.
19
- # @raise [RestClient::Exception] in the event of an error.
20
- def get(uri, params = {}, headers = {})
21
- url_str = url_str_with_params(uri, params)
22
- resp = get_or_raise(url_str, headers)
23
- resp.body
24
- end
25
-
26
- private
27
-
28
- def url_str_with_params(uri, params)
29
- raise ArgumentError, 'uri cannot be nil' unless (url_str = Validator.url_str_or_nil(uri))
30
-
31
- elements = [].tap do |ee|
32
- ee << url_str
33
- ee << '?' unless url_str.include?('?')
34
- ee << URI.encode_www_form(params)
35
- end
36
-
37
- uri = Appender.new(*elements).to_uri
38
- uri.to_s
39
- end
40
-
41
- def get_or_raise(url_str, headers)
42
- resp = RestClient.get(url_str, headers)
43
- begin
44
- return resp if (status = resp.code) == 200
45
-
46
- raise(exception_for(resp, status))
47
- ensure
48
- logger.info("GET #{url_str} returned #{status}")
49
- end
50
- end
51
-
52
- def exception_for(resp, status)
53
- RestClient::RequestFailed.new(resp, status).tap do |ex|
54
- status_message = RestClient::STATUSES[status] || '(Unknown)'
55
- ex.message = "#{status} #{status_message}"
56
- end
57
- end
58
- end
59
- end
60
- end
61
- end
62
- end
@@ -1,32 +0,0 @@
1
- require 'uri'
2
-
3
- module BerkeleyLibrary
4
- module Util
5
- module URIs
6
- module Validator
7
- class << self
8
-
9
- # Returns the specified URL as a URI.
10
- # @param url [String, URI] the URL.
11
- # @return [URI] the URI.
12
- # @raise [URI::InvalidURIError] if `url` cannot be parsed as a URI.
13
- def uri_or_nil(url)
14
- return unless url
15
-
16
- # noinspection RubyYardReturnMatch
17
- url.is_a?(URI) ? url : URI.parse(url.to_s)
18
- end
19
-
20
- # Returns the specified URL as a string.
21
- # @param url [String, URI] the URL.
22
- # @return [String] the URL.
23
- # @raise [URI::InvalidURIError] if `url` cannot be parsed as a URI.
24
- def url_str_or_nil(url)
25
- uri = Validator.uri_or_nil(url)
26
- uri.to_s if uri
27
- end
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,44 +0,0 @@
1
- require 'berkeley_library/util/uris/appender'
2
- require 'berkeley_library/util/uris/requester'
3
- require 'berkeley_library/util/uris/validator'
4
-
5
- module BerkeleyLibrary
6
- module Util
7
- module URIs
8
- class << self
9
- include URIs
10
- end
11
-
12
- # Appends the specified paths to the path of the specified URI, removing any extraneous slashes
13
- # and merging additional query parameters, and returns a new URI with that path and the same scheme,
14
- # host, query, fragment, etc. as the original.
15
- #
16
- # @param uri [URI, String] the original URI
17
- # @param elements [Array<String, Symbol>] the URI elements to join.
18
- # @return [URI] a new URI appending the joined path elements.
19
- # @raise URI::InvalidComponentError if appending the specified elements would create an invalid URI
20
- def append(uri, *elements)
21
- Appender.new(uri, *elements).to_uri
22
- end
23
-
24
- # Performs a GET request.
25
- #
26
- # @param uri [URI, String] the URI to GET
27
- # @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
28
- # @param headers [Hash] the request headers.
29
- # @return [String] the body as a string.
30
- # @raise [RestClient::Exception] in the event of an error.
31
- def get(uri, params = {}, headers = {})
32
- Requester.get(uri, params, headers)
33
- end
34
-
35
- # Returns the specified URL as a URI.
36
- # @param url [String, URI] the URL.
37
- # @return [URI] the URI.
38
- # @raise [URI::InvalidURIError] if `url` cannot be parsed as a URI.
39
- def uri_or_nil(url)
40
- Validator.uri_or_nil(url)
41
- end
42
- end
43
- end
44
- end
@@ -1,340 +0,0 @@
1
- require 'spec_helper'
2
-
3
- require 'berkeley_library/util/arrays'
4
-
5
- module BerkeleyLibrary::Util
6
- describe Arrays do
7
- describe :ordered_superset do
8
- let(:sup) { %w[a b c d e] }
9
-
10
- it 'returns true for an identical subset' do
11
- expect(Arrays.ordered_superset?(superset: sup, subset: sup.dup)).to eq(true)
12
- end
13
-
14
- it 'returns true for an empty subset' do
15
- expect(Arrays.ordered_superset?(superset: sup, subset: [])).to eq(true)
16
- end
17
-
18
- it 'returns true for an exact sublist' do
19
- subs = [
20
- %w[a b c],
21
- %w[b c d],
22
- %w[c d e]
23
- ]
24
- subs.each do |sub|
25
- expect(Arrays.ordered_superset?(superset: sup, subset: sub)).to eq(true)
26
- end
27
- end
28
-
29
- it 'returns true when the superset interpolates extra elements' do
30
- subs = [
31
- %w[a c e],
32
- %w[b d],
33
- %w[a b d e]
34
- ]
35
- subs.each do |sub|
36
- expect(Arrays.ordered_superset?(superset: sup, subset: sub)).to eq(true)
37
- end
38
- end
39
-
40
- it 'returns false for a too-large subset' do
41
- sub = %w[a b c d e f g]
42
- expect(Arrays.ordered_superset?(superset: sup, subset: sub)).to eq(false)
43
- end
44
-
45
- it 'returns false when extra elements are present' do
46
- subs = [
47
- %w[a b c x],
48
- %w[x b c d],
49
- %w[c d x e]
50
- ]
51
- subs.each do |sub|
52
- expect(Arrays.ordered_superset?(superset: sup, subset: sub)).to eq(false)
53
- end
54
- end
55
- end
56
-
57
- describe :count_while do
58
- it 'returns the count of matching elements' do
59
- a = [1, 3, 5, 2, 4, 6, 7, 11, 13]
60
- expect(Arrays.count_while(values: a, &:odd?)).to eq(3)
61
- end
62
-
63
- it 'returns 0 if the first element does not match' do
64
- a = [2, 4, 6, 7, 11, 13]
65
- expect(Arrays.count_while(values: a, &:odd?)).to eq(0)
66
- end
67
-
68
- it 'returns an enumerator if not passed a block' do
69
- a = [1, 3, 5, 2, 4, 6, 7, 11, 13]
70
- e = Arrays.count_while(values: a)
71
- expect(e.each(&:odd?)).to eq(3)
72
- end
73
-
74
- it 'works on non-arrays' do
75
- a = [1, 3, 5, 2, 4, 6, 7, 11, 13]
76
- e = Enumerator.new { |y| a.each { |x| y << x } }
77
- expect(Arrays.count_while(values: e, &:odd?)).to eq(3)
78
- end
79
- end
80
-
81
- describe :find_indices do
82
- let(:target) { %w[a b c d e] }
83
-
84
- it 'returns identity indices for an identical subset' do
85
- expect(Arrays.find_indices(for_array: target.dup, in_array: target)).to eq([0, 1, 2, 3, 4])
86
- end
87
-
88
- it 'returns an empty array for an empty subset' do
89
- expect(Arrays.find_indices(for_array: [], in_array: target)).to eq([])
90
- end
91
-
92
- it 'returns the expected subindices for an exact sublist' do
93
- sources = {
94
- %w[a b c] => [0, 1, 2],
95
- %w[b c d] => [1, 2, 3],
96
- %w[c d e] => [2, 3, 4]
97
- }
98
- sources.each do |source, expected|
99
- expect(Arrays.find_indices(for_array: source, in_array: target)).to eq(expected)
100
- end
101
- end
102
-
103
- it 'returns nil for a too-large subset' do
104
- source = %w[a b c d e f g]
105
- expect(Arrays.find_indices(for_array: source, in_array: target)).to be_nil
106
- end
107
-
108
- it 'returns nil when extra elements are present' do
109
- sources = [
110
- %w[a b c x],
111
- %w[x b c d],
112
- %w[c d x e]
113
- ]
114
- sources.each do |source|
115
- expect(Arrays.find_indices(for_array: source, in_array: target)).to be_nil
116
- end
117
- end
118
-
119
- it 'takes a comparison block' do
120
- sub = %i[a c e]
121
- expect(Arrays.find_indices(for_array: sub, in_array: target) { |source, target| target == source.to_s }).to eq([0, 2, 4])
122
- end
123
- end
124
-
125
- describe :find_index do
126
- let(:arr) { [0, 2, 4, 6, 4] }
127
-
128
- it 'finds an index based on a value' do
129
- expect(Arrays.find_index(4, in_array: arr)).to eq(2)
130
- expect(Arrays.find_index(4, in_array: arr, start_index: 3)).to eq(4)
131
- end
132
-
133
- it 'finds an index based on a block' do
134
- expect(Arrays.find_index(in_array: arr) { |x| x > 3 }).to eq(2)
135
- expect(Arrays.find_index(in_array: arr, start_index: 3) { |x| x < 5 }).to eq(4)
136
- end
137
-
138
- it 'returns nil if no equal value found' do
139
- expect(Arrays.find_index(7, in_array: arr)).to be_nil
140
- expect(Arrays.find_index(2, in_array: arr, start_index: 2)).to be_nil
141
- end
142
-
143
- it 'returns nil if no matching value found' do
144
- expect(Arrays.find_index(in_array: arr, &:odd?)).to be_nil
145
- expect(Arrays.find_index(in_array: arr, start_index: 2) { |x| x < 4 }).to be_nil
146
- end
147
-
148
- # rubocop:disable Lint/Void
149
- it 'returns an enumerator if given no arguments' do
150
- e = Arrays.find_index(in_array: arr)
151
- expect(e.each { |x| x > 3 }).to eq(2)
152
-
153
- e = Arrays.find_index(in_array: arr, start_index: 3)
154
- expect(e.each { |x| x < 5 }).to eq(4)
155
- end
156
- # rubocop:enable Lint/Void
157
- end
158
-
159
- describe :merge do
160
- it 'merges two arrays' do
161
- a1 = [1, 2, 3]
162
- a2 = [2, 3, 4]
163
- expect(Arrays.merge(a1, a2)).to eq([1, 2, 3, 4])
164
- expect(Arrays.merge(a2, a1)).to eq([1, 2, 3, 4])
165
- end
166
-
167
- it 'merges disjoint arrays' do
168
- a1 = [1, 3, 5]
169
- a2 = [2, 4, 6]
170
- expect(Arrays.merge(a1, a2)).to eq([1, 3, 5, 2, 4, 6])
171
- end
172
-
173
- it 'preserves duplicates' do
174
- a1 = [1, 2, 2, 3, 4, 5]
175
- a2 = [2, 4, 4, 5, 5, 6]
176
-
177
- merged = Arrays.merge(a1, a2)
178
- [a1, a2].each do |a|
179
- expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
180
- end
181
-
182
- expected = [1, 2, 2, 3, 4, 4, 5, 5, 6]
183
- expect(merged.size).to eq(expected.size)
184
- expect(merged).to eq(expected)
185
- end
186
-
187
- it 'merges gappy arrays' do
188
- a1 = [1, 4, 5, 7, 9]
189
- a2 = [2, 3, 4, 7, 8, 9]
190
-
191
- merged = Arrays.merge(a1, a2)
192
- [a1, a2].each do |a|
193
- expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
194
- end
195
-
196
- expected = [1, 2, 3, 4, 5, 7, 8, 9]
197
- expect(merged.size).to eq(expected.size)
198
- expect(merged).to eq(expected)
199
- end
200
-
201
- it 'preserves order when merging arrays with duplicates' do
202
- a1 = [1, 3, 2, 2, 4]
203
- a2 = [1, 2, 3, 2, 4]
204
-
205
- merged = Arrays.merge(a1, a2)
206
- [a1, a2].each do |a|
207
- expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
208
- end
209
-
210
- expected = [1, 2, 3, 2, 2, 4]
211
- expect(merged.size).to eq(expected.size)
212
- expect(merged).to eq(expected)
213
- end
214
-
215
- it 'preserves nil' do
216
- a1 = [1, 3, nil, nil, 4]
217
- a2 = [1, nil, 3, nil, 4]
218
-
219
- merged = Arrays.merge(a1, a2)
220
- [a1, a2].each do |a|
221
- expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
222
- end
223
-
224
- expected = [1, nil, 3, nil, nil, 4]
225
- expect(merged.size).to eq(expected.size)
226
- expect(merged).to eq(expected)
227
- end
228
-
229
- it 'works with non-comparable types' do
230
- a1 = [1, 3, 'two', 'two', 4]
231
- a2 = [1, 'two', 3, 'two', 4]
232
-
233
- merged = Arrays.merge(a1, a2)
234
- [a1, a2].each do |a|
235
- expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
236
- end
237
-
238
- expected = [1, 'two', 3, 'two', 'two', 4]
239
- expect(merged.size).to eq(expected.size)
240
- expect(merged).to eq(expected)
241
- end
242
-
243
- it 'returns the larger array if the smaller is already a subarray' do
244
- a1 = [2, 3, 4]
245
- a2 = [1, 2, 3, 4, 5]
246
-
247
- merged = Arrays.merge(a1, a2)
248
- [a1, a2].each do |a|
249
- expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
250
- end
251
-
252
- expected = a2
253
- expect(merged.size).to eq(expected.size)
254
- expect(merged).to eq(expected)
255
-
256
- expect(Arrays.merge(a2, a1)).to eq(expected)
257
- end
258
-
259
- it 'sorts where sorting preserves order' do
260
- a1 = [1, 2, 3, 4, 5]
261
- a2 = [2, 3, 6, 9]
262
-
263
- merged = Arrays.merge(a1, a2)
264
- [a1, a2].each do |a|
265
- expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
266
- end
267
-
268
- expected = [1, 2, 3, 4, 5, 6, 9]
269
- expect(merged.size).to eq(expected.size)
270
- expect(merged).to eq(expected)
271
-
272
- expect(Arrays.merge(a2, a1)).to eq(expected)
273
- end
274
-
275
- it "doesn't muck up partial matches" do
276
- a1 = [1, 2, 3, 4, 5]
277
- a2 = [6, 9, 2, 3]
278
-
279
- merged = Arrays.merge(a1, a2)
280
- [a1, a2].each do |a|
281
- expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
282
- end
283
-
284
- expected = [1, 6, 9, 2, 3, 4, 5]
285
- expect(merged.size).to eq(expected.size)
286
- expect(merged).to eq(expected)
287
-
288
- expect(Arrays.merge(a2, a1)).to eq(expected)
289
- end
290
-
291
- it "doesn't much up disjoints" do
292
- a1 = [1, 2, 3, 4, 5]
293
- a2 = [6, 9]
294
-
295
- merged = Arrays.merge(a1, a2)
296
- [a1, a2].each do |a|
297
- expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
298
- end
299
-
300
- expected = [1, 2, 3, 4, 5, 6, 9]
301
- expect(merged.size).to eq(expected.size)
302
- expect(merged).to eq(expected)
303
-
304
- expect(Arrays.merge(a2, a1)).to eq(expected)
305
- end
306
-
307
- it 'works on a selection of random values' do
308
- next_int = ->(n) { (n * rand).to_i }
309
- rand_array = -> { (0...next_int.call(10)).map { next_int.call(10) } }
310
- aggregate_failures 'random values' do
311
- 100.times do
312
- a1 = rand_array.call
313
- a2 = rand_array.call
314
-
315
- merged = Arrays.merge(a1, a2)
316
- [a1, a2].each do |a|
317
- expect(Arrays.ordered_superset?(superset: merged, subset: a)).to eq(true), "merge(#{[a1.join, a2.join].inspect}): #{a.join} not found in #{merged.join}"
318
- end
319
- end
320
- end
321
- end
322
-
323
- end
324
-
325
- describe :invert do
326
- it 'inverts an array of ints' do
327
- expect(Arrays.invert([0, 2, 3])).to eq([0, nil, 1, 2])
328
- end
329
-
330
- it 'fails if values are not ints' do
331
- # noinspection RubyYardParamTypeMatch
332
- expect { Arrays.invert(%i[a b c]) }.to raise_error(TypeError)
333
- end
334
-
335
- it 'fails if given duplicate values' do
336
- expect { Arrays.invert([1, 2, 3, 2]) }.to raise_error(ArgumentError)
337
- end
338
- end
339
- end
340
- end