csvlint 0.3.2 → 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 +5 -13
- data/.github/ISSUE_TEMPLATE.md +24 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +7 -0
- data/.github/dependabot.yml +7 -0
- data/.github/workflows/push.yml +23 -0
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +85 -130
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +32 -0
- data/Gemfile +0 -3
- data/README.md +54 -47
- data/bin/create_schema +2 -2
- data/csvlint.gemspec +9 -6
- data/features/fixtures/white space in filename.csv +3 -0
- data/features/support/aruba.rb +4 -3
- data/features/support/env.rb +1 -1
- data/features/support/load_tests.rb +12 -12
- data/features/support/webmock.rb +2 -0
- data/lib/csvlint/cli.rb +18 -24
- data/lib/csvlint/csvw/property_checker.rb +9 -2
- data/lib/csvlint/csvw/table.rb +6 -4
- data/lib/csvlint/schema.rb +18 -3
- data/lib/csvlint/validate.rb +18 -13
- data/lib/csvlint/version.rb +1 -1
- data/lib/csvlint.rb +1 -1
- data/spec/schema_spec.rb +2 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/validator_spec.rb +14 -9
- metadata +103 -80
- data/.travis.yml +0 -35
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Contributing to CSVlint.rb
|
2
|
+
|
3
|
+
The CSVlint library is open source, and contributions are gratefully accepted!
|
4
|
+
Details on how to contribute are below. By participating in this project, you agree to abide by our [Code of Conduct](https://github.com/theodi/csvlint.rb/blob/CODE_OF_CONDUCT.md).
|
5
|
+
|
6
|
+
Before you start coding, please reach out to us either on our [gitter channel](https://gitter.im/theodi/toolbox) or by tagging a repository administrator on the issue ticket you are interested in contributing towards to indicate your interest in helping.
|
7
|
+
|
8
|
+
If this is your first time contributing to the ODI’s codebase you will need to [create a fork of this repository](https://help.github.com/articles/fork-a-repo/).
|
9
|
+
|
10
|
+
Consult our [Getting Started Guide](https://github.com/theodi/toolbox/wiki/Developers-Guide:-Getting-Started) (if necessary) and then follow the [readme instructions](https://github.com/theodi/csvlint.rb/blob/master/README.md#development) to get your Development environment running locally
|
11
|
+
|
12
|
+
Ensure that the [tests](https://github.com/theodi/csvlint.rb/blob/master/README.md#tests) pass before working on your contribution
|
13
|
+
|
14
|
+
## Code Review Process
|
15
|
+
|
16
|
+
All contributions to the codebase - whether fork or pull request - will be reviewed per the below criteria.
|
17
|
+
To increase your chances of your push being accepted please be aware of the following
|
18
|
+
- Write [well formed commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
19
|
+
- Follow our [style guide recommendations](https://github.com/theodi/toolbox/blob/README.md#code-style-guide)
|
20
|
+
- Write tests for all changes (additions or refactors of existing code).
|
21
|
+
- Of the github integrations we use two will be utilised to check appraise your contribution. In order of priority these are
|
22
|
+
- Travis ensures that all tests (existing and additions) pass
|
23
|
+
- Travis/Coveralls ensures that overall test coverage for lines of code meets a certain threshold. If this metric dips below what it previously was for the repository you’re pushing to then your PR will be rejected
|
24
|
+
- Gemnasium ensures dependencies are up to date
|
25
|
+
- Once your PR is published and passes the above checks a repository administrator will review your contribution. Where appropriate comments may be provided and amendments suggested before your PR is merged into Master.
|
26
|
+
- Once your PR is accepted you will be granted push access to the repository you have contributed to! Congratulations on joining our community, you’ll no longer need to work from forks.
|
27
|
+
|
28
|
+
If you make a contribution to another repository in the Toolbox you will be expected to repeat this process. Read more about that [here](https://github.com/theodi/toolbox/blob/master/README.md#push-access).
|
29
|
+
|
30
|
+
## Code Style Guide
|
31
|
+
|
32
|
+
We follow the same code style conventions as detailed in Github’s [Ruby Style Guide](https://github.com/github/rubocop-github/blob/master/STYLEGUIDE.md)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -6,7 +6,46 @@
|
|
6
6
|
|
7
7
|
# CSV Lint
|
8
8
|
|
9
|
-
A ruby gem to support validating CSV files to check their syntax and contents.
|
9
|
+
A ruby gem to support validating CSV files to check their syntax and contents. You can either use this gem within your own Ruby code, or as a standalone command line application
|
10
|
+
|
11
|
+
## Summary of features
|
12
|
+
|
13
|
+
* Validation that checks the structural formatting of a CSV file
|
14
|
+
* Validation of a delimiter-separated values (dsv) file accesible via URL, File, or an IO-style object (e.g. StringIO)
|
15
|
+
* Validation against [CSV dialects](http://dataprotocols.org/csv-dialect/)
|
16
|
+
* Validation against multiple schema standards; [JSON Table Schema](https://github.com/theodi/csvlint.rb/blob/master/README.md#json-table-schema-support) and [CSV on the Web](https://github.com/theodi/csvlint.rb/blob/master/README.md#csv-on-the-web-validation-support)
|
17
|
+
|
18
|
+
## Development
|
19
|
+
|
20
|
+
`ruby version 2.1.4`
|
21
|
+
|
22
|
+
### Tests
|
23
|
+
|
24
|
+
The codebase includes both rspec and cucumber tests, which can be run together using:
|
25
|
+
|
26
|
+
$ rake
|
27
|
+
|
28
|
+
or separately:
|
29
|
+
|
30
|
+
$ rake spec
|
31
|
+
$ rake features
|
32
|
+
|
33
|
+
When the cucumber tests are first run, a script will create tests based on the latest version of the [CSV on the Web test suite](http://w3c.github.io/csvw/tests/), including creating a local cache of the test files. This requires an internet connection and some patience. Following that download, the tests will run locally; there's also a batch script:
|
34
|
+
|
35
|
+
$ bin/run-csvw-tests
|
36
|
+
|
37
|
+
which will run the tests from the command line.
|
38
|
+
|
39
|
+
If you need to refresh the CSV on the Web tests:
|
40
|
+
|
41
|
+
$ rm bin/run-csvw-tests
|
42
|
+
$ rm features/csvw_validation_tests.feature
|
43
|
+
$ rm -r features/fixtures/csvw
|
44
|
+
|
45
|
+
and then run the cucumber tests again or:
|
46
|
+
|
47
|
+
$ ruby features/support/load_tests.rb
|
48
|
+
|
10
49
|
|
11
50
|
## Installation
|
12
51
|
|
@@ -24,7 +63,7 @@ Or install it yourself as:
|
|
24
63
|
|
25
64
|
## Usage
|
26
65
|
|
27
|
-
You can either use this gem within your own Ruby code, or as a
|
66
|
+
You can either use this gem within your own Ruby code, or as a standalone command line application
|
28
67
|
|
29
68
|
## On the command line
|
30
69
|
|
@@ -32,6 +71,9 @@ After installing the gem, you can validate a CSV on the command line like so:
|
|
32
71
|
|
33
72
|
csvlint myfile.csv
|
34
73
|
|
74
|
+
You may need to add the gem exectuable directory to your path, by adding '/usr/local/lib/ruby/gems/2.6.0/bin'
|
75
|
+
or whatever your version is, to your .bash_profile PATH entry. [like so](https://stackoverflow.com/questions/2392293/ruby-gems-returns-command-not-found)
|
76
|
+
|
35
77
|
You will then see the validation result, together with any warnings or errors e.g.
|
36
78
|
|
37
79
|
```
|
@@ -265,7 +307,7 @@ Schema validation provides some additional types of error and warning messages:
|
|
265
307
|
* `:below_minimum` (error) -- a column with a `minimum` constraint contains a value that is below the minimum
|
266
308
|
* `:above_maximum` (error) -- a column with a `maximum` constraint contains a value that is above the maximum
|
267
309
|
|
268
|
-
|
310
|
+
### Other validation options
|
269
311
|
|
270
312
|
You can also provide an optional options hash as the fourth argument to Validator#new. Supported options are:
|
271
313
|
|
@@ -281,48 +323,13 @@ validator = Csvlint::Validator.new( "http://example.org/data.csv", nil, nil, opt
|
|
281
323
|
* :lambda -- Pass a block of code to be called when each line is validated, this will give you access to the `Validator` object. For example, this will return the current line number for every line validated:
|
282
324
|
|
283
325
|
```
|
284
|
-
options = {
|
285
|
-
|
286
|
-
}
|
287
|
-
validator = Csvlint::Validator.new( "http://example.org/data.csv", nil, nil, options )
|
288
|
-
=> 1
|
289
|
-
2
|
290
|
-
3
|
291
|
-
4
|
292
|
-
.....
|
326
|
+
options = {
|
327
|
+
lambda: ->(validator) { puts validator.current_line }
|
328
|
+
}
|
329
|
+
validator = Csvlint::Validator.new( "http://example.org/data.csv", nil, nil, options )
|
330
|
+
=> 1
|
331
|
+
2
|
332
|
+
3
|
333
|
+
4
|
334
|
+
.....
|
293
335
|
```
|
294
|
-
|
295
|
-
## Contributing
|
296
|
-
|
297
|
-
1. Fork it
|
298
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
299
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
300
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
301
|
-
5. Create new Pull Request
|
302
|
-
|
303
|
-
### Testing
|
304
|
-
|
305
|
-
The codebase includes both rspec and cucumber tests, which can be run together using:
|
306
|
-
|
307
|
-
$ rake
|
308
|
-
|
309
|
-
or separately:
|
310
|
-
|
311
|
-
$ rake spec
|
312
|
-
$ rake features
|
313
|
-
|
314
|
-
When the cucumber tests are first run, a script will create tests based on the latest version of the [CSV on the Web test suite](http://w3c.github.io/csvw/tests/), including creating a local cache of the test files. This requires an internet connection and some patience. Following that download, the tests will run locally; there's also a batch script:
|
315
|
-
|
316
|
-
$ bin/run-csvw-tests
|
317
|
-
|
318
|
-
which will run the tests from the command line.
|
319
|
-
|
320
|
-
If you need to refresh the CSV on the Web tests:
|
321
|
-
|
322
|
-
$ rm bin/run-csvw-tests
|
323
|
-
$ rm features/csvw_validation_tests.feature
|
324
|
-
$ rm -r features/fixtures/csvw
|
325
|
-
|
326
|
-
and then run the cucumber tests again or:
|
327
|
-
|
328
|
-
$ ruby features/support/load_tests.rb
|
data/bin/create_schema
CHANGED
@@ -5,7 +5,7 @@ require 'csvlint'
|
|
5
5
|
|
6
6
|
begin
|
7
7
|
puts ARGV[0]
|
8
|
-
|
8
|
+
csv = CSV.new( URI.open(ARGV[0]) )
|
9
9
|
headers = csv.shift
|
10
10
|
|
11
11
|
name = File.basename( ARGV[0] )
|
@@ -29,4 +29,4 @@ rescue => e
|
|
29
29
|
puts e
|
30
30
|
puts e.backtrace
|
31
31
|
puts "Unable to parse CSV file"
|
32
|
-
end
|
32
|
+
end
|
data/csvlint.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["pezholio@gmail.com"]
|
11
11
|
spec.description = %q{CSV Validator}
|
12
12
|
spec.summary = %q{CSV Validator}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/theodi/csvlint.rb"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
@@ -18,8 +18,9 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.
|
22
|
-
|
21
|
+
spec.required_ruby_version = ['>= 2.5', '< 3.2']
|
22
|
+
|
23
|
+
spec.add_dependency "rainbow"
|
23
24
|
spec.add_dependency "open_uri_redirections"
|
24
25
|
spec.add_dependency "activesupport"
|
25
26
|
spec.add_dependency "addressable"
|
@@ -27,8 +28,10 @@ Gem::Specification.new do |spec|
|
|
27
28
|
spec.add_dependency "escape_utils"
|
28
29
|
spec.add_dependency "uri_template"
|
29
30
|
spec.add_dependency "thor"
|
31
|
+
spec.add_dependency "rack"
|
32
|
+
spec.add_dependency "net-http-persistent"
|
30
33
|
|
31
|
-
spec.add_development_dependency "bundler", "
|
34
|
+
spec.add_development_dependency "bundler", ">= 1.3"
|
32
35
|
spec.add_development_dependency "rake"
|
33
36
|
spec.add_development_dependency "cucumber"
|
34
37
|
spec.add_development_dependency "simplecov"
|
@@ -39,10 +42,10 @@ Gem::Specification.new do |spec|
|
|
39
42
|
spec.add_development_dependency "rspec-pride"
|
40
43
|
spec.add_development_dependency "rspec-expectations"
|
41
44
|
spec.add_development_dependency "coveralls"
|
42
|
-
spec.add_development_dependency "
|
45
|
+
spec.add_development_dependency "byebug"
|
43
46
|
spec.add_development_dependency "github_changelog_generator"
|
44
47
|
spec.add_development_dependency "aruba"
|
45
|
-
spec.add_development_dependency "rdf"
|
48
|
+
spec.add_development_dependency "rdf", "< 4.0"
|
46
49
|
spec.add_development_dependency "rdf-turtle"
|
47
50
|
spec.add_development_dependency "henry"
|
48
51
|
|
data/features/support/aruba.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'aruba'
|
2
|
-
require 'aruba/in_process'
|
3
2
|
require 'aruba/cucumber'
|
4
3
|
|
5
4
|
require 'csvlint/cli'
|
@@ -52,5 +51,7 @@ module Csvlint
|
|
52
51
|
end
|
53
52
|
end
|
54
53
|
|
55
|
-
Aruba.
|
56
|
-
|
54
|
+
Aruba.configure do |config|
|
55
|
+
config.command_launcher = :in_process
|
56
|
+
config.main_class = Csvlint::CliRunner
|
57
|
+
end
|
data/features/support/env.rb
CHANGED
@@ -2,7 +2,7 @@ require 'json'
|
|
2
2
|
require 'open-uri'
|
3
3
|
require 'uri'
|
4
4
|
|
5
|
-
BASE_URI = "
|
5
|
+
BASE_URI = "https://w3c.github.io/csvw/tests/"
|
6
6
|
BASE_PATH = File.join(File.dirname(__FILE__), "..", "fixtures", "csvw")
|
7
7
|
FEATURE_BASE_PATH = File.join(File.dirname(__FILE__), "..")
|
8
8
|
VALIDATION_FEATURE_FILE_PATH = File.join(FEATURE_BASE_PATH, "csvw_validation_tests.feature")
|
@@ -23,7 +23,7 @@ def cache_file(filename)
|
|
23
23
|
end
|
24
24
|
STDERR.puts("storing #{file} locally")
|
25
25
|
File.open(file, 'wb') do |f|
|
26
|
-
|
26
|
+
f.puts URI.open(uri, 'rb').read
|
27
27
|
end
|
28
28
|
end
|
29
29
|
return uri, file
|
@@ -31,14 +31,14 @@ end
|
|
31
31
|
|
32
32
|
File.open(SCRIPT_FILE_PATH, 'w') do |file|
|
33
33
|
File.chmod(0755, SCRIPT_FILE_PATH)
|
34
|
-
|
34
|
+
manifest = JSON.parse( URI.open("#{BASE_URI}manifest-validation.jsonld").read )
|
35
35
|
manifest["entries"].each do |entry|
|
36
36
|
type = "valid"
|
37
|
-
case entry["type"]
|
38
|
-
when "csvt:WarningValidationTest"
|
39
|
-
type = "warnings"
|
40
|
-
when "csvt:NegativeValidationTest"
|
41
|
-
type = "errors"
|
37
|
+
case entry["type"]
|
38
|
+
when "csvt:WarningValidationTest"
|
39
|
+
type = "warnings"
|
40
|
+
when "csvt:NegativeValidationTest"
|
41
|
+
type = "errors"
|
42
42
|
end
|
43
43
|
file.puts "echo \"#{entry["id"].split("#")[-1]}: #{entry["name"].gsub("`", "'")}\""
|
44
44
|
file.puts "echo \"#{type}: #{entry["comment"].gsub("\"", "\\\"").gsub("`", "'")}\""
|
@@ -54,14 +54,14 @@ File.open(SCRIPT_FILE_PATH, 'w') do |file|
|
|
54
54
|
end unless File.exist? SCRIPT_FILE_PATH
|
55
55
|
|
56
56
|
File.open(VALIDATION_FEATURE_FILE_PATH, 'w') do |file|
|
57
|
-
file.puts "# Auto-generated file based on standard validation CSVW tests from
|
57
|
+
file.puts "# Auto-generated file based on standard validation CSVW tests from #{BASE_URI}manifest-validation.jsonld"
|
58
58
|
file.puts ""
|
59
59
|
|
60
|
-
|
60
|
+
manifest = JSON.parse( URI.open("#{BASE_URI}manifest-validation.jsonld").read )
|
61
61
|
|
62
62
|
file.puts "Feature: #{manifest["label"]}"
|
63
63
|
file.puts ""
|
64
|
-
|
64
|
+
|
65
65
|
manifest["entries"].each do |entry|
|
66
66
|
action_uri, action_file = cache_file(entry["action"])
|
67
67
|
metadata = nil
|
@@ -85,7 +85,7 @@ File.open(VALIDATION_FEATURE_FILE_PATH, 'w') do |file|
|
|
85
85
|
end
|
86
86
|
provided_files << action_uri.to_s
|
87
87
|
if entry["name"].include?("/.well-known/csvm")
|
88
|
-
file.puts "\t\tAnd I have a file called \"w3.org/.well-known/csvm\" at the url \"
|
88
|
+
file.puts "\t\tAnd I have a file called \"w3.org/.well-known/csvm\" at the url \"https://www.w3.org/.well-known/csvm\""
|
89
89
|
missing_files << "#{action_uri}.json"
|
90
90
|
missing_files << URI.join(action_uri, 'csvm.json').to_s
|
91
91
|
else
|
data/features/support/webmock.rb
CHANGED
data/lib/csvlint/cli.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'csvlint'
|
2
|
-
require '
|
2
|
+
require 'rainbow'
|
3
|
+
require 'active_support/json'
|
3
4
|
require 'json'
|
4
5
|
require 'pp'
|
5
6
|
require 'thor'
|
@@ -10,15 +11,20 @@ module Csvlint
|
|
10
11
|
class Cli < Thor
|
11
12
|
|
12
13
|
desc "myfile.csv OR csvlint http://example.com/myfile.csv", "Supports validating CSV files to check their syntax and contents"
|
14
|
+
|
13
15
|
option :dump_errors, desc: "Pretty print error and warning objects.", type: :boolean, aliases: :d
|
14
16
|
option :schema, banner: "FILENAME OR URL", desc: "Schema file", aliases: :s
|
15
17
|
option :json, desc: "Output errors as JSON", type: :boolean, aliases: :j
|
18
|
+
option :werror, desc: "Make all warnings into errors", type: :boolean, aliases: :w
|
19
|
+
|
16
20
|
def validate(source = nil)
|
17
21
|
source = read_source(source)
|
18
22
|
@schema = get_schema(options[:schema]) if options[:schema]
|
19
23
|
fetch_schema_tables(@schema, options) if source.nil?
|
20
24
|
|
21
|
-
|
25
|
+
Rainbow.enabled = $stdout.tty?
|
26
|
+
|
27
|
+
valid = validate_csv(source, @schema, options[:dump_errors], options[:json], options[:werror])
|
22
28
|
exit 1 unless valid
|
23
29
|
end
|
24
30
|
|
@@ -53,7 +59,7 @@ module Csvlint
|
|
53
59
|
|
54
60
|
def get_schema(schema)
|
55
61
|
begin
|
56
|
-
schema = Csvlint::Schema.
|
62
|
+
schema = Csvlint::Schema.load_from_uri(schema, false)
|
57
63
|
rescue Csvlint::Csvw::MetadataError => e
|
58
64
|
return_error "invalid metadata: #{e.message}#{" at " + e.path if e.path}"
|
59
65
|
rescue OpenURI::HTTPError, Errno::ENOENT
|
@@ -80,7 +86,7 @@ module Csvlint
|
|
80
86
|
rescue Errno::ENOENT
|
81
87
|
return_error "#{source} not found"
|
82
88
|
end unless source =~ /^http(s)?/
|
83
|
-
valid &= validate_csv(source, schema, options[:
|
89
|
+
valid &= validate_csv(source, schema, options[:dump_errors], nil, options[:werror])
|
84
90
|
end
|
85
91
|
|
86
92
|
exit 1 unless valid
|
@@ -103,11 +109,7 @@ module Csvlint
|
|
103
109
|
output_string += ". #{location}" unless location.empty?
|
104
110
|
output_string += ". #{error.content}" if error.content
|
105
111
|
|
106
|
-
|
107
|
-
puts output_string.colorize(color)
|
108
|
-
else
|
109
|
-
puts output_string
|
110
|
-
end
|
112
|
+
puts Rainbow(output_string).color(color)
|
111
113
|
|
112
114
|
if dump
|
113
115
|
pp error
|
@@ -121,15 +123,11 @@ module Csvlint
|
|
121
123
|
end
|
122
124
|
|
123
125
|
def return_error(message)
|
124
|
-
|
125
|
-
puts message.colorize(:red)
|
126
|
-
else
|
127
|
-
puts message
|
128
|
-
end
|
126
|
+
puts Rainbow(message).red
|
129
127
|
exit 1
|
130
128
|
end
|
131
129
|
|
132
|
-
def validate_csv(source, schema, dump, json)
|
130
|
+
def validate_csv(source, schema, dump, json, werror)
|
133
131
|
@error_count = 0
|
134
132
|
|
135
133
|
if json === true
|
@@ -157,16 +155,12 @@ module Csvlint
|
|
157
155
|
}.to_json
|
158
156
|
print json
|
159
157
|
else
|
160
|
-
|
161
|
-
|
162
|
-
else
|
163
|
-
puts "\r\n#{csv} is #{validator.valid? ? "VALID" : "INVALID"}"
|
164
|
-
end
|
165
|
-
|
166
|
-
print_errors(validator.errors, dump)
|
158
|
+
puts "\r\n#{csv} is #{validator.valid? ? Rainbow("VALID").green : Rainbow("INVALID").red}"
|
159
|
+
print_errors(validator.errors, dump)
|
167
160
|
print_errors(validator.warnings, dump)
|
168
161
|
end
|
169
162
|
|
163
|
+
return false if werror && validator.warnings.size > 0
|
170
164
|
return validator.valid?
|
171
165
|
end
|
172
166
|
|
@@ -191,9 +185,9 @@ module Csvlint
|
|
191
185
|
lambda do |row|
|
192
186
|
new_errors = row.errors.count
|
193
187
|
if new_errors > @error_count
|
194
|
-
print "!".red
|
188
|
+
print Rainbow("!").red
|
195
189
|
else
|
196
|
-
print ".".green
|
190
|
+
print Rainbow(".").green
|
197
191
|
end
|
198
192
|
@error_count = new_errors
|
199
193
|
end
|
@@ -11,7 +11,14 @@ module Csvlint
|
|
11
11
|
value, warnings = check_common_property_value(value, base_url, lang)
|
12
12
|
return value, warnings, :annotation
|
13
13
|
else
|
14
|
-
|
14
|
+
# property name must be an absolute URI
|
15
|
+
begin
|
16
|
+
return value, :invalid_property, nil if URI(property).scheme.nil?
|
17
|
+
value, warnings = check_common_property_value(value, base_url, lang)
|
18
|
+
return value, warnings, :annotation
|
19
|
+
rescue
|
20
|
+
return value, :invalid_property, nil
|
21
|
+
end
|
15
22
|
end
|
16
23
|
end
|
17
24
|
|
@@ -448,7 +455,7 @@ module Csvlint
|
|
448
455
|
schema_url = URI.join(base_url, value).to_s
|
449
456
|
schema_base_url = schema_url
|
450
457
|
schema_ref = schema_url.start_with?("file:") ? File.new(schema_url[5..-1]) : schema_url
|
451
|
-
schema = JSON.parse( open(schema_ref).read )
|
458
|
+
schema = JSON.parse( URI.open(schema_ref).read )
|
452
459
|
schema["@id"] = schema["@id"] ? URI.join(schema_url, schema["@id"]).to_s : schema_url
|
453
460
|
if schema["@context"]
|
454
461
|
if schema["@context"].instance_of?(Array) && schema["@context"].length > 1
|
data/lib/csvlint/csvw/table.rb
CHANGED
@@ -62,7 +62,8 @@ module Csvlint
|
|
62
62
|
if validate
|
63
63
|
unless @primary_key.nil?
|
64
64
|
key = @primary_key.map { |column| column.validate(values[column.number - 1], row) }
|
65
|
-
|
65
|
+
colnum = if primary_key.length == 1 then primary_key[0].number else nil end
|
66
|
+
build_errors(:duplicate_key, :schema, row, colnum, key.join(","), @primary_key_values[key]) if @primary_key_values.include?(key)
|
66
67
|
@primary_key_values[key] = row
|
67
68
|
end
|
68
69
|
# build a record of the unique values that are referenced by foreign keys from other tables
|
@@ -103,11 +104,12 @@ module Csvlint
|
|
103
104
|
reset
|
104
105
|
local = @foreign_key_reference_values[foreign_key]
|
105
106
|
context = { "from" => { "url" => remote_url.to_s.split("/")[-1], "columns" => foreign_key["columnReference"] }, "to" => { "url" => @url.to_s.split("/")[-1], "columns" => foreign_key["reference"]["columnReference"] }}
|
106
|
-
|
107
|
+
colnum = if foreign_key["referencing_columns"].length == 1 then foreign_key["referencing_columns"][0].number else nil end
|
108
|
+
remote.each_with_index do |r,i|
|
107
109
|
if local[r]
|
108
|
-
build_errors(:multiple_matched_rows, :schema,
|
110
|
+
build_errors(:multiple_matched_rows, :schema, i+1, colnum, r, context) if local[r].length > 1
|
109
111
|
else
|
110
|
-
build_errors(:unmatched_foreign_key_reference, :schema,
|
112
|
+
build_errors(:unmatched_foreign_key_reference, :schema, i+1, colnum, r, context)
|
111
113
|
end
|
112
114
|
end
|
113
115
|
return valid?
|
data/lib/csvlint/schema.rb
CHANGED
@@ -16,6 +16,8 @@ module Csvlint
|
|
16
16
|
|
17
17
|
class << self
|
18
18
|
|
19
|
+
extend Gem::Deprecate
|
20
|
+
|
19
21
|
def from_json_table(uri, json)
|
20
22
|
fields = []
|
21
23
|
json["fields"].each do |field_desc|
|
@@ -29,19 +31,32 @@ module Csvlint
|
|
29
31
|
return Csvlint::Csvw::TableGroup.from_json(uri, json)
|
30
32
|
end
|
31
33
|
|
34
|
+
# Deprecated method signature
|
32
35
|
def load_from_json(uri, output_errors = true)
|
36
|
+
load_from_uri(uri, output_errors)
|
37
|
+
end
|
38
|
+
deprecate :load_from_json, :load_from_uri, 2018, 1
|
39
|
+
|
40
|
+
def load_from_uri(uri, output_errors = true)
|
41
|
+
load_from_string(uri, URI.open(uri).read, output_errors)
|
42
|
+
rescue OpenURI::HTTPError, Errno::ENOENT => e
|
43
|
+
raise e
|
44
|
+
end
|
45
|
+
|
46
|
+
def load_from_string(uri, string, output_errors = true)
|
33
47
|
begin
|
34
|
-
json = JSON.parse(
|
48
|
+
json = JSON.parse( string )
|
35
49
|
if json["@context"]
|
36
50
|
uri = "file:#{File.expand_path(uri)}" unless uri.to_s =~ /^http(s)?/
|
37
51
|
return Schema.from_csvw_metadata(uri,json)
|
38
52
|
else
|
39
53
|
return Schema.from_json_table(uri,json)
|
40
54
|
end
|
55
|
+
rescue TypeError => e
|
56
|
+
# NO IDEA what this was even trying to do - SP 20160526
|
57
|
+
|
41
58
|
rescue Csvlint::Csvw::MetadataError => e
|
42
59
|
raise e
|
43
|
-
rescue OpenURI::HTTPError, Errno::ENOENT => e
|
44
|
-
raise e
|
45
60
|
rescue => e
|
46
61
|
if output_errors === true
|
47
62
|
STDERR.puts e.class
|
data/lib/csvlint/validate.rb
CHANGED
@@ -38,13 +38,15 @@ module Csvlint
|
|
38
38
|
ESCAPE_RE[@re_chars][@re_esc][str]
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
41
|
+
if RUBY_VERSION < '2.5'
|
42
|
+
# Optimization: Disable the CSV library's converters feature.
|
43
|
+
# @see https://github.com/ruby/ruby/blob/v2_2_3/lib/csv.rb#L2100
|
44
|
+
def init_converters(options, field_name = :converters)
|
45
|
+
@converters = []
|
46
|
+
@header_converters = []
|
47
|
+
options.delete(:unconverted_fields)
|
48
|
+
options.delete(field_name)
|
49
|
+
end
|
48
50
|
end
|
49
51
|
end
|
50
52
|
|
@@ -56,6 +58,7 @@ module Csvlint
|
|
56
58
|
"Missing or stray quote" => :stray_quote,
|
57
59
|
"Illegal quoting" => :whitespace,
|
58
60
|
"Unclosed quoted field" => :unclosed_quote,
|
61
|
+
"Any value after quoted field isn't allowed" => :unclosed_quote,
|
59
62
|
"Unquoted fields do not allow \\r or \\n" => :line_breaks,
|
60
63
|
}
|
61
64
|
|
@@ -123,6 +126,7 @@ module Csvlint
|
|
123
126
|
validate_metadata
|
124
127
|
end
|
125
128
|
request.on_body do |chunk|
|
129
|
+
chunk.force_encoding(Encoding::UTF_8) if chunk.encoding == Encoding::ASCII_8BIT
|
126
130
|
io = StringIO.new(chunk)
|
127
131
|
io.each_line do |line|
|
128
132
|
break if line_limit_reached?
|
@@ -178,9 +182,9 @@ module Csvlint
|
|
178
182
|
@csv_options[:encoding] = @encoding
|
179
183
|
|
180
184
|
begin
|
181
|
-
row = LineCSV.parse_line(stream,
|
185
|
+
row = LineCSV.parse_line(stream, **@csv_options)
|
182
186
|
rescue LineCSV::MalformedCSVError => e
|
183
|
-
build_exception_messages(e, stream, current_line)
|
187
|
+
build_exception_messages(e, stream, current_line) unless e.message.include?("UTF") && @reported_invalid_encoding
|
184
188
|
end
|
185
189
|
|
186
190
|
if row
|
@@ -249,7 +253,7 @@ module Csvlint
|
|
249
253
|
if rel == "describedby" && param == "type" && ["application/csvm+json", "application/ld+json", "application/json"].include?(param_value)
|
250
254
|
begin
|
251
255
|
url = URI.join(@source_url, uri)
|
252
|
-
schema = Schema.
|
256
|
+
schema = Schema.load_from_uri(url)
|
253
257
|
if schema.instance_of? Csvlint::Csvw::TableGroup
|
254
258
|
if schema.tables[@source_url]
|
255
259
|
@schema = schema
|
@@ -433,7 +437,8 @@ module Csvlint
|
|
433
437
|
when StringIO
|
434
438
|
return
|
435
439
|
when File
|
436
|
-
|
440
|
+
uri_parser = URI::Parser.new
|
441
|
+
@source_url = "file:#{uri_parser.escape(File.expand_path(@source))}"
|
437
442
|
else
|
438
443
|
@source_url = @source
|
439
444
|
end
|
@@ -448,7 +453,7 @@ module Csvlint
|
|
448
453
|
if @source_url =~ /^http(s)?/
|
449
454
|
begin
|
450
455
|
well_known_uri = URI.join(@source_url, "/.well-known/csvm")
|
451
|
-
paths = open(well_known_uri).read.split("\n")
|
456
|
+
paths = URI.open(well_known_uri.to_s).read.split("\n")
|
452
457
|
rescue OpenURI::HTTPError, URI::BadURIError
|
453
458
|
end
|
454
459
|
end
|
@@ -459,7 +464,7 @@ module Csvlint
|
|
459
464
|
path = template.expand('url' => @source_url)
|
460
465
|
url = URI.join(@source_url, path)
|
461
466
|
url = File.new(url.to_s.sub(/^file:/, "")) if url.to_s =~ /^file:/
|
462
|
-
schema = Schema.
|
467
|
+
schema = Schema.load_from_uri(url)
|
463
468
|
if schema.instance_of? Csvlint::Csvw::TableGroup
|
464
469
|
if schema.tables[@source_url]
|
465
470
|
@schema = schema
|
data/lib/csvlint/version.rb
CHANGED
data/lib/csvlint.rb
CHANGED