csvlint 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +4 -0
  3. data/.github/workflows/push.yml +14 -2
  4. data/.ruby-version +1 -1
  5. data/.standard_todo.yml +43 -0
  6. data/Dockerfile +16 -0
  7. data/Gemfile +2 -2
  8. data/README.md +9 -9
  9. data/Rakefile +7 -7
  10. data/csvlint.gemspec +14 -16
  11. data/docker_notes_for_windows.txt +20 -0
  12. data/features/step_definitions/cli_steps.rb +11 -11
  13. data/features/step_definitions/information_steps.rb +4 -4
  14. data/features/step_definitions/parse_csv_steps.rb +11 -11
  15. data/features/step_definitions/schema_validation_steps.rb +10 -10
  16. data/features/step_definitions/sources_steps.rb +1 -1
  17. data/features/step_definitions/validation_errors_steps.rb +19 -19
  18. data/features/step_definitions/validation_info_steps.rb +9 -9
  19. data/features/step_definitions/validation_warnings_steps.rb +11 -11
  20. data/features/support/aruba.rb +6 -6
  21. data/features/support/earl_formatter.rb +39 -39
  22. data/features/support/env.rb +10 -11
  23. data/features/support/load_tests.rb +107 -103
  24. data/features/support/webmock.rb +2 -2
  25. data/lib/csvlint/cli.rb +133 -130
  26. data/lib/csvlint/csvw/column.rb +279 -280
  27. data/lib/csvlint/csvw/date_format.rb +90 -92
  28. data/lib/csvlint/csvw/metadata_error.rb +1 -3
  29. data/lib/csvlint/csvw/number_format.rb +40 -32
  30. data/lib/csvlint/csvw/property_checker.rb +714 -717
  31. data/lib/csvlint/csvw/table.rb +49 -52
  32. data/lib/csvlint/csvw/table_group.rb +24 -23
  33. data/lib/csvlint/error_collector.rb +2 -0
  34. data/lib/csvlint/error_message.rb +0 -1
  35. data/lib/csvlint/field.rb +153 -141
  36. data/lib/csvlint/schema.rb +34 -42
  37. data/lib/csvlint/validate.rb +161 -143
  38. data/lib/csvlint/version.rb +1 -1
  39. data/lib/csvlint.rb +22 -23
  40. data/spec/csvw/column_spec.rb +15 -16
  41. data/spec/csvw/date_format_spec.rb +5 -7
  42. data/spec/csvw/number_format_spec.rb +2 -4
  43. data/spec/csvw/table_group_spec.rb +103 -105
  44. data/spec/csvw/table_spec.rb +71 -73
  45. data/spec/field_spec.rb +116 -121
  46. data/spec/schema_spec.rb +129 -139
  47. data/spec/spec_helper.rb +6 -6
  48. data/spec/validator_spec.rb +167 -190
  49. metadata +22 -55
@@ -1,21 +1,20 @@
1
1
  module Csvlint
2
-
3
2
  class Validator
4
3
  class LineCSV < CSV
5
- ENCODE_RE = Hash.new do |h,str|
4
+ ENCODE_RE = Hash.new do |h, str|
6
5
  h[str] = Regexp.new(str)
7
6
  end
8
7
 
9
- ENCODE_STR = Hash.new do |h,encoding_name|
10
- h[encoding_name] = Hash.new do |h,chunks|
11
- h[chunks] = chunks.map { |chunk| chunk.encode(encoding_name) }.join('')
8
+ ENCODE_STR = Hash.new do |h, encoding_name|
9
+ h[encoding_name] = Hash.new do |h, chunks|
10
+ h[chunks] = chunks.map { |chunk| chunk.encode(encoding_name) }.join("")
12
11
  end
13
12
  end
14
13
 
15
- ESCAPE_RE = Hash.new do |h,re_chars|
16
- h[re_chars] = Hash.new do |h,re_esc|
17
- h[re_esc] = Hash.new do |h,str|
18
- h[str] = str.gsub(re_chars) {|c| re_esc + c}
14
+ ESCAPE_RE = Hash.new do |h, re_chars|
15
+ h[re_chars] = Hash.new do |h, re_esc|
16
+ h[re_esc] = Hash.new do |h, str|
17
+ h[str] = str.gsub(re_chars) { |c| re_esc + c }
19
18
  end
20
19
  end
21
20
  end
@@ -38,7 +37,7 @@ module Csvlint
38
37
  ESCAPE_RE[@re_chars][@re_esc][str]
39
38
  end
40
39
 
41
- if RUBY_VERSION < '2.5'
40
+ if RUBY_VERSION < "2.5"
42
41
  # Optimization: Disable the CSV library's converters feature.
43
42
  # @see https://github.com/ruby/ruby/blob/v2_2_3/lib/csv.rb#L2100
44
43
  def init_converters(options, field_name = :converters)
@@ -55,11 +54,11 @@ module Csvlint
55
54
  attr_reader :encoding, :content_type, :extension, :headers, :link_headers, :dialect, :csv_header, :schema, :data, :current_line
56
55
 
57
56
  ERROR_MATCHERS = {
58
- "Missing or stray quote" => :stray_quote,
59
- "Illegal quoting" => :whitespace,
60
- "Unclosed quoted field" => :unclosed_quote,
61
- "Any value after quoted field isn't allowed" => :unclosed_quote,
62
- "Unquoted fields do not allow \\r or \\n" => :line_breaks,
57
+ "Missing or stray quote" => :stray_quote,
58
+ "Illegal quoting" => :whitespace,
59
+ "Unclosed quoted field" => :unclosed_quote,
60
+ "Any value after quoted field isn't allowed" => :unclosed_quote,
61
+ "Unquoted fields do not allow \\r or \\n" => :line_breaks
63
62
  }
64
63
 
65
64
  def initialize(source, dialect = {}, schema = nil, options = {})
@@ -90,14 +89,14 @@ module Csvlint
90
89
  end
91
90
 
92
91
  def validate
93
- if @extension =~ /.xls(x)?/
92
+ if /.xls(x)?/.match?(@extension)
94
93
  build_warnings(:excel, :context)
95
94
  return
96
95
  end
97
96
  locate_schema unless @schema.instance_of?(Csvlint::Schema)
98
97
  set_dialect
99
98
 
100
- if @source.class == String
99
+ if @source.instance_of?(String)
101
100
  validate_url
102
101
  else
103
102
  validate_metadata
@@ -120,7 +119,11 @@ module Csvlint
120
119
  request = Typhoeus::Request.new(@source, followlocation: true)
121
120
  request.on_headers do |response|
122
121
  @headers = response.headers || {}
123
- @content_type = response.headers["content-type"] rescue nil
122
+ @content_type = begin
123
+ response.headers["content-type"]
124
+ rescue
125
+ nil
126
+ end
124
127
  @response_code = response.code
125
128
  return build_errors(:not_found) if response.code == 404
126
129
  validate_metadata
@@ -148,7 +151,7 @@ module Csvlint
148
151
  else
149
152
  validate_line(line, @current_line)
150
153
  @leading = ""
151
- @current_line = @current_line+1
154
+ @current_line += 1
152
155
  end
153
156
  else
154
157
  # If it's not a full line, then prepare to add it to the beginning of the next chunk
@@ -156,7 +159,7 @@ module Csvlint
156
159
  end
157
160
  rescue ArgumentError => ae
158
161
  build_errors(:invalid_encoding, :structure, @current_line, nil, @current_line) unless @reported_invalid_encoding
159
- @current_line = @current_line+1
162
+ @current_line += 1
160
163
  @reported_invalid_encoding = true
161
164
  end
162
165
 
@@ -167,7 +170,7 @@ module Csvlint
167
170
  @encoding = input.encoding.to_s
168
171
  report_line_breaks(line)
169
172
  parse_contents(input, line)
170
- @lambda.call(self) unless @lambda.nil?
173
+ @lambda&.call(self)
171
174
  rescue ArgumentError => ae
172
175
  build_errors(:invalid_encoding, :structure, @current_line, nil, index) unless @reported_invalid_encoding
173
176
  @reported_invalid_encoding = true
@@ -204,8 +207,8 @@ module Csvlint
204
207
  @errors += @schema.errors
205
208
  all_errors += @schema.errors
206
209
  @warnings += @schema.warnings
207
- else
208
- build_errors(:ragged_rows, :structure, current_line, nil, stream.to_s) if !row.empty? && row.size != @expected_columns
210
+ elsif !row.empty? && row.size != @expected_columns
211
+ build_errors(:ragged_rows, :structure, current_line, nil, stream.to_s)
209
212
  end
210
213
  end
211
214
  end
@@ -228,8 +231,8 @@ module Csvlint
228
231
  def validate_metadata
229
232
  assumed_header = !@supplied_dialect
230
233
  unless @headers.empty?
231
- if @headers["content-type"] =~ /text\/csv/
232
- @csv_header = @csv_header && true
234
+ if /text\/csv/.match?(@headers["content-type"])
235
+ @csv_header &&= true
233
236
  assumed_header = @assumed_header.present?
234
237
  end
235
238
  if @headers["content-type"] =~ /header=(present|absent)/
@@ -237,19 +240,35 @@ module Csvlint
237
240
  @csv_header = false if $1 == "absent"
238
241
  assumed_header = false
239
242
  end
240
- build_warnings(:no_content_type, :context) if @content_type == nil
241
- build_errors(:wrong_content_type, :context) unless (@content_type && @content_type =~ /text\/csv/)
243
+ build_warnings(:no_content_type, :context) if @content_type.nil?
244
+ build_errors(:wrong_content_type, :context) unless @content_type && @content_type =~ /text\/csv/
242
245
  end
243
246
  @header_processed = true
244
247
  build_info_messages(:assumed_header, :structure) if assumed_header
245
248
 
246
- @link_headers = @headers["link"].split(",") rescue nil
247
- @link_headers.each do |link_header|
249
+ @link_headers = begin
250
+ @headers["link"].split(",")
251
+ rescue
252
+ nil
253
+ end
254
+ @link_headers&.each do |link_header|
248
255
  match = LINK_HEADER_REGEXP.match(link_header)
249
- uri = match["uri"].gsub(/(^\<|\>$)/, "") rescue nil
250
- rel = match["rel-relationship"].gsub(/(^\"|\"$)/, "") rescue nil
256
+ uri = begin
257
+ match["uri"].gsub(/(^<|>$)/, "")
258
+ rescue
259
+ nil
260
+ end
261
+ rel = begin
262
+ match["rel-relationship"].gsub(/(^"|"$)/, "")
263
+ rescue
264
+ nil
265
+ end
251
266
  param = match["param"]
252
- param_value = match["param-value"].gsub(/(^\"|\"$)/, "") rescue nil
267
+ param_value = begin
268
+ match["param-value"].gsub(/(^"|"$)/, "")
269
+ rescue
270
+ nil
271
+ end
253
272
  if rel == "describedby" && param == "type" && ["application/csvm+json", "application/ld+json", "application/json"].include?(param_value)
254
273
  begin
255
274
  url = URI.join(@source_url, uri)
@@ -265,14 +284,14 @@ module Csvlint
265
284
  rescue OpenURI::HTTPError
266
285
  end
267
286
  end
268
- end if @link_headers
287
+ end
269
288
  end
270
289
 
271
290
  def header?
272
291
  @csv_header && @dialect["header"]
273
292
  end
274
293
 
275
- def report_line_breaks(line_no=nil)
294
+ def report_line_breaks(line_no = nil)
276
295
  return unless @input[-1, 1].include?("\n") # Return straight away if there's no newline character - i.e. we're on the last line
277
296
  line_break = get_line_break(@input)
278
297
  @line_breaks << line_break
@@ -298,24 +317,24 @@ module Csvlint
298
317
  schema_dialect = {}
299
318
  end
300
319
  @dialect = {
301
- "header" => true,
302
- "headerRowCount" => 1,
303
- "delimiter" => ",",
304
- "skipInitialSpace" => true,
305
- "lineTerminator" => :auto,
306
- "quoteChar" => '"',
307
- "trim" => :true
320
+ "header" => true,
321
+ "headerRowCount" => 1,
322
+ "delimiter" => ",",
323
+ "skipInitialSpace" => true,
324
+ "lineTerminator" => :auto,
325
+ "quoteChar" => '"',
326
+ "trim" => :true
308
327
  }.merge(schema_dialect).merge(@dialect || {})
309
328
 
310
- @csv_header = @csv_header && @dialect["header"]
329
+ @csv_header &&= @dialect["header"]
311
330
  @csv_options = dialect_to_csv_options(@dialect)
312
331
  end
313
332
 
314
333
  def validate_encoding
315
334
  if @headers["content-type"]
316
- if @headers["content-type"] !~ /charset=/
335
+ if !/charset=/.match?(@headers["content-type"])
317
336
  build_warnings(:no_encoding, :context)
318
- elsif @headers["content-type"] !~ /charset=utf-8/i
337
+ elsif !/charset=utf-8/i.match?(@headers["content-type"])
319
338
  build_warnings(:encoding, :context)
320
339
  end
321
340
  end
@@ -339,10 +358,10 @@ module Csvlint
339
358
  end
340
359
 
341
360
  def build_exception_messages(csvException, errChars, lineNo)
342
- #TODO 1 - this is a change in logic, rather than straight refactor of previous error building, however original logic is bonkers
343
- #TODO 2 - using .kind_of? is a very ugly fix here and it meant to work around instances where :auto symbol is preserved in @csv_options
361
+ # TODO 1 - this is a change in logic, rather than straight refactor of previous error building, however original logic is bonkers
362
+ # TODO 2 - using .kind_of? is a very ugly fix here and it meant to work around instances where :auto symbol is preserved in @csv_options
344
363
  type = fetch_error(csvException)
345
- if !@csv_options[:row_sep].kind_of?(Symbol) && [:unclosed_quote,:stray_quote].include?(type) && !@input.match(@csv_options[:row_sep])
364
+ if !@csv_options[:row_sep].is_a?(Symbol) && [:unclosed_quote, :stray_quote].include?(type) && !@input.match(@csv_options[:row_sep])
346
365
  build_linebreak_error
347
366
  else
348
367
  build_errors(type, :structure, lineNo, nil, errChars)
@@ -355,11 +374,11 @@ module Csvlint
355
374
 
356
375
  def validate_header(header)
357
376
  names = Set.new
358
- header.map{|h| h.strip! } if @dialect["trim"] == :true
359
- header.each_with_index do |name,i|
360
- build_warnings(:empty_column_name, :schema, nil, i+1) if name == ""
377
+ header.map { |h| h.strip! } if @dialect["trim"] == :true
378
+ header.each_with_index do |name, i|
379
+ build_warnings(:empty_column_name, :schema, nil, i + 1) if name == ""
361
380
  if names.include?(name)
362
- build_warnings(:duplicate_column_name, :schema, nil, i+1)
381
+ build_warnings(:duplicate_column_name, :schema, nil, i + 1)
363
382
  else
364
383
  names << name
365
384
  end
@@ -369,24 +388,28 @@ module Csvlint
369
388
  @errors += @schema.errors
370
389
  @warnings += @schema.warnings
371
390
  end
372
- return valid?
391
+ valid?
373
392
  end
374
393
 
375
394
  def fetch_error(error)
376
395
  e = error.message.match(/^(.+?)(?: [io]n)? \(?line \d+\)?\.?$/i)
377
- message = e[1] rescue nil
396
+ message = begin
397
+ e[1]
398
+ rescue
399
+ nil
400
+ end
378
401
  ERROR_MATCHERS.fetch(message, :unknown_error)
379
402
  end
380
403
 
381
404
  def dialect_to_csv_options(dialect)
382
405
  skipinitialspace = dialect["skipInitialSpace"] || true
383
406
  delimiter = dialect["delimiter"]
384
- delimiter = delimiter + " " if !skipinitialspace
385
- return {
386
- :col_sep => delimiter,
387
- :row_sep => dialect["lineTerminator"],
388
- :quote_char => dialect["quoteChar"],
389
- :skip_blanks => false
407
+ delimiter += " " if !skipinitialspace
408
+ {
409
+ col_sep: delimiter,
410
+ row_sep: dialect["lineTerminator"],
411
+ quote_char: dialect["quoteChar"],
412
+ skip_blanks: false
390
413
  }
391
414
  end
392
415
 
@@ -396,25 +419,25 @@ module Csvlint
396
419
  @formats[i] ||= Hash.new(0)
397
420
 
398
421
  format =
399
- if col.strip[FORMATS[:numeric]]
400
- :numeric
401
- elsif uri?(col)
402
- :uri
403
- elsif possible_date?(col)
404
- date_formats(col)
405
- else
406
- :string
407
- end
422
+ if col.strip[FORMATS[:numeric]]
423
+ :numeric
424
+ elsif uri?(col)
425
+ :uri
426
+ elsif possible_date?(col)
427
+ date_formats(col)
428
+ else
429
+ :string
430
+ end
408
431
 
409
432
  @formats[i][format] += 1
410
433
  end
411
434
  end
412
435
 
413
436
  def check_consistency
414
- @formats.each_with_index do |format,i|
437
+ @formats.each_with_index do |format, i|
415
438
  if format
416
439
  total = format.values.reduce(:+).to_f
417
- if format.none?{|_,count| count / total >= 0.9}
440
+ if format.none? { |_, count| count / total >= 0.9 }
418
441
  build_warnings(:inconsistent_values, :schema, nil, i + 1)
419
442
  end
420
443
  end
@@ -430,17 +453,16 @@ module Csvlint
430
453
  end
431
454
 
432
455
  def locate_schema
433
-
434
456
  @source_url = nil
435
457
  warn_if_unsuccessful = false
436
458
  case @source
437
- when StringIO
438
- return
439
- when File
440
- uri_parser = URI::Parser.new
441
- @source_url = "file:#{uri_parser.escape(File.expand_path(@source))}"
442
- else
443
- @source_url = @source
459
+ when StringIO
460
+ return
461
+ when File
462
+ uri_parser = URI::DEFAULT_PARSER
463
+ @source_url = "file:#{uri_parser.escape(File.expand_path(@source))}"
464
+ else
465
+ @source_url = @source
444
466
  end
445
467
  unless @schema.nil?
446
468
  if @schema.tables[@source_url]
@@ -450,7 +472,7 @@ module Csvlint
450
472
  end
451
473
  end
452
474
  paths = []
453
- if @source_url =~ /^http(s)?/
475
+ if /^http(s)?/.match?(@source_url)
454
476
  begin
455
477
  well_known_uri = URI.join(@source_url, "/.well-known/csvm")
456
478
  paths = URI.open(well_known_uri.to_s).read.split("\n")
@@ -459,26 +481,24 @@ module Csvlint
459
481
  end
460
482
  paths = ["{+url}-metadata.json", "csv-metadata.json"] if paths.empty?
461
483
  paths.each do |template|
462
- begin
463
- template = URITemplate.new(template)
464
- path = template.expand('url' => @source_url)
465
- url = URI.join(@source_url, path)
466
- url = File.new(url.to_s.sub(/^file:/, "")) if url.to_s =~ /^file:/
467
- schema = Schema.load_from_uri(url)
468
- if schema.instance_of? Csvlint::Csvw::TableGroup
469
- if schema.tables[@source_url]
470
- @schema = schema
471
- return
472
- else
473
- warn_if_unsuccessful = true
474
- build_warnings(:schema_mismatch, :context, nil, nil, @source_url, schema)
475
- end
484
+ template = URITemplate.new(template)
485
+ path = template.expand("url" => @source_url)
486
+ url = URI.join(@source_url, path)
487
+ url = File.new(url.to_s.sub(/^file:/, "")) if /^file:/.match?(url.to_s)
488
+ schema = Schema.load_from_uri(url)
489
+ if schema.instance_of? Csvlint::Csvw::TableGroup
490
+ if schema.tables[@source_url]
491
+ @schema = schema
492
+ return
493
+ else
494
+ warn_if_unsuccessful = true
495
+ build_warnings(:schema_mismatch, :context, nil, nil, @source_url, schema)
476
496
  end
477
- rescue Errno::ENOENT
478
- rescue OpenURI::HTTPError, URI::BadURIError, ArgumentError
479
- rescue => e
480
- raise e
481
497
  end
498
+ rescue Errno::ENOENT
499
+ rescue OpenURI::HTTPError, URI::BadURIError, ArgumentError
500
+ rescue => e
501
+ raise e
482
502
  end
483
503
  build_warnings(:schema_mismatch, :context, nil, nil, @source_url, schema) if warn_if_unsuccessful
484
504
  @schema = nil
@@ -487,31 +507,30 @@ module Csvlint
487
507
  private
488
508
 
489
509
  def parse_extension(source)
490
-
491
510
  case source
492
- when File
493
- return File.extname( source.path )
494
- when IO
495
- return ""
496
- when StringIO
497
- return ""
498
- when Tempfile
499
- # this is triggered when the revalidate dialect use case happens
500
- return ""
501
- else
502
- begin
503
- parsed = URI.parse(source)
504
- File.extname(parsed.path)
505
- rescue URI::InvalidURIError
506
- return ""
507
- end
511
+ when File
512
+ File.extname(source.path)
513
+ when IO
514
+ ""
515
+ when StringIO
516
+ ""
517
+ when Tempfile
518
+ # this is triggered when the revalidate dialect use case happens
519
+ ""
520
+ else
521
+ begin
522
+ parsed = URI.parse(source)
523
+ File.extname(parsed.path)
524
+ rescue URI::InvalidURIError
525
+ ""
526
+ end
508
527
  end
509
528
  end
510
529
 
511
530
  def uri?(value)
512
531
  if value.strip[FORMATS[:uri]]
513
532
  uri = URI.parse(value)
514
- uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS)
533
+ uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
515
534
  end
516
535
  rescue URI::InvalidURIError
517
536
  false
@@ -522,25 +541,25 @@ module Csvlint
522
541
  end
523
542
 
524
543
  def date_formats(col)
525
- if col[FORMATS[:date_db]] && date_format?(Date, col, '%Y-%m-%d')
544
+ if col[FORMATS[:date_db]] && date_format?(Date, col, "%Y-%m-%d")
526
545
  :date_db
527
- elsif col[FORMATS[:date_short]] && date_format?(Date, col, '%e %b')
546
+ elsif col[FORMATS[:date_short]] && date_format?(Date, col, "%e %b")
528
547
  :date_short
529
- elsif col[FORMATS[:date_rfc822]] && date_format?(Date, col, '%e %b %Y')
548
+ elsif col[FORMATS[:date_rfc822]] && date_format?(Date, col, "%e %b %Y")
530
549
  :date_rfc822
531
- elsif col[FORMATS[:date_long]] && date_format?(Date, col, '%B %e, %Y')
550
+ elsif col[FORMATS[:date_long]] && date_format?(Date, col, "%B %e, %Y")
532
551
  :date_long
533
- elsif col[FORMATS[:dateTime_time]] && date_format?(Time, col, '%H:%M')
552
+ elsif col[FORMATS[:dateTime_time]] && date_format?(Time, col, "%H:%M")
534
553
  :dateTime_time
535
- elsif col[FORMATS[:dateTime_hms]] && date_format?(Time, col, '%H:%M:%S')
554
+ elsif col[FORMATS[:dateTime_hms]] && date_format?(Time, col, "%H:%M:%S")
536
555
  :dateTime_hms
537
- elsif col[FORMATS[:dateTime_db]] && date_format?(Time, col, '%Y-%m-%d %H:%M:%S')
556
+ elsif col[FORMATS[:dateTime_db]] && date_format?(Time, col, "%Y-%m-%d %H:%M:%S")
538
557
  :dateTime_db
539
- elsif col[FORMATS[:dateTime_iso8601]] && date_format?(Time, col, '%Y-%m-%dT%H:%M:%SZ')
558
+ elsif col[FORMATS[:dateTime_iso8601]] && date_format?(Time, col, "%Y-%m-%dT%H:%M:%SZ")
540
559
  :dateTime_iso8601
541
- elsif col[FORMATS[:dateTime_short]] && date_format?(Time, col, '%d %b %H:%M')
560
+ elsif col[FORMATS[:dateTime_short]] && date_format?(Time, col, "%d %b %H:%M")
542
561
  :dateTime_short
543
- elsif col[FORMATS[:dateTime_long]] && date_format?(Time, col, '%B %d, %Y %H:%M')
562
+ elsif col[FORMATS[:dateTime_long]] && date_format?(Time, col, "%B %d, %Y %H:%M")
544
563
  :dateTime_long
545
564
  else
546
565
  :string
@@ -567,25 +586,25 @@ module Csvlint
567
586
  end
568
587
 
569
588
  FORMATS = {
570
- :string => nil,
571
- :numeric => /\A[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?\z/,
572
- :uri => /\Ahttps?:/,
573
- :date_db => /\A\d{4,}-\d\d-\d\d\z/, # "12345-01-01"
574
- :date_long => /\A(?:#{Date::MONTHNAMES.join('|')}) [ \d]\d, \d{4,}\z/, # "January 1, 12345"
575
- :date_rfc822 => /\A[ \d]\d (?:#{Date::ABBR_MONTHNAMES.join('|')}) \d{4,}\z/, # " 1 Jan 12345"
576
- :date_short => /\A[ \d]\d (?:#{Date::ABBR_MONTHNAMES.join('|')})\z/, # "1 Jan"
577
- :dateTime_db => /\A\d{4,}-\d\d-\d\d \d\d:\d\d:\d\d\z/, # "12345-01-01 00:00:00"
578
- :dateTime_hms => /\A\d\d:\d\d:\d\d\z/, # "00:00:00"
579
- :dateTime_iso8601 => /\A\d{4,}-\d\d-\d\dT\d\d:\d\d:\d\dZ\z/, # "12345-01-01T00:00:00Z"
580
- :dateTime_long => /\A(?:#{Date::MONTHNAMES.join('|')}) \d\d, \d{4,} \d\d:\d\d\z/, # "January 01, 12345 00:00"
581
- :dateTime_short => /\A\d\d (?:#{Date::ABBR_MONTHNAMES.join('|')}) \d\d:\d\d\z/, # "01 Jan 00:00"
582
- :dateTime_time => /\A\d\d:\d\d\z/, # "00:00"
589
+ string: nil,
590
+ numeric: /\A[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?\z/,
591
+ uri: /\Ahttps?:/,
592
+ date_db: /\A\d{4,}-\d\d-\d\d\z/, # "12345-01-01"
593
+ date_long: /\A(?:#{Date::MONTHNAMES.join('|')}) [ \d]\d, \d{4,}\z/, # "January 1, 12345"
594
+ date_rfc822: /\A[ \d]\d (?:#{Date::ABBR_MONTHNAMES.join('|')}) \d{4,}\z/, # " 1 Jan 12345"
595
+ date_short: /\A[ \d]\d (?:#{Date::ABBR_MONTHNAMES.join('|')})\z/, # "1 Jan"
596
+ dateTime_db: /\A\d{4,}-\d\d-\d\d \d\d:\d\d:\d\d\z/, # "12345-01-01 00:00:00"
597
+ dateTime_hms: /\A\d\d:\d\d:\d\d\z/, # "00:00:00"
598
+ dateTime_iso8601: /\A\d{4,}-\d\d-\d\dT\d\d:\d\d:\d\dZ\z/, # "12345-01-01T00:00:00Z"
599
+ dateTime_long: /\A(?:#{Date::MONTHNAMES.join('|')}) \d\d, \d{4,} \d\d:\d\d\z/, # "January 01, 12345 00:00"
600
+ dateTime_short: /\A\d\d (?:#{Date::ABBR_MONTHNAMES.join('|')}) \d\d:\d\d\z/, # "01 Jan 00:00"
601
+ dateTime_time: /\A\d\d:\d\d\z/ # "00:00"
583
602
  }.freeze
584
603
 
585
604
  URI_REGEXP = /(?<uri>.*?)/
586
- TOKEN_REGEXP = /([^\(\)\<\>@,;:\\"\/\[\]\?=\{\} \t]+)/
605
+ TOKEN_REGEXP = /([^()<>@,;:\\"\/\[\]?={} \t]+)/
587
606
  QUOTED_STRING_REGEXP = /("[^"]*")/
588
- SGML_NAME_REGEXP = /([A-Za-z][-A-Za-z0-9\.]*)/
607
+ SGML_NAME_REGEXP = /([A-Za-z][-A-Za-z0-9.]*)/
589
608
  RELATIONSHIP_REGEXP = Regexp.new("(?<relationship>#{SGML_NAME_REGEXP}|(\"#{SGML_NAME_REGEXP}(\\s+#{SGML_NAME_REGEXP})*\"))")
590
609
  REL_REGEXP = Regexp.new("(?<rel>\\s*rel\\s*=\\s*(?<rel-relationship>#{RELATIONSHIP_REGEXP}))")
591
610
  REV_REGEXP = Regexp.new("(?<rev>\\s*rev\\s*=\\s*#{RELATIONSHIP_REGEXP})")
@@ -593,8 +612,7 @@ module Csvlint
593
612
  ANCHOR_REGEXP = Regexp.new("(?<anchor>\\s*anchor\\s*=\\s*\\<#{URI_REGEXP}\\>)")
594
613
  LINK_EXTENSION_REGEXP = Regexp.new("(?<link-extension>(?<param>#{TOKEN_REGEXP})(\\s*=\\s*(?<param-value>#{TOKEN_REGEXP}|#{QUOTED_STRING_REGEXP}))?)")
595
614
  LINK_PARAM_REGEXP = Regexp.new("(#{REL_REGEXP}|#{REV_REGEXP}|#{TITLE_REGEXP}|#{ANCHOR_REGEXP}|#{LINK_EXTENSION_REGEXP})")
596
- LINK_HEADER_REGEXP = Regexp.new("\<#{URI_REGEXP}\>(\\s*;\\s*#{LINK_PARAM_REGEXP})*")
597
- POSSIBLE_DATE_REGEXP = Regexp.new("\\A(\\d|\\s\\d#{Date::ABBR_MONTHNAMES.join('|')}#{Date::MONTHNAMES.join('|')})")
598
-
615
+ LINK_HEADER_REGEXP = Regexp.new("<#{URI_REGEXP}>(\\s*;\\s*#{LINK_PARAM_REGEXP})*")
616
+ POSSIBLE_DATE_REGEXP = Regexp.new("\\A(\\d|\\s\\d#{Date::ABBR_MONTHNAMES.join("|")}#{Date::MONTHNAMES.join("|")})")
599
617
  end
600
618
  end
@@ -1,3 +1,3 @@
1
1
  module Csvlint
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
data/lib/csvlint.rb CHANGED
@@ -1,27 +1,26 @@
1
- require 'csv'
2
- require 'date'
3
- require 'open-uri'
4
- require 'set'
5
- require 'tempfile'
6
- require 'typhoeus'
1
+ require "csv"
2
+ require "date"
3
+ require "open-uri"
4
+ require "tempfile"
5
+ require "typhoeus"
7
6
 
8
- require 'active_support/core_ext/date/conversions'
9
- require 'active_support/core_ext/time/conversions'
10
- require 'active_support/core_ext/object'
11
- require 'open_uri_redirections'
12
- require 'uri_template'
7
+ require "active_support/core_ext/date/conversions"
8
+ require "active_support/core_ext/time/conversions"
9
+ require "active_support/core_ext/object"
10
+ require "open_uri_redirections"
11
+ require "uri_template"
13
12
 
14
- require 'csvlint/error_message'
15
- require 'csvlint/error_collector'
16
- require 'csvlint/validate'
17
- require 'csvlint/field'
13
+ require "csvlint/error_message"
14
+ require "csvlint/error_collector"
15
+ require "csvlint/validate"
16
+ require "csvlint/field"
18
17
 
19
- require 'csvlint/csvw/metadata_error'
20
- require 'csvlint/csvw/number_format'
21
- require 'csvlint/csvw/date_format'
22
- require 'csvlint/csvw/property_checker'
23
- require 'csvlint/csvw/column'
24
- require 'csvlint/csvw/table'
25
- require 'csvlint/csvw/table_group'
18
+ require "csvlint/csvw/metadata_error"
19
+ require "csvlint/csvw/number_format"
20
+ require "csvlint/csvw/date_format"
21
+ require "csvlint/csvw/property_checker"
22
+ require "csvlint/csvw/column"
23
+ require "csvlint/csvw/table"
24
+ require "csvlint/csvw/table_group"
26
25
 
27
- require 'csvlint/schema'
26
+ require "csvlint/schema"