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,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing a BANK element.
|
|
7
|
+
class Bank
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'accountholder' => { key: :account_holder, required: false },
|
|
10
|
+
'bic' => { key: :bic, required: false },
|
|
11
|
+
'iban' => { key: :iban, required: true },
|
|
12
|
+
'name' => { key: :name, required: false },
|
|
13
|
+
'note' => { key: :note, required: false }
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.map { |definition| definition[:key] }.freeze
|
|
17
|
+
private_constant :ATTRIBUTE_KEYS
|
|
18
|
+
|
|
19
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
20
|
+
|
|
21
|
+
def initialize(**attributes)
|
|
22
|
+
ATTRIBUTES.each_value do |definition|
|
|
23
|
+
key = definition[:key]
|
|
24
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.from_xml(element)
|
|
29
|
+
raise ::Lenex::Parser::ParseError, 'BANK element is required' unless element
|
|
30
|
+
|
|
31
|
+
attributes = extract_attributes(element)
|
|
32
|
+
|
|
33
|
+
new(**attributes)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.extract_attributes(element)
|
|
37
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, definition), collected|
|
|
38
|
+
value = element.attribute(attribute_name)&.value
|
|
39
|
+
ensure_required_attribute!(attribute_name, definition, value)
|
|
40
|
+
collected[definition[:key]] = value if value
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
private_class_method :extract_attributes
|
|
44
|
+
|
|
45
|
+
def self.ensure_required_attribute!(attribute_name, definition, value)
|
|
46
|
+
return unless definition[:required]
|
|
47
|
+
return unless value.nil? || value.strip.empty?
|
|
48
|
+
|
|
49
|
+
message = "BANK #{attribute_name} attribute is required"
|
|
50
|
+
raise ::Lenex::Parser::ParseError, message
|
|
51
|
+
end
|
|
52
|
+
private_class_method :ensure_required_attribute!
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing a CLUB element.
|
|
7
|
+
class Club
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'name' => { key: :name, required: true },
|
|
10
|
+
'name.en' => { key: :name_en, required: false },
|
|
11
|
+
'shortname' => { key: :shortname, required: false },
|
|
12
|
+
'shortname.en' => { key: :shortname_en, required: false },
|
|
13
|
+
'code' => { key: :code, required: false },
|
|
14
|
+
'nation' => { key: :nation, required: false },
|
|
15
|
+
'number' => { key: :number, required: false },
|
|
16
|
+
'region' => { key: :region, required: false },
|
|
17
|
+
'swrid' => { key: :swrid, required: false },
|
|
18
|
+
'type' => { key: :type, required: false }
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.map { |definition| definition[:key] }.freeze
|
|
22
|
+
private_constant :ATTRIBUTE_KEYS
|
|
23
|
+
|
|
24
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
25
|
+
attr_reader :contact, :athletes, :officials, :relays
|
|
26
|
+
|
|
27
|
+
def initialize(contact: nil, athletes: [], officials: [], relays: [], **attributes)
|
|
28
|
+
ATTRIBUTES.each_value do |definition|
|
|
29
|
+
key = definition[:key]
|
|
30
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
31
|
+
end
|
|
32
|
+
@contact = contact
|
|
33
|
+
@athletes = Array(athletes)
|
|
34
|
+
@officials = Array(officials)
|
|
35
|
+
@relays = Array(relays)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.from_xml(element)
|
|
39
|
+
raise ::Lenex::Parser::ParseError, 'CLUB element is required' unless element
|
|
40
|
+
|
|
41
|
+
attributes = extract_attributes(element)
|
|
42
|
+
ensure_required_attributes!(attributes)
|
|
43
|
+
|
|
44
|
+
contact_element = element.at_xpath('CONTACT')
|
|
45
|
+
contact = contact_element ? Contact.from_xml(contact_element) : nil
|
|
46
|
+
athletes = extract_athletes(element.at_xpath('ATHLETES'))
|
|
47
|
+
officials = extract_officials(element.at_xpath('OFFICIALS'))
|
|
48
|
+
relays = extract_relays(element.at_xpath('RELAYS'))
|
|
49
|
+
|
|
50
|
+
new(**attributes, contact:, athletes:, officials:, relays:)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.extract_attributes(element)
|
|
54
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, definition), collected|
|
|
55
|
+
value = element.attribute(attribute_name)&.value
|
|
56
|
+
collected[definition[:key]] = value if value
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
private_class_method :extract_attributes
|
|
60
|
+
|
|
61
|
+
def self.ensure_required_attributes!(attributes)
|
|
62
|
+
name = attributes[:name]
|
|
63
|
+
type = attributes[:type]
|
|
64
|
+
|
|
65
|
+
return unless name.nil? || name.strip.empty?
|
|
66
|
+
return if type == 'UNATTACHED'
|
|
67
|
+
|
|
68
|
+
raise ::Lenex::Parser::ParseError, 'CLUB name attribute is required'
|
|
69
|
+
end
|
|
70
|
+
private_class_method :ensure_required_attributes!
|
|
71
|
+
|
|
72
|
+
def self.extract_athletes(collection_element)
|
|
73
|
+
return [] unless collection_element
|
|
74
|
+
|
|
75
|
+
collection_element.xpath('ATHLETE').map do |athlete_element|
|
|
76
|
+
Athlete.from_xml(athlete_element)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
private_class_method :extract_athletes
|
|
80
|
+
|
|
81
|
+
def self.extract_officials(collection_element)
|
|
82
|
+
return [] unless collection_element
|
|
83
|
+
|
|
84
|
+
collection_element.xpath('OFFICIAL').map do |official_element|
|
|
85
|
+
Official.from_xml(official_element)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
private_class_method :extract_officials
|
|
89
|
+
|
|
90
|
+
def self.extract_relays(collection_element)
|
|
91
|
+
return [] unless collection_element
|
|
92
|
+
|
|
93
|
+
collection_element.xpath('RELAY').map do |relay_element|
|
|
94
|
+
Relay.from_xml(relay_element)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
private_class_method :extract_relays
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing a CONSTRUCTOR element.
|
|
7
|
+
class Constructor
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'name' => :name,
|
|
10
|
+
'registration' => :registration,
|
|
11
|
+
'version' => :version
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.freeze
|
|
15
|
+
private_constant :ATTRIBUTE_KEYS
|
|
16
|
+
|
|
17
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
18
|
+
attr_reader :contact
|
|
19
|
+
|
|
20
|
+
def initialize(name:, registration:, version:, contact:)
|
|
21
|
+
@name = name
|
|
22
|
+
@registration = registration
|
|
23
|
+
@version = version
|
|
24
|
+
@contact = contact
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.from_xml(element)
|
|
28
|
+
raise ::Lenex::Parser::ParseError, 'CONSTRUCTOR element is required' unless element
|
|
29
|
+
|
|
30
|
+
data = attributes_from(element)
|
|
31
|
+
contact = Contact.from_xml(element.at_xpath('CONTACT'), email_required: true)
|
|
32
|
+
|
|
33
|
+
new(**data, contact:)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.attributes_from(element)
|
|
37
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, key), attributes|
|
|
38
|
+
value = element.attribute(attribute_name)&.value
|
|
39
|
+
if value.nil? || value.strip.empty?
|
|
40
|
+
message = "CONSTRUCTOR #{attribute_name} attribute is required"
|
|
41
|
+
raise ::Lenex::Parser::ParseError, message
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
attributes[key] = value
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
private_class_method :attributes_from
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
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 CONTACT element.
|
|
7
|
+
class Contact
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'name' => :name,
|
|
10
|
+
'street' => :street,
|
|
11
|
+
'street2' => :street2,
|
|
12
|
+
'zip' => :zip,
|
|
13
|
+
'city' => :city,
|
|
14
|
+
'state' => :state,
|
|
15
|
+
'country' => :country,
|
|
16
|
+
'phone' => :phone,
|
|
17
|
+
'mobile' => :mobile,
|
|
18
|
+
'fax' => :fax,
|
|
19
|
+
'email' => :email,
|
|
20
|
+
'internet' => :internet
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.freeze
|
|
24
|
+
private_constant :ATTRIBUTE_KEYS
|
|
25
|
+
|
|
26
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
27
|
+
|
|
28
|
+
def initialize(**attributes)
|
|
29
|
+
ATTRIBUTES.each_value do |key|
|
|
30
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.from_xml(element, email_required: false)
|
|
35
|
+
raise ::Lenex::Parser::ParseError, 'CONTACT element is required' unless element
|
|
36
|
+
|
|
37
|
+
data = extract_attributes(element)
|
|
38
|
+
if email_required && (data[:email].nil? || data[:email].strip.empty?)
|
|
39
|
+
raise ::Lenex::Parser::ParseError, 'CONTACT email attribute is required'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
new(**data)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.extract_attributes(element)
|
|
46
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, key), attributes|
|
|
47
|
+
value = element.attribute(attribute_name)&.value
|
|
48
|
+
attributes[key] = value if value
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
private_class_method :extract_attributes
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing an ENTRY element.
|
|
7
|
+
class Entry
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'agegroupid' => { key: :age_group_id, required: false },
|
|
10
|
+
'entrycourse' => { key: :entry_course, required: false },
|
|
11
|
+
'entrydistance' => { key: :entry_distance, required: false },
|
|
12
|
+
'entrytime' => { key: :entry_time, required: false },
|
|
13
|
+
'eventid' => { key: :event_id, required: true },
|
|
14
|
+
'handicap' => { key: :handicap, required: false },
|
|
15
|
+
'heatid' => { key: :heat_id, required: false },
|
|
16
|
+
'lane' => { key: :lane, required: false },
|
|
17
|
+
'status' => { key: :status, 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 :meet_info
|
|
25
|
+
|
|
26
|
+
def initialize(meet_info: nil, **attributes)
|
|
27
|
+
ATTRIBUTES.each_value do |definition|
|
|
28
|
+
key = definition[:key]
|
|
29
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
30
|
+
end
|
|
31
|
+
@meet_info = meet_info
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.from_xml(element)
|
|
35
|
+
raise ::Lenex::Parser::ParseError, 'ENTRY element is required' unless element
|
|
36
|
+
|
|
37
|
+
attributes = extract_attributes(element)
|
|
38
|
+
meet_info = meet_info_from(element.at_xpath('MEETINFO'))
|
|
39
|
+
|
|
40
|
+
new(**attributes, meet_info:)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.extract_attributes(element)
|
|
44
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, definition), collected|
|
|
45
|
+
value = element.attribute(attribute_name)&.value
|
|
46
|
+
ensure_required_attribute!(attribute_name, definition, value)
|
|
47
|
+
collected[definition[:key]] = value if value
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
private_class_method :extract_attributes
|
|
51
|
+
|
|
52
|
+
def self.ensure_required_attribute!(attribute_name, definition, value)
|
|
53
|
+
return unless definition[:required]
|
|
54
|
+
return unless value.nil? || value.strip.empty?
|
|
55
|
+
|
|
56
|
+
message = "ENTRY #{attribute_name} attribute is required"
|
|
57
|
+
raise ::Lenex::Parser::ParseError, message
|
|
58
|
+
end
|
|
59
|
+
private_class_method :ensure_required_attribute!
|
|
60
|
+
|
|
61
|
+
def self.meet_info_from(element)
|
|
62
|
+
return unless element
|
|
63
|
+
|
|
64
|
+
MeetInfo.from_xml(element)
|
|
65
|
+
end
|
|
66
|
+
private_class_method :meet_info_from
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object capturing entry schedule metadata on a MEET element.
|
|
7
|
+
class EntrySchedule
|
|
8
|
+
attr_reader :entry_start_date, :withdraw_until, :deadline_date, :deadline_time
|
|
9
|
+
|
|
10
|
+
def initialize(entry_start_date:, withdraw_until:, deadline_date:, deadline_time:)
|
|
11
|
+
@entry_start_date = entry_start_date
|
|
12
|
+
@withdraw_until = withdraw_until
|
|
13
|
+
@deadline_date = deadline_date
|
|
14
|
+
@deadline_time = deadline_time
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.from_xml(meet_element)
|
|
18
|
+
attributes = {
|
|
19
|
+
entry_start_date: attribute_value(meet_element, 'entrystartdate'),
|
|
20
|
+
withdraw_until: attribute_value(meet_element, 'withdrawuntil'),
|
|
21
|
+
deadline_date: attribute_value(meet_element, 'deadline'),
|
|
22
|
+
deadline_time: attribute_value(meet_element, 'deadlinetime')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return unless attributes.values.compact.any?
|
|
26
|
+
|
|
27
|
+
new(**attributes)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.attribute_value(element, attribute_name)
|
|
31
|
+
value = element.attribute(attribute_name)&.value
|
|
32
|
+
return if value.nil? || value.strip.empty?
|
|
33
|
+
|
|
34
|
+
value
|
|
35
|
+
end
|
|
36
|
+
private_class_method :attribute_value
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing an EVENT element.
|
|
7
|
+
class Event
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'daytime' => { key: :daytime, required: false },
|
|
10
|
+
'eventid' => { key: :event_id, required: true },
|
|
11
|
+
'gender' => { key: :gender, required: false },
|
|
12
|
+
'maxentries' => { key: :max_entries, required: false },
|
|
13
|
+
'number' => { key: :number, required: true },
|
|
14
|
+
'order' => { key: :order, required: false },
|
|
15
|
+
'preveventid' => { key: :previous_event_id, required: false },
|
|
16
|
+
'round' => { key: :round, required: false },
|
|
17
|
+
'run' => { key: :run, required: false },
|
|
18
|
+
'timing' => { key: :timing, required: false },
|
|
19
|
+
'type' => { key: :type, required: false }
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.map { |definition| definition[:key] }.freeze
|
|
23
|
+
private_constant :ATTRIBUTE_KEYS
|
|
24
|
+
|
|
25
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
26
|
+
attr_reader :fee, :swim_style, :age_groups, :heats, :time_standard_refs
|
|
27
|
+
|
|
28
|
+
def initialize(swim_style:, fee: nil, collections: {}, **attributes)
|
|
29
|
+
ATTRIBUTES.each_value do |definition|
|
|
30
|
+
key = definition[:key]
|
|
31
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
32
|
+
end
|
|
33
|
+
@fee = fee
|
|
34
|
+
@swim_style = swim_style
|
|
35
|
+
@age_groups = Array(collections.fetch(:age_groups, []))
|
|
36
|
+
@heats = Array(collections.fetch(:heats, []))
|
|
37
|
+
@time_standard_refs = Array(collections.fetch(:time_standard_refs, []))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.from_xml(element)
|
|
41
|
+
raise ::Lenex::Parser::ParseError, 'EVENT element is required' unless element
|
|
42
|
+
|
|
43
|
+
attributes = extract_attributes(element)
|
|
44
|
+
fee = fee_from(element.at_xpath('FEE'))
|
|
45
|
+
swim_style = SwimStyle.from_xml(element.at_xpath('SWIMSTYLE'))
|
|
46
|
+
collections = build_collections(element)
|
|
47
|
+
|
|
48
|
+
new(**attributes, fee:, swim_style:, collections:)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.extract_attributes(element)
|
|
52
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, definition), collected|
|
|
53
|
+
value = element.attribute(attribute_name)&.value
|
|
54
|
+
ensure_required_attribute!(attribute_name, definition, value)
|
|
55
|
+
collected[definition[:key]] = value if value
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
private_class_method :extract_attributes
|
|
59
|
+
|
|
60
|
+
def self.ensure_required_attribute!(attribute_name, definition, value)
|
|
61
|
+
return unless definition[:required]
|
|
62
|
+
return unless value.nil? || value.strip.empty?
|
|
63
|
+
|
|
64
|
+
message = "EVENT #{attribute_name} attribute is required"
|
|
65
|
+
raise ::Lenex::Parser::ParseError, message
|
|
66
|
+
end
|
|
67
|
+
private_class_method :ensure_required_attribute!
|
|
68
|
+
|
|
69
|
+
def self.fee_from(element)
|
|
70
|
+
return unless element
|
|
71
|
+
|
|
72
|
+
Fee.from_xml(element)
|
|
73
|
+
end
|
|
74
|
+
private_class_method :fee_from
|
|
75
|
+
|
|
76
|
+
def self.extract_age_groups(collection_element)
|
|
77
|
+
return [] unless collection_element
|
|
78
|
+
|
|
79
|
+
collection_element.xpath('AGEGROUP').map do |age_group_element|
|
|
80
|
+
AgeGroup.from_xml(age_group_element)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
private_class_method :extract_age_groups
|
|
84
|
+
|
|
85
|
+
def self.extract_heats(collection_element)
|
|
86
|
+
return [] unless collection_element
|
|
87
|
+
|
|
88
|
+
collection_element.xpath('HEAT').map do |heat_element|
|
|
89
|
+
Heat.from_xml(heat_element)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
private_class_method :extract_heats
|
|
93
|
+
|
|
94
|
+
def self.extract_time_standard_refs(collection_element)
|
|
95
|
+
return [] unless collection_element
|
|
96
|
+
|
|
97
|
+
collection_element.xpath('TIMESTANDARDREF').map do |time_standard_ref_element|
|
|
98
|
+
TimeStandardRef.from_xml(time_standard_ref_element)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
private_class_method :extract_time_standard_refs
|
|
102
|
+
|
|
103
|
+
def self.build_collections(element)
|
|
104
|
+
{
|
|
105
|
+
age_groups: extract_age_groups(element.at_xpath('AGEGROUPS')),
|
|
106
|
+
heats: extract_heats(element.at_xpath('HEATS')),
|
|
107
|
+
time_standard_refs: extract_time_standard_refs(element.at_xpath('TIMESTANDARDREFS'))
|
|
108
|
+
}
|
|
109
|
+
end
|
|
110
|
+
private_class_method :build_collections
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing a FACILITY element.
|
|
7
|
+
class Facility
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'city' => { key: :city, required: true },
|
|
10
|
+
'nation' => { key: :nation, required: true },
|
|
11
|
+
'name' => { key: :name, required: false },
|
|
12
|
+
'state' => { key: :state, required: false },
|
|
13
|
+
'street' => { key: :street, required: false },
|
|
14
|
+
'street2' => { key: :street2, required: false },
|
|
15
|
+
'zip' => { key: :zip, required: false }
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
ATTRIBUTE_KEYS = ATTRIBUTES.values.map { |definition| definition[:key] }.freeze
|
|
19
|
+
private_constant :ATTRIBUTE_KEYS
|
|
20
|
+
|
|
21
|
+
ATTRIBUTE_KEYS.each { |attribute| attr_reader attribute }
|
|
22
|
+
|
|
23
|
+
def initialize(**attributes)
|
|
24
|
+
ATTRIBUTES.each_value do |definition|
|
|
25
|
+
key = definition[:key]
|
|
26
|
+
instance_variable_set(:"@#{key}", attributes[key])
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.from_xml(element)
|
|
31
|
+
raise ::Lenex::Parser::ParseError, 'FACILITY element is required' unless element
|
|
32
|
+
|
|
33
|
+
attributes = extract_attributes(element)
|
|
34
|
+
|
|
35
|
+
new(**attributes)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.extract_attributes(element)
|
|
39
|
+
ATTRIBUTES.each_with_object({}) do |(attribute_name, definition), collected|
|
|
40
|
+
value = element.attribute(attribute_name)&.value
|
|
41
|
+
ensure_required_attribute!(attribute_name, definition, value)
|
|
42
|
+
collected[definition[:key]] = value if value
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
private_class_method :extract_attributes
|
|
46
|
+
|
|
47
|
+
def self.ensure_required_attribute!(attribute_name, definition, value)
|
|
48
|
+
return unless definition[:required]
|
|
49
|
+
return unless value.nil? || value.strip.empty?
|
|
50
|
+
|
|
51
|
+
message = "FACILITY #{attribute_name} attribute is required"
|
|
52
|
+
raise ::Lenex::Parser::ParseError, message
|
|
53
|
+
end
|
|
54
|
+
private_class_method :ensure_required_attribute!
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
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 FEE element.
|
|
7
|
+
class Fee
|
|
8
|
+
ATTRIBUTES = {
|
|
9
|
+
'currency' => { key: :currency, required: false },
|
|
10
|
+
'type' => { key: :type, required: false },
|
|
11
|
+
'value' => { key: :value, 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, 'FEE 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 = "FEE #{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,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lenex
|
|
4
|
+
module Parser
|
|
5
|
+
module Objects
|
|
6
|
+
# Value object representing a FEES collection.
|
|
7
|
+
class FeeSchedule
|
|
8
|
+
attr_reader :fees
|
|
9
|
+
|
|
10
|
+
def initialize(fees: [])
|
|
11
|
+
@fees = Array(fees)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.from_xml(element)
|
|
15
|
+
return unless element
|
|
16
|
+
|
|
17
|
+
fees = element.xpath('FEE').map do |fee_element|
|
|
18
|
+
Fee.from_xml(fee_element)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
new(fees:)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|