hdo-storting-importer 0.0.8 → 0.0.9
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.
- 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
|