fbtxt-document 0.9.0 → 0.9.1
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 +4 -4
- data/CHANGELOG.md +1 -1
- data/Manifest.txt +18 -5
- data/lib/fbtxt/document/export_utils.rb +100 -0
- data/lib/fbtxt/document/match_tree.rb +13 -31
- data/lib/fbtxt/document/match_tree_on/on_cards_line.rb +66 -0
- data/lib/fbtxt/document/match_tree_on/on_goal_line.rb +43 -25
- data/lib/fbtxt/document/match_tree_on/on_lineup_line.rb +96 -0
- data/lib/fbtxt/document/match_tree_on/on_match_bye.rb +35 -0
- data/lib/fbtxt/document/match_tree_on/on_match_line.rb +2 -20
- data/lib/fbtxt/document/match_tree_on/on_penalties.rb +57 -0
- data/lib/fbtxt/document/match_tree_on/on_referee_line.rb +25 -0
- data/lib/fbtxt/document/models/event.rb +72 -0
- data/lib/fbtxt/document/models/goal.rb +94 -0
- data/lib/fbtxt/document/{match_tree → models}/group.rb +2 -2
- data/lib/fbtxt/document/models/lineup.rb +147 -0
- data/lib/fbtxt/document/models/match.rb +194 -0
- data/lib/fbtxt/document/models/minute.rb +57 -0
- data/lib/fbtxt/document/models/penalty.rb +35 -0
- data/lib/fbtxt/document/models/player.rb +32 -0
- data/lib/fbtxt/document/models/referee.rb +24 -0
- data/lib/fbtxt/document/{match_tree → models}/round.rb +2 -2
- data/lib/fbtxt/document/models/score.rb +146 -0
- data/lib/fbtxt/document/version.rb +1 -1
- data/lib/fbtxt/document.rb +33 -6
- metadata +20 -7
- data/lib/fbtxt/document/match_tree/goal.rb +0 -67
- data/lib/fbtxt/document/match_tree/match.rb +0 -249
- /data/lib/fbtxt/document/{quick_match_reader.rb → document.rb} +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
|
|
2
|
+
module Fbtxt
|
|
3
|
+
module Model
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
##############
|
|
7
|
+
## e.g.
|
|
8
|
+
## 'Y', Player, 44
|
|
9
|
+
## 'YR', Player, 45+2
|
|
10
|
+
|
|
11
|
+
class EventCard ### use EventBooking - why? why not?
|
|
12
|
+
attr_reader :type
|
|
13
|
+
attr_reader :name
|
|
14
|
+
attr_reader :minute
|
|
15
|
+
|
|
16
|
+
def initialize( type:, name:, minute: nil )
|
|
17
|
+
@type = type
|
|
18
|
+
@name = name
|
|
19
|
+
|
|
20
|
+
## note - auto-convert to our own Minute format
|
|
21
|
+
@minute = if minute.is_a?( RaccMatchParser::Minute )
|
|
22
|
+
Minute.new( m: minute.m,
|
|
23
|
+
offset: minute.offset,
|
|
24
|
+
secs: minute.secs )
|
|
25
|
+
else
|
|
26
|
+
minute
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def as_json(*)
|
|
32
|
+
h = { 'type' => type,
|
|
33
|
+
'name' => name }
|
|
34
|
+
h['minute'] = minute.to_s if minute
|
|
35
|
+
|
|
36
|
+
h
|
|
37
|
+
end
|
|
38
|
+
end # class EventCard
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class EventSub ## use just Sub or ??
|
|
42
|
+
|
|
43
|
+
attr_reader :name_on ## use on only - why? why not?
|
|
44
|
+
attr_reader :name_off ## use off only
|
|
45
|
+
attr_reader :minute
|
|
46
|
+
|
|
47
|
+
def initialize( on:, off:, minute: nil )
|
|
48
|
+
@name_on = on
|
|
49
|
+
@name_off = off
|
|
50
|
+
|
|
51
|
+
## note - auto-convert to our own Minute format
|
|
52
|
+
@minute = if minute.is_a?( RaccMatchParser::Minute )
|
|
53
|
+
Minute.new( m: minute.m,
|
|
54
|
+
offset: minute.offset,
|
|
55
|
+
secs: minute.secs )
|
|
56
|
+
else
|
|
57
|
+
minute
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def as_json(*)
|
|
62
|
+
h = { 'on' => name_on,
|
|
63
|
+
'off' => name_off }
|
|
64
|
+
h['minute'] = minute.to_s if minute
|
|
65
|
+
|
|
66
|
+
h
|
|
67
|
+
end
|
|
68
|
+
end ## EventSub
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
end # module Model
|
|
72
|
+
end # module Fbtxt
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module Fbtxt
|
|
2
|
+
module Model
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Goal ### nested (non-freestanding) inside match (match is parent)
|
|
6
|
+
|
|
7
|
+
attr_reader :team, ## note - 1|2 expected
|
|
8
|
+
:name,
|
|
9
|
+
:minute,
|
|
10
|
+
:owngoal, ## true|false
|
|
11
|
+
:penalty ## true|false
|
|
12
|
+
|
|
13
|
+
## add alias for player => name - why? why not?
|
|
14
|
+
## alias_method :player, :name
|
|
15
|
+
|
|
16
|
+
def owngoal?() @owngoal==true; end
|
|
17
|
+
def penalty?() @penalty==true; end
|
|
18
|
+
def team1?() @team == 1; end
|
|
19
|
+
def team2?() @team == 2; end
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def initialize( team:,
|
|
23
|
+
name:,
|
|
24
|
+
minute: nil,
|
|
25
|
+
owngoal: false,
|
|
26
|
+
penalty: false
|
|
27
|
+
)
|
|
28
|
+
@team = team # 1|2
|
|
29
|
+
@name = name
|
|
30
|
+
|
|
31
|
+
## note - auto-convert to our own Minute format
|
|
32
|
+
## is obj/incl. (optional) offset/injury time!!
|
|
33
|
+
@minute = if minute.is_a?( RaccMatchParser::Minute )
|
|
34
|
+
Minute.new( m: minute.m,
|
|
35
|
+
offset: minute.offset,
|
|
36
|
+
secs: minute.secs )
|
|
37
|
+
else
|
|
38
|
+
minute
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@owngoal = owngoal
|
|
42
|
+
@penalty = penalty
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def state
|
|
46
|
+
[@team,
|
|
47
|
+
@name, @minute, @owngoal, @penalty
|
|
48
|
+
]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def ==(o)
|
|
52
|
+
o.class == self.class && o.state == state
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def pretty_print( q )
|
|
56
|
+
q.group( 4, '<Goal ', '>') do ## group( indent, open, close)
|
|
57
|
+
q.text( name )
|
|
58
|
+
q.text( " #{@minute}" ) if @minute
|
|
59
|
+
|
|
60
|
+
q.text( "(og)" ) if @owngoal
|
|
61
|
+
q.text( "(p)" ) if @penalty
|
|
62
|
+
q.text( " for #{@team}" ) ### team 1 or 2 - use home/away
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
### note - EXCLUDE team 1|2 from serialize
|
|
68
|
+
## maybe remove alltogether and split into goals[[],[]] - why? why not?
|
|
69
|
+
def as_json(*)
|
|
70
|
+
h = { 'name' => name }
|
|
71
|
+
|
|
72
|
+
=begin
|
|
73
|
+
## note - use a string for minutes for now
|
|
74
|
+
## allows e.g. 45+2 etc. too
|
|
75
|
+
minute_str = "#{goal.minute}"
|
|
76
|
+
minute_str += "+#{goal.offset}" if goal.offset
|
|
77
|
+
|
|
78
|
+
node['minute'] = minute_str
|
|
79
|
+
=end
|
|
80
|
+
|
|
81
|
+
h['minute'] = minute.to_s if minute
|
|
82
|
+
|
|
83
|
+
h['owngoal'] = true if owngoal?
|
|
84
|
+
h['penalty'] = true if penalty?
|
|
85
|
+
|
|
86
|
+
h
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
end # class Goal
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
end # module Model
|
|
94
|
+
end # module Fbtxt
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
|
|
3
|
+
(double) check bookings
|
|
4
|
+
- is trainer or co-trainer - why NOT included in lineup ???
|
|
5
|
+
==> reading >austria/2025-26/1-bundesliga-full.txt<...
|
|
6
|
+
!! warn - player >Thomas Turner< NOT found; skipping booking Thomas Turner 90+3
|
|
7
|
+
!! warn - player >Thomas Turner< NOT found; skipping booking Thomas Turner 88
|
|
8
|
+
!! warn - player >Tobias Schützenauer< NOT found; skipping booking Tobias Schützenauer 90+5
|
|
9
|
+
!! warn - player >Jeyland MITCHELL< NOT found; skipping booking Jeyland MITCHELL 77
|
|
10
|
+
!! warn - player >Lukas Gütlbauer< NOT found; skipping booking Lukas Gütlbauer 84
|
|
11
|
+
=end
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
module Fbtxt
|
|
16
|
+
module Model
|
|
17
|
+
|
|
18
|
+
=begin
|
|
19
|
+
## fix-fix-fix
|
|
20
|
+
## move into (nested inside) Lineup - why? why not?
|
|
21
|
+
class Card
|
|
22
|
+
attr_reader :name, :minute
|
|
23
|
+
def initialize( name:, minute: nil )
|
|
24
|
+
@name = name
|
|
25
|
+
@minute = minute
|
|
26
|
+
end
|
|
27
|
+
end ## class Card
|
|
28
|
+
=end
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## note - lineup incl. starter & (optional) bench & sub(stituion)s
|
|
34
|
+
class Lineup
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def self.build( lineup )
|
|
39
|
+
## add starter & bench (via substitutions)
|
|
40
|
+
starter = _collect_starter( lineup )
|
|
41
|
+
bench = _collect_bench( lineup )
|
|
42
|
+
subs = _collect_subs( lineup )
|
|
43
|
+
|
|
44
|
+
new( starter: starter,
|
|
45
|
+
bench: bench,
|
|
46
|
+
subs: subs )
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def self._collect_starter( lineup )
|
|
51
|
+
recs = []
|
|
52
|
+
lineup.each do |item|
|
|
53
|
+
## puts " lineup.each item:"
|
|
54
|
+
## pp item
|
|
55
|
+
rec = Player.new( name: item.name,
|
|
56
|
+
captain: item.captain )
|
|
57
|
+
|
|
58
|
+
recs << rec
|
|
59
|
+
end
|
|
60
|
+
recs
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self._collect_bench( lineup )
|
|
64
|
+
recs = []
|
|
65
|
+
lineup.each do |item|
|
|
66
|
+
current = item
|
|
67
|
+
while current && current.sub
|
|
68
|
+
rec = Player.new( name: current.sub.sub.name,
|
|
69
|
+
captain: current.sub.sub.captain )
|
|
70
|
+
|
|
71
|
+
recs << rec
|
|
72
|
+
current = current.sub.sub
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
recs
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self._collect_subs( lineup )
|
|
79
|
+
recs = []
|
|
80
|
+
lineup.each do |item|
|
|
81
|
+
current = item
|
|
82
|
+
while current && current.sub
|
|
83
|
+
## fix-fix-fix - use player-ref for off/on
|
|
84
|
+
rec = EventSub.new( off: current.name,
|
|
85
|
+
on: current.sub.sub.name,
|
|
86
|
+
minute: current.sub.minute )
|
|
87
|
+
recs << rec
|
|
88
|
+
current = current.sub.sub
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
## note - (auto-)sort by minute if present
|
|
93
|
+
### fix-fix-fix move sort to EventSub <=> - why? why not?
|
|
94
|
+
recs = recs.sort do |l,r|
|
|
95
|
+
if l.minute && r.minute
|
|
96
|
+
l.minute <=> r.minute
|
|
97
|
+
else
|
|
98
|
+
0 ## keep as is (no minutes available)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
recs
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def initialize( starter:,
|
|
109
|
+
bench: [],
|
|
110
|
+
subs: [] )
|
|
111
|
+
@starter = {} ## note - indexed by (player) name
|
|
112
|
+
starter.each do |player|
|
|
113
|
+
@starter[player.name] = player
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
@bench = {} ## note - indexed by (player) name
|
|
117
|
+
bench.each do |player|
|
|
118
|
+
@bench[player.name] = player
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
@subs = subs
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def starter() @starter.values; end
|
|
125
|
+
def bench() @bnech.values; end
|
|
126
|
+
def subs() @subs; end
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def find_player( name )
|
|
130
|
+
player = @starter[name] || @bench[name]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def as_json(*)
|
|
136
|
+
h = { 'starter' => @starter.values.as_json }
|
|
137
|
+
|
|
138
|
+
h['bench'] = @bench.values.as_json if @bench.size > 0
|
|
139
|
+
h['subs'] = @subs.as_json if @subs.size > 0
|
|
140
|
+
|
|
141
|
+
h
|
|
142
|
+
end
|
|
143
|
+
end # class Lineup
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
end # module Model
|
|
147
|
+
end # module Fbtxt
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
##
|
|
2
|
+
# move (simpler) struct version inline to MatchTree for now
|
|
3
|
+
#
|
|
4
|
+
module Fbtxt
|
|
5
|
+
module Model
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Match
|
|
10
|
+
|
|
11
|
+
attr_reader :num,
|
|
12
|
+
:date,
|
|
13
|
+
:time,
|
|
14
|
+
:time_local,
|
|
15
|
+
:team1, :team2, ## todo/fix: use team1_name, team2_name or similar - for compat with db activerecord version? why? why not?
|
|
16
|
+
:score,
|
|
17
|
+
:round, ## todo/fix: use round_num or similar - for compat with db activerecord version? why? why not?
|
|
18
|
+
:group,
|
|
19
|
+
:status, ## e.g. replay, cancelled, awarded, abadoned, postponed, etc.
|
|
20
|
+
:ground, ## (optional) add as text line for now (incl. city, timezone etc.)
|
|
21
|
+
:att ## (optional) attendance as (integer) number
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## e.g. support (flat/undefined) [], or (nested/paired) [[],[]]
|
|
25
|
+
attr_accessor :goals ## todo/fix: make goals like all other attribs!!
|
|
26
|
+
|
|
27
|
+
###
|
|
28
|
+
# add details match (report) info
|
|
29
|
+
## lineup,bench,subs,bookings(y/yr/r/etc.)
|
|
30
|
+
attr_accessor :lineup ## incl. starter/bench/subs!!!
|
|
31
|
+
attr_accessor :bookings ## use cards - why? why not?
|
|
32
|
+
|
|
33
|
+
attr_accessor :referees
|
|
34
|
+
|
|
35
|
+
attr_accessor :penalties
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def initialize( **kwargs )
|
|
39
|
+
update( **kwargs ) unless kwargs.empty?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def update( **kwargs )
|
|
44
|
+
@num = kwargs[:num] if kwargs.has_key?( :num )
|
|
45
|
+
|
|
46
|
+
## note: check with has_key? because value might be nil!!!
|
|
47
|
+
@date = kwargs[:date] if kwargs.has_key?( :date )
|
|
48
|
+
@time = kwargs[:time] if kwargs.has_key?( :time )
|
|
49
|
+
@time_local = kwargs[:time_local] if kwargs.has_key?( :time_local )
|
|
50
|
+
|
|
51
|
+
## todo/fix: use team1_name, team2_name or similar - for compat with db activerecord version? why? why not?
|
|
52
|
+
@team1 = kwargs[:team1] if kwargs.has_key?( :team1 )
|
|
53
|
+
@team2 = kwargs[:team2] if kwargs.has_key?( :team2 )
|
|
54
|
+
|
|
55
|
+
## note: round is a string!!! e.g. '1', '2' for matchday or 'Final', 'Semi-final', etc.
|
|
56
|
+
## todo: use to_s - why? why not?
|
|
57
|
+
@round = kwargs[:round] if kwargs.has_key?( :round )
|
|
58
|
+
@group = kwargs[:group] if kwargs.has_key?( :group )
|
|
59
|
+
@status = kwargs[:status] if kwargs.has_key?( :status )
|
|
60
|
+
|
|
61
|
+
@ground = kwargs[:ground] if kwargs.has_key?( :ground )
|
|
62
|
+
@att = kwargs[:att] if kwargs.has_key?( :att )
|
|
63
|
+
|
|
64
|
+
## check/expects all-in-one score struct for convenience!!!
|
|
65
|
+
@score = kwargs[:score] if kwargs.has_key?( :score )
|
|
66
|
+
|
|
67
|
+
self ## note - MUST return self for chaining
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def as_json
|
|
73
|
+
#####
|
|
74
|
+
## note - use string keys (NOT symbol for data keys)
|
|
75
|
+
## for easier json compatibility
|
|
76
|
+
data = {}
|
|
77
|
+
|
|
78
|
+
## check round
|
|
79
|
+
if @round
|
|
80
|
+
data['round'] = if round.is_a?( Integer )
|
|
81
|
+
"Matchday #{@round}"
|
|
82
|
+
else ## assume string
|
|
83
|
+
@round
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
data['num'] = @num if @num
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if @date
|
|
92
|
+
## assume 2020-09-19 date format!!
|
|
93
|
+
data['date'] = @date.is_a?( String ) ? @date : @date.strftime('%Y-%m-%d')
|
|
94
|
+
|
|
95
|
+
data['time'] = @time if @time
|
|
96
|
+
data['time_local'] = @time_local if @time_local
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
data['team1'] = @team1.is_a?( String ) ? @team1 : @team1.name
|
|
101
|
+
|
|
102
|
+
## note - for match status bye team2 is nil!!!
|
|
103
|
+
## e.g. Queen's Park bye
|
|
104
|
+
## Wanderers bye
|
|
105
|
+
## todo/check - keep bye as a match - why? why not?
|
|
106
|
+
## has no date/time & venue & score etc.
|
|
107
|
+
if @team2
|
|
108
|
+
data['team2'] = @team2.is_a?( String ) ? @team2 : @team2.name
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
## note - score might be
|
|
112
|
+
## 1) array e.g. [0,1] -- e.g. uses reported key internally
|
|
113
|
+
## 2) hash e.g. { 'ft' [0,1] } etc.
|
|
114
|
+
## note - w/o (walkout) do NOT add empty score
|
|
115
|
+
|
|
116
|
+
## note:
|
|
117
|
+
## use/assume full-time (ft) if reported - why? why not?
|
|
118
|
+
## in future check for score note or such
|
|
119
|
+
## to use "plain" array or such - why? why not?
|
|
120
|
+
|
|
121
|
+
data['score'] = @score.as_json if @score
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
### check for goals
|
|
126
|
+
if @goals.is_a?(Array) && @goals.size > 0
|
|
127
|
+
data['goals1'] = []
|
|
128
|
+
data['goals2'] = []
|
|
129
|
+
|
|
130
|
+
@goals.each do |goal|
|
|
131
|
+
if goal.team == 1
|
|
132
|
+
data['goals1'] << goal.as_json
|
|
133
|
+
else ## assume 2 (or better raise except if NOT 2)
|
|
134
|
+
data['goals2'] << goal.as_json
|
|
135
|
+
end
|
|
136
|
+
end # each goal
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
### check for penalties (shootout)
|
|
140
|
+
if @penalties.is_a?(Array) && @penalties.size > 0
|
|
141
|
+
data['penalties'] = @penalties.as_json
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
data['status'] = @status if @status
|
|
147
|
+
|
|
148
|
+
data['group'] = @group if @group
|
|
149
|
+
|
|
150
|
+
if @ground
|
|
151
|
+
## note: might be array of string e.g. ['Wembley', 'London']
|
|
152
|
+
##
|
|
153
|
+
## todo/check - auto-join to string - why? why not?
|
|
154
|
+
## e.g. ['Wembley', 'London']
|
|
155
|
+
## to 'Wembley, London'
|
|
156
|
+
## note - auto-join geo tree for now
|
|
157
|
+
data['ground'] = @ground.join(', ')
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
data['attendance'] = @att if @att
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
###
|
|
164
|
+
# check for match (report) details
|
|
165
|
+
# quick & dirty first try/version/dump
|
|
166
|
+
# refine (serialize) format later
|
|
167
|
+
if @lineup.is_a?(Array) && @lineup.size > 0
|
|
168
|
+
data['lineup'] = @lineup.as_json
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if @bookings.is_a?(Array) && @bookings.size > 0
|
|
173
|
+
if @bookings[0].empty? && @bookings[1].empty?
|
|
174
|
+
## skip empty bookings
|
|
175
|
+
## "bookings": [[], []]
|
|
176
|
+
else
|
|
177
|
+
data['bookings'] = @bookings.as_json
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
if @referees.is_a?(Array) && @referees.size > 0
|
|
183
|
+
data['referees'] = @referees.as_json
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
data
|
|
188
|
+
end
|
|
189
|
+
end # class Match
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
end # module Model
|
|
194
|
+
end # module Fbtxt
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Fbtxt
|
|
2
|
+
module Model
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Minute
|
|
6
|
+
|
|
7
|
+
attr_reader :m, :offset, :secs
|
|
8
|
+
|
|
9
|
+
def initialize( m:, offset: nil, secs: nil )
|
|
10
|
+
@m = m
|
|
11
|
+
@offset = offset
|
|
12
|
+
@secs = secs
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
## todo/check - rename offset to injury/stoppage (time) or such - why? why not?
|
|
16
|
+
=begin
|
|
17
|
+
Alternative naming conventions
|
|
18
|
+
here are the three best ways to rename it
|
|
19
|
+
- stoppage: Short, clean, and highly readable in a class context
|
|
20
|
+
Minute.new(m: 90, stoppage: 4).
|
|
21
|
+
- stoppage_time: Highly explicit if you want zero ambiguity.
|
|
22
|
+
- added_time: Another officially recognized term that reads very naturally.
|
|
23
|
+
=end
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def as_json(*) to_s; end
|
|
27
|
+
|
|
28
|
+
def to_s
|
|
29
|
+
buf = String.new
|
|
30
|
+
buf << "#{m}"
|
|
31
|
+
### note - do NOT autoadd minute quote for now
|
|
32
|
+
## make it an option e.g. to_s( :quote) or such - why? why not?
|
|
33
|
+
### buf << "'"
|
|
34
|
+
buf << "+#{offset}" if offset
|
|
35
|
+
buf << "/#{secs} secs" if secs
|
|
36
|
+
buf
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def pretty_print( q )
|
|
40
|
+
q.text( to_s )
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
include Comparable ## note - auto-adds <, <=, ==, >=, >, and between?
|
|
45
|
+
def <=>(other)
|
|
46
|
+
return nil unless other.is_a?(Minute)
|
|
47
|
+
|
|
48
|
+
## returns -1/0/1
|
|
49
|
+
res = m <=> other.m
|
|
50
|
+
res = (offset||0) <=> (other.offset||0) if res == 0
|
|
51
|
+
res
|
|
52
|
+
end
|
|
53
|
+
end # class Minute
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
end # module Model
|
|
57
|
+
end # module Fbtxt
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Fbtxt
|
|
2
|
+
module Model
|
|
3
|
+
|
|
4
|
+
class Penalty
|
|
5
|
+
|
|
6
|
+
## todo
|
|
7
|
+
## - add attr_reader :team ## note - 1|2 expected - why? why not?
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
attr_reader :name
|
|
11
|
+
attr_reader :score
|
|
12
|
+
attr_reader :note
|
|
13
|
+
|
|
14
|
+
def initialize( name:, score: nil, note: nil )
|
|
15
|
+
@name = name
|
|
16
|
+
@score = score
|
|
17
|
+
@note = note
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## add alias for name => player - why? why not?
|
|
22
|
+
## alias_method :player, :name
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def as_json(*)
|
|
26
|
+
h = { 'name' => name }
|
|
27
|
+
h['score'] = score if score.is_a?(Array) && !score.empty?
|
|
28
|
+
h['note'] = note if note
|
|
29
|
+
|
|
30
|
+
h
|
|
31
|
+
end
|
|
32
|
+
end ## Penalty
|
|
33
|
+
|
|
34
|
+
end # module Model
|
|
35
|
+
end # module Fbtxt
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
module Fbtxt
|
|
3
|
+
module Model
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Player
|
|
7
|
+
|
|
8
|
+
attr_reader :name
|
|
9
|
+
## fix-fix-fix
|
|
10
|
+
## add pos(ition) e.g. GK,DF,MF,FW
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def initialize( name:, captain: false )
|
|
14
|
+
@name = name
|
|
15
|
+
@captain = captain
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def captain?() @captain; end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def as_json(*)
|
|
23
|
+
h = { 'name' => name }
|
|
24
|
+
h['captain'] = true if captain?
|
|
25
|
+
|
|
26
|
+
h
|
|
27
|
+
end
|
|
28
|
+
end # class Player
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
end # module Model
|
|
32
|
+
end # module Fbtxt
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
module Fbtxt
|
|
3
|
+
module Model
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Referee ## rename to/use Official - why? why not?
|
|
7
|
+
attr_reader :name, :country
|
|
8
|
+
|
|
9
|
+
def initialize( name:, country: nil )
|
|
10
|
+
@name = name
|
|
11
|
+
@country = country
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def as_json(*)
|
|
15
|
+
h = { 'name' => name }
|
|
16
|
+
h['country'] = country if country
|
|
17
|
+
|
|
18
|
+
h
|
|
19
|
+
end
|
|
20
|
+
end # class Referee
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
end # module Model
|
|
24
|
+
end # module Fbtxt
|