jsontableschema 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 1cf851281ccf698eb2f50ce29916e8b2632e841a
4
- data.tar.gz: 253a669fbee44572cb3af3e9ed68592f444f08a1
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OWU5YWEyM2JmOTY3ZTY0ZGJmN2I5Mjc1ODQzN2M4ZjA2YTZhOTcxNw==
5
+ data.tar.gz: !binary |-
6
+ NWIyNWRjNGQzMjU0NmM2MjkzNmMwZGE0ZDJhMzg1YjQxNWMyYTUzOQ==
5
7
  SHA512:
6
- metadata.gz: 3215fdb1aa97951f83dc9f69484fc054befe9c9f0e9c8441692c011659fb5035bb8d41c42cd01863d8d474fd6f899caa48513d264872443e77961dd66f696635
7
- data.tar.gz: 2cc35932e7a71d369e75c5818e979d2a6db0c0ad26217f856b98f663dba9bafc2f8fbb2a8516324dbd6209764bb5dce6a0f0e7a348077e31cf6c8c146b0fbee7
8
+ metadata.gz: !binary |-
9
+ NDMyNzcwNzQ0ZjRiYjdhZDgzMjczNzYxMDY0ZWQ2OTI5MGEyZGFlMmYxMTI5
10
+ ZTEzMjE5MGQzMGE0NWFhY2E5OWU5NTJmYWRjMmI5MTExMjIxNTRlNzM5MGE5
11
+ NDcxMjE1MDhlZmI3YTgwMGNiMGNkZTc3N2FmZmUyMWY3M2VhZDM=
12
+ data.tar.gz: !binary |-
13
+ OWI5ZWY5ZDNmZDQ2MzY3N2RmNjVkMTQyNTZiODI4MDg1YTkwOTBhNzc1OWNj
14
+ MDZiYmU2YjU0NGQ4OTE2YTNiMTFiZjFjN2NkODNlMThiZDM3NGE0OTU5MTA0
15
+ YTA2ZmJkNmM2MDlkYmFmNTIyNTY0NmE5Y2E1NGFmN2ZmMDEwZTg=
data/README.md CHANGED
@@ -164,8 +164,6 @@ schema.primary_keys
164
164
  #=> ["id"]
165
165
  schema.foreign_keys
166
166
  #=> [{"fields" => "state", "reference" => { "datapackage" => "http://data.okfn.org/data/mydatapackage/", "resource" => "the-resource", "fields" => "state_id" } } ]
167
- schema.cast('height', '10')
168
- #=> 10.0
169
167
  schema.get_field('id')
170
168
  #=> {"name"=>"id", "constraints"=>{"required"=>true}, "type"=>"string", "format"=>"default"}
171
169
  schema.has_field?('foo')
@@ -176,13 +174,13 @@ schema.get_fields_by_type('string')
176
174
  #=> [{"name"=>"id", "constraints"=>{"required"=>true}, "type"=>"string", "format"=>"default"}, {"name"=>"height", "type"=>"string", "format"=>"default"}]
177
175
  schema.get_constraints('id')
178
176
  #=> {"required" => true}
179
- schema.convert_row(['string', '10.0'])
177
+ schema.cast_row(['string', '10.0'])
180
178
  #=> ['string', 10.0]
181
- schema.convert([['foo', '12.0'],['bar', '10.0']])
179
+ schema.cast([['foo', '12.0'],['bar', '10.0']])
182
180
  #=> [['foo', 12.0],['bar', 10.0]]
183
181
  ```
184
182
 
185
- When converting a row (using `convert_row`), or a number of rows (using `convert`), by default the converter will fail on the first error it finds. If you pass `false` as the second argument, the errors will be collected into a `errors` attribute for you to review later. For example:
183
+ When casting a row (using `cast_row`), or a number of rows (using `cast`), by default the converter will fail on the first error it finds. If you pass `false` as the second argument, the errors will be collected into a `errors` attribute for you to review later. For example:
186
184
 
187
185
  ```ruby
188
186
  schema_hash = {
@@ -209,14 +207,31 @@ rows = [
209
207
  ['wrong column count']
210
208
  ]
211
209
 
212
- schema.convert(rows)
210
+ schema.cast(rows)
213
211
  #=> JsonTableSchema::InvalidCast: notanumber is not a number
214
- schema.convert(rows, false)
212
+ schema.cast(rows, false)
215
213
  #=> JsonTableSchema::MultipleInvalid
216
214
  schema.errors
217
215
  #=> [#<JsonTableSchema::InvalidCast: notanumber is not a number>, #<JsonTableSchema::InvalidCast: notanumber is not a number>, #<JsonTableSchema::ConversionError: The number of items to convert (1) does not match the number of headers in the schema (2)>]
218
216
  ```
219
217
 
218
+ ## Field
219
+
220
+ ```ruby
221
+ # Init field
222
+ field = JsonTableSchema::Field.new({'type': 'number'})
223
+
224
+ # Cast a value
225
+ field.cast_value('12345')
226
+ #=> 12345.0
227
+ ```
228
+
229
+ Data values can be cast to native Ruby objects with a Field instance. Type instances can be initialized with f[ield descriptors](http://dataprotocols.org/json-table-schema/#field-descriptors). This allows formats and constraints to be defined.
230
+
231
+ Casting a value will check the value is of the expected type, is in the correct format, and complies with any constraints imposed by a schema. E.g. a date value (in ISO 8601 format) can be cast with a DateType instance. Values that can't be cast will raise an `InvalidCast` exception.
232
+
233
+ Casting a value that doesn't meet the constraints will raise a `ConstraintError` exception.
234
+
220
235
  ## Development
221
236
 
222
237
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -227,7 +242,6 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
227
242
 
228
243
  Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/jsontableschema. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
229
244
 
230
-
231
245
  ## License
232
246
 
233
247
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -28,6 +28,7 @@ require "jsontableschema/types/object"
28
28
  require "jsontableschema/types/string"
29
29
  require "jsontableschema/types/time"
30
30
 
31
+ require "jsontableschema/field"
31
32
  require "jsontableschema/validate"
32
33
  require "jsontableschema/model"
33
34
  require "jsontableschema/data"
@@ -3,33 +3,36 @@ module JsonTableSchema
3
3
 
4
4
  attr_reader :errors
5
5
 
6
- def convert(rows, fail_fast = true)
6
+ def cast_rows(rows, fail_fast = true, limit = nil)
7
7
  @errors ||= []
8
- rows.map! do |r|
8
+ parsed_rows = []
9
+ rows.each_with_index do |r, i|
9
10
  begin
10
- convert_row(r, fail_fast)
11
+ break if limit && (limit <= i)
12
+ r = r.fields if r.class == CSV::Row
13
+ parsed_rows << cast_row(r, fail_fast)
11
14
  rescue MultipleInvalid, ConversionError => e
12
15
  raise e if fail_fast == true
13
16
  @errors << e if e.is_a?(ConversionError)
14
17
  end
15
18
  end
16
19
  check_for_errors
17
- rows
20
+ parsed_rows
18
21
  end
19
22
 
20
- def convert_row(row, fail_fast = true)
23
+ alias_method :convert, :cast_rows
24
+
25
+ def cast_row(row, fail_fast = true)
21
26
  @errors ||= []
22
27
  raise_header_error(row) if row.count != fields.count
23
28
  fields.each_with_index do |field,i|
24
- row[i] = convert_column(row[i], field, fail_fast)
29
+ row[i] = cast_column(field, row[i], fail_fast)
25
30
  end
26
31
  check_for_errors
27
32
  row
28
33
  end
29
34
 
30
- def cast(field_name, value)
31
- convert_column(value, get_field(field_name), true)
32
- end
35
+ alias_method :convert_row, :cast_row
33
36
 
34
37
  private
35
38
 
@@ -41,10 +44,8 @@ module JsonTableSchema
41
44
  raise(JsonTableSchema::MultipleInvalid.new("There were errors parsing the data")) if @errors.count > 0
42
45
  end
43
46
 
44
- def convert_column(col, field, fail_fast)
45
- klass = get_class_for_type(field['type'] || 'string')
46
- converter = Kernel.const_get(klass).new(field)
47
- converter.cast(col)
47
+ def cast_column(field, col, fail_fast)
48
+ field.cast_value(col)
48
49
  rescue Exception => e
49
50
  if fail_fast == true
50
51
  raise e
@@ -53,5 +54,7 @@ module JsonTableSchema
53
54
  end
54
55
  end
55
56
 
57
+ alias_method :convert_column, :cast_column
58
+
56
59
  end
57
60
  end
@@ -0,0 +1,41 @@
1
+ module JsonTableSchema
2
+ class Field < Hash
3
+ include JsonTableSchema::Helpers
4
+
5
+ attr_reader :type_class
6
+
7
+ def initialize(descriptor)
8
+ self.merge! descriptor
9
+ @type_class = get_type
10
+ end
11
+
12
+ def name
13
+ self['name']
14
+ end
15
+
16
+ def type
17
+ self['type'] || 'string'
18
+ end
19
+
20
+ def format
21
+ self['format'] || 'default'
22
+ end
23
+
24
+ def constraints
25
+ self['constraints'] || {}
26
+ end
27
+
28
+ def cast_value(col)
29
+ klass = get_class_for_type(type)
30
+ converter = Kernel.const_get(klass).new(self)
31
+ converter.cast(col)
32
+ end
33
+
34
+ private
35
+
36
+ def get_type
37
+ Object.const_get get_class_for_type(type)
38
+ end
39
+
40
+ end
41
+ end
@@ -22,7 +22,7 @@ module JsonTableSchema
22
22
  end
23
23
 
24
24
  def get_class_for_type(type)
25
- "JsonTableSchema::Types::#{type_class_lookup[type]}"
25
+ "JsonTableSchema::Types::#{type_class_lookup[type] || 'String'}"
26
26
  end
27
27
 
28
28
  def type_class_lookup
@@ -40,6 +40,7 @@ module JsonTableSchema
40
40
  type_matches = []
41
41
  @rows.each_with_index do |row, i|
42
42
  break if @row_limit && i > @row_limit
43
+ row = row.fields if row.class == CSV::Row
43
44
 
44
45
  row_length = row.count
45
46
  headers_length = @headers.count
@@ -53,10 +53,6 @@ module JsonTableSchema
53
53
 
54
54
  private
55
55
 
56
- def fields
57
- self['fields']
58
- end
59
-
60
56
  def transform(name)
61
57
  name.downcase! if @opts[:case_insensitive_headers]
62
58
  name
@@ -69,5 +65,9 @@ module JsonTableSchema
69
65
  end
70
66
  end
71
67
 
68
+ def load_fields!
69
+ self['fields'] = (self['fields'] || []).map { |f| JsonTableSchema::Field.new(f) }
70
+ end
71
+
72
72
  end
73
73
  end
@@ -5,26 +5,27 @@ module JsonTableSchema
5
5
  include JsonTableSchema::Data
6
6
  include JsonTableSchema::Helpers
7
7
 
8
- def initialize(schema, opts = {})
9
- self.merge! parse_schema(schema)
8
+ def initialize(descriptor, opts = {})
9
+ self.merge! parse_schema(descriptor)
10
10
  @messages = []
11
11
  @opts = opts
12
+ load_fields!
12
13
  load_validator!
13
14
  expand!
14
15
  end
15
16
 
16
- def parse_schema(schema)
17
- if schema.class == Hash
18
- schema
19
- elsif schema.class == String
17
+ def parse_schema(descriptor)
18
+ if descriptor.class == Hash
19
+ descriptor
20
+ elsif descriptor.class == String
20
21
  begin
21
- JSON.parse open(schema).read
22
+ JSON.parse open(descriptor).read
22
23
  rescue Errno::ENOENT
23
- raise SchemaException.new("File not found at `#{schema}`")
24
+ raise SchemaException.new("File not found at `#{descriptor}`")
24
25
  rescue OpenURI::HTTPError => e
25
- raise SchemaException.new("URL `#{schema}` returned #{e.message}")
26
+ raise SchemaException.new("URL `#{descriptor}` returned #{e.message}")
26
27
  rescue JSON::ParserError
27
- raise SchemaException.new("File at `#{schema}` is not valid JSON")
28
+ raise SchemaException.new("File at `#{descriptor}` is not valid JSON")
28
29
  end
29
30
  else
30
31
  raise SchemaException.new("A schema must be a hash, path or URL")
@@ -7,15 +7,15 @@ module JsonTableSchema
7
7
  JsonTableSchema::Table.new(csv, nil, opts)
8
8
  end
9
9
 
10
- def initialize(csv, schema, opts = {})
10
+ def initialize(csv, descriptor, opts = {})
11
11
  @opts = opts
12
12
  @csv = parse_csv(csv)
13
- @schema = schema.nil? ? infer_schema(@csv) : JsonTableSchema::Schema.new(schema)
13
+ @schema = descriptor.nil? ? infer_schema(@csv) : JsonTableSchema::Schema.new(descriptor)
14
14
  end
15
15
 
16
16
  def parse_csv(csv)
17
- csv_string = csv.is_a?(Array) ? array_to_csv(csv) : open(csv).read
18
- CSV.parse(csv_string, csv_options)
17
+ csv = csv.is_a?(Array) ? StringIO.new(array_to_csv csv) : open(csv)
18
+ CSV.new(csv, csv_options)
19
19
  end
20
20
 
21
21
  def csv_options
@@ -24,8 +24,7 @@ module JsonTableSchema
24
24
 
25
25
  def rows(opts = {})
26
26
  fail_fast = opts[:fail_fast] || opts[:fail_fast].nil?
27
- rows = opts[:limit] ? @csv.to_a.drop(1).take(opts[:limit]) : @csv.to_a.drop(1)
28
- converted = @schema.convert(rows, fail_fast)
27
+ converted = @schema.cast_rows(@csv, fail_fast, opts[:limit])
29
28
  opts[:keyed] ? coverted_to_hash(@csv.headers, converted) : converted
30
29
  end
31
30
 
@@ -42,7 +41,9 @@ module JsonTableSchema
42
41
  end
43
42
 
44
43
  def infer_schema(csv)
45
- inferer = JsonTableSchema::Infer.new(csv.headers, csv.to_a)
44
+ headers = csv.first.to_h.keys
45
+ csv.rewind
46
+ inferer = JsonTableSchema::Infer.new(headers, csv)
46
47
  inferer.schema
47
48
  end
48
49
 
@@ -1,3 +1,3 @@
1
1
  module JsonTableSchema
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,153 +1,153 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsontableschema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - pezholio
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-09-19 00:00:00.000000000 Z
11
+ date: 2016-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.11'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.11'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
33
  version: '10.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ~>
46
46
  - !ruby/object:Gem::Version
47
47
  version: '3.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: pry
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ~>
60
60
  - !ruby/object:Gem::Version
61
61
  version: 0.10.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ~>
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.10.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: webmock
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ~>
74
74
  - !ruby/object:Gem::Version
75
75
  version: 2.1.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ~>
81
81
  - !ruby/object:Gem::Version
82
82
  version: 2.1.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: coveralls
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ~>
88
88
  - !ruby/object:Gem::Version
89
89
  version: 0.8.13
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ~>
95
95
  - !ruby/object:Gem::Version
96
96
  version: 0.8.13
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: json-schema
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - "~>"
101
+ - - ~>
102
102
  - !ruby/object:Gem::Version
103
103
  version: 2.6.0
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - "~>"
108
+ - - ~>
109
109
  - !ruby/object:Gem::Version
110
110
  version: 2.6.0
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: uuid
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - "~>"
115
+ - - ~>
116
116
  - !ruby/object:Gem::Version
117
117
  version: 2.3.8
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - "~>"
122
+ - - ~>
123
123
  - !ruby/object:Gem::Version
124
124
  version: 2.3.8
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: currencies
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - "~>"
129
+ - - ~>
130
130
  - !ruby/object:Gem::Version
131
131
  version: 0.4.2
132
132
  type: :runtime
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - "~>"
136
+ - - ~>
137
137
  - !ruby/object:Gem::Version
138
138
  version: 0.4.2
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: tod
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - "~>"
143
+ - - ~>
144
144
  - !ruby/object:Gem::Version
145
145
  version: 2.1.0
146
146
  type: :runtime
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - "~>"
150
+ - - ~>
151
151
  - !ruby/object:Gem::Version
152
152
  version: 2.1.0
153
153
  description:
@@ -157,9 +157,9 @@ executables: []
157
157
  extensions: []
158
158
  extra_rdoc_files: []
159
159
  files:
160
- - ".gitignore"
161
- - ".rspec"
162
- - ".travis.yml"
160
+ - .gitignore
161
+ - .rspec
162
+ - .travis.yml
163
163
  - CHANGELOG.md
164
164
  - CODE_OF_CONDUCT.md
165
165
  - Gemfile
@@ -182,6 +182,7 @@ files:
182
182
  - lib/jsontableschema/constraints/required.rb
183
183
  - lib/jsontableschema/data.rb
184
184
  - lib/jsontableschema/exceptions.rb
185
+ - lib/jsontableschema/field.rb
185
186
  - lib/jsontableschema/helpers.rb
186
187
  - lib/jsontableschema/infer.rb
187
188
  - lib/jsontableschema/model.rb
@@ -213,17 +214,17 @@ require_paths:
213
214
  - lib
214
215
  required_ruby_version: !ruby/object:Gem::Requirement
215
216
  requirements:
216
- - - ">="
217
+ - - ! '>='
217
218
  - !ruby/object:Gem::Version
218
219
  version: '0'
219
220
  required_rubygems_version: !ruby/object:Gem::Requirement
220
221
  requirements:
221
- - - ">="
222
+ - - ! '>='
222
223
  - !ruby/object:Gem::Version
223
224
  version: '0'
224
225
  requirements: []
225
226
  rubyforge_project:
226
- rubygems_version: 2.5.1
227
+ rubygems_version: 2.4.5
227
228
  signing_key:
228
229
  specification_version: 4
229
230
  summary: A Ruby library for working with JSON Table Schema