tableschema 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/README.md +8 -17
- data/lib/profiles/table-schema.json +51 -116
- data/lib/tableschema.rb +0 -2
- data/lib/tableschema/defaults.rb +4 -1
- data/lib/tableschema/field.rb +10 -5
- data/lib/tableschema/helpers.rb +0 -20
- data/lib/tableschema/schema.rb +175 -19
- data/lib/tableschema/table.rb +24 -17
- data/lib/tableschema/types/boolean.rb +11 -3
- data/lib/tableschema/types/integer.rb +4 -0
- data/lib/tableschema/types/number.rb +7 -19
- data/lib/tableschema/version.rb +1 -1
- data/tableschema.gemspec +1 -1
- metadata +4 -6
- data/lib/tableschema/model.rb +0 -96
- data/lib/tableschema/validate.rb +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af9d2ac90e521ace72b172703da49b220a957978
|
4
|
+
data.tar.gz: 95a2e84830de62dbe00e0b4a107222b60d48bdd4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9de112e6d3f5bc137fde4fd9e972ca2ff97a6b818e3a66e184bc3d0868c9e77e71221ad76bbad56a2014753a24bef71705607063b08a96afa730b6dbd4114d0
|
7
|
+
data.tar.gz: 5caf4a5bde09a437ccf4d7a6cb91ae44fc07395366179ac682b6ba9b0a90dc966a5d5c51f2f80b70ae0911250aa5244e59b0b77e9df8821901929663807f631b
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -46,11 +46,11 @@ The gem `jsontableschema` is no longer maintained. Here are the steps to transit
|
|
46
46
|
2. Replace module name `JsonTableSchema` with module name `TableSchema`. For example:
|
47
47
|
|
48
48
|
```ruby
|
49
|
-
JsonTableSchema::Table.
|
49
|
+
JsonTableSchema::Table.new(source, schema)
|
50
50
|
```
|
51
51
|
with
|
52
52
|
```ruby
|
53
|
-
TableSchema::Table.
|
53
|
+
TableSchema::Table.new(source, schema)
|
54
54
|
```
|
55
55
|
|
56
56
|
## Usage
|
@@ -91,9 +91,9 @@ table.read
|
|
91
91
|
```
|
92
92
|
|
93
93
|
Both `iter` and `read` take the optional parameters:
|
94
|
-
- `row_limit`: integer, default `nil` - stop at this many rows
|
95
|
-
- `cast`: boolean, default `true` - cast values for each row
|
96
94
|
- `keyed`: boolean, default: `false` - return the rows as Hashes with headers as keys
|
95
|
+
- `cast`: boolean, default `true` - cast values for each row
|
96
|
+
- `limit`: integer, default `nil` - stop at this many rows
|
97
97
|
|
98
98
|
### Infer a schema
|
99
99
|
|
@@ -102,7 +102,8 @@ If you don't have a schema for a CSV, and want to generate one, you can infer a
|
|
102
102
|
```ruby
|
103
103
|
csv = 'https://github.com/frictionlessdata/tableschema-rb/raw/master/spec/fixtures/simple_data.csv' # Can also be a url or array of arrays
|
104
104
|
|
105
|
-
table = TableSchema::Table.
|
105
|
+
table = TableSchema::Table.new(csv, nil)
|
106
|
+
table.infer()
|
106
107
|
table.schema
|
107
108
|
#=> {:fields=>[{:name=>"id", :title=>"", :description=>"", :type=>"integer", :format=>"default", :constraints=>{}}, {:name=>"title", :title=>"", :description=>"", :type=>"string", :format=>"default", :constraints=>{}}]}
|
108
109
|
```
|
@@ -169,26 +170,16 @@ schema_hash = {
|
|
169
170
|
}
|
170
171
|
schema = TableSchema::Schema.new(schema_hash)
|
171
172
|
|
172
|
-
schema.
|
173
|
+
schema.field_names
|
173
174
|
#=> ["id", "height"]
|
174
|
-
schema.required_headers
|
175
|
-
#=> ["id"]
|
176
175
|
schema.fields
|
177
176
|
#=> [{:name=>"id", :type=>"string", :constraints=>{:required=>true}, :format=>"default"}, {:name=>"height", :type=>"number", :format=>"default", :constraints=>{}}]
|
178
|
-
schema.
|
177
|
+
schema.primary_key
|
179
178
|
#=> ["id"]
|
180
179
|
schema.foreign_keys
|
181
180
|
# => [{:fields=>"state", :reference=>{:resource=>"the-resource", :fields=>"state_id"}}]
|
182
181
|
schema.get_field('id')
|
183
182
|
# => {:name=>"id", :type=>"string", :constraints=>{:required=>true}, :format=>"default"}
|
184
|
-
schema.has_field?('foo')
|
185
|
-
#=> false
|
186
|
-
schema.get_type('id')
|
187
|
-
#=> 'string'
|
188
|
-
schema.get_fields_by_type('string')
|
189
|
-
# => [{:name=>"id", :type=>"string", :constraints=>{:required=>true}, :format=>"default"}, {:name=>"state", :type=>"string", :format=>"default", :constraints=>{}}]
|
190
|
-
schema.get_constraints('id')
|
191
|
-
# => {:required=>true}
|
192
183
|
```
|
193
184
|
|
194
185
|
#### Cast row
|
@@ -24,13 +24,8 @@
|
|
24
24
|
"properties": {
|
25
25
|
"name": {
|
26
26
|
"title": "Name",
|
27
|
-
"description": "
|
28
|
-
"type": "string"
|
29
|
-
"pattern": "^([-a-z0-9._/])+$",
|
30
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
31
|
-
"examples": [
|
32
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
33
|
-
]
|
27
|
+
"description": "A name for this field.",
|
28
|
+
"type": "string"
|
34
29
|
},
|
35
30
|
"title": {
|
36
31
|
"title": "Title",
|
@@ -125,13 +120,8 @@
|
|
125
120
|
"properties": {
|
126
121
|
"name": {
|
127
122
|
"title": "Name",
|
128
|
-
"description": "
|
129
|
-
"type": "string"
|
130
|
-
"pattern": "^([-a-z0-9._/])+$",
|
131
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
132
|
-
"examples": [
|
133
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
134
|
-
]
|
123
|
+
"description": "A name for this field.",
|
124
|
+
"type": "string"
|
135
125
|
},
|
136
126
|
"title": {
|
137
127
|
"title": "Title",
|
@@ -162,6 +152,12 @@
|
|
162
152
|
],
|
163
153
|
"default": "default"
|
164
154
|
},
|
155
|
+
"bareNumber": {
|
156
|
+
"type": "boolean",
|
157
|
+
"title": "bareNumber",
|
158
|
+
"description": "a boolean field with a default of `true`. If `true` the physical contents of this field must follow the formatting constraints already set out. If `false` the contents of this field may contain leading and/or trailing non-numeric characters (which implementors MUST therefore strip). The purpose of `bareNumber` is to allow publishers to publish numeric data that contains trailing characters such as percentages e.g. `95%` or leading characters such as currencies e.g. `€95` or `EUR 95`. Note that it is entirely up to implementors what, if anything, they do with stripped text.",
|
159
|
+
"default": true
|
160
|
+
},
|
165
161
|
"decimalChar": {
|
166
162
|
"type": "string",
|
167
163
|
"description": "A string whose value is used to represent a decimal point within the number. The default value is `.`."
|
@@ -170,10 +166,6 @@
|
|
170
166
|
"type": "string",
|
171
167
|
"description": "A string whose value is used to group digits within the number. The default value is `null`. A common value is `,` e.g. '100,000'."
|
172
168
|
},
|
173
|
-
"currency": {
|
174
|
-
"type": "string",
|
175
|
-
"description": "A number that may include additional currency symbols."
|
176
|
-
},
|
177
169
|
"constraints": {
|
178
170
|
"title": "Constraints",
|
179
171
|
"description": "The following constraints are supported for `number` fields.",
|
@@ -257,13 +249,8 @@
|
|
257
249
|
"properties": {
|
258
250
|
"name": {
|
259
251
|
"title": "Name",
|
260
|
-
"description": "
|
261
|
-
"type": "string"
|
262
|
-
"pattern": "^([-a-z0-9._/])+$",
|
263
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
264
|
-
"examples": [
|
265
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
266
|
-
]
|
252
|
+
"description": "A name for this field.",
|
253
|
+
"type": "string"
|
267
254
|
},
|
268
255
|
"title": {
|
269
256
|
"title": "Title",
|
@@ -294,6 +281,12 @@
|
|
294
281
|
],
|
295
282
|
"default": "default"
|
296
283
|
},
|
284
|
+
"bareNumber": {
|
285
|
+
"type": "boolean",
|
286
|
+
"title": "bareNumber",
|
287
|
+
"description": "a boolean field with a default of `true`. If `true` the physical contents of this field must follow the formatting constraints already set out. If `false` the contents of this field may contain leading and/or trailing non-numeric characters (which implementors MUST therefore strip). The purpose of `bareNumber` is to allow publishers to publish numeric data that contains trailing characters such as percentages e.g. `95%` or leading characters such as currencies e.g. `€95` or `EUR 95`. Note that it is entirely up to implementors what, if anything, they do with stripped text.",
|
288
|
+
"default": true
|
289
|
+
},
|
297
290
|
"constraints": {
|
298
291
|
"title": "Constraints",
|
299
292
|
"description": "The following constraints are supported for `integer` fields.",
|
@@ -375,13 +368,8 @@
|
|
375
368
|
"properties": {
|
376
369
|
"name": {
|
377
370
|
"title": "Name",
|
378
|
-
"description": "
|
379
|
-
"type": "string"
|
380
|
-
"pattern": "^([-a-z0-9._/])+$",
|
381
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
382
|
-
"examples": [
|
383
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
384
|
-
]
|
371
|
+
"description": "A name for this field.",
|
372
|
+
"type": "string"
|
385
373
|
},
|
386
374
|
"title": {
|
387
375
|
"title": "Title",
|
@@ -462,13 +450,8 @@
|
|
462
450
|
"properties": {
|
463
451
|
"name": {
|
464
452
|
"title": "Name",
|
465
|
-
"description": "
|
466
|
-
"type": "string"
|
467
|
-
"pattern": "^([-a-z0-9._/])+$",
|
468
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
469
|
-
"examples": [
|
470
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
471
|
-
]
|
453
|
+
"description": "A name for this field.",
|
454
|
+
"type": "string"
|
472
455
|
},
|
473
456
|
"title": {
|
474
457
|
"title": "Title",
|
@@ -548,13 +531,8 @@
|
|
548
531
|
"properties": {
|
549
532
|
"name": {
|
550
533
|
"title": "Name",
|
551
|
-
"description": "
|
552
|
-
"type": "string"
|
553
|
-
"pattern": "^([-a-z0-9._/])+$",
|
554
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
555
|
-
"examples": [
|
556
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
557
|
-
]
|
534
|
+
"description": "A name for this field.",
|
535
|
+
"type": "string"
|
558
536
|
},
|
559
537
|
"title": {
|
560
538
|
"title": "Title",
|
@@ -634,13 +612,8 @@
|
|
634
612
|
"properties": {
|
635
613
|
"name": {
|
636
614
|
"title": "Name",
|
637
|
-
"description": "
|
638
|
-
"type": "string"
|
639
|
-
"pattern": "^([-a-z0-9._/])+$",
|
640
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
641
|
-
"examples": [
|
642
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
643
|
-
]
|
615
|
+
"description": "A name for this field.",
|
616
|
+
"type": "string"
|
644
617
|
},
|
645
618
|
"title": {
|
646
619
|
"title": "Title",
|
@@ -748,13 +721,8 @@
|
|
748
721
|
"properties": {
|
749
722
|
"name": {
|
750
723
|
"title": "Name",
|
751
|
-
"description": "
|
752
|
-
"type": "string"
|
753
|
-
"pattern": "^([-a-z0-9._/])+$",
|
754
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
755
|
-
"examples": [
|
756
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
757
|
-
]
|
724
|
+
"description": "A name for this field.",
|
725
|
+
"type": "string"
|
758
726
|
},
|
759
727
|
"title": {
|
760
728
|
"title": "Title",
|
@@ -841,13 +809,8 @@
|
|
841
809
|
"properties": {
|
842
810
|
"name": {
|
843
811
|
"title": "Name",
|
844
|
-
"description": "
|
845
|
-
"type": "string"
|
846
|
-
"pattern": "^([-a-z0-9._/])+$",
|
847
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
848
|
-
"examples": [
|
849
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
850
|
-
]
|
812
|
+
"description": "A name for this field.",
|
813
|
+
"type": "string"
|
851
814
|
},
|
852
815
|
"title": {
|
853
816
|
"title": "Title",
|
@@ -937,13 +900,8 @@
|
|
937
900
|
"properties": {
|
938
901
|
"name": {
|
939
902
|
"title": "Name",
|
940
|
-
"description": "
|
941
|
-
"type": "string"
|
942
|
-
"pattern": "^([-a-z0-9._/])+$",
|
943
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
944
|
-
"examples": [
|
945
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
946
|
-
]
|
903
|
+
"description": "A name for this field.",
|
904
|
+
"type": "string"
|
947
905
|
},
|
948
906
|
"title": {
|
949
907
|
"title": "Title",
|
@@ -1038,13 +996,8 @@
|
|
1038
996
|
"properties": {
|
1039
997
|
"name": {
|
1040
998
|
"title": "Name",
|
1041
|
-
"description": "
|
1042
|
-
"type": "string"
|
1043
|
-
"pattern": "^([-a-z0-9._/])+$",
|
1044
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
1045
|
-
"examples": [
|
1046
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
1047
|
-
]
|
999
|
+
"description": "A name for this field.",
|
1000
|
+
"type": "string"
|
1048
1001
|
},
|
1049
1002
|
"title": {
|
1050
1003
|
"title": "Title",
|
@@ -1146,13 +1099,8 @@
|
|
1146
1099
|
"properties": {
|
1147
1100
|
"name": {
|
1148
1101
|
"title": "Name",
|
1149
|
-
"description": "
|
1150
|
-
"type": "string"
|
1151
|
-
"pattern": "^([-a-z0-9._/])+$",
|
1152
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
1153
|
-
"examples": [
|
1154
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
1155
|
-
]
|
1102
|
+
"description": "A name for this field.",
|
1103
|
+
"type": "string"
|
1156
1104
|
},
|
1157
1105
|
"title": {
|
1158
1106
|
"title": "Title",
|
@@ -1250,13 +1198,8 @@
|
|
1250
1198
|
"properties": {
|
1251
1199
|
"name": {
|
1252
1200
|
"title": "Name",
|
1253
|
-
"description": "
|
1254
|
-
"type": "string"
|
1255
|
-
"pattern": "^([-a-z0-9._/])+$",
|
1256
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
1257
|
-
"examples": [
|
1258
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
1259
|
-
]
|
1201
|
+
"description": "A name for this field.",
|
1202
|
+
"type": "string"
|
1260
1203
|
},
|
1261
1204
|
"title": {
|
1262
1205
|
"title": "Title",
|
@@ -1352,13 +1295,8 @@
|
|
1352
1295
|
"properties": {
|
1353
1296
|
"name": {
|
1354
1297
|
"title": "Name",
|
1355
|
-
"description": "
|
1356
|
-
"type": "string"
|
1357
|
-
"pattern": "^([-a-z0-9._/])+$",
|
1358
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
1359
|
-
"examples": [
|
1360
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
1361
|
-
]
|
1298
|
+
"description": "A name for this field.",
|
1299
|
+
"type": "string"
|
1362
1300
|
},
|
1363
1301
|
"title": {
|
1364
1302
|
"title": "Title",
|
@@ -1439,13 +1377,8 @@
|
|
1439
1377
|
"properties": {
|
1440
1378
|
"name": {
|
1441
1379
|
"title": "Name",
|
1442
|
-
"description": "
|
1443
|
-
"type": "string"
|
1444
|
-
"pattern": "^([-a-z0-9._/])+$",
|
1445
|
-
"context": "This is ideally a url-usable and human-readable name. Name `SHOULD` be invariant, meaning it `SHOULD NOT` change when its parent descriptor is updated.",
|
1446
|
-
"examples": [
|
1447
|
-
"{\n \"name\": \"my-nice-name\"\n}\n"
|
1448
|
-
]
|
1380
|
+
"description": "A name for this field.",
|
1381
|
+
"type": "string"
|
1449
1382
|
},
|
1450
1383
|
"title": {
|
1451
1384
|
"title": "Title",
|
@@ -1539,9 +1472,9 @@
|
|
1539
1472
|
"fields",
|
1540
1473
|
"reference"
|
1541
1474
|
],
|
1542
|
-
"
|
1543
|
-
|
1544
|
-
{
|
1475
|
+
"oneOf": [
|
1476
|
+
{
|
1477
|
+
"properties": {
|
1545
1478
|
"fields": {
|
1546
1479
|
"type": "array",
|
1547
1480
|
"items": {
|
@@ -1572,8 +1505,10 @@
|
|
1572
1505
|
}
|
1573
1506
|
}
|
1574
1507
|
}
|
1575
|
-
}
|
1576
|
-
|
1508
|
+
}
|
1509
|
+
},
|
1510
|
+
{
|
1511
|
+
"properties": {
|
1577
1512
|
"fields": {
|
1578
1513
|
"type": "string",
|
1579
1514
|
"description": "Fields that make up the primary key."
|
@@ -1595,8 +1530,8 @@
|
|
1595
1530
|
}
|
1596
1531
|
}
|
1597
1532
|
}
|
1598
|
-
|
1599
|
-
|
1533
|
+
}
|
1534
|
+
]
|
1600
1535
|
},
|
1601
1536
|
"examples": [
|
1602
1537
|
"{\n \"foreignKeys\": [\n {\n \"fields\": \"state\",\n \"reference\": {\n \"resource\": \"the-resource\",\n \"fields\": \"state_id\"\n }\n }\n ]\n}\n",
|
data/lib/tableschema.rb
CHANGED
@@ -32,8 +32,6 @@ require "tableschema/types/duration"
|
|
32
32
|
require "tableschema/defaults"
|
33
33
|
|
34
34
|
require "tableschema/field"
|
35
|
-
require "tableschema/validate"
|
36
|
-
require "tableschema/model"
|
37
35
|
require "tableschema/schema"
|
38
36
|
require "tableschema/table"
|
39
37
|
require "tableschema/infer"
|
data/lib/tableschema/defaults.rb
CHANGED
data/lib/tableschema/field.rb
CHANGED
@@ -4,7 +4,9 @@ module TableSchema
|
|
4
4
|
class Field < Hash
|
5
5
|
include TableSchema::Helpers
|
6
6
|
|
7
|
-
|
7
|
+
# Public
|
8
|
+
|
9
|
+
attr_reader :name, :type, :format, :required, :constraints
|
8
10
|
|
9
11
|
def initialize(descriptor, missing_values=nil)
|
10
12
|
self.merge! deep_symbolize_keys(descriptor)
|
@@ -12,6 +14,7 @@ module TableSchema
|
|
12
14
|
@type = self[:type] = self.fetch(:type, TableSchema::DEFAULTS[:type])
|
13
15
|
@format = self[:format] = self.fetch(:format, TableSchema::DEFAULTS[:format])
|
14
16
|
@constraints = self[:constraints] = self.fetch(:constraints, {})
|
17
|
+
@required = @constraints.fetch(:required, false)
|
15
18
|
@missing_values = missing_values || default_missing_values
|
16
19
|
end
|
17
20
|
|
@@ -19,15 +22,15 @@ module TableSchema
|
|
19
22
|
self.to_h
|
20
23
|
end
|
21
24
|
|
22
|
-
def cast_value(value,
|
25
|
+
def cast_value(value, constraints: true)
|
23
26
|
cast_value = cast_type(value)
|
24
|
-
return cast_value if
|
27
|
+
return cast_value if constraints == false
|
25
28
|
TableSchema::Constraints.new(self, cast_value).validate!
|
26
29
|
cast_value
|
27
30
|
end
|
28
31
|
|
29
|
-
def test_value(value,
|
30
|
-
cast_value(value,
|
32
|
+
def test_value(value, constraints: true)
|
33
|
+
cast_value(value, constraints: constraints)
|
31
34
|
true
|
32
35
|
rescue TableSchema::Exception
|
33
36
|
false
|
@@ -41,6 +44,8 @@ module TableSchema
|
|
41
44
|
end
|
42
45
|
end
|
43
46
|
|
47
|
+
# Private
|
48
|
+
|
44
49
|
private
|
45
50
|
|
46
51
|
def default_missing_values
|
data/lib/tableschema/helpers.rb
CHANGED
@@ -16,26 +16,6 @@ module TableSchema
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def convert_to_boolean(value)
|
20
|
-
if value.is_a?(Boolean)
|
21
|
-
return value
|
22
|
-
elsif true_values.include?(value.to_s.downcase)
|
23
|
-
true
|
24
|
-
elsif false_values.include?(value.to_s.downcase)
|
25
|
-
false
|
26
|
-
else
|
27
|
-
nil
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def true_values
|
32
|
-
['yes', 'y', 'true', 't', '1']
|
33
|
-
end
|
34
|
-
|
35
|
-
def false_values
|
36
|
-
['no', 'n', 'false', 'f', '0']
|
37
|
-
end
|
38
|
-
|
39
19
|
def get_class_for_type(type)
|
40
20
|
"TableSchema::Types::#{type_class_lookup[type.to_sym] || 'String'}"
|
41
21
|
end
|
data/lib/tableschema/schema.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
+
require 'tableschema/defaults'
|
2
|
+
|
1
3
|
module TableSchema
|
2
4
|
class Schema < Hash
|
3
|
-
include TableSchema::Validate
|
4
|
-
include TableSchema::Model
|
5
5
|
include TableSchema::Helpers
|
6
6
|
|
7
|
+
# Public
|
8
|
+
|
7
9
|
attr_reader :errors
|
8
10
|
|
9
|
-
def initialize(descriptor,
|
11
|
+
def initialize(descriptor, strict: false, case_insensitive_headers: false)
|
10
12
|
self.merge! deep_symbolize_keys(parse_schema(descriptor))
|
11
13
|
@case_insensitive_headers = case_insensitive_headers
|
12
14
|
@strict = strict
|
@@ -17,26 +19,60 @@ module TableSchema
|
|
17
19
|
self
|
18
20
|
end
|
19
21
|
|
22
|
+
def validate
|
23
|
+
@errors = Set.new(JSON::Validator.fully_validate(@profile, self))
|
24
|
+
check_primary_key
|
25
|
+
check_foreign_keys
|
26
|
+
@errors.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate!
|
30
|
+
validate
|
31
|
+
raise SchemaException.new(@errors.first) unless @errors.empty?
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
20
35
|
def descriptor
|
21
36
|
self.to_h
|
22
37
|
end
|
23
38
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
def primary_key
|
40
|
+
[self[:primaryKey]].flatten.reject { |k| k.nil? }
|
41
|
+
end
|
42
|
+
|
43
|
+
def foreign_keys
|
44
|
+
self[:foreignKeys] || []
|
45
|
+
end
|
46
|
+
|
47
|
+
def fields
|
48
|
+
self[:fields]
|
49
|
+
end
|
50
|
+
|
51
|
+
def field_names
|
52
|
+
fields.map { |f| transform(f[:name]) }
|
53
|
+
rescue NoMethodError
|
54
|
+
[]
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_field(field_name)
|
58
|
+
fields.find { |f| f[:name] == field_name }
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_field(descriptor)
|
62
|
+
self[:fields].push(descriptor)
|
63
|
+
validate!
|
64
|
+
descriptor
|
65
|
+
rescue TableSchema::SchemaException => e
|
66
|
+
self[:fields].pop
|
67
|
+
raise e if @strict
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def remove_field(field_name)
|
72
|
+
field = get_field(field_name)
|
73
|
+
self[:fields].reject!{ |f| f.name == field_name }
|
74
|
+
validate
|
75
|
+
field
|
40
76
|
end
|
41
77
|
|
42
78
|
def cast_row(row, fail_fast: true)
|
@@ -66,5 +102,125 @@ module TableSchema
|
|
66
102
|
true
|
67
103
|
end
|
68
104
|
|
105
|
+
# Deprecated
|
106
|
+
|
107
|
+
alias :headers :field_names
|
108
|
+
|
109
|
+
def missing_values
|
110
|
+
self.fetch(:missingValues, TableSchema::DEFAULTS[:missing_values])
|
111
|
+
end
|
112
|
+
|
113
|
+
def get_type(field_name)
|
114
|
+
get_field(field_name)[:type]
|
115
|
+
end
|
116
|
+
|
117
|
+
def get_constraints(field_name)
|
118
|
+
get_field(field_name)[:constraints] || {}
|
119
|
+
end
|
120
|
+
|
121
|
+
def required_headers
|
122
|
+
fields.select { |f| f.fetch(:constraints, {}).fetch(:required, nil).to_s == 'true' }
|
123
|
+
.map { |f| transform(f[:name]) }
|
124
|
+
end
|
125
|
+
|
126
|
+
def unique_headers
|
127
|
+
fields.select { |f| f.fetch(:constraints, {}).fetch(:unique, nil).to_s == 'true' }
|
128
|
+
.map { |f| transform(f[:name]) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def has_field?(field_name)
|
132
|
+
get_field(field_name) != nil
|
133
|
+
end
|
134
|
+
|
135
|
+
def get_fields_by_type(type)
|
136
|
+
fields.select { |f| f[:type] == type }
|
137
|
+
end
|
138
|
+
|
139
|
+
# Private
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def parse_schema(descriptor)
|
144
|
+
if descriptor.class == Hash
|
145
|
+
descriptor
|
146
|
+
elsif descriptor.class == String
|
147
|
+
begin
|
148
|
+
JSON.parse(open(descriptor).read, symbolize_names: true)
|
149
|
+
rescue Errno::ENOENT
|
150
|
+
raise SchemaException.new("File not found at `#{descriptor}`")
|
151
|
+
rescue OpenURI::HTTPError => e
|
152
|
+
raise SchemaException.new("URL `#{descriptor}` returned #{e.message}")
|
153
|
+
rescue JSON::ParserError
|
154
|
+
raise SchemaException.new("File at `#{descriptor}` is not valid JSON")
|
155
|
+
end
|
156
|
+
else
|
157
|
+
raise SchemaException.new("A schema must be a hash, path or URL")
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def transform(name)
|
162
|
+
name.downcase! if @case_insensitive_headers == true
|
163
|
+
name
|
164
|
+
end
|
165
|
+
|
166
|
+
def expand!
|
167
|
+
(self[:fields] || []).each do |f|
|
168
|
+
f[:type] = TableSchema::DEFAULTS[:type] if f[:type] == nil
|
169
|
+
f[:format] = TableSchema::DEFAULTS[:format] if f[:format] == nil
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def load_fields!
|
174
|
+
self[:fields] = (self[:fields] || []).map { |f| TableSchema::Field.new(f, missing_values) }
|
175
|
+
end
|
176
|
+
|
177
|
+
def load_validator!
|
178
|
+
filepath = File.join(File.dirname(__FILE__), '..', 'profiles', 'table-schema.json')
|
179
|
+
@profile ||= JSON.parse(File.read(filepath), symbolize_names: true)
|
180
|
+
end
|
181
|
+
|
182
|
+
def check_primary_key
|
183
|
+
return if self[:primaryKey].nil?
|
184
|
+
primary_key.each { |pk| check_field_value(pk, 'primaryKey') }
|
185
|
+
end
|
186
|
+
|
187
|
+
def check_foreign_keys
|
188
|
+
return if self[:foreignKeys].nil?
|
189
|
+
self[:foreignKeys].each do |key|
|
190
|
+
if field_type_mismatch?(key)
|
191
|
+
add_error("A TableSchema `foreignKey.fields` value must be the same type as `foreignKey.reference.fields`")
|
192
|
+
end
|
193
|
+
if field_count_mismatch?(key)
|
194
|
+
add_error("A TableSchema `foreignKey.fields` must contain the same number of entries as `foreignKey.reference.fields`")
|
195
|
+
end
|
196
|
+
foreign_key_fields(key).each { |fk| check_field_value(fk, 'foreignKey.fields') }
|
197
|
+
if key.fetch(:reference).fetch(:resource).empty?
|
198
|
+
foreign_key_fields(key.fetch(:reference)).each { |fk| check_field_value(fk, 'foreignKey.reference.fields')}
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def check_field_value(key, type)
|
204
|
+
if headers.select { |f| key == f }.count == 0
|
205
|
+
add_error("The TableSchema #{type} value `#{key}` is not found in any of the schema's field names")
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def foreign_key_fields(key)
|
210
|
+
[key.fetch(:fields)].flatten
|
211
|
+
end
|
212
|
+
|
213
|
+
def field_count_mismatch?(key)
|
214
|
+
foreign_key_fields(key).count != foreign_key_fields(key.fetch(:reference)).count
|
215
|
+
end
|
216
|
+
|
217
|
+
def field_type_mismatch?(key)
|
218
|
+
key.fetch(:fields).class.name != key.fetch(:reference).fetch(:fields).class.name
|
219
|
+
end
|
220
|
+
|
221
|
+
def add_error(error)
|
222
|
+
@errors << error
|
223
|
+
end
|
224
|
+
|
69
225
|
end
|
70
226
|
end
|
data/lib/tableschema/table.rb
CHANGED
@@ -1,27 +1,28 @@
|
|
1
1
|
module TableSchema
|
2
2
|
class Table
|
3
3
|
|
4
|
-
|
4
|
+
# Public
|
5
5
|
|
6
|
-
|
7
|
-
TableSchema::Table.new(csv, nil, csv_options)
|
8
|
-
end
|
6
|
+
attr_reader :headers, :schema
|
9
7
|
|
10
8
|
def initialize(csv, descriptor, csv_options: {})
|
11
9
|
@csv_options = csv_options.merge(headers: true)
|
10
|
+
@descriptor = descriptor
|
12
11
|
@csv = parse_csv(csv)
|
13
12
|
@headers = initialize_headers
|
14
|
-
|
15
|
-
|
13
|
+
if !descriptor.nil?
|
14
|
+
@schema = TableSchema::Schema.new(@descriptor)
|
15
|
+
initialize_unique_colums
|
16
|
+
end
|
16
17
|
end
|
17
18
|
|
18
|
-
def iter(
|
19
|
+
def iter(keyed: false, cast: true, limit: nil)
|
19
20
|
unless block_given?
|
20
|
-
return enum_for(:iter,
|
21
|
+
return enum_for(:iter, limit: limit, cast: cast, keyed: keyed)
|
21
22
|
end
|
22
23
|
|
23
24
|
@csv.each_with_index do |row, i|
|
24
|
-
break if
|
25
|
+
break if limit && (limit <= i)
|
25
26
|
if cast == true
|
26
27
|
cast_values = @schema.cast_row(row)
|
27
28
|
row = CSV::Row.new(@headers, cast_values)
|
@@ -38,11 +39,21 @@ module TableSchema
|
|
38
39
|
@csv.rewind
|
39
40
|
end
|
40
41
|
|
41
|
-
def read(
|
42
|
-
iterator = self.iter(
|
42
|
+
def read(keyed: false, cast: true, limit: nil)
|
43
|
+
iterator = self.iter(keyed: keyed, cast: cast, limit: limit)
|
43
44
|
iterator.to_a
|
44
45
|
end
|
45
46
|
|
47
|
+
def infer()
|
48
|
+
if !@schema
|
49
|
+
inferer = TableSchema::Infer.new(@headers, @csv)
|
50
|
+
@schema = inferer.schema
|
51
|
+
initialize_unique_colums
|
52
|
+
@csv.rewind
|
53
|
+
end
|
54
|
+
@schema.descriptor
|
55
|
+
end
|
56
|
+
|
46
57
|
def save(target)
|
47
58
|
CSV.open(target, "wb", @csv_options) do |csv|
|
48
59
|
csv << @headers
|
@@ -51,6 +62,8 @@ module TableSchema
|
|
51
62
|
true
|
52
63
|
end
|
53
64
|
|
65
|
+
# Private
|
66
|
+
|
54
67
|
private
|
55
68
|
|
56
69
|
def parse_csv(csv)
|
@@ -62,12 +75,6 @@ module TableSchema
|
|
62
75
|
array.map { |row| row.to_csv(row_sep: nil) }.join("\r\n")
|
63
76
|
end
|
64
77
|
|
65
|
-
def infer_schema
|
66
|
-
inferer = TableSchema::Infer.new(@headers, @csv)
|
67
|
-
@csv.rewind
|
68
|
-
inferer.schema
|
69
|
-
end
|
70
|
-
|
71
78
|
def initialize_headers
|
72
79
|
headers = @csv.first.to_h.keys
|
73
80
|
@csv.rewind
|
@@ -25,9 +25,17 @@ module TableSchema
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def cast_default(value)
|
28
|
-
|
29
|
-
|
30
|
-
value
|
28
|
+
true_values = @field.fetch(:trueValues, TableSchema::DEFAULTS[:true_values])
|
29
|
+
false_values = @field.fetch(:falseValues, TableSchema::DEFAULTS[:false_values])
|
30
|
+
if [true, false].include?(value)
|
31
|
+
return value
|
32
|
+
elsif true_values.include?(value)
|
33
|
+
return true
|
34
|
+
elsif false_values.include?(value)
|
35
|
+
return false
|
36
|
+
else
|
37
|
+
raise TableSchema::InvalidCast.new("#{value} is not a #{name}")
|
38
|
+
end
|
31
39
|
end
|
32
40
|
|
33
41
|
end
|
@@ -25,6 +25,10 @@ module TableSchema
|
|
25
25
|
if value.is_a?(type)
|
26
26
|
value
|
27
27
|
else
|
28
|
+
bare_number = @field.fetch(:bareNumber, TableSchema::DEFAULTS[:bare_number])
|
29
|
+
if !bare_number
|
30
|
+
value = value.gsub(/((^\D*)|(\D*$))/, '')
|
31
|
+
end
|
28
32
|
Integer(value)
|
29
33
|
end
|
30
34
|
rescue ArgumentError
|
@@ -48,28 +48,16 @@ module TableSchema
|
|
48
48
|
else
|
49
49
|
group_char = @field.fetch(:groupChar, TableSchema::DEFAULTS[:group_char])
|
50
50
|
decimal_char = @field.fetch(:decimalChar, TableSchema::DEFAULTS[:decimal_char])
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
Float(formatted_value)
|
51
|
+
bare_number = @field.fetch(:bareNumber, TableSchema::DEFAULTS[:bare_number])
|
52
|
+
formatted_value = value
|
53
|
+
formatted_value = formatted_value.gsub(group_char, '')
|
54
|
+
formatted_value = formatted_value.gsub(decimal_char, '.')
|
55
|
+
if !bare_number
|
56
|
+
formatted_value = formatted_value.gsub(/((^\D*)|(\D*$))/, '')
|
58
57
|
end
|
58
|
+
Float(formatted_value)
|
59
59
|
end
|
60
60
|
end
|
61
|
-
|
62
|
-
def process_percent(value)
|
63
|
-
Float(value.gsub(percent_chars, '')) / 100
|
64
|
-
end
|
65
|
-
|
66
|
-
def process_currency(value)
|
67
|
-
Float(value.gsub(@field[:currency], ''))
|
68
|
-
end
|
69
|
-
|
70
|
-
def percent_chars
|
71
|
-
/%|‰|‱|%|﹪|٪/
|
72
|
-
end
|
73
61
|
end
|
74
62
|
end
|
75
63
|
end
|
data/lib/tableschema/version.rb
CHANGED
data/tableschema.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency "coveralls", "~> 0.8.13"
|
27
27
|
spec.add_development_dependency "rubocop", "~> 0.49.1"
|
28
28
|
|
29
|
-
spec.add_dependency "json-schema", "~> 2.
|
29
|
+
spec.add_dependency "json-schema", "~> 2.8.0"
|
30
30
|
spec.add_dependency "uuid", "~> 2.3.8"
|
31
31
|
spec.add_dependency "tod", "~> 2.1.0"
|
32
32
|
spec.add_dependency "activesupport", "~> 5.1.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tableschema
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Open Knowledge Foundation
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-08-
|
11
|
+
date: 2017-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -114,14 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 2.
|
117
|
+
version: 2.8.0
|
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
|
-
version: 2.
|
124
|
+
version: 2.8.0
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: uuid
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -201,7 +201,6 @@ files:
|
|
201
201
|
- lib/tableschema/field.rb
|
202
202
|
- lib/tableschema/helpers.rb
|
203
203
|
- lib/tableschema/infer.rb
|
204
|
-
- lib/tableschema/model.rb
|
205
204
|
- lib/tableschema/schema.rb
|
206
205
|
- lib/tableschema/table.rb
|
207
206
|
- lib/tableschema/types/any.rb
|
@@ -220,7 +219,6 @@ files:
|
|
220
219
|
- lib/tableschema/types/time.rb
|
221
220
|
- lib/tableschema/types/year.rb
|
222
221
|
- lib/tableschema/types/yearmonth.rb
|
223
|
-
- lib/tableschema/validate.rb
|
224
222
|
- lib/tableschema/version.rb
|
225
223
|
- tableschema.gemspec
|
226
224
|
homepage: https://github.com/frictionlessdata/tableschema-rb
|
data/lib/tableschema/model.rb
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
require 'tableschema/defaults'
|
2
|
-
|
3
|
-
module TableSchema
|
4
|
-
module Model
|
5
|
-
|
6
|
-
def headers
|
7
|
-
fields.map { |f| transform(f[:name]) }
|
8
|
-
rescue NoMethodError
|
9
|
-
[]
|
10
|
-
end
|
11
|
-
|
12
|
-
alias :field_names :headers
|
13
|
-
|
14
|
-
def fields
|
15
|
-
self[:fields]
|
16
|
-
end
|
17
|
-
|
18
|
-
def primary_keys
|
19
|
-
[self[:primaryKey]].flatten.reject { |k| k.nil? }
|
20
|
-
end
|
21
|
-
|
22
|
-
def foreign_keys
|
23
|
-
self[:foreignKeys] || []
|
24
|
-
end
|
25
|
-
|
26
|
-
def missing_values
|
27
|
-
self.fetch(:missingValues, TableSchema::DEFAULTS[:missing_values])
|
28
|
-
end
|
29
|
-
|
30
|
-
def get_type(field_name)
|
31
|
-
get_field(field_name)[:type]
|
32
|
-
end
|
33
|
-
|
34
|
-
def get_constraints(field_name)
|
35
|
-
get_field(field_name)[:constraints] || {}
|
36
|
-
end
|
37
|
-
|
38
|
-
def required_headers
|
39
|
-
fields.select { |f| f.fetch(:constraints, {}).fetch(:required, nil).to_s == 'true' }
|
40
|
-
.map { |f| transform(f[:name]) }
|
41
|
-
end
|
42
|
-
|
43
|
-
def unique_headers
|
44
|
-
fields.select { |f| f.fetch(:constraints, {}).fetch(:unique, nil).to_s == 'true' }
|
45
|
-
.map { |f| transform(f[:name]) }
|
46
|
-
end
|
47
|
-
|
48
|
-
def has_field?(field_name)
|
49
|
-
get_field(field_name) != nil
|
50
|
-
end
|
51
|
-
|
52
|
-
def get_field(field_name)
|
53
|
-
fields.find { |f| f[:name] == field_name }
|
54
|
-
end
|
55
|
-
|
56
|
-
def get_fields_by_type(type)
|
57
|
-
fields.select { |f| f[:type] == type }
|
58
|
-
end
|
59
|
-
|
60
|
-
def add_field(descriptor)
|
61
|
-
self[:fields].push(descriptor)
|
62
|
-
validate!
|
63
|
-
descriptor
|
64
|
-
rescue TableSchema::SchemaException => e
|
65
|
-
self[:fields].pop
|
66
|
-
raise e if @strict
|
67
|
-
nil
|
68
|
-
end
|
69
|
-
|
70
|
-
def remove_field(field_name)
|
71
|
-
field = get_field(field_name)
|
72
|
-
self[:fields].reject!{ |f| f.name == field_name }
|
73
|
-
validate
|
74
|
-
field
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
|
79
|
-
def transform(name)
|
80
|
-
name.downcase! if @case_insensitive_headers == true
|
81
|
-
name
|
82
|
-
end
|
83
|
-
|
84
|
-
def expand!
|
85
|
-
(self[:fields] || []).each do |f|
|
86
|
-
f[:type] = TableSchema::DEFAULTS[:type] if f[:type] == nil
|
87
|
-
f[:format] = TableSchema::DEFAULTS[:format] if f[:format] == nil
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def load_fields!
|
92
|
-
self[:fields] = (self[:fields] || []).map { |f| TableSchema::Field.new(f, missing_values) }
|
93
|
-
end
|
94
|
-
|
95
|
-
end
|
96
|
-
end
|
data/lib/tableschema/validate.rb
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
module TableSchema
|
2
|
-
module Validate
|
3
|
-
|
4
|
-
attr_reader :errors
|
5
|
-
|
6
|
-
def load_validator!
|
7
|
-
filepath = File.join(File.dirname(__FILE__), '..', 'profiles', 'table-schema.json')
|
8
|
-
@profile ||= JSON.parse(File.read(filepath), symbolize_names: true)
|
9
|
-
end
|
10
|
-
|
11
|
-
def validate
|
12
|
-
@errors = Set.new(JSON::Validator.fully_validate(@profile, self))
|
13
|
-
check_primary_keys
|
14
|
-
check_foreign_keys
|
15
|
-
@errors.empty?
|
16
|
-
end
|
17
|
-
|
18
|
-
def validate!
|
19
|
-
validate
|
20
|
-
raise SchemaException.new(@errors.first) unless @errors.empty?
|
21
|
-
true
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def check_primary_keys
|
27
|
-
return if self[:primaryKey].nil?
|
28
|
-
primary_keys.each { |pk| check_field_value(pk, 'primaryKey') }
|
29
|
-
end
|
30
|
-
|
31
|
-
def check_foreign_keys
|
32
|
-
return if self[:foreignKeys].nil?
|
33
|
-
self[:foreignKeys].each do |key|
|
34
|
-
if field_type_mismatch?(key)
|
35
|
-
add_error("A TableSchema `foreignKey.fields` value must be the same type as `foreignKey.reference.fields`")
|
36
|
-
end
|
37
|
-
if field_count_mismatch?(key)
|
38
|
-
add_error("A TableSchema `foreignKey.fields` must contain the same number of entries as `foreignKey.reference.fields`")
|
39
|
-
end
|
40
|
-
foreign_key_fields(key).each { |fk| check_field_value(fk, 'foreignKey.fields') }
|
41
|
-
if key.fetch(:reference).fetch(:resource).empty?
|
42
|
-
foreign_key_fields(key.fetch(:reference)).each { |fk| check_field_value(fk, 'foreignKey.reference.fields')}
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def check_field_value(key, type)
|
48
|
-
if headers.select { |f| key == f }.count == 0
|
49
|
-
add_error("The TableSchema #{type} value `#{key}` is not found in any of the schema's field names")
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def foreign_key_fields(key)
|
54
|
-
[key.fetch(:fields)].flatten
|
55
|
-
end
|
56
|
-
|
57
|
-
def field_count_mismatch?(key)
|
58
|
-
foreign_key_fields(key).count != foreign_key_fields(key.fetch(:reference)).count
|
59
|
-
end
|
60
|
-
|
61
|
-
def field_type_mismatch?(key)
|
62
|
-
key.fetch(:fields).class.name != key.fetch(:reference).fetch(:fields).class.name
|
63
|
-
end
|
64
|
-
|
65
|
-
def add_error(error)
|
66
|
-
@errors << error
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|
70
|
-
end
|