lenex-parser 3.0.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 +7 -0
- data/.rubocop.yml +21 -0
- data/.yardopts +2 -0
- data/LICENSE +21 -0
- data/README.md +796 -0
- data/Rakefile +43 -0
- data/bin/console +8 -0
- data/bin/setup +5 -0
- data/lenex-parser.gemspec +35 -0
- data/lib/lenex/document/serializer.rb +191 -0
- data/lib/lenex/document.rb +163 -0
- data/lib/lenex/parser/objects/age_date.rb +53 -0
- data/lib/lenex/parser/objects/age_group.rb +86 -0
- data/lib/lenex/parser/objects/athlete.rb +93 -0
- data/lib/lenex/parser/objects/bank.rb +56 -0
- data/lib/lenex/parser/objects/club.rb +101 -0
- data/lib/lenex/parser/objects/constructor.rb +51 -0
- data/lib/lenex/parser/objects/contact.rb +55 -0
- data/lib/lenex/parser/objects/entry.rb +70 -0
- data/lib/lenex/parser/objects/entry_schedule.rb +40 -0
- data/lib/lenex/parser/objects/event.rb +114 -0
- data/lib/lenex/parser/objects/facility.rb +58 -0
- data/lib/lenex/parser/objects/fee.rb +54 -0
- data/lib/lenex/parser/objects/fee_schedule.rb +26 -0
- data/lib/lenex/parser/objects/handicap.rb +86 -0
- data/lib/lenex/parser/objects/heat.rb +58 -0
- data/lib/lenex/parser/objects/host_club.rb +34 -0
- data/lib/lenex/parser/objects/judge.rb +55 -0
- data/lib/lenex/parser/objects/lenex.rb +72 -0
- data/lib/lenex/parser/objects/meet.rb +175 -0
- data/lib/lenex/parser/objects/meet_info.rb +60 -0
- data/lib/lenex/parser/objects/official.rb +70 -0
- data/lib/lenex/parser/objects/organizer.rb +34 -0
- data/lib/lenex/parser/objects/point_table.rb +54 -0
- data/lib/lenex/parser/objects/pool.rb +44 -0
- data/lib/lenex/parser/objects/qualify.rb +55 -0
- data/lib/lenex/parser/objects/ranking.rb +54 -0
- data/lib/lenex/parser/objects/record.rb +107 -0
- data/lib/lenex/parser/objects/record_athlete.rb +92 -0
- data/lib/lenex/parser/objects/record_list.rb +106 -0
- data/lib/lenex/parser/objects/record_relay.rb +62 -0
- data/lib/lenex/parser/objects/record_relay_position.rb +62 -0
- data/lib/lenex/parser/objects/relay.rb +93 -0
- data/lib/lenex/parser/objects/relay_entry.rb +81 -0
- data/lib/lenex/parser/objects/relay_position.rb +74 -0
- data/lib/lenex/parser/objects/relay_result.rb +85 -0
- data/lib/lenex/parser/objects/result.rb +76 -0
- data/lib/lenex/parser/objects/session.rb +107 -0
- data/lib/lenex/parser/objects/split.rb +53 -0
- data/lib/lenex/parser/objects/swim_style.rb +58 -0
- data/lib/lenex/parser/objects/time_standard.rb +55 -0
- data/lib/lenex/parser/objects/time_standard_list.rb +98 -0
- data/lib/lenex/parser/objects/time_standard_ref.rb +63 -0
- data/lib/lenex/parser/objects.rb +52 -0
- data/lib/lenex/parser/sax/document_handler.rb +184 -0
- data/lib/lenex/parser/version.rb +8 -0
- data/lib/lenex/parser/zip_source.rb +111 -0
- data/lib/lenex/parser.rb +184 -0
- data/lib/lenex-parser.rb +16 -0
- metadata +132 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing a POINTTABLE element.
|
|
7
|
+
class PointTable
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'name' => { key: :name, required: true },
|
|
10
|
+
'pointtableid' => { key: :point_table_id, required: false },
|
|
11
|
+
'version' => { key: :version, required: true }
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.map { |definition| definition[:key] }.freeze
|
|
15
|
+
private_constant :ATTRIBUTE_KEYS
|
|
16
|
+
|
|
17
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
18
|
+
|
|
19
|
+
def initialize(**attributes)
|
|
20
|
+
ATTRIBUTES.each_value do |definition|
|
|
21
|
+
key = definition[:key]
|
|
22
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.from_xml(element)
|
|
27
|
+
raise ::Lenex::Parser::ParseError, 'POINTTABLE element is required' unless element
|
|
28
|
+
|
|
29
|
+
attributes = extract_attributes(element)
|
|
30
|
+
|
|
31
|
+
new(**attributes)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.extract_attributes(element)
|
|
35
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, definition), collected|
|
|
36
|
+
value = element.attribute(attribute_name)&.value
|
|
37
|
+
ensure_required_attribute!(attribute_name, definition, value)
|
|
38
|
+
collected[definition[:key]] = value if value
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
private_class_method :extract_attributes
|
|
42
|
+
|
|
43
|
+
def self.ensure_required_attribute!(attribute_name, definition, value)
|
|
44
|
+
return unless definition[:required]
|
|
45
|
+
return unless value.nil? || value.strip.empty?
|
|
46
|
+
|
|
47
|
+
message = "POINTTABLE #{attribute_name} attribute is required"
|
|
48
|
+
raise ::Lenex::Parser::ParseError, message
|
|
49
|
+
end
|
|
50
|
+
private_class_method :ensure_required_attribute!
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing a POOL element.
|
|
7
|
+
class Pool
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'lanemax' => :lane_max,
|
|
10
|
+
'lanemin' => :lane_min,
|
|
11
|
+
'temperature' => :temperature,
|
|
12
|
+
'type' => :type
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.freeze
|
|
16
|
+
private_constant :ATTRIBUTE_KEYS
|
|
17
|
+
|
|
18
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
19
|
+
|
|
20
|
+
def initialize(**attributes)
|
|
21
|
+
ATTRIBUTES.each_value do |key|
|
|
22
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.from_xml(element)
|
|
27
|
+
raise ::Lenex::Parser::ParseError, 'POOL element is required' unless element
|
|
28
|
+
|
|
29
|
+
attributes = extract_attributes(element)
|
|
30
|
+
|
|
31
|
+
new(**attributes)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.extract_attributes(element)
|
|
35
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, key), collected|
|
|
36
|
+
value = element.attribute(attribute_name)&.value
|
|
37
|
+
collected[key] = value if value
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
private_class_method :extract_attributes
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing a QUALIFY element.
|
|
7
|
+
class Qualify
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'conversion' => { key: :conversion, required: false },
|
|
10
|
+
'from' => { key: :from, required: true },
|
|
11
|
+
'percent' => { key: :percent, required: false },
|
|
12
|
+
'until' => { key: :until, required: false }
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.map { |definition| definition[:key] }.freeze
|
|
16
|
+
private_constant :ATTRIBUTE_KEYS
|
|
17
|
+
|
|
18
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
19
|
+
|
|
20
|
+
def initialize(**attributes)
|
|
21
|
+
ATTRIBUTES.each_value do |definition|
|
|
22
|
+
key = definition[:key]
|
|
23
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.from_xml(element)
|
|
28
|
+
raise ::Lenex::Parser::ParseError, 'QUALIFY element is required' unless element
|
|
29
|
+
|
|
30
|
+
attributes = extract_attributes(element)
|
|
31
|
+
|
|
32
|
+
new(**attributes)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.extract_attributes(element)
|
|
36
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, definition), collected|
|
|
37
|
+
value = element.attribute(attribute_name)&.value
|
|
38
|
+
ensure_required_attribute!(attribute_name, definition, value)
|
|
39
|
+
collected[definition[:key]] = value if value
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
private_class_method :extract_attributes
|
|
43
|
+
|
|
44
|
+
def self.ensure_required_attribute!(attribute_name, definition, value)
|
|
45
|
+
return unless definition[:required]
|
|
46
|
+
return unless value.nil? || value.strip.empty?
|
|
47
|
+
|
|
48
|
+
message = "QUALIFY #{attribute_name} attribute is required"
|
|
49
|
+
raise ::Lenex::Parser::ParseError, message
|
|
50
|
+
end
|
|
51
|
+
private_class_method :ensure_required_attribute!
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing a RANKING element.
|
|
7
|
+
class Ranking
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'order' => { key: :order, required: false },
|
|
10
|
+
'place' => { key: :place, required: true },
|
|
11
|
+
'resultid' => { key: :result_id, required: true }
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.map { |definition| definition[:key] }.freeze
|
|
15
|
+
private_constant :ATTRIBUTE_KEYS
|
|
16
|
+
|
|
17
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
18
|
+
|
|
19
|
+
def initialize(**attributes)
|
|
20
|
+
ATTRIBUTES.each_value do |definition|
|
|
21
|
+
key = definition[:key]
|
|
22
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.from_xml(element)
|
|
27
|
+
raise ::Lenex::Parser::ParseError, 'RANKING element is required' unless element
|
|
28
|
+
|
|
29
|
+
attributes = extract_attributes(element)
|
|
30
|
+
|
|
31
|
+
new(**attributes)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.extract_attributes(element)
|
|
35
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, definition), collected|
|
|
36
|
+
value = element.attribute(attribute_name)&.value
|
|
37
|
+
ensure_required_attribute!(attribute_name, definition, value)
|
|
38
|
+
collected[definition[:key]] = value if value
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
private_class_method :extract_attributes
|
|
42
|
+
|
|
43
|
+
def self.ensure_required_attribute!(attribute_name, definition, value)
|
|
44
|
+
return unless definition[:required]
|
|
45
|
+
return unless value.nil? || value.strip.empty?
|
|
46
|
+
|
|
47
|
+
message = "RANKING #{attribute_name} attribute is required"
|
|
48
|
+
raise ::Lenex::Parser::ParseError, message
|
|
49
|
+
end
|
|
50
|
+
private_class_method :ensure_required_attribute!
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing a RECORD element.
|
|
7
|
+
class Record
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'swimtime' => { key: :swim_time, required: true },
|
|
10
|
+
'status' => { key: :status, required: false },
|
|
11
|
+
'comment' => { key: :comment, required: false }
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.map { |definition| definition[:key] }.freeze
|
|
15
|
+
private_constant :ATTRIBUTE_KEYS
|
|
16
|
+
|
|
17
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
18
|
+
attr_reader :meet_info, :swim_style, :athlete, :relay, :splits
|
|
19
|
+
|
|
20
|
+
def initialize(associations: {}, **attributes)
|
|
21
|
+
ATTRIBUTES.each_value do |definition|
|
|
22
|
+
key = definition[:key]
|
|
23
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
24
|
+
end
|
|
25
|
+
@meet_info = associations[:meet_info]
|
|
26
|
+
@swim_style = associations[:swim_style]
|
|
27
|
+
@athlete = associations[:athlete]
|
|
28
|
+
@relay = associations[:relay]
|
|
29
|
+
@splits = Array(associations.fetch(:splits, []))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.from_xml(element)
|
|
33
|
+
raise ::Lenex::Parser::ParseError, 'RECORD element is required' unless element
|
|
34
|
+
|
|
35
|
+
attributes = extract_attributes(element)
|
|
36
|
+
ensure_required_attributes!(attributes)
|
|
37
|
+
|
|
38
|
+
new(**attributes, associations: associations_from(element))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.extract_attributes(element)
|
|
42
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, definition), collected|
|
|
43
|
+
value = element.attribute(attribute_name)&.value
|
|
44
|
+
collected[definition[:key]] = value if value
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
private_class_method :extract_attributes
|
|
48
|
+
|
|
49
|
+
def self.ensure_required_attributes!(attributes)
|
|
50
|
+
value = attributes[:swim_time]
|
|
51
|
+
return unless value.nil? || value.strip.empty?
|
|
52
|
+
|
|
53
|
+
raise ::Lenex::Parser::ParseError, 'RECORD swimtime attribute is required'
|
|
54
|
+
end
|
|
55
|
+
private_class_method :ensure_required_attributes!
|
|
56
|
+
|
|
57
|
+
def self.meet_info_from(element)
|
|
58
|
+
return unless element
|
|
59
|
+
|
|
60
|
+
MeetInfo.from_xml(element)
|
|
61
|
+
end
|
|
62
|
+
private_class_method :meet_info_from
|
|
63
|
+
|
|
64
|
+
def self.swim_style_from(element)
|
|
65
|
+
raise ::Lenex::Parser::ParseError, 'RECORD SWIMSTYLE element is required' unless element
|
|
66
|
+
|
|
67
|
+
SwimStyle.from_xml(element)
|
|
68
|
+
end
|
|
69
|
+
private_class_method :swim_style_from
|
|
70
|
+
|
|
71
|
+
def self.athlete_from(element)
|
|
72
|
+
return unless element
|
|
73
|
+
|
|
74
|
+
RecordAthlete.from_xml(element)
|
|
75
|
+
end
|
|
76
|
+
private_class_method :athlete_from
|
|
77
|
+
|
|
78
|
+
def self.relay_from(element)
|
|
79
|
+
return unless element
|
|
80
|
+
|
|
81
|
+
RecordRelay.from_xml(element)
|
|
82
|
+
end
|
|
83
|
+
private_class_method :relay_from
|
|
84
|
+
|
|
85
|
+
def self.extract_splits(collection_element)
|
|
86
|
+
return [] unless collection_element
|
|
87
|
+
|
|
88
|
+
collection_element.xpath('SPLIT').map do |split_element|
|
|
89
|
+
Split.from_xml(split_element)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
private_class_method :extract_splits
|
|
93
|
+
|
|
94
|
+
def self.associations_from(element)
|
|
95
|
+
{
|
|
96
|
+
meet_info: meet_info_from(element.at_xpath('MEETINFO')),
|
|
97
|
+
swim_style: swim_style_from(element.at_xpath('SWIMSTYLE')),
|
|
98
|
+
athlete: athlete_from(element.at_xpath('ATHLETE')),
|
|
99
|
+
relay: relay_from(element.at_xpath('RELAY')),
|
|
100
|
+
splits: extract_splits(element.at_xpath('SPLITS'))
|
|
101
|
+
}
|
|
102
|
+
end
|
|
103
|
+
private_class_method :associations_from
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing an ATHLETE element within a record.
|
|
7
|
+
class RecordAthlete
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'athleteid' => { key: :athlete_id, required: false },
|
|
10
|
+
'birthdate' => { key: :birthdate, required: false },
|
|
11
|
+
'firstname' => { key: :first_name, required: false },
|
|
12
|
+
'firstname.en' => { key: :first_name_en, required: false },
|
|
13
|
+
'gender' => { key: :gender, required: true },
|
|
14
|
+
'lastname' => { key: :last_name, required: false },
|
|
15
|
+
'lastname.en' => { key: :last_name_en, required: false },
|
|
16
|
+
'level' => { key: :level, required: false },
|
|
17
|
+
'license' => { key: :license, required: false },
|
|
18
|
+
'license_dbs' => { key: :license_dbs, required: false },
|
|
19
|
+
'license_dsv' => { key: :license_dsv, required: false },
|
|
20
|
+
'license_ipc' => { key: :license_ipc, required: false },
|
|
21
|
+
'nameprefix' => { key: :name_prefix, required: false },
|
|
22
|
+
'nation' => { key: :nation, required: false },
|
|
23
|
+
'passport' => { key: :passport, required: false },
|
|
24
|
+
'status' => { key: :status, required: false },
|
|
25
|
+
'swrid' => { key: :swrid, required: false }
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
28
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.map { |definition| definition[:key] }.freeze
|
|
29
|
+
private_constant :ATTRIBUTE_KEYS
|
|
30
|
+
|
|
31
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
32
|
+
attr_reader :club, :handicap
|
|
33
|
+
|
|
34
|
+
def initialize(club: nil, handicap: nil, **attributes)
|
|
35
|
+
ATTRIBUTES.each_value do |definition|
|
|
36
|
+
key = definition[:key]
|
|
37
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
38
|
+
end
|
|
39
|
+
@club = club
|
|
40
|
+
@handicap = handicap
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.from_xml(element)
|
|
44
|
+
raise ::Lenex::Parser::ParseError, 'ATHLETE element is required' unless element
|
|
45
|
+
|
|
46
|
+
attributes = extract_attributes(element)
|
|
47
|
+
ensure_required_attributes!(attributes)
|
|
48
|
+
|
|
49
|
+
club = club_from(element.at_xpath('CLUB'))
|
|
50
|
+
handicap = Handicap.from_xml(element.at_xpath('HANDICAP'))
|
|
51
|
+
|
|
52
|
+
new(**attributes, club:, handicap:)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.extract_attributes(element)
|
|
56
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, definition), collected|
|
|
57
|
+
value = element.attribute(attribute_name)&.value
|
|
58
|
+
collected[definition[:key]] = value if value
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
private_class_method :extract_attributes
|
|
62
|
+
|
|
63
|
+
def self.ensure_required_attributes!(attributes)
|
|
64
|
+
ATTRIBUTES.each_value do |definition|
|
|
65
|
+
next unless definition[:required]
|
|
66
|
+
|
|
67
|
+
key = definition[:key]
|
|
68
|
+
value = attributes[key]
|
|
69
|
+
next unless value.nil? || value.strip.empty?
|
|
70
|
+
|
|
71
|
+
message = "ATHLETE #{ATTRIBUTE_NAME_FOR[key]} attribute is required"
|
|
72
|
+
raise ::Lenex::Parser::ParseError, message
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
private_class_method :ensure_required_attributes!
|
|
76
|
+
|
|
77
|
+
ATTRIBUTE_NAME_FOR = ATTRIBUTES.each_with_object({}) do |(attribute_name, definition),
|
|
78
|
+
mapping|
|
|
79
|
+
mapping[definition[:key]] = attribute_name
|
|
80
|
+
end.freeze
|
|
81
|
+
private_constant :ATTRIBUTE_NAME_FOR
|
|
82
|
+
|
|
83
|
+
def self.club_from(element)
|
|
84
|
+
return unless element
|
|
85
|
+
|
|
86
|
+
Club.from_xml(element)
|
|
87
|
+
end
|
|
88
|
+
private_class_method :club_from
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing a RECORDLIST element.
|
|
7
|
+
class RecordList
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'course' => { key: :course, required: true },
|
|
10
|
+
'gender' => { key: :gender, required: true },
|
|
11
|
+
'handicap' => { key: :handicap, required: false },
|
|
12
|
+
'name' => { key: :name, required: true },
|
|
13
|
+
'nation' => { key: :nation, required: false },
|
|
14
|
+
'order' => { key: :order, required: false },
|
|
15
|
+
'region' => { key: :region, required: false },
|
|
16
|
+
'type' => { key: :type, required: false },
|
|
17
|
+
'updated' => { key: :updated, required: false }
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
20
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.map { |definition| definition[:key] }.freeze
|
|
21
|
+
private_constant :ATTRIBUTE_KEYS
|
|
22
|
+
|
|
23
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
24
|
+
attr_reader :age_group, :records
|
|
25
|
+
|
|
26
|
+
def initialize(age_group: nil, records: [], **attributes)
|
|
27
|
+
ATTRIBUTES.each_value do |definition|
|
|
28
|
+
key = definition[:key]
|
|
29
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
30
|
+
end
|
|
31
|
+
@age_group = age_group
|
|
32
|
+
@records = Array(records)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.from_xml(element)
|
|
36
|
+
raise ::Lenex::Parser::ParseError, 'RECORDLIST element is required' unless element
|
|
37
|
+
|
|
38
|
+
attributes = extract_attributes(element)
|
|
39
|
+
ensure_required_attributes!(attributes)
|
|
40
|
+
|
|
41
|
+
age_group = age_group_from(element.at_xpath('AGEGROUP'))
|
|
42
|
+
records = extract_records(element.at_xpath('RECORDS'))
|
|
43
|
+
|
|
44
|
+
new(**attributes, age_group:, records:)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.extract_attributes(element)
|
|
48
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, definition), collected|
|
|
49
|
+
value = element.attribute(attribute_name)&.value
|
|
50
|
+
collected[definition[:key]] = value if value
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
private_class_method :extract_attributes
|
|
54
|
+
|
|
55
|
+
def self.ensure_required_attributes!(attributes)
|
|
56
|
+
%i[course gender name].each do |key|
|
|
57
|
+
value = attributes[key]
|
|
58
|
+
next unless value.nil? || value.strip.empty?
|
|
59
|
+
|
|
60
|
+
message = "RECORDLIST #{ATTRIBUTE_NAME_FOR[key]} attribute is required"
|
|
61
|
+
raise ::Lenex::Parser::ParseError, message
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
ensure_region_requires_nation!(attributes)
|
|
65
|
+
end
|
|
66
|
+
private_class_method :ensure_required_attributes!
|
|
67
|
+
|
|
68
|
+
def self.ensure_region_requires_nation!(attributes)
|
|
69
|
+
region = attributes[:region]
|
|
70
|
+
return unless region
|
|
71
|
+
|
|
72
|
+
nation = attributes[:nation]
|
|
73
|
+
return if nation && !nation.strip.empty?
|
|
74
|
+
|
|
75
|
+
message = 'RECORDLIST nation attribute is required when region attribute is present'
|
|
76
|
+
raise ::Lenex::Parser::ParseError, message
|
|
77
|
+
end
|
|
78
|
+
private_class_method :ensure_region_requires_nation!
|
|
79
|
+
|
|
80
|
+
ATTRIBUTE_NAME_FOR = ATTRIBUTES.each_with_object({}) do |(attribute_name, definition),
|
|
81
|
+
mapping|
|
|
82
|
+
mapping[definition[:key]] = attribute_name
|
|
83
|
+
end.freeze
|
|
84
|
+
private_constant :ATTRIBUTE_NAME_FOR
|
|
85
|
+
|
|
86
|
+
def self.age_group_from(element)
|
|
87
|
+
return unless element
|
|
88
|
+
|
|
89
|
+
AgeGroup.from_xml(element)
|
|
90
|
+
end
|
|
91
|
+
private_class_method :age_group_from
|
|
92
|
+
|
|
93
|
+
def self.extract_records(collection_element)
|
|
94
|
+
unless collection_element
|
|
95
|
+
raise ::Lenex::Parser::ParseError, 'RECORDLIST RECORDS element is required'
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
collection_element.xpath('RECORD').map do |record_element|
|
|
99
|
+
Record.from_xml(record_element)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
private_class_method :extract_records
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing a RELAY element within a record.
|
|
7
|
+
class RecordRelay
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'name' => :name
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.freeze
|
|
13
|
+
private_constant :ATTRIBUTE_KEYS
|
|
14
|
+
|
|
15
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
16
|
+
attr_reader :club, :relay_positions
|
|
17
|
+
|
|
18
|
+
def initialize(club: nil, relay_positions: [], **attributes)
|
|
19
|
+
ATTRIBUTES.each_value do |key|
|
|
20
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
21
|
+
end
|
|
22
|
+
@club = club
|
|
23
|
+
@relay_positions = Array(relay_positions)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.from_xml(element)
|
|
27
|
+
raise ::Lenex::Parser::ParseError, 'RELAY element is required' unless element
|
|
28
|
+
|
|
29
|
+
attributes = extract_attributes(element)
|
|
30
|
+
club = club_from(element.at_xpath('CLUB'))
|
|
31
|
+
relay_positions = extract_relay_positions(element.at_xpath('RELAYPOSITIONS'))
|
|
32
|
+
|
|
33
|
+
new(**attributes, club:, relay_positions:)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.extract_attributes(element)
|
|
37
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, key), collected|
|
|
38
|
+
value = element.attribute(attribute_name)&.value
|
|
39
|
+
collected[key] = value if value
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
private_class_method :extract_attributes
|
|
43
|
+
|
|
44
|
+
def self.club_from(element)
|
|
45
|
+
return unless element
|
|
46
|
+
|
|
47
|
+
Club.from_xml(element)
|
|
48
|
+
end
|
|
49
|
+
private_class_method :club_from
|
|
50
|
+
|
|
51
|
+
def self.extract_relay_positions(collection_element)
|
|
52
|
+
return [] unless collection_element
|
|
53
|
+
|
|
54
|
+
collection_element.xpath('RELAYPOSITION').map do |position_element|
|
|
55
|
+
RecordRelayPosition.from_xml(position_element)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
private_class_method :extract_relay_positions
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing a RELAYPOSITION element within a record.
|
|
7
|
+
class RecordRelayPosition
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'number' => { key: :number, required: true },
|
|
10
|
+
'reactiontime' => { key: :reaction_time, required: false },
|
|
11
|
+
'status' => { key: :status, required: false }
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.map { |definition| definition[:key] }.freeze
|
|
15
|
+
private_constant :ATTRIBUTE_KEYS
|
|
16
|
+
|
|
17
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
18
|
+
attr_reader :athlete
|
|
19
|
+
|
|
20
|
+
def initialize(athlete:, **attributes)
|
|
21
|
+
ATTRIBUTES.each_value do |definition|
|
|
22
|
+
key = definition[:key]
|
|
23
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
24
|
+
end
|
|
25
|
+
@athlete = athlete
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.from_xml(element)
|
|
29
|
+
raise ::Lenex::Parser::ParseError, 'RELAYPOSITION element is required' unless element
|
|
30
|
+
|
|
31
|
+
attributes = extract_attributes(element)
|
|
32
|
+
ensure_required_attributes!(attributes)
|
|
33
|
+
|
|
34
|
+
athlete_element = element.at_xpath('ATHLETE')
|
|
35
|
+
if athlete_element.nil?
|
|
36
|
+
raise ::Lenex::Parser::ParseError, 'RELAYPOSITION ATHLETE element is required'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
athlete = RecordAthlete.from_xml(athlete_element)
|
|
40
|
+
|
|
41
|
+
new(**attributes, athlete:)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.extract_attributes(element)
|
|
45
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, definition), collected|
|
|
46
|
+
value = element.attribute(attribute_name)&.value
|
|
47
|
+
collected[definition[:key]] = value if value
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
private_class_method :extract_attributes
|
|
51
|
+
|
|
52
|
+
def self.ensure_required_attributes!(attributes)
|
|
53
|
+
value = attributes[:number]
|
|
54
|
+
return unless value.nil? || value.strip.empty?
|
|
55
|
+
|
|
56
|
+
raise ::Lenex::Parser::ParseError, 'RELAYPOSITION number attribute is required'
|
|
57
|
+
end
|
|
58
|
+
private_class_method :ensure_required_attributes!
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|