hdo-storting-importer 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/Rakefile +0 -1
- data/features/convert.feature +97 -79
- data/hdo-storting-importer.gemspec +5 -0
- data/lib/hdo/storting_importer/category.rb +16 -56
- data/lib/hdo/storting_importer/cli.rb +6 -34
- data/lib/hdo/storting_importer/committee.rb +13 -24
- data/lib/hdo/storting_importer/converter.rb +4 -9
- data/lib/hdo/storting_importer/district.rb +12 -24
- data/lib/hdo/storting_importer/fusion_table.rb +52 -0
- data/lib/hdo/storting_importer/has_json_schema.rb +85 -0
- data/lib/hdo/storting_importer/issue.rb +29 -60
- data/lib/hdo/storting_importer/party.rb +13 -24
- data/lib/hdo/storting_importer/promise.rb +60 -55
- data/lib/hdo/storting_importer/proposition.rb +61 -0
- data/lib/hdo/storting_importer/representative.rb +37 -70
- data/lib/hdo/storting_importer/schema/category.json +28 -0
- data/lib/hdo/storting_importer/schema/committee.json +21 -0
- data/lib/hdo/storting_importer/schema/district.json +21 -0
- data/lib/hdo/storting_importer/schema/issue.json +62 -0
- data/lib/hdo/storting_importer/schema/party.json +21 -0
- data/lib/hdo/storting_importer/schema/promise.json +42 -0
- data/lib/hdo/storting_importer/schema/proposition.json +34 -0
- data/lib/hdo/storting_importer/schema/representative.json +61 -0
- data/lib/hdo/storting_importer/schema/vote.json +64 -0
- data/lib/hdo/storting_importer/schema.json +5 -0
- data/lib/hdo/storting_importer/util.rb +13 -11
- data/lib/hdo/storting_importer/version.rb +1 -1
- data/lib/hdo/storting_importer/vote.rb +46 -143
- data/lib/hdo/storting_importer.rb +12 -3
- data/spec/fixtures/output/categories.json +1 -0
- data/spec/fixtures/output/committees.json +1 -0
- data/spec/fixtures/output/districts.json +1 -0
- data/spec/fixtures/output/issues.json +1 -0
- data/spec/fixtures/output/parties.json +1 -0
- data/spec/fixtures/output/representatives.json +1 -0
- data/spec/fixtures/output/votes.json +1 -0
- data/spec/hdo/storting_importer/category_spec.rb +29 -33
- data/spec/hdo/storting_importer/committee_spec.rb +29 -16
- data/spec/hdo/storting_importer/converter_spec.rb +3 -3
- data/spec/hdo/storting_importer/district_spec.rb +27 -18
- data/spec/hdo/storting_importer/issue_spec.rb +44 -27
- data/spec/hdo/storting_importer/party_spec.rb +25 -16
- data/spec/hdo/storting_importer/promise_spec.rb +54 -40
- data/spec/hdo/storting_importer/proposition_spec.rb +44 -0
- data/spec/hdo/storting_importer/representative_spec.rb +73 -26
- data/spec/hdo/storting_importer/vote_spec.rb +73 -75
- data/spec/spec_helper.rb +21 -1
- metadata +95 -3
- data/.gitmodules +0 -3
data/Gemfile
CHANGED
data/Rakefile
CHANGED
data/features/convert.feature
CHANGED
@@ -107,83 +107,101 @@ Feature: Import data
|
|
107
107
|
When I run `hdo-converter districts fylker.xml`
|
108
108
|
Then the stdout should contain:
|
109
109
|
"""
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
110
|
+
[
|
111
|
+
{
|
112
|
+
"kind": "hdo#district",
|
113
|
+
"externalId": "AA",
|
114
|
+
"name": "Aust-Agder"
|
115
|
+
},
|
116
|
+
{
|
117
|
+
"kind": "hdo#district",
|
118
|
+
"externalId": "VA",
|
119
|
+
"name": "Vest-Agder"
|
120
|
+
},
|
121
|
+
{
|
122
|
+
"kind": "hdo#district",
|
123
|
+
"externalId": "Ak",
|
124
|
+
"name": "Akershus"
|
125
|
+
},
|
126
|
+
{
|
127
|
+
"kind": "hdo#district",
|
128
|
+
"externalId": "Bu",
|
129
|
+
"name": "Buskerud"
|
130
|
+
},
|
131
|
+
{
|
132
|
+
"kind": "hdo#district",
|
133
|
+
"externalId": "Fi",
|
134
|
+
"name": "Finnmark"
|
135
|
+
},
|
136
|
+
{
|
137
|
+
"kind": "hdo#district",
|
138
|
+
"externalId": "He",
|
139
|
+
"name": "Hedmark"
|
140
|
+
},
|
141
|
+
{
|
142
|
+
"kind": "hdo#district",
|
143
|
+
"externalId": "Ho",
|
144
|
+
"name": "Hordaland"
|
145
|
+
},
|
146
|
+
{
|
147
|
+
"kind": "hdo#district",
|
148
|
+
"externalId": "MR",
|
149
|
+
"name": "Møre og Romsdal"
|
150
|
+
},
|
151
|
+
{
|
152
|
+
"kind": "hdo#district",
|
153
|
+
"externalId": "No",
|
154
|
+
"name": "Nordland"
|
155
|
+
},
|
156
|
+
{
|
157
|
+
"kind": "hdo#district",
|
158
|
+
"externalId": "Op",
|
159
|
+
"name": "Oppland"
|
160
|
+
},
|
161
|
+
{
|
162
|
+
"kind": "hdo#district",
|
163
|
+
"externalId": "Os",
|
164
|
+
"name": "Oslo"
|
165
|
+
},
|
166
|
+
{
|
167
|
+
"kind": "hdo#district",
|
168
|
+
"externalId": "Ro",
|
169
|
+
"name": "Rogaland"
|
170
|
+
},
|
171
|
+
{
|
172
|
+
"kind": "hdo#district",
|
173
|
+
"externalId": "SF",
|
174
|
+
"name": "Sogn og Fjordane"
|
175
|
+
},
|
176
|
+
{
|
177
|
+
"kind": "hdo#district",
|
178
|
+
"externalId": "Te",
|
179
|
+
"name": "Telemark"
|
180
|
+
},
|
181
|
+
{
|
182
|
+
"kind": "hdo#district",
|
183
|
+
"externalId": "Tr",
|
184
|
+
"name": "Troms"
|
185
|
+
},
|
186
|
+
{
|
187
|
+
"kind": "hdo#district",
|
188
|
+
"externalId": "NT",
|
189
|
+
"name": "Nord-Trøndelag"
|
190
|
+
},
|
191
|
+
{
|
192
|
+
"kind": "hdo#district",
|
193
|
+
"externalId": "ST",
|
194
|
+
"name": "Sør-Trøndelag"
|
195
|
+
},
|
196
|
+
{
|
197
|
+
"kind": "hdo#district",
|
198
|
+
"externalId": "Ve",
|
199
|
+
"name": "Vestfold"
|
200
|
+
},
|
201
|
+
{
|
202
|
+
"kind": "hdo#district",
|
203
|
+
"externalId": "Øs",
|
204
|
+
"name": "Østfold"
|
205
|
+
}
|
206
|
+
]
|
189
207
|
"""
|
@@ -22,4 +22,9 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.add_runtime_dependency "rest-client"
|
23
23
|
gem.add_runtime_dependency "unicode_utils"
|
24
24
|
gem.add_runtime_dependency "multi_json"
|
25
|
+
gem.add_runtime_dependency "yajl-ruby"
|
26
|
+
gem.add_runtime_dependency "jschematic", ">= 0.1.0"
|
27
|
+
|
28
|
+
gem.add_development_dependency 'pry'
|
29
|
+
gem.add_development_dependency 'rspec'
|
25
30
|
end
|
@@ -3,25 +3,12 @@ module Hdo
|
|
3
3
|
class Category
|
4
4
|
include IvarEquality
|
5
5
|
include Inspectable
|
6
|
+
include HasJsonSchema
|
6
7
|
|
7
8
|
attr_reader :external_id, :name
|
8
9
|
attr_accessor :children
|
9
10
|
|
10
|
-
|
11
|
-
'category'
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.description
|
15
|
-
'a parliamentary category, used to categorize issues and promises'
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.fields
|
19
|
-
[
|
20
|
-
EXTERNAL_ID_FIELD,
|
21
|
-
Field.new(:name, true, :string, 'The name of the category.'),
|
22
|
-
Field.new(:subcategories, false, 'list<category>', 'A list of subcategories.'),
|
23
|
-
]
|
24
|
-
end
|
11
|
+
schema_path StortingImporter.lib.join("hdo/storting_importer/schema/category.json").to_s
|
25
12
|
|
26
13
|
def self.example
|
27
14
|
cat = new("5", "Employment")
|
@@ -30,8 +17,8 @@ module Hdo
|
|
30
17
|
cat
|
31
18
|
end
|
32
19
|
|
33
|
-
def self.
|
34
|
-
example
|
20
|
+
def self.json_example
|
21
|
+
Util.json_pretty example
|
35
22
|
end
|
36
23
|
|
37
24
|
#
|
@@ -61,32 +48,11 @@ module Hdo
|
|
61
48
|
cat
|
62
49
|
end
|
63
50
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
# @param [Nokogiri::XML::Element]
|
68
|
-
# @return [Array<Category>]
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
def self.from_hdo_doc(doc)
|
73
|
-
doc.css("categories > category").map { |node| from_hdo_node(node) }
|
74
|
-
end
|
75
|
-
|
76
|
-
#
|
77
|
-
# Deserialize from a HDO XML node
|
78
|
-
#
|
79
|
-
# @return [Category]
|
80
|
-
#
|
81
|
-
|
82
|
-
def self.from_hdo_node(node)
|
83
|
-
external_id = node.css("externalId").first.text
|
84
|
-
name = node.css("name").first.text
|
51
|
+
def self.from_hash(data)
|
52
|
+
obj = new data['externalId'], data['name']
|
53
|
+
obj.children = Array(data['subCategories']).map { |e| from_hash(e) }
|
85
54
|
|
86
|
-
|
87
|
-
cat.children = node.css("subcategories category").map { |e| from_hdo_node(e) }
|
88
|
-
|
89
|
-
cat
|
55
|
+
obj
|
90
56
|
end
|
91
57
|
|
92
58
|
def initialize(external_id, name)
|
@@ -99,23 +65,17 @@ module Hdo
|
|
99
65
|
short_inspect_string :include => [:external_id, :name]
|
100
66
|
end
|
101
67
|
|
102
|
-
|
103
|
-
|
104
|
-
|
68
|
+
def to_hash
|
69
|
+
h = {
|
70
|
+
:kind => self.class.kind,
|
71
|
+
:externalId => @external_id,
|
72
|
+
:name => @name
|
73
|
+
}
|
105
74
|
|
106
|
-
|
107
|
-
builder.category do |cat|
|
108
|
-
cat.externalId external_id
|
109
|
-
cat.name name
|
75
|
+
h[:subCategories] = @children.map { |e| e.to_hash } if @children.any?
|
110
76
|
|
111
|
-
|
112
|
-
cat.subcategories do |sub|
|
113
|
-
children.each { |child| child.to_hdo_xml(sub) }
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
77
|
+
h
|
117
78
|
end
|
118
|
-
|
119
79
|
end
|
120
80
|
end
|
121
81
|
end
|
@@ -44,21 +44,14 @@ module Hdo
|
|
44
44
|
klass.from_storting_doc(doc)
|
45
45
|
end.flatten
|
46
46
|
|
47
|
-
|
48
|
-
xml.instruct!
|
49
|
-
xml.__send__(plural) do |builder|
|
50
|
-
objs.each { |e| e.to_hdo_xml(builder) }
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
str
|
47
|
+
Util.json_pretty objs
|
55
48
|
end
|
56
49
|
|
57
50
|
def parse(args)
|
58
51
|
options = {}
|
59
52
|
|
60
53
|
parser = OptionParser.new do |opt|
|
61
|
-
types = TYPE_TO_CLASS.keys + [:dld_issues, :dld_votes, :promises]
|
54
|
+
types = TYPE_TO_CLASS.keys + [:dld_issues, :dld_votes, :promises, :any]
|
62
55
|
opt.banner = "Usage: #{$0} <#{types.join '|'}> <file(s)>"
|
63
56
|
opt.on("--help", "You're looking at it.") { puts opt; exit; }
|
64
57
|
end
|
@@ -76,31 +69,15 @@ module Hdo
|
|
76
69
|
end
|
77
70
|
|
78
71
|
def read_dld_issues
|
79
|
-
|
80
|
-
issues = Issue.from_hdo_doc doc
|
81
|
-
|
82
|
-
Util.builder do |xml|
|
83
|
-
xml.instruct!
|
84
|
-
xml.issues do |issues_builder|
|
85
|
-
issues.each { |i| i.to_hdo_xml(issues_builder) }
|
86
|
-
end
|
87
|
-
end
|
72
|
+
Util.json_pretty Issue.from_json(StortingImporter.root.join('data/dld-issues.json').read)
|
88
73
|
end
|
89
74
|
|
90
75
|
def read_dld_votes
|
91
|
-
|
92
|
-
votes = Vote.from_hdo_doc doc
|
93
|
-
|
94
|
-
Util.builder do |xml|
|
95
|
-
xml.instruct!
|
96
|
-
xml.votes do |votes_builder|
|
97
|
-
votes.each { |v| v.to_hdo_xml(votes_builder) }
|
98
|
-
end
|
99
|
-
end
|
76
|
+
Util.json_pretty Vote.from_json StortingImporter.root.join('data/dld-votes.json').read
|
100
77
|
end
|
101
78
|
|
102
79
|
def read_promises
|
103
|
-
csvs = @files.any? ? @files : Dir[
|
80
|
+
csvs = @files.any? ? @files : Dir[StortingImporter.root.join('data/promises-*.csv').to_s].sort_by { |e| File.basename(e) }
|
104
81
|
content = ''
|
105
82
|
csvs.each do |csv|
|
106
83
|
if csv =~ /^http/
|
@@ -110,12 +87,7 @@ module Hdo
|
|
110
87
|
end
|
111
88
|
end
|
112
89
|
|
113
|
-
Util.
|
114
|
-
xml.instruct!
|
115
|
-
xml.promises do |promises|
|
116
|
-
Promise.from_csv(content).each { |e| e.to_hdo_xml(promises) }
|
117
|
-
end
|
118
|
-
end
|
90
|
+
Util.json_pretty Promise.from_csv(content)
|
119
91
|
end
|
120
92
|
|
121
93
|
end
|
@@ -1,29 +1,20 @@
|
|
1
1
|
module Hdo
|
2
2
|
module StortingImporter
|
3
3
|
class Committee
|
4
|
+
include HasJsonSchema
|
4
5
|
include IvarEquality
|
5
6
|
|
6
7
|
attr_reader :external_id, :name
|
7
8
|
alias_method :short_inspect, :inspect
|
8
9
|
|
9
|
-
|
10
|
-
'committee'
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.description
|
14
|
-
'a parliamentary committe'
|
15
|
-
end
|
10
|
+
schema_path StortingImporter.lib.join("hdo/storting_importer/schema/committee.json").to_s
|
16
11
|
|
17
12
|
def self.example
|
18
13
|
new "ARBSOS", "Arbeids- og sosialkomiteen"
|
19
14
|
end
|
20
15
|
|
21
|
-
def self.
|
22
|
-
example
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.fields
|
26
|
-
[EXTERNAL_ID_FIELD, Field.new(:name, true, :string, 'The name of the committee.')]
|
16
|
+
def self.json_example
|
17
|
+
Util.json_pretty example
|
27
18
|
end
|
28
19
|
|
29
20
|
def self.from_storting_doc(doc)
|
@@ -36,12 +27,8 @@ module Hdo
|
|
36
27
|
new node.css("id").first.text, node.css("navn").first.text
|
37
28
|
end
|
38
29
|
|
39
|
-
def self.
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.from_hdo_node(node)
|
44
|
-
new node.css("externalId").first.text, node.css("name").first.text
|
30
|
+
def self.from_hash(hash)
|
31
|
+
new hash['externalId'], hash['name']
|
45
32
|
end
|
46
33
|
|
47
34
|
def initialize(external_id, name)
|
@@ -49,12 +36,14 @@ module Hdo
|
|
49
36
|
@name = name
|
50
37
|
end
|
51
38
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
39
|
+
def to_hash
|
40
|
+
{
|
41
|
+
:kind => self.class.kind,
|
42
|
+
:externalId => @external_id,
|
43
|
+
:name => @name
|
44
|
+
}
|
57
45
|
end
|
46
|
+
|
58
47
|
end
|
59
48
|
end
|
60
49
|
end
|
@@ -7,15 +7,10 @@ module Hdo
|
|
7
7
|
@cache = {}
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
data_for(name).each do |obj|
|
15
|
-
obj.to_hdo_xml(builder)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
10
|
+
def json_for(name, opts = nil)
|
11
|
+
obj = data_for(name)
|
12
|
+
|
13
|
+
Yajl::Encoder.encode(obj, opts && opts[:pretty])
|
19
14
|
end
|
20
15
|
|
21
16
|
private
|
@@ -1,29 +1,20 @@
|
|
1
1
|
module Hdo
|
2
2
|
module StortingImporter
|
3
3
|
class District
|
4
|
+
include HasJsonSchema
|
4
5
|
include IvarEquality
|
5
6
|
|
6
7
|
attr_reader :external_id, :name
|
7
8
|
alias_method :short_inspect, :inspect
|
8
9
|
|
9
|
-
|
10
|
-
'district'
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.description
|
14
|
-
'an electoral district'
|
15
|
-
end
|
10
|
+
schema_path StortingImporter.lib.join("hdo/storting_importer/schema/district.json").to_s
|
16
11
|
|
17
12
|
def self.example
|
18
13
|
new("Db", "Duckburg")
|
19
14
|
end
|
20
15
|
|
21
|
-
def self.
|
22
|
-
example
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.fields
|
26
|
-
[EXTERNAL_ID_FIELD, Field.new(:name, true, :string, 'The name of the electoral district.')]
|
16
|
+
def self.json_example
|
17
|
+
Util.json_pretty example
|
27
18
|
end
|
28
19
|
|
29
20
|
def self.from_storting_doc(doc)
|
@@ -36,12 +27,8 @@ module Hdo
|
|
36
27
|
new node.css("id").first.text, node.css("navn").first.text
|
37
28
|
end
|
38
29
|
|
39
|
-
def self.
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.from_hdo_node(node)
|
44
|
-
new node.css("externalId").first.text, node.css("name").first.text
|
30
|
+
def self.from_hash(hash)
|
31
|
+
new hash.fetch('externalId'), hash.fetch('name')
|
45
32
|
end
|
46
33
|
|
47
34
|
def initialize(external_id, name)
|
@@ -49,11 +36,12 @@ module Hdo
|
|
49
36
|
@name = name
|
50
37
|
end
|
51
38
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
39
|
+
def to_hash
|
40
|
+
{
|
41
|
+
:kind => self.class.kind,
|
42
|
+
:externalId => @external_id,
|
43
|
+
:name => @name
|
44
|
+
}
|
57
45
|
end
|
58
46
|
|
59
47
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Hdo
|
2
|
+
module StortingImporter
|
3
|
+
#
|
4
|
+
# Simple query public Fusion Tables (onle need API key for auth).
|
5
|
+
# If we need to write to the table programatically, see https://gist.github.com/3265043.
|
6
|
+
#
|
7
|
+
|
8
|
+
class FusionTable
|
9
|
+
def initialize(api_key)
|
10
|
+
@api_key = api_key
|
11
|
+
end
|
12
|
+
|
13
|
+
def query(sql, opts = {})
|
14
|
+
resp = fix_request_failure do
|
15
|
+
RestClient.get("https://www.googleapis.com/fusiontables/v1/query", :params => {:sql => sql, :key => @api_key})
|
16
|
+
end
|
17
|
+
|
18
|
+
data = MultiJson.decode(resp)
|
19
|
+
|
20
|
+
if opts[:rows]
|
21
|
+
data.fetch('rows')
|
22
|
+
else
|
23
|
+
cols = data.fetch('columns')
|
24
|
+
rows = data.fetch('rows')
|
25
|
+
|
26
|
+
rows.map do |row|
|
27
|
+
res = {}
|
28
|
+
cols.each_with_index { |col, idx| res[col] = row[idx] }
|
29
|
+
|
30
|
+
res
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def columns_for(table_id)
|
36
|
+
resp = fix_request_failure do
|
37
|
+
RestClient.get("https://www.googleapis.com/fusiontables/v1/tables/#{table_id}/columns", :params => {:key => @api_key})
|
38
|
+
end
|
39
|
+
|
40
|
+
data = MultiJson.decode(resp)
|
41
|
+
data['items']
|
42
|
+
end
|
43
|
+
|
44
|
+
def fix_request_failure(&blk)
|
45
|
+
yield
|
46
|
+
rescue RestClient::RequestFailed => ex
|
47
|
+
raise unless ex.respond_to?(:http_body)
|
48
|
+
raise "#{ex.message}: #{ex.http_body}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Hdo
|
2
|
+
module StortingImporter
|
3
|
+
class ValidationError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
#
|
7
|
+
# Includer must define these methods:
|
8
|
+
#
|
9
|
+
# .schema_path
|
10
|
+
# #from_hash(hash)
|
11
|
+
# #to_hash
|
12
|
+
#
|
13
|
+
|
14
|
+
module HasJsonSchema
|
15
|
+
def self.schemas
|
16
|
+
@schemas ||= []
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.included(base)
|
20
|
+
base.extend ClassMethods
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_json(*args)
|
24
|
+
to_hash.to_json(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def as_json(*args)
|
28
|
+
to_hash
|
29
|
+
end
|
30
|
+
|
31
|
+
module ClassMethods
|
32
|
+
attr_reader :schema
|
33
|
+
|
34
|
+
def schema_path(path)
|
35
|
+
@schema = MultiJson.decode(open(path).read)
|
36
|
+
HasJsonSchema.schemas << @schema
|
37
|
+
end
|
38
|
+
|
39
|
+
def schema
|
40
|
+
@schema or raise "schema must be set with #{self}.schema_path"
|
41
|
+
end
|
42
|
+
|
43
|
+
def kind
|
44
|
+
@kind ||= schema['properties']['kind']['default']
|
45
|
+
end
|
46
|
+
|
47
|
+
def description
|
48
|
+
@description ||= schema['description']
|
49
|
+
end
|
50
|
+
|
51
|
+
def properties
|
52
|
+
@properties ||= (
|
53
|
+
schema['properties'].map do |name, data|
|
54
|
+
Field.new(name, !!data['required'], data['type'], data['description'] || 'unknown')
|
55
|
+
end
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
# TODO: remove #fields usage
|
60
|
+
alias_method :fields, :properties
|
61
|
+
|
62
|
+
def from_json(str)
|
63
|
+
data = MultiJson.decode(str)
|
64
|
+
|
65
|
+
case data
|
66
|
+
when Array
|
67
|
+
data.map { |e| from_hash validate!(e) }
|
68
|
+
when Hash
|
69
|
+
from_hash validate!(data)
|
70
|
+
else
|
71
|
+
raise TypeError, "expected Array or Hash, got #{data.inspect}:#{data.class}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def validate!(e)
|
76
|
+
Jschematic.validate!(e, schema, :context => HasJsonSchema.schemas, :debug => true)
|
77
|
+
e
|
78
|
+
rescue Jschematic::ValidationError => ex
|
79
|
+
raise ValidationError, "#{ex.message}: #{e.inspect}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|