media_types-serialization 1.0.3 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +7 -3
- data/.gitignore +12 -12
- data/.idea/.rakeTasks +5 -5
- data/.idea/inspectionProfiles/Project_Default.xml +5 -5
- data/.idea/runConfigurations/test.xml +19 -19
- data/.prettierrc +1 -0
- data/CHANGELOG.md +21 -9
- data/CODE_OF_CONDUCT.md +11 -11
- data/Gemfile +4 -4
- data/Gemfile.lock +63 -69
- data/LICENSE.txt +21 -21
- data/README.md +94 -47
- data/Rakefile +10 -10
- data/bin/console +14 -14
- data/bin/setup +8 -8
- data/lib/media_types/serialization.rb +61 -10
- data/lib/media_types/serialization/base.rb +8 -4
- data/lib/media_types/serialization/error.rb +11 -3
- data/lib/media_types/serialization/serialization_registration.rb +19 -9
- data/lib/media_types/serialization/serializers/problem_serializer.rb +23 -10
- data/lib/media_types/serialization/utils/accept_header.rb +77 -0
- data/lib/media_types/serialization/utils/accept_language_header.rb +82 -0
- data/lib/media_types/serialization/utils/header_list.rb +89 -0
- data/lib/media_types/serialization/version.rb +1 -1
- data/media_types-serialization.gemspec +0 -2
- metadata +11 -41
@@ -30,6 +30,10 @@ module MediaTypes
|
|
30
30
|
self.serializer_disable_wildcards = true
|
31
31
|
end
|
32
32
|
|
33
|
+
def enable_wildcards
|
34
|
+
self.serializer_disable_wildcards = false
|
35
|
+
end
|
36
|
+
|
33
37
|
def output(view: nil, version: nil, versions: nil, &block)
|
34
38
|
versions = [version] if versions.nil?
|
35
39
|
raise VersionsNotAnArrayError unless versions.is_a? Array
|
@@ -57,18 +61,18 @@ module MediaTypes
|
|
57
61
|
end
|
58
62
|
end
|
59
63
|
|
60
|
-
def output_alias(media_type_identifier, view: nil)
|
64
|
+
def output_alias(media_type_identifier, view: nil, hide_variant: false)
|
61
65
|
validator = serializer_validator.view(view)
|
62
66
|
victim_identifier = validator.identifier
|
63
67
|
|
64
|
-
serializer_output_registration.register_alias(self, media_type_identifier, victim_identifier, false, wildcards: !self.serializer_disable_wildcards)
|
68
|
+
serializer_output_registration.register_alias(self, media_type_identifier, victim_identifier, false, hide_variant, wildcards: !self.serializer_disable_wildcards)
|
65
69
|
end
|
66
70
|
|
67
|
-
def output_alias_optional(media_type_identifier, view: nil)
|
71
|
+
def output_alias_optional(media_type_identifier, view: nil, hide_variant: false)
|
68
72
|
validator = serializer_validator.view(view)
|
69
73
|
victim_identifier = validator.identifier
|
70
74
|
|
71
|
-
serializer_output_registration.register_alias(self, media_type_identifier, victim_identifier, true, wildcards: !self.serializer_disable_wildcards)
|
75
|
+
serializer_output_registration.register_alias(self, media_type_identifier, victim_identifier, true, hide_variant, wildcards: !self.serializer_disable_wildcards)
|
72
76
|
end
|
73
77
|
|
74
78
|
def input(view: nil, version: nil, versions: nil, &block)
|
@@ -73,21 +73,29 @@ module MediaTypes
|
|
73
73
|
def initialize(identifier, inout)
|
74
74
|
super(
|
75
75
|
"Serializer tried to define an #{inout}_alias that points to the media type identifier #{identifier} but no such #{inout} has been defined yet. Please move the #{inout} definition above the alias.\n\n" \
|
76
|
-
"Move the
|
76
|
+
"Move the #{inout} definition above the alias:\n" \
|
77
77
|
"\n" \
|
78
78
|
"class MySerializer < MediaTypes::Serialization::Base\n" \
|
79
79
|
"#...\n" \
|
80
|
-
"
|
80
|
+
"#{inout} do\n" \
|
81
81
|
" # ...\n" \
|
82
82
|
"end\n" \
|
83
83
|
"\n" \
|
84
|
-
"
|
84
|
+
"#{inout}_alias 'text/html'\n" \
|
85
85
|
"# ^----- move here\n" \
|
86
86
|
'end'
|
87
87
|
)
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
+
class VersionedAliasDefinitionError < ConfigurationError
|
92
|
+
def initialize(identifier, inout, prefix_match)
|
93
|
+
super(
|
94
|
+
"Serializer tried to define an #{inout}_alias that points to the media type identifier #{identifier} but no such #{inout} has been defined yet. An #{inout} named #{prefix_match} was found. Often this can be fixed by providing an #{inout} with a nil version."
|
95
|
+
)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
91
99
|
class DuplicateDefinitionError < ConfigurationError
|
92
100
|
def initialize(identifier, inout)
|
93
101
|
super("Serializer tried to define an #{inout} using the media type identifier #{identifier}, but another #{inout} was already defined with that identifier. Please remove one of the two.")
|
@@ -31,14 +31,23 @@ module MediaTypes
|
|
31
31
|
register_wildcards(identifier, registration) if wildcards && inout == :output
|
32
32
|
end
|
33
33
|
|
34
|
-
def register_alias(serializer, alias_identifier, target_identifier, optional, wildcards: true)
|
35
|
-
raise DuplicateDefinitionError.new(
|
34
|
+
def register_alias(serializer, alias_identifier, target_identifier, optional, hide_variant, wildcards: true)
|
35
|
+
raise DuplicateDefinitionError.new(alias_identifier, inout) if registrations.key? alias_identifier
|
36
36
|
|
37
|
-
|
37
|
+
unless registrations.key? target_identifier
|
38
|
+
potential_match = registrations.keys.find do |k|
|
39
|
+
k.starts_with? target_identifier
|
40
|
+
end
|
41
|
+
raise VersionedAliasDefinitionError.new(target_identifier, inout, potential_match) unless potential_match.nil?
|
42
|
+
raise UnbackedAliasDefinitionError.new(target_identifier, inout)
|
43
|
+
end
|
38
44
|
|
39
45
|
target = registrations[target_identifier]
|
40
46
|
|
41
|
-
|
47
|
+
result_content_type = alias_identifier
|
48
|
+
result_content_type += "; variant=#{target_identifier}" unless hide_variant
|
49
|
+
|
50
|
+
registration = SerializationAliasRegistration.new serializer, inout, target.validator, result_content_type, target, optional, hide_variant
|
42
51
|
registrations[alias_identifier] = registration
|
43
52
|
|
44
53
|
register_wildcards(alias_identifier, registration) if wildcards && inout == :output
|
@@ -103,7 +112,7 @@ module MediaTypes
|
|
103
112
|
private
|
104
113
|
|
105
114
|
def register_wildcards(identifier, registration)
|
106
|
-
new_alias = SerializationAliasRegistration.new registration.serializer, registration.inout, registration.validator, identifier, registration, true
|
115
|
+
new_alias = SerializationAliasRegistration.new registration.serializer, registration.inout, registration.validator, identifier, registration, true, true
|
107
116
|
|
108
117
|
registrations['*/*'] = new_alias unless has? '*/*'
|
109
118
|
|
@@ -160,14 +169,14 @@ module MediaTypes
|
|
160
169
|
begin
|
161
170
|
victim = MediaTypes::Serialization.json_decoder.call(victim)
|
162
171
|
validator.validate!(victim)
|
163
|
-
rescue MediaTypes::Scheme::ValidationError, Oj::ParseError, JSON::ParserError => inner
|
172
|
+
rescue MediaTypes::Scheme::ValidationError, Oj::ParseError, JSON::ParserError, EncodingError => inner
|
164
173
|
raise InputValidationFailedError, inner
|
165
174
|
end
|
166
175
|
else
|
167
176
|
begin
|
168
177
|
victim = MediaTypes::Serialization.json_decoder.call(victim)
|
169
178
|
validator.validate!(victim)
|
170
|
-
rescue MediaTypes::Scheme::ValidationError, JSON::ParserError => inner
|
179
|
+
rescue MediaTypes::Scheme::ValidationError, JSON::ParserError, EncodingError => inner
|
171
180
|
raise InputValidationFailedError, inner
|
172
181
|
end
|
173
182
|
end
|
@@ -204,9 +213,10 @@ module MediaTypes
|
|
204
213
|
|
205
214
|
# A registration that calls another registration when called.
|
206
215
|
class SerializationAliasRegistration < SerializationBaseRegistration
|
207
|
-
def initialize(serializer, inout, validator, display_identifier, target, optional)
|
216
|
+
def initialize(serializer, inout, validator, display_identifier, target, optional, hide_variant)
|
208
217
|
self.target = target
|
209
218
|
self.optional = optional
|
219
|
+
self.hide_variant = hide_variant
|
210
220
|
super(serializer, inout, validator, display_identifier)
|
211
221
|
end
|
212
222
|
|
@@ -229,7 +239,7 @@ module MediaTypes
|
|
229
239
|
target.call(victim, context, dsl: dsl, raw: raw)
|
230
240
|
end
|
231
241
|
|
232
|
-
attr_accessor :target, :optional
|
242
|
+
attr_accessor :target, :optional, :hide_variant
|
233
243
|
end
|
234
244
|
end
|
235
245
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'erb'
|
4
4
|
require 'media_types/serialization/base'
|
5
|
+
require 'media_types/serialization/utils/accept_language_header'
|
5
6
|
|
6
7
|
module MediaTypes
|
7
8
|
module Serialization
|
@@ -9,13 +10,19 @@ module MediaTypes
|
|
9
10
|
class ProblemSerializer < MediaTypes::Serialization::Base
|
10
11
|
|
11
12
|
unvalidated 'application/vnd.delftsolutions.problem'
|
13
|
+
disable_wildcards
|
12
14
|
|
13
15
|
output do |problem, _, context|
|
14
16
|
raise 'No translations defined, add at least one title' unless problem.translations.keys.any?
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
accept_language_header = Utils::AcceptLanguageHeader.new(context.request.get_header(HEADER_ACCEPT_LANGUAGE) || '')
|
19
|
+
translation_entry = accept_language_header.map do |locale|
|
20
|
+
problem.translations.keys.find do |l|
|
21
|
+
l.start_with? locale.locale
|
22
|
+
end
|
23
|
+
end.compact.first || problem.translations.keys.first
|
24
|
+
translation = problem.translations[translation_entry]
|
25
|
+
|
19
26
|
title = translation[:title]
|
20
27
|
detail = translation[:detail] || problem.error.message
|
21
28
|
|
@@ -33,12 +40,19 @@ module MediaTypes
|
|
33
40
|
output_alias 'application/problem+json'
|
34
41
|
|
35
42
|
output_raw view: :html do |problem, _, context|
|
36
|
-
|
37
|
-
|
38
|
-
|
43
|
+
accept_language_header = Utils::AcceptLanguageHeader.new(context.request.get_header(HEADER_ACCEPT_LANGUAGE) || '')
|
44
|
+
translation_entry = accept_language_header.map do |locale|
|
45
|
+
problem.translations.keys.find do |l|
|
46
|
+
l.starts_with? locale.locale
|
47
|
+
end
|
48
|
+
end.compact.first || problem.translations.keys.first
|
49
|
+
translation = problem.translations[translation_entry]
|
50
|
+
|
39
51
|
title = translation[:title]
|
40
52
|
detail = translation[:detail] || problem.error.message
|
41
53
|
|
54
|
+
detail_lang = translation[:detail].nil? ? 'en' : translation_entry
|
55
|
+
|
42
56
|
input = OpenStruct.new(
|
43
57
|
title: title,
|
44
58
|
detail: detail,
|
@@ -61,11 +75,11 @@ module MediaTypes
|
|
61
75
|
</header>
|
62
76
|
<section id="content">
|
63
77
|
<nav>
|
64
|
-
<section id="description">
|
78
|
+
<section id="description" lang="#{translation_entry}">
|
65
79
|
<h2><a href="<%= help_url %>"><%= CGI::escapeHTML(title) %></a></h2>
|
66
80
|
</section>
|
67
81
|
</nav>
|
68
|
-
<main>
|
82
|
+
<main lang="#{detail_lang}">
|
69
83
|
<p><%= detail %>
|
70
84
|
</main>
|
71
85
|
</section>
|
@@ -76,8 +90,7 @@ module MediaTypes
|
|
76
90
|
template.result(input.instance_eval { binding })
|
77
91
|
end
|
78
92
|
|
79
|
-
|
80
|
-
self.serializer_output_registration.registrations.delete('*/*')
|
93
|
+
enable_wildcards
|
81
94
|
|
82
95
|
output_alias_optional 'text/html', view: :html
|
83
96
|
|
@@ -0,0 +1,77 @@
|
|
1
|
+
=begin
|
2
|
+
The MIT License (MIT)
|
3
|
+
|
4
|
+
Copyright (c) 2019 Derk-Jan Karrenbeld
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
23
|
+
=end
|
24
|
+
|
25
|
+
require 'media_types/serialization/utils/header_list'
|
26
|
+
|
27
|
+
module MediaTypes
|
28
|
+
module Serialization
|
29
|
+
module Utils
|
30
|
+
class AcceptHeader < DelegateClass(Array)
|
31
|
+
def initialize(value)
|
32
|
+
__setobj__ HeaderList.new(value, entry_klazz: AcceptHeader::Entry)
|
33
|
+
end
|
34
|
+
|
35
|
+
class Entry
|
36
|
+
def initialize(media_type, index:, parameters:)
|
37
|
+
self.media_type = media_type
|
38
|
+
self.parameters = parameters
|
39
|
+
self.index = index
|
40
|
+
|
41
|
+
freeze
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :media_type
|
45
|
+
|
46
|
+
# noinspection RubyInstanceMethodNamingConvention
|
47
|
+
def q
|
48
|
+
parameters.fetch(:q) { 1.0 }.to_f
|
49
|
+
end
|
50
|
+
|
51
|
+
def <=>(other)
|
52
|
+
quality = other.q <=> q
|
53
|
+
return quality unless quality.zero?
|
54
|
+
index <=> other.send(:index)
|
55
|
+
end
|
56
|
+
|
57
|
+
def [](parameter)
|
58
|
+
parameters.fetch(String(parameter).to_sym)
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_header
|
62
|
+
to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
[media_type].concat(parameters.map { |k, v| "#{k}=#{v}" }).compact.reject(&:empty?).join('; ')
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
attr_writer :media_type
|
72
|
+
attr_accessor :parameters, :index
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
=begin
|
2
|
+
The MIT License (MIT)
|
3
|
+
|
4
|
+
Copyright (c) 2019 Derk-Jan Karrenbeld
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
23
|
+
=end
|
24
|
+
|
25
|
+
require 'media_types/serialization/utils/header_list'
|
26
|
+
|
27
|
+
module MediaTypes
|
28
|
+
module Serialization
|
29
|
+
module Utils
|
30
|
+
class AcceptLanguageHeader < DelegateClass(Array)
|
31
|
+
def initialize(value)
|
32
|
+
__setobj__ HeaderList.new(value, entry_klazz: Entry)
|
33
|
+
end
|
34
|
+
|
35
|
+
class Entry
|
36
|
+
|
37
|
+
DELIMITER = '-'
|
38
|
+
|
39
|
+
attr_reader :locale, :region, :language
|
40
|
+
|
41
|
+
def initialize(locale, index:, parameters:)
|
42
|
+
self.locale = locale
|
43
|
+
# TODO: support extlang correctly, maybe we don't even need this
|
44
|
+
self.language, self.region = locale.split(DELIMITER)
|
45
|
+
self.parameters = parameters
|
46
|
+
self.index = index
|
47
|
+
|
48
|
+
freeze
|
49
|
+
end
|
50
|
+
|
51
|
+
# noinspection RubyInstanceMethodNamingConvention
|
52
|
+
def q
|
53
|
+
parameters.fetch(:q) { 1.0 }.to_f
|
54
|
+
end
|
55
|
+
|
56
|
+
def <=>(other)
|
57
|
+
quality = other.q <=> q
|
58
|
+
return quality unless quality.zero?
|
59
|
+
index <=> other.send(:index)
|
60
|
+
end
|
61
|
+
|
62
|
+
def [](parameter)
|
63
|
+
parameters.fetch(String(parameter).to_sym)
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_header
|
67
|
+
to_s
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s
|
71
|
+
[locale].concat(parameters.map { |k, v| "#{k}=#{v}" }).compact.reject(&:empty?).join('; ')
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
attr_writer :locale, :region, :language
|
77
|
+
attr_accessor :parameters, :index
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
=begin
|
2
|
+
The MIT License (MIT)
|
3
|
+
|
4
|
+
Copyright (c) 2019 Derk-Jan Karrenbeld
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
23
|
+
=end
|
24
|
+
|
25
|
+
module MediaTypes
|
26
|
+
module Serialization
|
27
|
+
module Utils
|
28
|
+
##
|
29
|
+
# @example Accept values
|
30
|
+
#
|
31
|
+
# class AcceptHeader < DelegateClass(Array)
|
32
|
+
# def initialize(value)
|
33
|
+
# super MediaTypes::Serialization::Utils::HeaderList.new(value, entry_klazz: AcceptHeader::Entry)
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# class Entry
|
37
|
+
# def initialize(media_type, index: parameters:)
|
38
|
+
# ...
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# def q
|
42
|
+
# parameters.fetch(:q) { 1.0 }.to_f
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# def <=>(other)
|
46
|
+
# quality = other.q <=> q
|
47
|
+
# return quality unless quality.zero?
|
48
|
+
# index <=> other.index
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# Accept.new(['*/*; q=0.1', 'application/json, text/html; q=0.8'])
|
54
|
+
# # => List['application/json', 'text/html', '*/*']
|
55
|
+
#
|
56
|
+
module HeaderList
|
57
|
+
HEADER_DELIMITER = ','
|
58
|
+
PARAMETER_DELIMITER = ';'
|
59
|
+
|
60
|
+
module_function
|
61
|
+
|
62
|
+
def parse(combined, entry_klazz:)
|
63
|
+
Array(combined).map { |line| line.split(HEADER_DELIMITER) }.flatten.each_with_index.map do |entry, index|
|
64
|
+
value, *parameters = entry.strip.split(PARAMETER_DELIMITER)
|
65
|
+
indexed_parameters = ::Hash[Array(parameters).map { |p| p.strip.split('=') }].transform_keys!(&:to_sym)
|
66
|
+
entry_klazz.new(value, index: index, parameters: indexed_parameters)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def new(combined, entry_klazz:)
|
71
|
+
result = parse(combined, entry_klazz: entry_klazz)
|
72
|
+
entry_klazz.instance_methods(false).include?(:<=>) ? result.sort! : result
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_header(list)
|
76
|
+
# noinspection RubyBlockToMethodReference
|
77
|
+
list.map { |entry| stringify_entry(entry) }
|
78
|
+
.join("#{HEADER_DELIMITER} ")
|
79
|
+
end
|
80
|
+
|
81
|
+
def stringify_entry(entry)
|
82
|
+
return entry.to_header if entry.respond_to?(:to_header)
|
83
|
+
return entry.to_s if entry.respond_to?(:to_s)
|
84
|
+
entry.inspect
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|