rdf-tabular 0.4.0 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e84b41fe3c25549c84acc75984afc3c2fdd320b2
4
- data.tar.gz: fe3b1a0987bb7cc28e3ef9f8f7ed11617a2e6d8e
3
+ metadata.gz: 5fd3ba26034d83ae78387e86545e7020a2299141
4
+ data.tar.gz: 79494794a8a96f1799c0ecf7e28891f230a97f70
5
5
  SHA512:
6
- metadata.gz: 1c867b3e2abc2b5800b0f53367a1aadb6406471911f275878f9b79ecd00a8d3f7f19a47cdc58c8a7614575be8d993aebc8a3ac893bf04220b907d66833c33106
7
- data.tar.gz: 7e47390a977454aad3f15765501e56acf587f2aa829deec58bcc6b3857c3bec59e0ac9c136fee57994b6fe9a2ab2fa4d5251527cc18dcd05509600979756ca30
6
+ metadata.gz: def477f7bc65c716906f2e5bf75afde8e79a8abb5304cffc4cb5624f9dc412ae936a5ef4308e3555c865e1b0562d7b398b4706927df632ef47f1b706ea8fc66b
7
+ data.tar.gz: c61c6772c207e44062e6a92726e64e07a28ab18149f18b564403fd15e6f85badb96f0eb5e39fe253547f9c6837c4538b9a5db52881011d521f51217a2682bcf6
data/README.md CHANGED
@@ -248,7 +248,7 @@ Full documentation available on [RubyDoc](http://rubydoc.info/gems/rdf-tabular/f
248
248
  * {RDF::Tabular::Reader}
249
249
 
250
250
  ## Dependencies
251
- * [Ruby](http://ruby-lang.org/) (>= 2.0)
251
+ * [Ruby](http://ruby-lang.org/) (>= 2.2.2)
252
252
  * [RDF.rb](http://rubygems.org/gems/rdf) (>= 2.0)
253
253
  * [JSON](https://rubygems.org/gems/json) (>= 1.5)
254
254
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.0
1
+ 1.0.0
@@ -24,10 +24,10 @@ module RDF::Tabular
24
24
  #
25
25
  # @see http://www.w3.org/TR/rdf-testcases/#ntriples
26
26
  class Format < RDF::Format
27
- content_type 'text/csv',
27
+ content_type 'text/csv;q=0.4',
28
28
  extensions: [:csv, :tsv],
29
29
  alias: %w{
30
- text/tab-separated-values
30
+ text/tab-separated-values;q=0.4
31
31
  application/csvm+json
32
32
  }
33
33
  content_encoding 'utf-8'
@@ -2171,33 +2171,13 @@ module RDF::Tabular
2171
2171
  decimalChar = format["decimalChar"] || '.'
2172
2172
  pattern = format["pattern"]
2173
2173
 
2174
- if !datatype.parse_uax35_number(pattern, value, groupChar || ",", decimalChar)
2174
+ begin
2175
+ value = datatype.parse_uax35_number(pattern, value, groupChar || ",", decimalChar)
2176
+ rescue UAX35::ParseError
2175
2177
  value_errors << "#{value} does not match numeric pattern #{pattern ? pattern.inspect : 'default'}"
2176
2178
  end
2177
2179
 
2178
- # pattern facet failed
2179
- value_errors << "#{value} has repeating #{groupChar.inspect}" if groupChar && value.include?(groupChar*2)
2180
- value = value.gsub(groupChar || ',', '')
2181
- value = value.sub(decimalChar, '.')
2182
-
2183
- # Extract percent or per-mille sign
2184
- percent = permille = false
2185
- case value
2186
- when /%/
2187
- value = value.sub('%', '')
2188
- percent = true
2189
- when /‰/
2190
- value = value.sub('‰', '')
2191
- permille = true
2192
- end
2193
-
2194
2180
  lit = RDF::Literal(value, datatype: expanded_dt)
2195
- if percent || permille
2196
- o = lit.object
2197
- o = o / 100 if percent
2198
- o = o / 1000 if permille
2199
- lit = RDF::Literal(o, datatype: expanded_dt)
2200
- end
2201
2181
 
2202
2182
  if !lit.plain? && datatype.minimum && lit < datatype.minimum
2203
2183
  value_errors << "#{value} < minimum #{datatype.minimum}"
@@ -2238,10 +2218,11 @@ module RDF::Tabular
2238
2218
  end
2239
2219
  end
2240
2220
  when :date, :time, :dateTime, :dateTimeStamp, :datetime
2241
- if value = datatype.parse_uax35_date(format, value)
2221
+ begin
2222
+ value = datatype.parse_uax35_date(format, value)
2242
2223
  lit = RDF::Literal(value, datatype: expanded_dt)
2243
- else
2244
- value_errors << "#{original_value} does not match format #{format}"
2224
+ rescue UAX35::ParseError
2225
+ value_errors << "#{value} does not match format #{format}"
2245
2226
  end
2246
2227
  when :duration, :dayTimeDuration, :yearMonthDuration
2247
2228
  # SPEC CONFUSION: surely format also includes that for other duration types?
@@ -7,50 +7,99 @@ module RDF::Tabular
7
7
  module UAX35
8
8
 
9
9
  ##
10
- # Parse the date format (if provided), and match against the value (if provided)
11
- # Otherwise, validate format and raise an error
10
+ # Parse the date pattern (if provided), and match against the value (if provided)
11
+ # Otherwise, validate pattern and raise an error.
12
12
  #
13
- # @param [String] format
13
+ # Supported patterns are:
14
+ #
15
+ # * yyyy-MM-dd
16
+ # * yyyyMMdd
17
+ # * dd-MM-yyyy
18
+ # * d-M-yyyy
19
+ # * d-M-yy
20
+ # * d-M-y
21
+ # * MM-dd-yyyy
22
+ # * M-d-yyyy
23
+ # * M-d-yy
24
+ # * M-d-y
25
+ # * dd/MM/yyyy
26
+ # * d/M/yyyy
27
+ # * d/M/yy
28
+ # * d/M/y
29
+ # * MM/dd/yyyy
30
+ # * M/d/yyyy
31
+ # * M/d/yy
32
+ # * M/d/y
33
+ # * dd.MM.yyyy
34
+ # * d.M.yyyy
35
+ # * d.M.yy
36
+ # * d.M.y
37
+ # * MM.dd.yyyy
38
+ # * M.d.yyyy
39
+ # * M.d.yy
40
+ # * M.d.y
41
+ # * yyyy-MM-ddTHH:mm
42
+ # * yyyy-MM-ddTHH:mm:ss
43
+ # * yyyy-MM-ddTHH:mm:ss.S+
44
+ #
45
+ # Year comonents less than four digits are normalized to 1900 or 2000 based on if the value is <= 99 or >= 70, it is considered to be in the 1900 range, otherwise, based on 2000.
46
+ #
47
+ # @param [String] pattern
14
48
  # @param [String] value
15
49
  # @return [String] XMLSchema version of value
16
- # @raise [ArgumentError] if format is not valid, or nil, if value does not match
17
- def parse_uax35_date(format, value)
18
- date_format, time_format = nil, nil
19
- return value unless format
20
- value ||= ""
50
+ # @raise [ArgumentError] if pattern is not valid, or nil
51
+ # @raise [ParseError] if value does not match
52
+ def parse_uax35_date(pattern, value)
53
+ date_pattern, time_pattern = nil, nil
54
+ return value unless pattern
55
+ orig_value = value ||= ""
56
+ orig_pattern = pattern
21
57
 
22
58
  # Extract tz info
23
- if md = format.match(/^(.*[dyms])+(\s*[xX]+)$/)
24
- format, tz_format = md[1], md[2]
59
+ if md = pattern.match(/^(.*[dyms])+(\s*[xX]+)$/)
60
+ pattern, tz_pattern = md[1], md[2]
25
61
  end
26
62
 
27
- date_format, time_format = format.split(' ')
28
- date_format, time_format = nil, date_format if self.base.to_sym == :time
63
+ date_pattern, time_pattern = pattern.split(' ')
64
+ # Snuff out if this is a Time pattern
65
+ date_pattern, time_pattern = nil, date_pattern if time_pattern.nil? && !date_pattern.match(/[TyMd]/)
29
66
 
30
67
  # Extract date, of specified
31
- date_part = case date_format
68
+ date_part = case date_pattern
32
69
  when 'yyyy-MM-dd' then value.match(/^(?<yr>\d{4})-(?<mo>\d{2})-(?<da>\d{2})/)
33
70
  when 'yyyyMMdd' then value.match(/^(?<yr>\d{4})(?<mo>\d{2})(?<da>\d{2})/)
34
71
  when 'dd-MM-yyyy' then value.match(/^(?<da>\d{2})-(?<mo>\d{2})-(?<yr>\d{4})/)
35
72
  when 'd-M-yyyy' then value.match(/^(?<da>\d{1,2})-(?<mo>\d{1,2})-(?<yr>\d{4})/)
73
+ when 'd-M-yy' then value.match(/^(?<da>\d{1,2})-(?<mo>\d{1,2})-(?<yr>\d{2})/)
74
+ when 'd-M-y' then value.match(/^(?<da>\d{1,2})-(?<mo>\d{1,2})-(?<yr>\d{1,4})/)
36
75
  when 'MM-dd-yyyy' then value.match(/^(?<mo>\d{2})-(?<da>\d{2})-(?<yr>\d{4})/)
37
76
  when 'M-d-yyyy' then value.match(/^(?<mo>\d{1,2})-(?<da>\d{1,2})-(?<yr>\d{4})/)
38
- when 'dd/MM/yyyy' then value.match(/^(?<da>\d{2})\/(?<mo>\d{2})\/(?<yr>\d{4})/)
77
+ when 'M-d-yy' then value.match(/^(?<mo>\d{1,2})-(?<da>\d{1,2})-(?<yr>\d{2})/)
78
+ when 'M-d-y' then value.match(/^(?<mo>\d{1,2})-(?<da>\d{1,2})-(?<yr>\d{1,4})/)
79
+ when 'dd/MM/yyyy' then value.match(/^(?<da>\d{2})\/(?<mo>\d{2})\/(?<yr>\d{1,4})/)
39
80
  when 'd/M/yyyy' then value.match(/^(?<da>\d{1,2})\/(?<mo>\d{1,2})\/(?<yr>\d{4})/)
40
- when 'MM/dd/yyyy' then value.match(/^(?<mo>\d{2})\/(?<da>\d{2})\/(?<yr>\d{4})/)
81
+ when 'd/M/yy' then value.match(/^(?<da>\d{1,2})\/(?<mo>\d{1,2})\/(?<yr>\d{2})/)
82
+ when 'd/M/y' then value.match(/^(?<da>\d{1,2})\/(?<mo>\d{1,2})\/(?<yr>\d{1,4})/)
83
+ when 'MM/dd/yyyy' then value.match(/^(?<mo>\d{2})\/(?<da>\d{2})\/(?<yr>\d{1,4})/)
41
84
  when 'M/d/yyyy' then value.match(/^(?<mo>\d{1,2})\/(?<da>\d{1,2})\/(?<yr>\d{4})/)
85
+ when 'M/d/yy' then value.match(/^(?<mo>\d{1,2})\/(?<da>\d{1,2})\/(?<yr>\d{2})/)
86
+ when 'M/d/y' then value.match(/^(?<mo>\d{1,2})\/(?<da>\d{1,2})\/(?<yr>\d{1,4})/)
42
87
  when 'dd.MM.yyyy' then value.match(/^(?<da>\d{2})\.(?<mo>\d{2})\.(?<yr>\d{4})/)
43
88
  when 'd.M.yyyy' then value.match(/^(?<da>\d{1,2})\.(?<mo>\d{1,2})\.(?<yr>\d{4})/)
89
+ when 'd.M.yy' then value.match(/^(?<da>\d{1,2})\.(?<mo>\d{1,2})\.(?<yr>\d{2})/)
90
+ when 'd.M.y' then value.match(/^(?<da>\d{1,2})\.(?<mo>\d{1,2})\.(?<yr>\d{1,4})/)
44
91
  when 'MM.dd.yyyy' then value.match(/^(?<mo>\d{2})\.(?<da>\d{2})\.(?<yr>\d{4})/)
45
92
  when 'M.d.yyyy' then value.match(/^(?<mo>\d{1,2})\.(?<da>\d{1,2})\.(?<yr>\d{4})/)
93
+ when 'M.d.yy' then value.match(/^(?<mo>\d{1,2})\.(?<da>\d{1,2})\.(?<yr>\d{2})/)
94
+ when 'M.d.y' then value.match(/^(?<mo>\d{1,2})\.(?<da>\d{1,2})\.(?<yr>\d{1,4})/)
46
95
  when 'yyyy-MM-ddTHH:mm' then value.match(/^(?<yr>\d{4})-(?<mo>\d{2})-(?<da>\d{2})T(?<hr>\d{2}):(?<mi>\d{2})(?<se>(?<ms>))/)
47
96
  when 'yyyy-MM-ddTHH:mm:ss' then value.match(/^(?<yr>\d{4})-(?<mo>\d{2})-(?<da>\d{2})T(?<hr>\d{2}):(?<mi>\d{2}):(?<se>\d{2})(?<ms>)/)
48
97
  when /yyyy-MM-ddTHH:mm:ss\.S+/
49
98
  md = value.match(/^(?<yr>\d{4})-(?<mo>\d{2})-(?<da>\d{2})T(?<hr>\d{2}):(?<mi>\d{2}):(?<se>\d{2})\.(?<ms>\d+)/)
50
- num_ms = date_format.match(/S+/).to_s.length
99
+ num_ms = date_pattern.match(/S+/).to_s.length
51
100
  md if md && md[:ms].length <= num_ms
52
101
  else
53
- raise ArgumentError, "unrecognized date/time format #{date_format}" if date_format
102
+ raise ArgumentError, "unrecognized date/time pattern #{date_pattern}" if date_pattern
54
103
  nil
55
104
  end
56
105
 
@@ -61,25 +110,25 @@ module RDF::Tabular
61
110
  end
62
111
 
63
112
  # Extract time, of specified
64
- time_part = case time_format
113
+ time_part = case time_pattern
65
114
  when 'HH:mm:ss' then value.match(/^(?<hr>\d{2}):(?<mi>\d{2}):(?<se>\d{2})(?<ms>)/)
66
115
  when 'HHmmss' then value.match(/^(?<hr>\d{2})(?<mi>\d{2})(?<se>\d{2})(?<ms>)/)
67
116
  when 'HH:mm' then value.match(/^(?<hr>\d{2}):(?<mi>\d{2})(?<se>)(?<ms>)/)
68
117
  when 'HHmm' then value.match(/^(?<hr>\d{2})(?<mi>\d{2})(?<se>)(?<ms>)/)
69
118
  when /HH:mm:ss\.S+/
70
119
  md = value.match(/^(?<hr>\d{2}):(?<mi>\d{2}):(?<se>\d{2})\.(?<ms>\d+)/)
71
- num_ms = time_format.match(/S+/).to_s.length
120
+ num_ms = time_pattern.match(/S+/).to_s.length
72
121
  md if md && md[:ms].length <= num_ms
73
122
  else
74
- raise ArgumentError, "unrecognized date/time format #{time_format}" if time_format
123
+ raise ArgumentError, "unrecognized date/time pattern #{pattern}" if time_pattern
75
124
  nil
76
125
  end
77
126
 
78
- # If there's a date_format but no date_part, match fails
79
- return nil if date_format && date_part.nil?
127
+ # If there's a date_pattern but no date_part, match fails
128
+ raise ParseError, "#{orig_value} does not match pattern #{orig_pattern}" if !orig_value.empty? && date_pattern && date_part.nil?
80
129
 
81
- # If there's a time_format but no time_part, match fails
82
- return nil if time_format && time_part.nil?
130
+ # If there's a time_pattern but no time_part, match fails
131
+ raise ParseError, "#{orig_value} does not match pattern #{orig_pattern}" if !orig_value.empty? && time_pattern && time_part.nil?
83
132
 
84
133
  # Forward past time part
85
134
  value = value[time_part.to_s.length..-1] if time_part
@@ -88,8 +137,8 @@ module RDF::Tabular
88
137
  time_part = date_part if date_part && date_part.names.include?("hr")
89
138
 
90
139
  # If there's a timezone, it may optionally start with whitespace
91
- value = value.lstrip if tz_format.to_s.start_with?(' ')
92
- tz_part = case tz_format.to_s.lstrip
140
+ value = value.lstrip if tz_pattern.to_s.start_with?(' ')
141
+ tz_part = case tz_pattern.to_s.lstrip
93
142
  when 'x' then value.match(/^(?:(?<hr>[+-]\d{2})(?<mi>\d{2})?)$/)
94
143
  when 'X' then value.match(/^(?:(?:(?<hr>[+-]\d{2})(?<mi>\d{2})?)|(?<z>Z))$/)
95
144
  when 'xx' then value.match(/^(?:(?<hr>[+-]\d{2})(?<mi>\d{2}))|$/)
@@ -97,15 +146,30 @@ module RDF::Tabular
97
146
  when 'xxx' then value.match(/^(?:(?<hr>[+-]\d{2}):(?<mi>\d{2}))$/)
98
147
  when 'XXX' then value.match(/^(?:(?:(?<hr>[+-]\d{2}):(?<mi>\d{2}))|(?<z>Z))$/)
99
148
  else
100
- raise ArgumentError, "unrecognized timezone format #{tz_format.to_s.lstrip}" if tz_format
149
+ raise ArgumentError, "unrecognized timezone pattern #{tz_pattern.to_s.lstrip}" if tz_pattern
101
150
  nil
102
151
  end
103
152
 
104
- # If there's a tz_format but no time_part, match fails
105
- return nil if tz_format && tz_part.nil?
153
+ # If there's a tz_pattern but no time_part, match fails
154
+ raise ParseError, "#{orig_value} does not match pattern #{orig_pattern}" if !orig_value.empty? && tz_pattern && tz_part.nil?
106
155
 
107
156
  # Compose normalized value
108
- vd = ("%04d-%02d-%02d" % [date_part[:yr].to_i, date_part[:mo].to_i, date_part[:da].to_i]) if date_part
157
+ vd = if date_part
158
+ yr, mo, da = [date_part[:yr], date_part[:mo], date_part[:da]].map(&:to_i)
159
+
160
+ if date_part[:yr].length < 4
161
+ # Make sure that yr makes sense, if given
162
+ yr = case yr
163
+ when 0..69 then yr + 2000
164
+ when 100..999 then yr + 2000
165
+ when 70..99 then yr + 1900
166
+ else yr
167
+ end
168
+ end
169
+
170
+ ("%04d-%02d-%02d" % [yr, mo, da])
171
+ end
172
+
109
173
  vt = ("%02d:%02d:%02d" % [time_part[:hr].to_i, time_part[:mi].to_i, time_part[:se].to_i]) if time_part
110
174
 
111
175
  # Add milliseconds, if matched
@@ -117,37 +181,74 @@ module RDF::Tabular
117
181
  end
118
182
 
119
183
  ##
120
- # Parse the date format (if provided), and match against the value (if provided)
121
- # Otherwise, validate format and raise an error
184
+ # Parse the date pattern (if provided), and match against the value (if provided)
185
+ # Otherwise, validate pattern and raise an error
122
186
  #
123
187
  # @param [String] pattern
124
188
  # @param [String] value
125
189
  # @param [String] groupChar
126
190
  # @param [String] decimalChar
127
191
  # @return [String] XMLSchema version of value or nil, if value does not match
128
- # @raise [ArgumentError] if format is not valid
192
+ # @raise [ArgumentError] if pattern is not valid
129
193
  def parse_uax35_number(pattern, value, groupChar=",", decimalChar=".")
130
194
  value ||= ""
131
195
 
132
196
  re = build_number_re(pattern, groupChar, decimalChar)
133
197
 
198
+ raise ParseError, "#{value} has repeating #{groupChar.inspect}" if groupChar.length == 1 && value.include?(groupChar*2)
199
+
134
200
  # Upcase value and remove internal spaces
135
201
  value = value.upcase
136
202
 
137
203
  if value =~ re
138
-
139
204
  # Upcase value and remove internal spaces
140
205
  value = value.
141
- upcase.
142
206
  gsub(/\s+/, '').
143
207
  gsub(groupChar, '').
144
208
  gsub(decimalChar, '.')
145
209
 
146
210
  # result re-assembles parts removed from value
147
211
  value
148
- else
212
+ elsif !value.empty?
149
213
  # no match
150
- nil
214
+ raise ParseError, "#{value.inspect} does not match #{pattern.inspect}"
215
+ end
216
+
217
+ # Extract percent or per-mille sign
218
+ case value
219
+ when /%/
220
+ value = value.sub('%', '')
221
+ lhs, rhs = value.split('.')
222
+
223
+ # Shift decimal
224
+ value = case lhs.length
225
+ when 0 then "0.00#{rhs}".sub('E', 'e')
226
+ when 1 then "0.0#{lhs}#{rhs}".sub('E', 'e')
227
+ when 2 then "0.#{lhs}#{rhs}".sub('E', 'e')
228
+ else
229
+ ll, lr = lhs[0..lhs.length-3], lhs[-2..-1]
230
+ ll = ll + "0" unless ll =~ /\d+/
231
+ "#{ll}.#{lr}#{rhs}".sub('E', 'e')
232
+ end
233
+ when /‰/
234
+ value = value.sub('‰', '')
235
+ lhs, rhs = value.split('.')
236
+
237
+ # Shift decimal
238
+ value = case lhs.length
239
+ when 0 then "0.000#{rhs}".sub('E', 'e')
240
+ when 1 then "0.00#{lhs}#{rhs}".sub('E', 'e')
241
+ when 2 then "0.0#{lhs}#{rhs}".sub('E', 'e')
242
+ when 3 then "0.#{lhs}#{rhs}".sub('E', 'e')
243
+ else
244
+ ll, lr = lhs[0..lhs.length-4], lhs[-3..-1]
245
+ ll = ll + "0" unless ll =~ /\d+/
246
+ "#{ll}.#{lr}#{rhs}".sub('E', 'e')
247
+ end
248
+ when /NAN/ then value.sub('NAN', 'NaN')
249
+ when /E/ then value.sub('E', 'e')
250
+ else
251
+ value
151
252
  end
152
253
  end
153
254
 
@@ -157,9 +258,10 @@ module RDF::Tabular
157
258
  # @param [String] groupChar
158
259
  # @param [String] decimalChar
159
260
  # @return [Regexp] Regular expression matching value
160
- # @raise [ArgumentError] if format is not valid
261
+ # @raise [ArgumentError] if pattern is not valid
161
262
  def build_number_re(pattern, groupChar, decimalChar)
162
263
  # pattern must be composed of only 0, #, decimalChar, groupChar, E, %, and ‰
264
+
163
265
  ge = Regexp.escape groupChar
164
266
  de = Regexp.escape decimalChar
165
267
 
@@ -320,5 +422,8 @@ module RDF::Tabular
320
422
 
321
423
  Regexp.new("^(?<prefix>#{prefix})(?<numeric_part>#{integer_str}#{fractional_str}#{exponent_str})(?<suffix>#{suffix})$")
322
424
  end
425
+
426
+ # ParseError is raised when a value does not match the pattern
427
+ class ParseError < RuntimeError; end
323
428
  end
324
429
  end
@@ -54,13 +54,14 @@ describe RDF::Tabular::Format do
54
54
  end
55
55
  })
56
56
  end
57
- after(:each) {|example| puts logger.to_s if example.exception}
58
57
 
59
58
  require 'rdf/cli'
60
- let(:input) {File.expand_path("../data/countries.json", __FILE__)}
59
+ let(:input) {"http://example.org/data/countries.json"}
61
60
  describe "#tabular-json" do
62
61
  it "serializes to JSON" do
63
- expect {RDF::CLI.exec(["tabular-json", input], format: :tabular)}.to write.to(:output)
62
+ expect {
63
+ RDF::CLI.exec(["tabular-json", input], format: :tabular)
64
+ }.to write.to(:output)
64
65
  end
65
66
  end
66
67
  end
@@ -1136,7 +1136,7 @@ describe RDF::Tabular::Metadata do
1136
1136
  format: {"groupChar" => ";"},
1137
1137
  value: "123;;456.789",
1138
1138
  result: "123;;456.789",
1139
- errors: [/has repeating/]
1139
+ errors: [/does not match numeric pattern/]
1140
1140
  },
1141
1141
  "decimal with explicit decimalChar" => {
1142
1142
  base: "decimal",
@@ -1184,19 +1184,19 @@ describe RDF::Tabular::Metadata do
1184
1184
  "invalid nonPositiveInteger" => {base: "nonPositiveInteger", value: "1", errors: ["1 is not a valid nonPositiveInteger"]},
1185
1185
  "valid nonNegativeInteger" => {base: "nonNegativeInteger", value: "0"},
1186
1186
  "invalid nonNegativeInteger" => {base: "nonNegativeInteger", value: "-1", errors: ["-1 is not a valid nonNegativeInteger"]},
1187
- "valid double" => {base: "double", value: "1234.456E789"},
1187
+ "valid double" => {base: "double", value: "1234.456e789"},
1188
1188
  "invalid double" => {base: "double", value: "1z", errors: ["1z is not a valid double"]},
1189
- "NaN double" => {base: "double", value: "NaN"},
1189
+ "NaN double" => {base: "double", value: "NaN", result: "NaN"},
1190
1190
  "INF double" => {base: "double", value: "INF"},
1191
1191
  "-INF double" => {base: "double", value: "-INF"},
1192
- "valid number" => {base: "number", value: "1234.456E789"},
1192
+ "valid number" => {base: "number", value: "1234.456e789"},
1193
1193
  "invalid number" => {base: "number", value: "1z", errors: ["1z is not a valid number"]},
1194
- "NaN number" => {base: "number", value: "NaN"},
1194
+ "NaN number" => {base: "number", value: "NaN", result: "NaN"},
1195
1195
  "INF number" => {base: "number", value: "INF"},
1196
1196
  "-INF number" => {base: "number", value: "-INF"},
1197
- "valid float" => {base: "float", value: "1234.456E7"},
1197
+ "valid float" => {base: "float", value: "1234.456e7"},
1198
1198
  "invalid float" => {base: "float", value: "1z", errors: ["1z is not a valid float"]},
1199
- "NaN float" => {base: "float", value: "NaN"},
1199
+ "NaN float" => {base: "float", value: "NaN", result: "NaN"},
1200
1200
  "INF float" => {base: "float", value: "INF"},
1201
1201
  "-INF float" => {base: "float", value: "-INF"},
1202
1202
 
@@ -1327,7 +1327,7 @@ describe RDF::Tabular::Metadata do
1327
1327
  "valid NMTOKEN" => {base: "NMTOKEN", value: "someThing", result: RDF::Literal("someThing", datatype: RDF::XSD.NMTOKEN)},
1328
1328
 
1329
1329
  # Aliases
1330
- "number is alias for double" => {base: "number", value: "1234.456E789", result: RDF::Literal("1234.456E789", datatype: RDF::XSD.double)},
1330
+ "number is alias for double" => {base: "number", value: "1234.456e789", result: RDF::Literal("1234.456e789", datatype: RDF::XSD.double)},
1331
1331
  "binary is alias for base64Binary" => {base: "binary", value: "Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g", result: RDF::Literal("Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g", datatype: RDF::XSD.base64Binary)},
1332
1332
  "datetime is alias for dateTime" => {base: "dateTime", value: "15-3-2015 1502", format: "d-M-yyyy HHmm", result: RDF::Literal("2015-03-15T15:02:00", datatype: RDF::XSD.dateTime)},
1333
1333
  "any is alias for anyAtomicType" => {base: "any", value: "some thing", result: RDF::Literal("some thing", datatype: RDF::XSD.anyAtomicType)},
@@ -18,7 +18,7 @@ describe RDF::Tabular::Reader do
18
18
  m.entries.each do |t|
19
19
  next if t.approval =~ /Rejected/
20
20
  specify "#{t.id.split("/").last}: #{t.name} - #{t.comment}" do
21
- pending "rdf#test158 should be isomorphic" if t.id.include?("rdf#test158")
21
+ pending "rdf#test283 literal normalization" if t.id.include?("rdf#test283")
22
22
  t.logger = RDF::Spec.logger
23
23
  t.logger.formatter = lambda {|severity, datetime, progname, msg| "#{severity}: #{msg}\n"}
24
24
  t.logger.info t.inspect
@@ -0,0 +1,239 @@
1
+ # encoding: UTF-8
2
+ $:.unshift "."
3
+ require 'spec_helper'
4
+
5
+ describe RDF::Tabular::UAX35 do
6
+ subject {"".extend(RDF::Tabular::UAX35)}
7
+
8
+ describe "parse_uax35_date" do
9
+ {
10
+ # Dates
11
+ "valid date yyyy-MM-dd" => {value: "2015-03-22", pattern: "yyyy-MM-dd", result: "2015-03-22"},
12
+ "valid date yyyyMMdd" => {value: "20150322", pattern: "yyyyMMdd", result: "2015-03-22"},
13
+ "valid date dd-MM-yyyy" => {value: "22-03-2015", pattern: "dd-MM-yyyy", result: "2015-03-22"},
14
+ "valid date d-M-yyyy" => {value: "22-3-2015", pattern: "d-M-yyyy", result: "2015-03-22"},
15
+ "valid date d-M-yy" => {value: "22-3-15", pattern: "d-M-yy", result: "2015-03-22"},
16
+ "valid date d-M-y" => {value: "22-3-15", pattern: "d-M-y", result: "2015-03-22"},
17
+ "valid date MM-dd-yyyy" => {value: "03-22-2015", pattern: "MM-dd-yyyy", result: "2015-03-22"},
18
+ "valid date M-d-yyyy" => {value: "3-22-2015", pattern: "M-d-yyyy", result: "2015-03-22"},
19
+ "valid date M-d-yy" => {value: "3-22-70", pattern: "M-d-yy", result: "1970-03-22"},
20
+ "valid date M-d-y" => {value: "3-22-70", pattern: "M-d-y", result: "1970-03-22"},
21
+ "valid date dd/MM/yyyy" => {value: "22/03/2015", pattern: "dd/MM/yyyy", result: "2015-03-22"},
22
+ "valid date d/M/yyyy" => {value: "22/3/2015", pattern: "d/M/yyyy", result: "2015-03-22"},
23
+ "valid date d/M/yy" => {value: "22/3/15", pattern: "d/M/yy", result: "2015-03-22"},
24
+ "valid date d/M/y" => {value: "22/3/15", pattern: "d/M/y", result: "2015-03-22"},
25
+ "valid date MM/dd/yyyy" => {value: "03/22/2015", pattern: "MM/dd/yyyy", result: "2015-03-22"},
26
+ "valid date M/d/yyyy" => {value: "3/22/2015", pattern: "M/d/yyyy", result: "2015-03-22"},
27
+ "valid date M/d/yy" => {value: "3/22/15", pattern: "M/d/yy", result: "2015-03-22"},
28
+ "valid date M/d/y" => {value: "3/22/15", pattern: "M/d/y", result: "2015-03-22"},
29
+ "valid date dd.MM.yyyy" => {value: "22.03.2015", pattern: "dd.MM.yyyy", result: "2015-03-22"},
30
+ "valid date d.M.yyyy" => {value: "22.3.2015", pattern: "d.M.yyyy", result: "2015-03-22"},
31
+ "valid date d.M.yy" => {value: "22.3.15", pattern: "d.M.yy", result: "2015-03-22"},
32
+ "valid date d.M.y" => {value: "22.3.15", pattern: "d.M.y", result: "2015-03-22"},
33
+ "valid date MM.dd.yyyy" => {value: "03.22.2015", pattern: "MM.dd.yyyy", result: "2015-03-22"},
34
+ "valid date M.d.yyyy" => {value: "3.22.2015", pattern: "M.d.yyyy", result: "2015-03-22"},
35
+ "valid date M.d.yy" => {value: "3.22.15", pattern: "M.d.yy", result: "2015-03-22"},
36
+ "valid date M.d.y" => {value: "3.22.15", pattern: "M.d.y", result: "2015-03-22"},
37
+
38
+ # Times
39
+ "valid time HH:mm:ss.S" => {value: "15:02:37.1", pattern: "HH:mm:ss.S", result: "15:02:37.1"},
40
+ "valid time HH:mm:ss" => {value: "15:02:37", pattern: "HH:mm:ss", result: "15:02:37"},
41
+ "valid time HHmmss" => {value: "150237", pattern: "HHmmss", result: "15:02:37"},
42
+ "valid time HH:mm" => {value: "15:02", pattern: "HH:mm", result: "15:02:00"},
43
+ "valid time HHmm" => {value: "1502", pattern: "HHmm", result: "15:02:00"},
44
+
45
+ # DateTimes
46
+ "valid dateTime yyyy-MM-ddTHH:mm:ss" => {value: "2015-03-15T15:02:37", pattern: "yyyy-MM-ddTHH:mm:ss", result: "2015-03-15T15:02:37"},
47
+ "valid dateTime yyyy-MM-ddTHH:mm:ss.S"=> {value: "2015-03-15T15:02:37.1", pattern: "yyyy-MM-ddTHH:mm:ss.S", result: "2015-03-15T15:02:37.1"},
48
+ "valid dateTime yyyy-MM-dd HH:mm:ss" => {value: "2015-03-15 15:02:37", pattern: "yyyy-MM-dd HH:mm:ss", result: "2015-03-15T15:02:37"},
49
+ "valid dateTime yyyyMMdd HHmmss" => {value: "20150315 150237", pattern: "yyyyMMdd HHmmss", result: "2015-03-15T15:02:37"},
50
+ "valid dateTime dd-MM-yyyy HH:mm" => {value: "15-03-2015 15:02", pattern: "dd-MM-yyyy HH:mm", result: "2015-03-15T15:02:00"},
51
+ "valid dateTime d-M-yyyy HHmm" => {value: "15-3-2015 1502", pattern: "d-M-yyyy HHmm", result: "2015-03-15T15:02:00"},
52
+ "valid dateTime yyyy-MM-ddTHH:mm" => {value: "2015-03-15T15:02", pattern: "yyyy-MM-ddTHH:mm", result: "2015-03-15T15:02:00"},
53
+ "valid dateTimeStamp d-M-yyyy HHmm X" => {value: "15-3-2015 1502 Z", pattern: "d-M-yyyy HHmm X", result: "2015-03-15T15:02:00Z"},
54
+ "valid datetime yyyy-MM-ddTHH:mm:ss" => {value: "2015-03-15T15:02:37", pattern: "yyyy-MM-ddTHH:mm:ss", result: "2015-03-15T15:02:37"},
55
+ "valid datetime yyyy-MM-dd HH:mm:ss" => {value: "2015-03-15 15:02:37", pattern: "yyyy-MM-dd HH:mm:ss", result: "2015-03-15T15:02:37"},
56
+ "valid datetime yyyyMMdd HHmmss" => {value: "20150315 150237", pattern: "yyyyMMdd HHmmss", result: "2015-03-15T15:02:37"},
57
+ "valid datetime dd-MM-yyyy HH:mm" => {value: "15-03-2015 15:02", pattern: "dd-MM-yyyy HH:mm", result: "2015-03-15T15:02:00"},
58
+ "valid datetime d-M-yyyy HHmm" => {value: "15-3-2015 1502", pattern: "d-M-yyyy HHmm", result: "2015-03-15T15:02:00"},
59
+ "valid datetime yyyy-MM-ddTHH:mm" => {value: "2015-03-15T15:02", pattern: "yyyy-MM-ddTHH:mm", result: "2015-03-15T15:02:00"},
60
+
61
+ # Timezones
62
+ "valid w/TZ yyyy-MM-ddX" => {value: "2015-03-22Z", pattern: "yyyy-MM-ddX", result: "2015-03-22Z"},
63
+ "valid w/TZ HH:mm:ssX" => {value: "15:02:37-05", pattern: "HH:mm:ssX", result: "15:02:37-05:00"},
64
+ "valid w/TZ yyyy-MM-dd HH:mm:ss X" => {value: "2015-03-15 15:02:37 +0800", pattern: "yyyy-MM-dd HH:mm:ss X", result: "2015-03-15T15:02:37+08:00"},
65
+ "valid w/TZ HHmm XX" => {value: "1502 +0800", pattern: "HHmm XX", result: "15:02:00+08:00"},
66
+ "valid w/TZ yyyy-MM-dd HH:mm:ss XX" => {value: "2015-03-15 15:02:37 -0800", pattern: "yyyy-MM-dd HH:mm:ss XX", result: "2015-03-15T15:02:37-08:00"},
67
+ "valid w/TZ HHmm XXX" => {value: "1502 +08:00", pattern: "HHmm XXX", result: "15:02:00+08:00"},
68
+ "valid w/TZ yyyy-MM-ddTHH:mm:ssXXX" => {value: "2015-03-15T15:02:37-05:00", pattern: "yyyy-MM-ddTHH:mm:ssXXX", result: "2015-03-15T15:02:37-05:00"},
69
+ "invalid w/TZ HH:mm:ssX" => {value: "15:02:37-05:00", pattern: "HH:mm:ssX", error: "15:02:37-05:00 does not match pattern HH:mm:ssX"},
70
+ "invalid w/TZ HH:mm:ssXX" => {value: "15:02:37-05", pattern: "HH:mm:ssXX", error: "15:02:37-05 does not match pattern HH:mm:ssXX"},
71
+ }.each do |name, props|
72
+ context name do
73
+ let(:base) {props[:base]}
74
+ let(:pattern) {props[:pattern]}
75
+ let(:value) {props[:value]}
76
+ let(:result) {props.fetch(:result, value)}
77
+ if props[:error]
78
+ it "finds error" do
79
+ expect {subject.parse_uax35_date(pattern, value)}.to raise_error(RDF::Tabular::UAX35::ParseError, props[:error])
80
+ end
81
+ else
82
+ it "generates #{props[:result] || props[:value]}" do
83
+ expect(subject.parse_uax35_date(pattern, value)).to eql result
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ describe "parse_uax35_number" do
91
+ {
92
+ # Numbers
93
+ "default no constraints" => {valid: %w(4)},
94
+ "default matching pattern" => {pattern: "000", valid: %w(123)},
95
+ "default explicit groupChar" => {groupChar: ";", valid: {"123;456.789" => "123456.789"}},
96
+ "default repeated groupChar" => {groupChar: ";", invalid: %w(123;;456.789)},
97
+ "default explicit decimalChar" => {decimalChar: ";", valid: {"123456;789" => "123456.789"}},
98
+ "default percent" => {groupChar: ",", valid: {"123456.789%" => "1234.56789"}},
99
+ "default per-mille" => {groupChar: ",", valid: {"123456.789‰" => "123.456789"}},
100
+
101
+ "0" => {pattern: "0", valid: %w(1 -1 12), invalid: %w(1.2)},
102
+ "00" => {pattern: "00", valid: %w(12 123), invalid: %w(1 1,2)},
103
+ "#" => {pattern: "#", valid: %w(1 12 123), invalid: %w(1.2)},
104
+ "##" => {pattern: "##", valid: %w(1 12 123), invalid: %w(1.2)},
105
+ "#0" => {pattern: "#0", valid: %w(1 12 123), invalid: %w(1.2)},
106
+ '0.0' => {pattern: "0.0", valid: %w(1.1 -1.1 12.1), invalid: %w(1.12)},
107
+ '0.00' => {pattern: '0.00', valid: %w(1.12 +1.12 12.12), invalid: %w(1.1 1.123)},
108
+ '0.#' => {pattern: '0.#', valid: %w(1 1.1 12.1), invalid: %w(1.12)},
109
+ '-0' => {pattern: '-0', valid: %w(-1 -10), invalid: %w(1 +1)},
110
+ '%000' => {pattern: '%000', valid: {"%123" => "1.23", "%+123" => "+1.23", "%1234" => "12.34"}, invalid: %w(%12 123%)},
111
+ '‰000' => {pattern: '‰000', valid: {"‰123" => "0.123", "‰+123" => "+0.123", "‰1234" => "1.234"}, invalid: %w(‰12 123‰)},
112
+ '000%' => {pattern: '000%', valid: {"123%" => "1.23", "+123%" => "+1.23", "1234%" => "12.34"}, invalid: %w(12% %123)},
113
+ '000‰' => {pattern: '000‰', valid: {"123‰" => "0.123", "+123‰" => "+0.123", "1234‰" => "1.234"}, invalid: %w(12‰ ‰123)},
114
+ '000.0%' => {pattern: '000.0%', valid: {"123.4%" => "1.234", "+123.4%" => "+1.234"}, invalid: %w(123.4‰ 123.4 1.234% 12.34% 123.45%)},
115
+
116
+ '###0.#####' => {pattern: '###0.#####', valid: %w(1 1.1 12345.12345), invalid: %w(1,234.1 1.123456)},
117
+ '###0.0000#' => {pattern: '###0.0000#', valid: %w(1.1234 1.12345 12345.12345), invalid: %w(1,234.1234 1.12)},
118
+ '00000.0000' => {pattern: '00000.0000', valid: %w(12345.1234), invalid: %w(1.2 1,234.123,4)},
119
+
120
+ '#0.0#E#0' => {pattern: '#0.0#E#0', valid: %w(1.2e3 12.34e56)},
121
+ '#0.0#E+#0' => {pattern: '#0.0#E+#0', valid: %w(1.2e+3 12.34e+56), invalid: %w(1.2e3 12.34e56)},
122
+ '#0.0#E#0%' => {pattern: '#0.0#E#0%', valid: {"1.2e3%" => "0.012e3", "12.34e56%" => "0.1234e56"}, invalid: %w(1.2e+3 12.34e+56 1.2e3 12.34e56)},
123
+
124
+ # Grouping
125
+ '#,##,##0' => {pattern: '#,##,##0', valid: {"1" => "1", "12" => "12", "123" => "123", "1,234" => "1234", "12,345" => "12345", "1,23,456" => "123456"}, invalid: %w(1,2 12,34)},
126
+ '#,##,#00' => {pattern: '#,##,#00', valid: {"12" => "12", "123" => "123", "1,234" => "1234", "12,345" => "12345"}, invalid: %w(1)},
127
+ '#,##,000' => {pattern: '#,##,000', valid: {"123" => "123", "1,234" => "1234", "12,345" => "12345"}, invalid: %w(1 12)},
128
+ '#,#0,000' => {pattern: '#,#0,000', valid: {"1,234" => "1234", "12,345" => "12345"}, invalid: %w(1 12 123)},
129
+ '#,00,000' => {pattern: '#,00,000', valid: {"12,345" => "12345"}, invalid: %w(1 12 123 1,234)},
130
+ '0,00,000' => {pattern: '0,00,000'},
131
+
132
+ '0.0##,###' => {pattern: '0.0##,###'},
133
+ '0.00#,###' => {pattern: '0.00#,###'},
134
+ '0.000,###' => {pattern: '0.000,###'},
135
+ '0.000,0##' => {pattern: '0.000,0##'},
136
+ '0.000,00#' => {pattern: '0.000,00#'},
137
+ '0.000,000' => {pattern: '0.000,000'},
138
+
139
+ # Jeni's
140
+ '##0' => {pattern: '##0', valid: %w(1 12 123 1234), invalid: %w(1,234 123.4)},
141
+ '#,#00' => {pattern: '#,#00', valid: {"12" => "12", "123" => "123", "1,234" => "1234", "1,234,567" => "1234567"}, invalid: %w(1 1234 12,34 12,34,567)},
142
+ '#0.#' => {pattern: '#0.#', valid: %w(1 1.2 1234.5), invalid: %w(12.34 1,234.5)},
143
+ '#0.0#,#' => {pattern: '#0.0#,#', valid: {"12.3" => "12.3", "12.34" => "12.34", "12.34,5" => "12.345"}, invalid: %w(1 12.345 12.34,56,7 12.34,567)},
144
+ }.each do |name, props|
145
+ context name do
146
+ let(:pattern) {props[:pattern]}
147
+ let(:groupChar) {props.fetch(:groupChar, ',')}
148
+ let(:decimalChar) {props.fetch(:decimalChar, '.')}
149
+
150
+ describe "valid" do
151
+ case props[:valid]
152
+ when Hash
153
+ props[:valid].each do |value, result|
154
+ it "with #{props[:pattern].inspect} #{value.inspect} => #{result.inspect}" do
155
+ expect(subject.parse_uax35_number(pattern, value, groupChar, decimalChar)).to eql result
156
+ end
157
+ end
158
+ when Array
159
+ props[:valid].each do |value|
160
+ it "with #{props[:pattern].inspect} #{value.inspect} => #{value.inspect}" do
161
+ expect(subject.parse_uax35_number(pattern, value, groupChar, decimalChar)).to eql value
162
+ end
163
+ end
164
+ end
165
+ end
166
+ describe "invalid" do
167
+ Array(props[:invalid]).each do |value|
168
+ it "with #{props[:pattern].inspect} #{value.inspect} invalid" do
169
+ expect {subject.parse_uax35_number(pattern, value, groupChar, decimalChar)}.to raise_error RDF::Tabular::UAX35::ParseError
170
+ end
171
+ end
172
+ end
173
+
174
+ it "recognizes bad pattern #{pattern.inspect}" do
175
+ expect{subject.parse_uax35_number(pattern, "", groupChar, decimalChar)}.to raise_error(ArgumentError)
176
+ end if props[:exception]
177
+ end
178
+ end
179
+ end
180
+
181
+ describe "#build_number_re" do
182
+ {
183
+ '0' => {valid: %w(1 -1 +1 12), invalid: %w(1.2), base: "integer", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,})(?<suffix>)$/},
184
+ '00' => {valid: %w(12 123), invalid: %w(1 1,2), base: "integer", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{2,})(?<suffix>)$/},
185
+ '#' => {valid: %w(1 12 123), invalid: %w(1.2), base: "integer", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{0,})(?<suffix>)$/},
186
+ '##' => {re: /^(?<prefix>[+-]?)(?<numeric_part>\d{0,})(?<suffix>)$/},
187
+ '#0' => {re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,})(?<suffix>)$/},
188
+
189
+ '0.0' => {valid: %w(1.1 -1.1 12.1), invalid: %w(1.12), base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}\.\d{1})(?<suffix>)$/},
190
+ '0.00' => {valid: %w(1.12 +1.12 12.12), invalid: %w(1.1 1.123), base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}\.\d{2})(?<suffix>)$/},
191
+ '0.#' => {valid: %w(1 1.1 12.1), invalid: %w(1.12), base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}(?:\.\d{0,1})?)(?<suffix>)$/},
192
+ '-0' => {valid: %w(-1 -10), invalid: %w(1 +1), base: "decimal", re: /^(?<prefix>\-)(?<numeric_part>\d{1,})(?<suffix>)$/},
193
+ '%000' => {valid: %w(%123 %+123 %-123 %1234), invalid: %w(%12 123%), base: "decimal", re: /^(?<prefix>%[+-]?)(?<numeric_part>\d{3,})(?<suffix>)$/},
194
+ '‰000' => {valid: %w(‰123 ‰+123 ‰-123 ‰1234), invalid: %w(‰12 123‰), base: "decimal", re: /^(?<prefix>‰[+-]?)(?<numeric_part>\d{3,})(?<suffix>)$/},
195
+ '000%' => {valid: %w(123% +123% -123% 1234%), invalid: %w(12% %123), base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{3,})(?<suffix>%)$/},
196
+ '000‰' => {valid: %w(123‰ +123‰ -123‰ 1234‰), invalid: %w(12‰ ‰123), base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{3,})(?<suffix>‰)$/},
197
+ '000.0%' => {base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{3,}\.\d{1})(?<suffix>%)$/},
198
+
199
+ '###0.#####' => {valid: %w(1 1.1 12345.12345), invalid: %w(1,234.1 1.123456), base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}(?:\.\d{0,5})?)(?<suffix>)$/},
200
+ '###0.0000#' => {valid: %w(1.1234 1.12345 12345.12345), invalid: %w(1,234.1234 1.12), base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}\.\d{4,5})(?<suffix>)$/},
201
+ '00000.0000' => {valid: %w(12345.1234), invalid: %w(1.2 1,234.123,4), base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{5,}\.\d{4})(?<suffix>)$/},
202
+
203
+ '#0.0#E#0' => {base: "double", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}\.\d{1,2}E[+-]?\d{1,2})(?<suffix>)$/},
204
+ '#0.0#E+#0' => {base: "double", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}\.\d{1,2}E\+\d{1,2})(?<suffix>)$/},
205
+ '#0.0#E#0%' => {base: "double", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}\.\d{1,2}E[+-]?\d{1,2})(?<suffix>%)$/},
206
+
207
+ # Grouping
208
+ '#,##,##0' => {base: "integer", re: /^(?<prefix>[+-]?)(?<numeric_part>(?:(?:(?:\d{1,2},)?(?:\d{2},)*\d)?\d)?\d{1})(?<suffix>)$/},
209
+ '#,##,#00' => {base: "integer", re: /^(?<prefix>[+-]?)(?<numeric_part>(?:(?:\d{1,2},)?(?:\d{2},)*\d)?\d{2})(?<suffix>)$/},
210
+ '#,##,000' => {base: "integer", re: /^(?<prefix>[+-]?)(?<numeric_part>(?:\d{1,2},)?(?:\d{2},)*\d{3})(?<suffix>)$/},
211
+ '#,#0,000' => {base: "integer", re: /^(?<prefix>[+-]?)(?<numeric_part>(?:(?:\d{1,2},)?(?:\d{2},)*\d)?\d{1},\d{3})(?<suffix>)$/},
212
+ '#,00,000' => {base: "integer", re: /^(?<prefix>[+-]?)(?<numeric_part>(?:\d{1,2},)?(?:\d{2},)*\d{2},\d{3})(?<suffix>)$/},
213
+ '0,00,000' => {base: "integer", re: /^(?<prefix>[+-]?)(?<numeric_part>(?:(?:\d{1,2},)?(?:\d{2},)*\d)?\d{1},\d{2},\d{3})(?<suffix>)$/},
214
+
215
+ '0.0##,###' => {base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}\.\d{1}(?:\d(?:\d(?:,\d(?:\d(?:\d)?)?)?)?)?)(?<suffix>)$/},
216
+ '0.00#,###' => {base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}\.\d{2}(?:\d(?:,\d(?:\d(?:\d)?)?)?)?)(?<suffix>)$/},
217
+ '0.000,###' => {base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}\.\d{3}(?:,\d(?:\d(?:\d)?)?)?)(?<suffix>)$/},
218
+ '0.000,0##' => {base: "decimal", re:/^(?<prefix>[+-]?)(?<numeric_part>\d{1,}\.\d{3},\d{1}(?:\d(?:\d)?)?)(?<suffix>)$/},
219
+ '0.000,00#' => {base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}\.\d{3},\d{2}(?:\d)?)(?<suffix>)$/},
220
+ '0.000,000' => {base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}\.\d{3},\d{3})(?<suffix>)$/},
221
+
222
+ # Jeni's
223
+ '##0' => {valid: %w(1 12 123 1234), invalid: %w(1,234 123.4), base: "integer", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,})(?<suffix>)$/},
224
+ '#,#00' => {valid: %w(12 123 1,234 1,234,567), invalid: %w(1 1234 12,34 12,34,567), base: "integer", re: /^(?<prefix>[+-]?)(?<numeric_part>(?:(?:\d{1,3},)?(?:\d{3},)*\d)?\d{2})(?<suffix>)$/},
225
+ '#0.#' => {valid: %w(1 1.2 1234.5), invalid: %w(12.34 1,234.5), base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}(?:\.\d{0,1})?)(?<suffix>)$/},
226
+ '#0.0#,#' => {valid: %w(12.3 12.34 12.34,5), invalid: %w(1 12.345 12.34,56,7 12.34,567), base: "decimal", re: /^(?<prefix>[+-]?)(?<numeric_part>\d{1,}\.\d{1}(?:\d(?:,\d)?)?)(?<suffix>)$/},
227
+ }.each do |pattern, props|
228
+ context pattern do
229
+ it "generates #{props[:re]} for #{pattern}" do
230
+ expect(subject.build_number_re(pattern, ",", ".")).to eql props[:re]
231
+ end if props[:re].is_a?(Regexp)
232
+
233
+ it "recognizes bad pattern #{pattern}" do
234
+ expect{subject.build_number_re(pattern, ",", ".")}.to raise_error(ArgumentError)
235
+ end if props[:re] == ArgumentError
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1 @@
1
+ spec/../../w3c-csvw
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rdf-tabular
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gregg Kellogg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-10 00:00:00.000000000 Z
11
+ date: 2016-12-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bcp47
@@ -36,14 +36,14 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '2.0'
39
+ version: '2.1'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '2.0'
46
+ version: '2.1'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rdf-vocab
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -204,14 +204,14 @@ dependencies:
204
204
  requirements:
205
205
  - - "~>"
206
206
  - !ruby/object:Gem::Version
207
- version: '1.17'
207
+ version: '2.3'
208
208
  type: :development
209
209
  prerelease: false
210
210
  version_requirements: !ruby/object:Gem::Requirement
211
211
  requirements:
212
212
  - - "~>"
213
213
  - !ruby/object:Gem::Version
214
- version: '1.17'
214
+ version: '2.3'
215
215
  - !ruby/object:Gem::Dependency
216
216
  name: yard
217
217
  requirement: !ruby/object:Gem::Requirement
@@ -263,6 +263,8 @@ files:
263
263
  - spec/spec_helper.rb
264
264
  - spec/suite_helper.rb
265
265
  - spec/suite_spec.rb
266
+ - spec/uax35_spec.rb
267
+ - spec/w3c-csvw
266
268
  homepage: http://github.com/ruby-rdf/rdf-tabular
267
269
  licenses:
268
270
  - Unlicense
@@ -275,7 +277,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
275
277
  requirements:
276
278
  - - ">="
277
279
  - !ruby/object:Gem::Version
278
- version: '2.0'
280
+ version: 2.2.2
279
281
  required_rubygems_version: !ruby/object:Gem::Requirement
280
282
  requirements:
281
283
  - - ">="
@@ -283,7 +285,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
283
285
  version: '0'
284
286
  requirements: []
285
287
  rubyforge_project:
286
- rubygems_version: 2.4.8
288
+ rubygems_version: 2.6.8
287
289
  signing_key:
288
290
  specification_version: 4
289
291
  summary: Tabular Data RDF Reader and JSON serializer.
@@ -295,4 +297,5 @@ test_files:
295
297
  - spec/spec_helper.rb
296
298
  - spec/suite_helper.rb
297
299
  - spec/suite_spec.rb
298
- has_rdoc: false
300
+ - spec/uax35_spec.rb
301
+ - spec/w3c-csvw