rdf-tabular 0.4.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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