score-formats 0.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0ce70736d26500e8d3866ae96a2f75e6ce8c1725
4
+ data.tar.gz: 16e29461a44bb4c232039b82f38d805596b47acf
5
+ SHA512:
6
+ metadata.gz: 28ed77625082a2f1b29713b03cf92354df0799c8b0189f5e40e4ccddbad34cb6e14b286b580adee97304cb3b84e8c14064dd26c33efcfea80e94e4caa7354d6e
7
+ data.tar.gz: bf023b724232b5ca987615d223c8d8aef928a3afcf7b9807e1f1b9167fbabfc2a80d78404220cb5de6633585be885ea5e9f05c0770d28da59a1cb89d827b63b7
@@ -0,0 +1,3 @@
1
+ ### 0.0.1 / 2020-08-17
2
+
3
+ * Everything is new. First release.
@@ -0,0 +1,13 @@
1
+ CHANGELOG.md
2
+ Manifest.txt
3
+ README.md
4
+ Rakefile
5
+ lib/score-formats.rb
6
+ lib/score-formats/formats.rb
7
+ lib/score-formats/parser.rb
8
+ lib/score-formats/score.rb
9
+ lib/score-formats/version.rb
10
+ lib/score/formats.rb
11
+ test/helper.rb
12
+ test/test_formats.rb
13
+ test/test_score.rb
@@ -0,0 +1,29 @@
1
+ # score-formats - read / parse and print sports match scores (incl. half time, full time, extra time, penalties and more)
2
+
3
+
4
+ * home :: [github.com/sportdb/sport.db](https://github.com/sportdb/sport.db)
5
+ * bugs :: [github.com/sportdb/sport.db/issues](https://github.com/sportdb/sport.db/issues)
6
+ * gem :: [rubygems.org/gems/score-formats](https://rubygems.org/gems/score-formats)
7
+ * rdoc :: [rubydoc.info/gems/score-formats](http://rubydoc.info/gems/score-formats)
8
+ * forum :: [opensport](http://groups.google.com/group/opensport)
9
+
10
+
11
+ ## Usage
12
+
13
+
14
+ to be done
15
+
16
+
17
+
18
+
19
+ ## License
20
+
21
+ The `score-formats` scripts are dedicated to the public domain.
22
+ Use it as you please with no restrictions whatsoever.
23
+
24
+
25
+ ## Questions? Comments?
26
+
27
+ Send them along to the
28
+ [Open Sports & Friends Forum/Mailing List](http://groups.google.com/group/opensport).
29
+ Thanks!
@@ -0,0 +1,27 @@
1
+ require 'hoe'
2
+ require './lib/score-formats/version.rb'
3
+
4
+ Hoe.spec 'score-formats' do
5
+
6
+ self.version = ScoreFormats::VERSION
7
+
8
+ self.summary = "score-formats - read / parse and print sports match scores (incl. half time, full time, extra time, penalties and more)"
9
+ self.description = summary
10
+
11
+ self.urls = ['https://github.com/sportdb/sport.db']
12
+
13
+ self.author = 'Gerald Bauer'
14
+ self.email = 'opensport@googlegroups.com'
15
+
16
+ # switch extension to .markdown for gihub formatting
17
+ self.readme_file = 'README.md'
18
+ self.history_file = 'CHANGELOG.md'
19
+
20
+ self.licenses = ['Public Domain']
21
+
22
+ self.extra_deps = []
23
+
24
+ self.spec_extras = {
25
+ required_ruby_version: '>= 2.2.2'
26
+ }
27
+ end
@@ -0,0 +1,72 @@
1
+ require 'pp'
2
+ require 'date'
3
+ require 'time'
4
+
5
+
6
+ ###
7
+ # our own code
8
+ require 'score-formats/version' # let version always go first
9
+
10
+ ## todo/fix: make logging class configurable - lets you use logutils etc.
11
+ module ScoreFormats
12
+ module Logging
13
+ def logger() @logger ||= Logger.new; end
14
+
15
+ class Logger ## for now use quick "dummy" logger to
16
+ def debug( msg ) puts "[debug] #{msg}"; end
17
+ end # class Logger
18
+ end # module Logging
19
+ end # module ScoreFormats
20
+
21
+
22
+
23
+ require 'score-formats/score'
24
+ require 'score-formats/formats'
25
+ require 'score-formats/parser'
26
+
27
+
28
+
29
+ module ScoreFormats
30
+ def self.lang
31
+ @@lang ||= :en ## defaults to english (:en)
32
+ end
33
+ def self.lang=( value )
34
+ @@lang = value.to_sym ## note: make sure lang is always a symbol for now (NOT a string)
35
+ @@lang ## todo/check: remove =() method always returns passed in value? double check
36
+ end
37
+
38
+ def self.parser( lang: ) ## find parser
39
+ lang = lang.to_sym ## note: make sure lang is always a symbol for now (NOT a string)
40
+
41
+ ## note: cache all "built-in" lang versions (e.g. formats == nil)
42
+ @@parser ||= {}
43
+ parser = @@parser[ lang ] ||= ScoreParser.new( lang: lang )
44
+ end
45
+
46
+ def self.parse( line, lang: ScoreFormats.lang )
47
+ parser( lang: lang ).parse( line )
48
+ end
49
+
50
+ def self.find!( line, lang: ScoreFormats.lang )
51
+ parser( lang: lang ).find!( line )
52
+ end
53
+ end
54
+
55
+
56
+
57
+ ##
58
+ # add more convenience / shortcut helpers / named ctors to score class itself
59
+
60
+ class Score
61
+ def self.parse( line, lang: ScoreFormats.lang )
62
+ ScoreFormats.parse( line, lang: lang )
63
+ end
64
+
65
+ def self.find!( line, lang: ScoreFormats.lang )
66
+ ScoreFormats.find!( line, lang: lang )
67
+ end
68
+ end
69
+
70
+
71
+
72
+ puts ScoreFormats.banner # say hello
@@ -0,0 +1,239 @@
1
+
2
+ module ScoreFormats
3
+
4
+ ## todo/check: use ‹› (unicode chars) to mark optional parts in regex constant name - why? why not?
5
+
6
+ #####
7
+ # english helpers (penalty, extra time, ...)
8
+ P_EN = '(?: p | pen\.? | pso )' # e.g. p, pen, pen., PSO, etc.
9
+ ET_EN = '(?: aet | a\.e\.t\.? )' # note: make last . optional (e.g a.e.t) allowed too
10
+
11
+
12
+ ## note: allow SPECIAL cases WITHOUT full time scores (just a.e.t or pen. + a.e.t.)
13
+ ## 3-4 pen. 2-2 a.e.t.
14
+ ## 2-2 a.e.t.
15
+ EN__P_ET__RE = /\b
16
+ (?:
17
+ (?<score1p>\d{1,2})
18
+ [ ]* - [ ]* # note: sep in optional block; CANNOT use a reference
19
+ (?<score2p>\d{1,2})
20
+ [ ]* #{P_EN} [ ]*
21
+ )? # note: make penalty (P) score optional for now
22
+ (?<score1et>\d{1,2})
23
+ [ ]* - [ ]*
24
+ (?<score2et>\d{1,2})
25
+ [ ]* #{ET_EN}
26
+ (?=[ \]]|$)/xi ## todo/check: remove loakahead assertion here - why require space?
27
+ ## note: \b works only after non-alphanum e.g. )
28
+
29
+
30
+ ## e.g. 3-4 pen. 2-2 a.e.t. (1-1, 1-1) or
31
+ ## 3-4 pen. 2-2 a.e.t. (1-1, ) or
32
+ ## 3-4 pen. 2-2 a.e.t. (1-1) or
33
+ ## 2-2 a.e.t. (1-1, 1-1) or
34
+ ## 2-2 a.e.t. (1-1, ) or
35
+ ## 2-2 a.e.t. (1-1)
36
+
37
+ EN__P_ET_FT_HT__RE = /\b
38
+ (?:
39
+ (?<score1p>\d{1,2})
40
+ [ ]* - [ ]* # note: sep in optional block; CANNOT use a reference
41
+ (?<score2p>\d{1,2})
42
+ [ ]* #{P_EN} [ ]*
43
+ )? # note: make penalty (P) score optional for now
44
+ (?<score1et>\d{1,2})
45
+ [ ]* - [ ]*
46
+ (?<score2et>\d{1,2})
47
+ [ ]* #{ET_EN} [ ]*
48
+ \(
49
+ [ ]*
50
+ (?<score1>\d{1,2})
51
+ [ ]* - [ ]*
52
+ (?<score2>\d{1,2})
53
+ [ ]*
54
+ (?:
55
+ , [ ]*
56
+ (?: (?<score1i>\d{1,2})
57
+ [ ]* - [ ]*
58
+ (?<score2i>\d{1,2})
59
+ [ ]*
60
+ )?
61
+ )? # note: make half time (HT) score optional for now
62
+ \)
63
+ (?=[ \]]|$)/xi ## todo/check: remove loakahead assertion here - why require space?
64
+ ## note: \b works only after non-alphanum e.g. )
65
+
66
+ ###
67
+ ## special case for case WITHOUT extra time!!
68
+ ## same as above (but WITHOUT extra time and pen required)
69
+ EN__P_FT_HT__RE = /\b
70
+ (?<score1p>\d{1,2})
71
+ [ ]* - [ ]* # note: sep in optional block; CANNOT use a reference
72
+ (?<score2p>\d{1,2})
73
+ [ ]* #{P_EN} [ ]*
74
+ \(
75
+ [ ]*
76
+ (?<score1>\d{1,2})
77
+ [ ]* - [ ]*
78
+ (?<score2>\d{1,2})
79
+ [ ]*
80
+ (?:
81
+ , [ ]*
82
+ (?: (?<score1i>\d{1,2})
83
+ [ ]* - [ ]*
84
+ (?<score2i>\d{1,2})
85
+ [ ]*
86
+ )?
87
+ )? # note: make half time (HT) score optional for now
88
+ \)
89
+ (?=[ \]]|$)/xi ## todo/check: remove loakahead assertion here - why require space?
90
+ ## note: \b works only after non-alphanum e.g. )
91
+
92
+
93
+
94
+ ## e.g. 2-1 (1-1) or
95
+ ## 2-1
96
+ ## note: for now add here used in Brazil / Portugal
97
+ ## e.g 1x1 or 1X1 or 0x2 or 3x3 too
98
+ ## todo/check/fix: move to its own use PT__FT_HT etc!!!!
99
+
100
+ EN__FT_HT__RE = /\b
101
+ (?<score1>\d{1,2})
102
+ [ ]* (?<sep>[x-]) [ ]*
103
+ (?<score2>\d{1,2})
104
+ (?:
105
+ [ ]* \( [ ]*
106
+ (?<score1i>\d{1,2})
107
+ [ ]* \k<sep> [ ]*
108
+ (?<score2i>\d{1,2})
109
+ [ ]* \)
110
+ )? # note: make half time (HT) score optional for now
111
+ (?=[ \]]|$)/xi ## todo/check: remove loakahead assertion here - why require space?
112
+ ## note: \b works only after non-alphanum e.g. )
113
+
114
+
115
+ #####
116
+ # deutsch / german helpers (penalty, extra time, ...)
117
+ ## todo add more marker e.g. im Elf. or such!!!
118
+ P_DE = '(?: ie | i\.e\.? )' # e.g. iE, i.E., i.E etc.
119
+ ET_DE = '(?: nv | n\.v\.? )' # e.g. nV, n.V., n.V etc.
120
+
121
+
122
+ ## support alternate all-in-one score e.g.
123
+ ## i.E. 2:4, n.V. 3:3 (1:1, 1:1) or
124
+ ## n.V. 3:2 (2:2, 1:2)
125
+ DE__P_ET_FT_HT__RE = /\b
126
+ (?:
127
+ #{P_DE}
128
+ [ ]*
129
+ (?<score1p>\d{1,2})
130
+ [ ]* : [ ]*
131
+ (?<score2p>\d{1,2})
132
+ [ ]* (?:, [ ]*)?
133
+ )? # note: make penalty (P) score optional for now
134
+ #{ET_DE}
135
+ [ ]*
136
+ (?<score1et>\d{1,2})
137
+ [ ]* : [ ]*
138
+ (?<score2et>\d{1,2})
139
+ [ ]*
140
+ \(
141
+ [ ]*
142
+ (?<score1>\d{1,2})
143
+ [ ]* : [ ]*
144
+ (?<score2>\d{1,2})
145
+ [ ]*
146
+ (?:
147
+ , [ ]*
148
+ (?:
149
+ (?<score1i>\d{1,2})
150
+ [ ]* : [ ]*
151
+ (?<score2i>\d{1,2})
152
+ [ ]*
153
+ )?
154
+ )? # note: make half time (HT) score optional for now
155
+ \)
156
+ (?=[ \]]|$)
157
+ /xi
158
+
159
+ ## support all-in-one "literal form e.g.
160
+ # 2:2 (1:1, 1:0) n.V. 5:1 i.E. or
161
+ # 2-2 (1-1, 1-0) n.V. 5-1 i.E.
162
+ DE__ET_FT_HT_P__RE = /\b
163
+ (?<score1et>\d{1,2})
164
+ [ ]* (?<sep>[:-]) [ ]* ## note: for now allow : or - as separator!!
165
+ (?<score2et>\d{1,2})
166
+ [ ]*
167
+ \(
168
+ [ ]*
169
+ (?<score1>\d{1,2})
170
+ [ ]* \k<sep> [ ]*
171
+ (?<score2>\d{1,2})
172
+ [ ]*
173
+ (?:
174
+ , [ ]*
175
+ (?:
176
+ (?<score1i>\d{1,2})
177
+ [ ]* \k<sep> [ ]*
178
+ (?<score2i>\d{1,2})
179
+ [ ]*
180
+ )?
181
+ )? # note: make half time (HT) score optional for now
182
+ \)
183
+ [ ]*
184
+ #{ET_DE}
185
+ (?:
186
+ [ ]*
187
+ (?<score1p>\d{1,2})
188
+ [ ]* \k<sep> [ ]*
189
+ (?<score2p>\d{1,2})
190
+ [ ]*
191
+ #{P_DE}
192
+ )? # note: make penalty (P) score optional for now
193
+ (?=[ \]]|$)
194
+ /xi ## todo/check: remove loakahead assertion here - why require space?
195
+ ## note: \b works only after non-alphanum e.g. )
196
+
197
+
198
+ ## e.g. 2:1 (1:1) or
199
+ ## 2-1 (1-1) or
200
+ ## 2:1 or
201
+ ## 2-1
202
+ DE__FT_HT__RE = /\b
203
+ (?<score1>\d{1,2})
204
+ [ ]* (?<sep>[:-]) [ ]*
205
+ (?<score2>\d{1,2})
206
+ (?:
207
+ [ ]* \( [ ]*
208
+ (?<score1i>\d{1,2})
209
+ [ ]* \k<sep> [ ]*
210
+ (?<score2i>\d{1,2})
211
+ [ ]* \)
212
+ )? # note: make half time (HT) score optional for now
213
+ (?=[ \]]|$)/x ## todo/check: remove loakahead assertion here - why require space?
214
+ ## note: \b works only after non-alphanum e.g. )
215
+
216
+
217
+ #############################################
218
+ # map tables - 1) regex, 2) tag - note: order matters; first come-first matched/served
219
+
220
+
221
+ FORMATS_EN = [
222
+ [ EN__P_ET_FT_HT__RE, '[SCORE.EN__P?_ET_(FT_HT?)]' ], # e.g. 5-1 pen. 2-2 a.e.t. (1-1, 1-0)
223
+ [ EN__P_FT_HT__RE, '[SCORE.EN__P_(FT_HT?)]' ], # e.g. 5-1 pen. (1-1)
224
+ [ EN__P_ET__RE, '[SCORE.EN__P?_ET]' ], # e.g. 2-2 a.e.t. or 5-1 pen. 2-2 a.e.t.
225
+ [ EN__FT_HT__RE, '[SCORE.EN__FT_(HT)?]' ], # e.g. 1-1 (1-0)
226
+ ]
227
+
228
+ FORMATS_DE = [
229
+ [ DE__ET_FT_HT_P__RE, '[SCORE.DE__ET_(FT_HT?)_P?]' ], # e.g. 2:2 (1:1, 1:0) n.V. 5:1 i.E.
230
+ [ DE__P_ET_FT_HT__RE, '[SCORE.DE__P?_ET_(FT_HT?)]' ], # e.g. i.E. 2:4, n.V. 3:3 (1:1, 1:1)
231
+ [ DE__FT_HT__RE, '[SCORE.DE__FT_(HT)?]' ], # e.g. 1:1 (1:0)
232
+ ]
233
+
234
+ FORMATS = {
235
+ en: FORMATS_EN,
236
+ de: FORMATS_DE,
237
+ }
238
+
239
+ end # module ScoreFormats
@@ -0,0 +1,129 @@
1
+
2
+ module ScoreFormats
3
+
4
+ class ScoreParser
5
+
6
+ include Logging
7
+
8
+ def initialize( lang: )
9
+ @lang = lang.to_sym ## note: make sure lang is always a symbol for now (NOT a string)
10
+
11
+ ## fallback to english if lang not available
12
+ ## todo/fix: add/issue warning - why? why not?
13
+ @formats = FORMATS[ @lang ] || FORMATS[ :en ]
14
+ end
15
+
16
+
17
+ def parse( line )
18
+
19
+ ##########
20
+ ## todo/fix/check: add unicode to regular dash conversion - why? why not?
21
+ ## e.g. – becomes - (yes, the letters a different!!!)
22
+ #############
23
+
24
+ score = nil
25
+ @formats.each do |format|
26
+ re = format[0]
27
+ m = re.match( line )
28
+ if m
29
+ score = parse_matchdata( m )
30
+ break
31
+ end
32
+ # no match; continue; try next regex pattern
33
+ end
34
+
35
+ ## todo/fix - raise ArgumentError - invalid score; no format match found
36
+ score # note: nil if no match found
37
+ end # method parse
38
+
39
+
40
+ def find!( line )
41
+ ### fix: add and match all-in-one literal first, followed by
42
+
43
+ # note: always call after find_dates !!!
44
+ # scores match date-like patterns!! e.g. 10-11 or 10:00 etc.
45
+ # -- note: score might have two digits too
46
+
47
+ ### fix: depending on language allow 1:1 or 1-1
48
+ ## do NOT allow mix and match
49
+ ## e.g. default to en is 1-1
50
+ ## de is 1:1 etc.
51
+
52
+
53
+ # extract score from line
54
+ # and return it
55
+ # note: side effect - removes date from line string
56
+
57
+ score = nil
58
+ @formats.each do |format|
59
+ re = format[0]
60
+ tag = format[1]
61
+ m = re.match( line )
62
+ if m
63
+ score = parse_matchdata( m )
64
+ line.sub!( m[0], tag )
65
+ break
66
+ end
67
+ # no match; continue; try next regex pattern
68
+ end
69
+
70
+ score # note: nil if no match found
71
+ end # method find!
72
+
73
+ private
74
+ def parse_matchdata( m )
75
+ # convert regex match_data captures to hash
76
+ # - note: cannont use match_data like a hash (e.g. raises exception if key/name not present/found)
77
+ h = {}
78
+ # - note: do NOT forget to turn name into symbol for lookup in new hash (name.to_sym)
79
+ m.names.each { |name| h[name.to_sym] = m[name] } # or use match_data.names.zip( match_data.captures ) - more cryptic but "elegant"??
80
+
81
+ ## puts "[parse_date_time] match_data:"
82
+ ## pp h
83
+ logger.debug " [parse_matchdata] hash: >#{h.inspect}<"
84
+
85
+ score1i = nil # half time (ht) scores
86
+ score2i = nil
87
+
88
+ score1 = nil # full time (ft) scores
89
+ score2 = nil
90
+
91
+ score1et = nil # extra time (et) scores
92
+ score2et = nil
93
+
94
+ score1p = nil # penalty (p) scores
95
+ score2p = nil
96
+
97
+
98
+ if h[:score1i] && h[:score2i] ## note: half time (HT) score is optional now
99
+ score1i = h[:score1i].to_i
100
+ score2i = h[:score2i].to_i
101
+ end
102
+
103
+ if h[:score1] && h[:score2] ## note: full time (FT) score can be optional too!!!
104
+ score1 = h[:score1].to_i
105
+ score2 = h[:score2].to_i
106
+ end
107
+
108
+ if h[:score1et] && h[:score2et]
109
+ score1et = h[:score1et].to_i
110
+ score2et = h[:score2et].to_i
111
+ end
112
+
113
+ if h[:score1p] && h[:score2p]
114
+ score1p = h[:score1p].to_i
115
+ score2p = h[:score2p].to_i
116
+ end
117
+
118
+ score = Score.new( score1i, score2i,
119
+ score1, score2,
120
+ score1et, score2et,
121
+ score1p, score2p )
122
+ score
123
+ end # method parse_matchdata
124
+
125
+
126
+
127
+ end # class ScoreParser
128
+ end # module ScoreFormats
129
+
@@ -0,0 +1,165 @@
1
+
2
+
3
+ ## note: make Score top-level and use like Date - yes, yes, yes - why? why not?
4
+ class Score
5
+
6
+ SCORE_SPLIT_RE = %r{^ [ ]*
7
+ ([0-9]+)
8
+ [ ]*
9
+ [:x–-] ## note: allow some unicode dashes too
10
+ [ ]*
11
+ ([0-9]+)
12
+ [ ]* $}xi
13
+
14
+ def self.split( str ) ## note: return array of two integers or empty array
15
+ ## e.g. allow/support
16
+ ## 1-1 or 1 - 1 - "english" style
17
+ ## 1:1 - "german / deutsch" style
18
+ ## 1x1 1X1 - "brazil/portugese" style
19
+
20
+ ## note: add unicode "fancy" dash too (e.g. –)
21
+ ## add some more - why? why not?
22
+
23
+ if m=SCORE_SPLIT_RE.match(str)
24
+ [m[1].to_i, m[2].to_i]
25
+ else
26
+ # no match - warn if str is not empty? why? why not?
27
+ ##
28
+ ## todo/fix:
29
+ ## do NOT warn on
30
+ ## assert_equal [], Score.split( '-' )
31
+ ## assert_equal [], Score.split( '-:-' )
32
+ ## assert_equal [], Score.split( '?' )
33
+ ## for now - add more?
34
+
35
+ puts "!! WARN - cannot match (split) score format >#{str}<" unless str.empty?
36
+ []
37
+ end
38
+ end
39
+
40
+
41
+
42
+
43
+ attr_reader :score1i, :score2i, # half time (ht) score
44
+ :score1, :score2, # full time (ft) score
45
+ :score1et, :score2et, # extra time (et) score
46
+ :score1p, :score2p # penalty (p) score
47
+ ## todo/fix: add :score1agg, score2agg too - why? why not?!!!
48
+ ## add state too e.g. canceled or abadoned etc - why? why not?
49
+
50
+ ## alternate accessor via array e.g. ft[0] and ft[1]
51
+ def ft() [@score1, @score2]; end ## e.g. 90 mins (in football)
52
+ def ht() [@score1i, @score2i]; end ## e.g. 45 mins
53
+ def et() [@score1et, @score2et]; end ## e.g. 90+15mins
54
+ def p() [@score1p, @score2p]; end ## e.g. note - starts "fresh" score from 0-0
55
+ alias_method :pen, :p ## add alias - why? why not?
56
+
57
+ ## todo/check: allow one part missing why? why not?
58
+ ## e.g. 1-nil or nil-1 - why? why not?
59
+ def ft?() @score1 && @score2; end
60
+ def ht?() @score1i && @score2i; end
61
+ def et?() @score1et && @score2et; end
62
+ def p?() @score1p && @score2p; end
63
+ alias_method :pen?, :p?
64
+
65
+
66
+
67
+ def initialize( *values )
68
+ ## note: for now always assumes integers
69
+ ## todo/check - check/require integer args - why? why not?
70
+
71
+ ### todo/fix: add more init options
72
+ ## allow kwargs (keyword args) via hash - why? why not?
73
+ ## use kwargs for "perfect" init where you can only set the half time (ht) score
74
+ ## or only the penalty or other "edge" cases
75
+ ## allow int pairs e.g. [1,2], [2,2]
76
+ ## allow values array MUST be of size 8 (or 4 or 6) - why? why not?
77
+
78
+ raise ArgumentError, "expected even integer number (pairs), but got #{values.size}" if values.size % 2 == 1
79
+
80
+ if values.size == 2
81
+ @score1 = values[0] # full time (ft) score
82
+ @score2 = values[1]
83
+
84
+ @score1i = @score2i = nil
85
+ @score1et = @score2et = nil
86
+ @score1p = @score2p = nil
87
+ else
88
+ @score1i = values[0] # half time (ht) score
89
+ @score2i = values[1]
90
+
91
+ @score1 = values[2] # full time (ft) score
92
+ @score2 = values[3]
93
+
94
+ @score1et = values[4] # extra time (et) score
95
+ @score2et = values[5]
96
+
97
+ @score1p = values[6] # penalty (p) score
98
+ @score2p = values[7]
99
+ end
100
+ end
101
+
102
+
103
+
104
+ def to_h( format = :default )
105
+ case format.to_sym
106
+ when :default, :std
107
+ ## check/todo: only add entries if ft, ht, etc. have values (non-null) or always - why? why not?
108
+ h = {}
109
+ h[:ht] = [@score1i, @score2i] if @score1i || @score2i
110
+ h[:ft] = [@score1, @score2] if @score1 || @score2
111
+ h[:et] = [@score1et, @score2et] if @score1et || @score2et
112
+ h[:p] = [@score1p, @score2p] if @score1p || @score2p
113
+ h
114
+ when :db
115
+ ## use a "flat" structure with "internal" std names
116
+ { score1i: @score1i, score2i: @score2i,
117
+ score1: @score1, score2: @score2,
118
+ score1et: @score1et, score2et: @score2et,
119
+ score1p: @score1p, score2p: @score2p
120
+ }
121
+ else
122
+ puts "!! ERROR: unknown score to_h format >#{format}<"
123
+ exit 1
124
+ end
125
+ end
126
+ alias_method :to_hash, :to_h ## add alias - why? why not?
127
+
128
+
129
+ def values
130
+ ## todo/ fix: always return complete array
131
+ ## e.g. [score1i, score2i, score1, score2, score1et, score2et, score1p, score2p]
132
+
133
+ ## todo: how to handle game w/o extra time
134
+ # but w/ optional penalty ??? e.g. used in copa liberatores, for example
135
+ # retrun 0,0 or nil,nil for extra time score ?? or -1, -1 ??
136
+ # for now use nil,nil
137
+ score = []
138
+ score += [@score1i, @score2i] if @score1p || @score2p || @score1et || @score2et || @score1 || score2 || score1i || score2i
139
+ score += [@score1, @score2] if @score1p || @score2p || @score1et || @score2et || @score1 || score2
140
+ score += [@score1et, @score2et] if @score1p || @score2p || @score1et || @score2et
141
+ score += [@score1p, @score2p] if @score1p || @score2p
142
+ score
143
+ end
144
+
145
+ def to_a
146
+ ## pairs with values
147
+ pairs = []
148
+ ## note: allow 1-nil, nil-1 for now in pairs (or use && and NOT ||) - why? why not?
149
+ pairs << [@score1i, @score2i] if @score1i || @score2i
150
+ pairs << [@score1, @score2] if @score1 || @score2
151
+ pairs << [@score1et, @score2et] if @score1et || @score2et
152
+ pairs << [@score1p, @score2p] if @score1p || @score2p
153
+
154
+ if pairs.empty?
155
+ pairs # e.g. return []
156
+ elsif pairs.size == 1
157
+ pairs[0] # return single pair "unwrapped" e.g. [0,1] instead of [[0,1]] - why? why not?
158
+ else
159
+ pairs
160
+ end
161
+ end
162
+
163
+ end # class Score
164
+
165
+
@@ -0,0 +1,19 @@
1
+
2
+ module ScoreFormats
3
+ MAJOR = 0 ## todo: namespace inside version or something - why? why not??
4
+ MINOR = 0
5
+ PATCH = 1
6
+ VERSION = [MAJOR,MINOR,PATCH].join('.')
7
+
8
+ def self.version
9
+ VERSION
10
+ end
11
+
12
+ def self.banner
13
+ "score-formats/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
14
+ end
15
+
16
+ def self.root
17
+ File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )
18
+ end
19
+ end # module ScoreFormats
@@ -0,0 +1,5 @@
1
+ # note: allow require 'score/formats' too
2
+ # (in addition to require 'score-formats')
3
+
4
+ require_relative '../score-formats'
5
+
@@ -0,0 +1,12 @@
1
+ ## $:.unshift(File.dirname(__FILE__))
2
+
3
+ ## minitest setup
4
+
5
+ require 'minitest/autorun'
6
+
7
+
8
+ ## our own code
9
+
10
+ require 'score/formats' ## or require 'score-formats'
11
+
12
+
@@ -0,0 +1,129 @@
1
+ ###
2
+ # to run use
3
+ # ruby -I ./lib -I ./test test/test_formats.rb
4
+
5
+
6
+ require 'helper'
7
+
8
+ class TestFormats < MiniTest::Test
9
+
10
+ def test_de
11
+ ScoreFormats.lang = :de
12
+ data = [
13
+ [ '10:0', [nil,nil, 10,0]],
14
+ [ '1:22', [nil,nil, 1,22]],
15
+ [ '1-22', [nil,nil, 1,22]],
16
+
17
+ ## do not support three digits for now - why? why not?
18
+ [ '1:222', nil],
19
+ [ '111:0', nil],
20
+ [ '1-222', nil],
21
+ [ '111-0', nil],
22
+
23
+ [ '2:2 (1:1, 1:0) n.V. 5:1 i.E.', [1,0, 1,1, 2,2, 5,1]],
24
+ [ '2:2 (1:1, 1:0) n.V.', [1,0, 1,1, 2,2]],
25
+ [ '2:2 (1:1, ) n.V. 5:1 i.E.', [nil,nil, 1,1, 2,2, 5,1]],
26
+ [ '2:2 (1:1, ) n.V.', [nil,nil, 1,1, 2,2]],
27
+
28
+ [ '2:2 (1:1) n.V. 5:1 i.E.', [nil,nil, 1,1, 2,2, 5,1]],
29
+ [ '2:2 (1:1) n.V.', [nil,nil, 1,1, 2,2]],
30
+
31
+ [ '2-2 (1-1, 1-0) n.V. 5-1 i.E.', [1,0, 1,1, 2,2, 5,1]],
32
+ [ '2-2 (1-1, 1-0) n.V.', [1,0, 1,1, 2,2]],
33
+ [ '2-2 (1-1, ) n.V. 5-1 i.E.', [nil,nil, 1,1, 2,2, 5,1]],
34
+ [ '2-2 (1-1, ) n.V.', [nil,nil, 1,1, 2,2]],
35
+
36
+ [ '2 : 2 ( 1 : 1 , 1 : 0 ) n.V. 5 : 1 i.E.', [1,0, 1,1, 2,2, 5,1]],
37
+ [ '2 : 2 ( 1 : 1 , 1 : 0 ) n.V.', [1,0, 1,1, 2,2]],
38
+ [ '2 : 2 ( 1 : 1 , ) n.V. 5 : 1 i.E.', [nil,nil, 1,1, 2,2, 5,1]],
39
+ [ '2 : 2 ( 1 : 1 , ) n.V.', [nil,nil, 1,1, 2,2]],
40
+
41
+ ## alternate format
42
+ ['i.E. 2:4, n.V. 3:3 (1:1, 1:0)', [1,0, 1,1, 3,3, 2,4]],
43
+ ['iE 2:4 nV 3:3 (1:1, 1:0)', [1,0, 1,1, 3,3, 2,4]],
44
+ ['i.E. 2:4 n.V. 3:3 (1:1, 1:0)', [1,0, 1,1, 3,3, 2,4]],
45
+ ['i.E. 2:4, n.V. 3:3 (1:1)', [nil, nil, 1,1, 3,3, 2,4]],
46
+ ['i.E. 2:4 n.V. 3:3 (1:1)', [nil, nil, 1,1, 3,3, 2,4]],
47
+ ['n.V. 3:2 (2:2, 1:2)', [1,2, 2,2, 3,2]],
48
+ ['n.V. 3:2 (2:2)', [nil, nil, 2,2, 3,2]],
49
+ ]
50
+
51
+ assert_score( data )
52
+ end
53
+
54
+ def test_en
55
+ ScoreFormats.lang = :en
56
+
57
+ data = [
58
+ [ '1-22', [nil,nil, 1, 22]],
59
+ [ '1x22', [nil,nil, 1, 22]],
60
+ [ '1X22', [nil,nil, 1,22]],
61
+
62
+ ## do not support three digits for now - why? why not?
63
+ [ '1-222', nil],
64
+ [ '111-0', nil],
65
+ [ '111x0', nil],
66
+ [ '111X0', nil],
67
+
68
+ ## do not support colon sep for now in en locale - why? why not?
69
+ [ '2:1', nil],
70
+ [ '2:1 (1:1)', nil],
71
+
72
+
73
+ [ '2-1 (1-1)', [1,1, 2,1]],
74
+ [ '2x1 (1x1)', [1,1, 2,1]],
75
+ [ '2X1 (1X1)', [1,1, 2,1]],
76
+
77
+ [ '2-1 a.e.t. (1-1, 0-0)', [0,0, 1,1, 2,1]],
78
+ [ '2-1aet (1-1, 0-0)', [0,0, 1,1, 2,1]],
79
+ [ '2-1 A.E.T. (1-1, 0-0)', [0,0, 1,1, 2,1]],
80
+ [ '2-1AET (1-1, 0-0)', [0,0, 1,1, 2,1]],
81
+ [ '2-1 a.e.t.', [nil,nil,nil,nil, 2,1]],
82
+
83
+ [ '3-4 pen. 2-2 a.e.t. (1-1, 1-1)', [1,1, 1,1, 2,2, 3,4]],
84
+ [ '3-4 pen 2-2 a.e.t. (1-1, 1-1)', [1,1, 1,1, 2,2, 3,4]],
85
+ [ '3-4 pen 2-2 a.e.t. (1-1, 1-1)', [1,1, 1,1, 2,2, 3,4]],
86
+ [ '3-4p 2-2aet (1-1, 1-1)', [1,1, 1,1, 2,2, 3,4]],
87
+ [ '3-4PSO 2-2AET (1-1, 1-1)', [1,1, 1,1, 2,2, 3,4]],
88
+ [ '3-4 pen. 2-2 a.e.t.', [nil,nil,nil,nil, 2,2, 3,4]],
89
+
90
+ [ '4-3 pen. 1-0 a.e.t. (1-0, )', [nil,nil, 1,0, 1,0, 4,3]],
91
+ [ '3-4 pen. 2-1 a.e.t. (2-1, )', [nil,nil, 2,1, 2,1, 3,4]],
92
+ [ '4-1 a.e.t. (3-1, )', [nil,nil, 3,1, 4,1]],
93
+ [ '3-4aet (1-1,)', [nil,nil, 1,1, 3,4]],
94
+ [ '3-4 a.e.t. (1-1,)', [nil,nil, 1,1, 3,4]],
95
+
96
+ [ '4-3 pen. 1-0 a.e.t. (1-0)', [nil,nil, 1,0, 1,0, 4,3]],
97
+ [ '3-4 pen. 2-1 a.e.t. (2-1)', [nil,nil, 2,1, 2,1, 3,4]],
98
+ [ '4-1 a.e.t. (3-1)', [nil,nil, 3,1, 4,1]],
99
+ [ '3-4aet (1-1)', [nil,nil, 1,1, 3,4]],
100
+ [ '3-4 a.e.t. (1-1)', [nil,nil, 1,1, 3,4]],
101
+
102
+ [ '3-1 pen (1-1)', [nil,nil, 1,1, nil, nil, 3,1]],
103
+
104
+ ## try with more "liberal" spaces
105
+ [ '2 - 1 ( 1 - 1 )', [1,1, 2,1]],
106
+ [ '2 - 1 a.e.t. ( 1 - 1 , 0 - 0 )', [0,0, 1,1, 2,1]],
107
+ [ '4 - 1 a.e.t. ( 3 - 1, )', [nil,nil, 3,1, 4,1]],
108
+ ]
109
+
110
+ assert_score( data )
111
+ end
112
+
113
+ private
114
+ def assert_score( data )
115
+ data.each do |rec|
116
+ line = rec[0].dup
117
+ exp = rec[1]
118
+
119
+ score = ScoreFormats.find!( line )
120
+ ## pp score
121
+
122
+ if exp.nil?
123
+ assert_nil score, "failed >#{rec[0]}< - >#{line}<"
124
+ else
125
+ assert_equal exp, score.values, "failed >#{rec[0]}< - >#{line}<"
126
+ end
127
+ end
128
+ end
129
+ end # class TestScores
@@ -0,0 +1,76 @@
1
+ ###
2
+ # to run use
3
+ # ruby -I ./lib -I ./test test/test_score.rb
4
+
5
+
6
+ require 'helper'
7
+
8
+ class TestScore < MiniTest::Test
9
+
10
+ def test_split
11
+ assert_equal [], Score.split( '' )
12
+ assert_equal [], Score.split( '-' )
13
+ assert_equal [], Score.split( '-:-' )
14
+ assert_equal [], Score.split( '?' )
15
+
16
+ assert_equal [0,1], Score.split( '0-1' )
17
+ assert_equal [0,1], Score.split( ' 0 - 1 ' )
18
+ assert_equal [0,1], Score.split( '0:1' )
19
+ assert_equal [0,1], Score.split( ' 0 : 1 ' )
20
+ assert_equal [0,1], Score.split( '0x1' )
21
+ assert_equal [0,1], Score.split( '0X1' )
22
+
23
+ assert_equal [10,11], Score.split( '10 - 11' )
24
+ assert_equal [10,11], Score.split( '10 : 11' )
25
+ end
26
+
27
+ def test_to_h
28
+ assert_equal Hash( ft: [1,0] ), Score.parse( '1-0' ).to_h
29
+ assert_equal Hash( ht: [1,0],
30
+ ft: [3,2] ), Score.parse( '3-2 (1-0)' ).to_h
31
+ end
32
+
33
+
34
+ def test_new_et_al
35
+ score = Score.new
36
+ assert_equal [], score.to_a
37
+ assert_equal [], score.values
38
+ assert_equal [nil,nil], score.ft
39
+ assert_equal [nil,nil], score.ht
40
+ assert_equal Hash({}), score.to_h ## e.g. {} - empty hash
41
+ assert_equal Hash( score1i: nil, score2i: nil,
42
+ score1: nil, score2: nil,
43
+ score1et: nil, score2et: nil,
44
+ score1p: nil, score2p: nil ), score.to_h( :db )
45
+
46
+
47
+ [
48
+ Score.new( 0, 1 ),
49
+ Score.new( nil, nil, 0, 1 ),
50
+ ].each do |score|
51
+ assert_equal [0,1], score.to_a
52
+ assert_equal [nil,nil,0,1], score.values
53
+ assert_equal [nil,nil], score.ht
54
+ assert_equal [0,1], score.ft
55
+ assert_equal Hash(ft: [0,1]), score.to_h
56
+ assert_equal Hash( score1i: nil, score2i: nil,
57
+ score1: 0, score2: 1,
58
+ score1et: nil, score2et: nil,
59
+ score1p: nil, score2p: nil ), score.to_h( :db )
60
+ end
61
+
62
+
63
+ score = Score.new( 0, 1, 2, 3 )
64
+ assert_equal [[0,1],[2,3]], score.to_a
65
+ assert_equal [0,1,2,3], score.values
66
+ assert_equal [0,1], score.ht
67
+ assert_equal [2,3], score.ft
68
+ assert_equal Hash(ht: [0,1],
69
+ ft: [2,3]), score.to_h
70
+ assert_equal Hash( score1i: 0, score2i: 1,
71
+ score1: 2, score2: 3,
72
+ score1et: nil, score2et: nil,
73
+ score1p: nil, score2p: nil ), score.to_h( :db )
74
+ end
75
+
76
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: score-formats
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Gerald Bauer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-08-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdoc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hoe
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.16'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.16'
41
+ description: score-formats - read / parse and print sports match scores (incl. half
42
+ time, full time, extra time, penalties and more)
43
+ email: opensport@googlegroups.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files:
47
+ - CHANGELOG.md
48
+ - Manifest.txt
49
+ - README.md
50
+ files:
51
+ - CHANGELOG.md
52
+ - Manifest.txt
53
+ - README.md
54
+ - Rakefile
55
+ - lib/score-formats.rb
56
+ - lib/score-formats/formats.rb
57
+ - lib/score-formats/parser.rb
58
+ - lib/score-formats/score.rb
59
+ - lib/score-formats/version.rb
60
+ - lib/score/formats.rb
61
+ - test/helper.rb
62
+ - test/test_formats.rb
63
+ - test/test_score.rb
64
+ homepage: https://github.com/sportdb/sport.db
65
+ licenses:
66
+ - Public Domain
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options:
70
+ - "--main"
71
+ - README.md
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 2.2.2
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 2.5.2
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: score-formats - read / parse and print sports match scores (incl. half time,
90
+ full time, extra time, penalties and more)
91
+ test_files: []