yaml-ld 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/AUTHORS +1 -0
- data/README.md +150 -0
- data/UNLICENSE +24 -0
- data/VERSION +1 -0
- data/lib/yaml_ld/api.rb +295 -0
- data/lib/yaml_ld/format.rb +56 -0
- data/lib/yaml_ld/reader.rb +40 -0
- data/lib/yaml_ld/version.rb +20 -0
- data/lib/yaml_ld/writer.rb +42 -0
- data/lib/yaml_ld.rb +37 -0
- data/spec/api_spec.rb +92 -0
- data/spec/compact_spec.rb +358 -0
- data/spec/expand_spec.rb +565 -0
- data/spec/flatten_spec.rb +225 -0
- data/spec/format_spec.rb +54 -0
- data/spec/frame_spec.rb +662 -0
- data/spec/from_rdf_spec.rb +730 -0
- data/spec/matchers.rb +22 -0
- data/spec/reader_spec.rb +138 -0
- data/spec/spec_helper.rb +265 -0
- data/spec/support/extensions.rb +44 -0
- data/spec/test-files/test-1-compacted.jsonld +10 -0
- data/spec/test-files/test-1-context.jsonld +7 -0
- data/spec/test-files/test-1-expanded.jsonld +5 -0
- data/spec/test-files/test-1-input.yamlld +8 -0
- data/spec/test-files/test-1-rdf.ttl +8 -0
- data/spec/test-files/test-2-compacted.jsonld +20 -0
- data/spec/test-files/test-2-context.jsonld +7 -0
- data/spec/test-files/test-2-expanded.jsonld +16 -0
- data/spec/test-files/test-2-input.yamlld +16 -0
- data/spec/test-files/test-2-rdf.ttl +14 -0
- data/spec/test-files/test-3-compacted.jsonld +11 -0
- data/spec/test-files/test-3-context.jsonld +8 -0
- data/spec/test-files/test-3-expanded.jsonld +10 -0
- data/spec/test-files/test-3-input.yamlld +13 -0
- data/spec/test-files/test-3-rdf.ttl +8 -0
- data/spec/test-files/test-4-compacted.jsonld +10 -0
- data/spec/test-files/test-4-context.jsonld +7 -0
- data/spec/test-files/test-4-expanded.jsonld +6 -0
- data/spec/test-files/test-4-input.yamlld +9 -0
- data/spec/test-files/test-4-rdf.ttl +5 -0
- data/spec/test-files/test-5-compacted.jsonld +13 -0
- data/spec/test-files/test-5-context.jsonld +7 -0
- data/spec/test-files/test-5-expanded.jsonld +9 -0
- data/spec/test-files/test-5-input.yamlld +10 -0
- data/spec/test-files/test-5-rdf.ttl +7 -0
- data/spec/test-files/test-6-compacted.jsonld +10 -0
- data/spec/test-files/test-6-context.jsonld +7 -0
- data/spec/test-files/test-6-expanded.jsonld +10 -0
- data/spec/test-files/test-6-input.yamlld +12 -0
- data/spec/test-files/test-6-rdf.ttl +6 -0
- data/spec/test-files/test-7-compacted.jsonld +23 -0
- data/spec/test-files/test-7-context.jsonld +4 -0
- data/spec/test-files/test-7-expanded.jsonld +20 -0
- data/spec/test-files/test-7-input.yamlld +16 -0
- data/spec/test-files/test-7-rdf.ttl +14 -0
- data/spec/test-files/test-8-compacted.jsonld +34 -0
- data/spec/test-files/test-8-context.jsonld +11 -0
- data/spec/test-files/test-8-expanded.jsonld +24 -0
- data/spec/test-files/test-8-frame.jsonld +18 -0
- data/spec/test-files/test-8-framed.jsonld +25 -0
- data/spec/test-files/test-8-input.yamlld +24 -0
- data/spec/test-files/test-8-rdf.ttl +15 -0
- data/spec/test-files/test-9-compacted.jsonld +20 -0
- data/spec/test-files/test-9-context.jsonld +13 -0
- data/spec/test-files/test-9-expanded.jsonld +14 -0
- data/spec/test-files/test-9-input.yamlld +16 -0
- data/spec/to_rdf_spec.rb +556 -0
- data/spec/writer_spec.rb +441 -0
- metadata +350 -0
data/spec/matchers.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rspec/matchers' # @see https://rubygems.org/gems/rspec
|
2
|
+
require_relative 'support/extensions'
|
3
|
+
|
4
|
+
RSpec::Matchers.define :produce_yamlld do |expected, logger|
|
5
|
+
match do |actual|
|
6
|
+
actual = Psych.load(actual, aliases: true) if actual.is_a?(String)
|
7
|
+
expected = Psych.load(expected, aliases: true) if expected.is_a?(String)
|
8
|
+
expect(actual).to be_equivalent_structure expected
|
9
|
+
end
|
10
|
+
|
11
|
+
failure_message do |actual|
|
12
|
+
"Expected: #{expected.is_a?(String) ? expected : expected.to_yaml rescue 'malformed structure'}\n" +
|
13
|
+
"Actual : #{actual.is_a?(String) ? actual : actual.to_yaml rescue 'malformed structure'}\n" +
|
14
|
+
"\nDebug:\n#{logger}"
|
15
|
+
end
|
16
|
+
|
17
|
+
failure_message_when_negated do |actual|
|
18
|
+
"Expected not to produce the following:\n" +
|
19
|
+
"Actual : #{actual.is_a?(String) ? actual : actual.to_yaml rescue 'malformed structure'}\n" +
|
20
|
+
"\nDebug:\n#{logger}"
|
21
|
+
end
|
22
|
+
end
|
data/spec/reader_spec.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require_relative 'spec_helper'
|
3
|
+
require 'rdf/spec/reader'
|
4
|
+
|
5
|
+
describe YAML_LD::Reader do
|
6
|
+
let!(:doap) {File.expand_path("../../etc/doap.yamlld", __FILE__)}
|
7
|
+
let!(:doap_nt) {File.expand_path("../../etc/doap.nt", __FILE__)}
|
8
|
+
let!(:doap_count) {File.open(doap_nt).each_line.to_a.length}
|
9
|
+
let(:logger) {RDF::Spec.logger}
|
10
|
+
|
11
|
+
after(:each) {|example| puts logger.to_s if example.exception}
|
12
|
+
|
13
|
+
it_behaves_like 'an RDF::Reader' do
|
14
|
+
let(:reader_input) {File.read(doap)}
|
15
|
+
let(:reader) {YAML_LD::Reader.new(reader_input)}
|
16
|
+
let(:reader_count) {doap_count}
|
17
|
+
end
|
18
|
+
|
19
|
+
describe ".for" do
|
20
|
+
formats = [
|
21
|
+
:yamlld,
|
22
|
+
"etc/doap.yamlld",
|
23
|
+
{file_name: 'etc/doap.yamlld'},
|
24
|
+
{file_extension: 'yamlld'},
|
25
|
+
{content_type: 'application/ld+yaml'},
|
26
|
+
].each do |arg|
|
27
|
+
it "discovers with #{arg.inspect}" do
|
28
|
+
expect(RDF::Reader.for(arg)).to eq YAML_LD::Reader
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when validating" do
|
34
|
+
it "detects invalid YAML" do
|
35
|
+
yaml = %(
|
36
|
+
---
|
37
|
+
"@context":
|
38
|
+
dc: http://purl.org/dc/terms/
|
39
|
+
- foo
|
40
|
+
)
|
41
|
+
expect do |b|
|
42
|
+
described_class.new(StringIO.new(yaml), validate: true, logger: false).each_statement(&b)
|
43
|
+
end.to raise_error(RDF::ReaderError)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context :interface do
|
48
|
+
{
|
49
|
+
plain: %q(
|
50
|
+
"@context":
|
51
|
+
foaf:
|
52
|
+
http://xmlns.com/foaf/0.1/
|
53
|
+
"@id":
|
54
|
+
_:bnode1
|
55
|
+
"@type":
|
56
|
+
foaf:Person
|
57
|
+
"foaf:homepage":
|
58
|
+
http://example.com/bob/
|
59
|
+
"foaf:name":
|
60
|
+
Bob
|
61
|
+
}),
|
62
|
+
leading_comment: %q(---
|
63
|
+
# A comment before content
|
64
|
+
"@context":
|
65
|
+
foaf:
|
66
|
+
http://xmlns.com/foaf/0.1/
|
67
|
+
"@id":
|
68
|
+
_:bnode1
|
69
|
+
"@type":
|
70
|
+
foaf:Person
|
71
|
+
"foaf:homepage":
|
72
|
+
http://example.com/bob/
|
73
|
+
"foaf:name":
|
74
|
+
Bob
|
75
|
+
),
|
76
|
+
yaml_version: %(%YAML 1.2\n---
|
77
|
+
# A comment before content
|
78
|
+
"@context":
|
79
|
+
foaf:
|
80
|
+
http://xmlns.com/foaf/0.1/
|
81
|
+
"@id":
|
82
|
+
_:bnode1
|
83
|
+
"@type":
|
84
|
+
foaf:Person
|
85
|
+
"foaf:homepage":
|
86
|
+
http://example.com/bob/
|
87
|
+
"foaf:name":
|
88
|
+
Bob
|
89
|
+
),
|
90
|
+
}.each do |variant, src|
|
91
|
+
context variant do
|
92
|
+
subject {src}
|
93
|
+
|
94
|
+
describe "#initialize" do
|
95
|
+
it "yields reader given string" do
|
96
|
+
inner = double("inner")
|
97
|
+
expect(inner).to receive(:called).with(YAML_LD::Reader)
|
98
|
+
YAML_LD::Reader.new(subject) do |reader|
|
99
|
+
inner.called(reader.class)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it "yields reader given IO" do
|
104
|
+
inner = double("inner")
|
105
|
+
expect(inner).to receive(:called).with(YAML_LD::Reader)
|
106
|
+
YAML_LD::Reader.new(StringIO.new(subject)) do |reader|
|
107
|
+
inner.called(reader.class)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it "returns reader" do
|
112
|
+
expect(YAML_LD::Reader.new(subject)).to be_a(YAML_LD::Reader)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "#each_statement" do
|
117
|
+
it "yields statements" do
|
118
|
+
inner = double("inner")
|
119
|
+
expect(inner).to receive(:called).with(RDF::Statement).exactly(3)
|
120
|
+
YAML_LD::Reader.new(subject).each_statement do |statement|
|
121
|
+
inner.called(statement.class)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "#each_triple" do
|
127
|
+
it "yields statements" do
|
128
|
+
inner = double("inner")
|
129
|
+
expect(inner).to receive(:called).exactly(3)
|
130
|
+
YAML_LD::Reader.new(subject).each_triple do |subject, predicate, object|
|
131
|
+
inner.called(subject.class, predicate.class, object.class)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,265 @@
|
|
1
|
+
$:.unshift(File.join("../../lib", __FILE__))
|
2
|
+
$:.unshift File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require 'rspec'
|
6
|
+
require 'rdf'
|
7
|
+
require 'rdf/isomorphic'
|
8
|
+
require 'rdf/nquads'
|
9
|
+
require 'rdf/turtle'
|
10
|
+
require 'rdf/trig'
|
11
|
+
require 'rdf/vocab'
|
12
|
+
require 'rdf/spec'
|
13
|
+
require 'rdf/spec/matchers'
|
14
|
+
require_relative 'matchers'
|
15
|
+
require 'yaml'
|
16
|
+
begin
|
17
|
+
require 'simplecov'
|
18
|
+
require 'simplecov-lcov'
|
19
|
+
SimpleCov::Formatter::LcovFormatter.config do |config|
|
20
|
+
#Coveralls is coverage by default/lcov. Send info results
|
21
|
+
config.report_with_single_file = true
|
22
|
+
config.single_report_path = 'coverage/lcov.info'
|
23
|
+
end
|
24
|
+
|
25
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
|
26
|
+
SimpleCov::Formatter::HTMLFormatter,
|
27
|
+
SimpleCov::Formatter::LcovFormatter
|
28
|
+
])
|
29
|
+
SimpleCov.start do
|
30
|
+
add_filter "/spec/"
|
31
|
+
end
|
32
|
+
rescue LoadError
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'yaml_ld'
|
36
|
+
|
37
|
+
# Left-justifies a block string
|
38
|
+
class ::String
|
39
|
+
def unindent
|
40
|
+
self.gsub(/^#{self.scan(/^[ \t]+(?=\S)/).min}/, '')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Heuristically detect the input stream
|
45
|
+
def detect_format(stream)
|
46
|
+
# Got to look into the file to see
|
47
|
+
if stream.respond_to?(:rewind) && stream.respond_to?(:read)
|
48
|
+
stream.rewind
|
49
|
+
string = stream.read(1000)
|
50
|
+
stream.rewind
|
51
|
+
else
|
52
|
+
string = stream.to_s
|
53
|
+
end
|
54
|
+
case string
|
55
|
+
when /<html/i then RDF::RDFa::Reader
|
56
|
+
when /\{\s*\"@\"/i then JSON::LD::Reader
|
57
|
+
else RDF::Turtle::Reader
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Creates a bijection between the two objects and replaces nodes in actual from expected.
|
62
|
+
def remap_bnodes(actual, expected)
|
63
|
+
# Transform each to RDF and perform a blank node bijection.
|
64
|
+
# Replace the blank nodes in action with the mapping from bijection.
|
65
|
+
ds_actual = RDF::Repository.new << JSON::LD::API.toRdf(actual, rdfstar: true, rename_bnodes: false)
|
66
|
+
ds_expected = RDF::Repository.new << JSON::LD::API.toRdf(expected, rdfstar: true, rename_bnodes: false)
|
67
|
+
if bijection = ds_actual.bijection_to(ds_expected)
|
68
|
+
bijection = bijection.inject({}) {|memo, (k, v)| memo.merge(k.to_s => v.to_s)}
|
69
|
+
|
70
|
+
# Recursively replace blank nodes in actual with the bijection
|
71
|
+
replace_nodes(actual, bijection)
|
72
|
+
else
|
73
|
+
actual
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def replace_nodes(object, bijection)
|
78
|
+
case object
|
79
|
+
when Array
|
80
|
+
object.map {|o| replace_nodes(o, bijection)}
|
81
|
+
when Hash
|
82
|
+
object.inject({}) do |memo, (k, v)|
|
83
|
+
memo.merge(bijection.fetch(k, k) => replace_nodes(v, bijection))
|
84
|
+
end
|
85
|
+
when String
|
86
|
+
bijection.fetch(object, object)
|
87
|
+
else
|
88
|
+
object
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
LIBRARY_INPUT = JSON.parse(%([
|
93
|
+
{
|
94
|
+
"@id": "http://example.org/library",
|
95
|
+
"@type": "http://example.org/vocab#Library",
|
96
|
+
"http://example.org/vocab#contains": {"@id": "http://example.org/library/the-republic"}
|
97
|
+
}, {
|
98
|
+
"@id": "http://example.org/library/the-republic",
|
99
|
+
"@type": "http://example.org/vocab#Book",
|
100
|
+
"http://purl.org/dc/elements/1.1/creator": "Plato",
|
101
|
+
"http://purl.org/dc/elements/1.1/title": "The Republic",
|
102
|
+
"http://example.org/vocab#contains": {
|
103
|
+
"@id": "http://example.org/library/the-republic#introduction",
|
104
|
+
"@type": "http://example.org/vocab#Chapter",
|
105
|
+
"http://purl.org/dc/elements/1.1/description": "An introductory chapter on The Republic.",
|
106
|
+
"http://purl.org/dc/elements/1.1/title": "The Introduction"
|
107
|
+
}
|
108
|
+
}
|
109
|
+
]))
|
110
|
+
|
111
|
+
LIBRARY_EXPANDED = JSON.parse(%([
|
112
|
+
{
|
113
|
+
"@id": "http://example.org/library",
|
114
|
+
"@type": ["http://example.org/vocab#Library"],
|
115
|
+
"http://example.org/vocab#contains": [{"@id": "http://example.org/library/the-republic"}]
|
116
|
+
}, {
|
117
|
+
"@id": "http://example.org/library/the-republic",
|
118
|
+
"@type": ["http://example.org/vocab#Book"],
|
119
|
+
"http://purl.org/dc/elements/1.1/creator": [{"@value": "Plato"}],
|
120
|
+
"http://purl.org/dc/elements/1.1/title": [{"@value": "The Republic"}],
|
121
|
+
"http://example.org/vocab#contains": [{
|
122
|
+
"@id": "http://example.org/library/the-republic#introduction",
|
123
|
+
"@type": ["http://example.org/vocab#Chapter"],
|
124
|
+
"http://purl.org/dc/elements/1.1/description": [{"@value": "An introductory chapter on The Republic."}],
|
125
|
+
"http://purl.org/dc/elements/1.1/title": [{"@value": "The Introduction"}]
|
126
|
+
}]
|
127
|
+
}
|
128
|
+
]))
|
129
|
+
|
130
|
+
LIBRARY_COMPACTED_DEFAULT = JSON.parse(%({
|
131
|
+
"@context": "http://schema.org",
|
132
|
+
"@graph": [
|
133
|
+
{
|
134
|
+
"id": "http://example.org/library",
|
135
|
+
"type": "http://example.org/vocab#Library",
|
136
|
+
"http://example.org/vocab#contains": {"id": "http://example.org/library/the-republic"}
|
137
|
+
}, {
|
138
|
+
"id": "http://example.org/library/the-republic",
|
139
|
+
"type": "http://example.org/vocab#Book",
|
140
|
+
"http://purl.org/dc/elements/1.1/creator": "Plato",
|
141
|
+
"http://purl.org/dc/elements/1.1/title": "The Republic",
|
142
|
+
"http://example.org/vocab#contains": {
|
143
|
+
"id": "http://example.org/library/the-republic#introduction",
|
144
|
+
"type": "http://example.org/vocab#Chapter",
|
145
|
+
"http://purl.org/dc/elements/1.1/description": "An introductory chapter on The Republic.",
|
146
|
+
"http://purl.org/dc/elements/1.1/title": "The Introduction"
|
147
|
+
}
|
148
|
+
}
|
149
|
+
]
|
150
|
+
}))
|
151
|
+
|
152
|
+
LIBRARY_COMPACTED = JSON.parse(%({
|
153
|
+
"@context": "http://conneg.example.com/context",
|
154
|
+
"@graph": [
|
155
|
+
{
|
156
|
+
"@id": "http://example.org/library",
|
157
|
+
"@type": "ex:Library",
|
158
|
+
"ex:contains": {
|
159
|
+
"@id": "http://example.org/library/the-republic"
|
160
|
+
}
|
161
|
+
},
|
162
|
+
{
|
163
|
+
"@id": "http://example.org/library/the-republic",
|
164
|
+
"@type": "ex:Book",
|
165
|
+
"dc:creator": "Plato",
|
166
|
+
"dc:title": "The Republic",
|
167
|
+
"ex:contains": {
|
168
|
+
"@id": "http://example.org/library/the-republic#introduction",
|
169
|
+
"@type": "ex:Chapter",
|
170
|
+
"dc:description": "An introductory chapter on The Republic.",
|
171
|
+
"dc:title": "The Introduction"
|
172
|
+
}
|
173
|
+
}
|
174
|
+
]
|
175
|
+
}))
|
176
|
+
|
177
|
+
LIBRARY_FLATTENED_EXPANDED = JSON.parse(%([
|
178
|
+
{
|
179
|
+
"@id": "http://example.org/library",
|
180
|
+
"@type": ["http://example.org/vocab#Library"],
|
181
|
+
"http://example.org/vocab#contains": [{"@id": "http://example.org/library/the-republic"}]
|
182
|
+
},
|
183
|
+
{
|
184
|
+
"@id": "http://example.org/library/the-republic",
|
185
|
+
"@type": ["http://example.org/vocab#Book"],
|
186
|
+
"http://purl.org/dc/elements/1.1/creator": [{"@value": "Plato"}],
|
187
|
+
"http://purl.org/dc/elements/1.1/title": [{"@value": "The Republic"}],
|
188
|
+
"http://example.org/vocab#contains": [{"@id": "http://example.org/library/the-republic#introduction"}]
|
189
|
+
},
|
190
|
+
{
|
191
|
+
"@id": "http://example.org/library/the-republic#introduction",
|
192
|
+
"@type": ["http://example.org/vocab#Chapter"],
|
193
|
+
"http://purl.org/dc/elements/1.1/description": [{"@value": "An introductory chapter on The Republic."}],
|
194
|
+
"http://purl.org/dc/elements/1.1/title": [{"@value": "The Introduction"}]
|
195
|
+
}
|
196
|
+
]))
|
197
|
+
|
198
|
+
LIBRARY_FLATTENED_COMPACTED_DEFAULT = JSON.parse(%({
|
199
|
+
"@context": "http://schema.org",
|
200
|
+
"@graph": [
|
201
|
+
{
|
202
|
+
"id": "http://example.org/library",
|
203
|
+
"type": "http://example.org/vocab#Library",
|
204
|
+
"http://example.org/vocab#contains": {"id": "http://example.org/library/the-republic"}
|
205
|
+
},
|
206
|
+
{
|
207
|
+
"id": "http://example.org/library/the-republic",
|
208
|
+
"type": "http://example.org/vocab#Book",
|
209
|
+
"http://purl.org/dc/elements/1.1/creator": "Plato",
|
210
|
+
"http://purl.org/dc/elements/1.1/title": "The Republic",
|
211
|
+
"http://example.org/vocab#contains": {"id": "http://example.org/library/the-republic#introduction"}
|
212
|
+
},
|
213
|
+
{
|
214
|
+
"id": "http://example.org/library/the-republic#introduction",
|
215
|
+
"type": "http://example.org/vocab#Chapter",
|
216
|
+
"http://purl.org/dc/elements/1.1/description": "An introductory chapter on The Republic.",
|
217
|
+
"http://purl.org/dc/elements/1.1/title": "The Introduction"
|
218
|
+
}
|
219
|
+
]
|
220
|
+
}))
|
221
|
+
|
222
|
+
LIBRARY_FLATTENED_COMPACTED = JSON.parse(%({
|
223
|
+
"@context": "http://conneg.example.com/context",
|
224
|
+
"@graph": [
|
225
|
+
{
|
226
|
+
"@id": "http://example.org/library",
|
227
|
+
"@type": "ex:Library",
|
228
|
+
"ex:contains": {"@id": "http://example.org/library/the-republic"}
|
229
|
+
},
|
230
|
+
{
|
231
|
+
"@id": "http://example.org/library/the-republic",
|
232
|
+
"@type": "ex:Book",
|
233
|
+
"dc:creator": "Plato",
|
234
|
+
"dc:title": "The Republic",
|
235
|
+
"ex:contains": {"@id": "http://example.org/library/the-republic#introduction"}
|
236
|
+
},
|
237
|
+
{
|
238
|
+
"@id": "http://example.org/library/the-republic#introduction",
|
239
|
+
"@type": "ex:Chapter",
|
240
|
+
"dc:description": "An introductory chapter on The Republic.",
|
241
|
+
"dc:title": "The Introduction"
|
242
|
+
}
|
243
|
+
]
|
244
|
+
}))
|
245
|
+
|
246
|
+
LIBRARY_FRAMED = JSON.parse(%({
|
247
|
+
"@context": {
|
248
|
+
"dc": "http://purl.org/dc/elements/1.1/",
|
249
|
+
"ex": "http://example.org/vocab#"
|
250
|
+
},
|
251
|
+
"@id": "http://example.org/library",
|
252
|
+
"@type": "ex:Library",
|
253
|
+
"ex:contains": {
|
254
|
+
"@id": "http://example.org/library/the-republic",
|
255
|
+
"@type": "ex:Book",
|
256
|
+
"dc:creator": "Plato",
|
257
|
+
"dc:title": "The Republic",
|
258
|
+
"ex:contains": {
|
259
|
+
"@id": "http://example.org/library/the-republic#introduction",
|
260
|
+
"@type": "ex:Chapter",
|
261
|
+
"dc:description": "An introductory chapter on The Republic.",
|
262
|
+
"dc:title": "The Introduction"
|
263
|
+
}
|
264
|
+
}
|
265
|
+
}))
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Object
|
2
|
+
def equivalent_structure?(other, ordered: false)
|
3
|
+
self == other
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class Hash
|
8
|
+
def equivalent_structure?(other, ordered: false)
|
9
|
+
return false unless other.is_a?(Hash) && other.length == length
|
10
|
+
all? do |key, value|
|
11
|
+
# List values are still ordered
|
12
|
+
if key == '@language' && value.is_a?(String)
|
13
|
+
value.downcase.equivalent_structure?(other[key].to_s.downcase, ordered: key == '@list')
|
14
|
+
else
|
15
|
+
value.equivalent_structure?(other[key], ordered: key == '@list')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def diff(other)
|
21
|
+
self.keys.inject({}) do |memo, key|
|
22
|
+
unless self[key] == other[key]
|
23
|
+
memo[key] = [self[key], other[key]]
|
24
|
+
end
|
25
|
+
memo
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Array
|
31
|
+
def equivalent_structure?(other, ordered: false)
|
32
|
+
return false unless other.is_a?(Array) && other.length == length
|
33
|
+
if ordered
|
34
|
+
b = other.dup
|
35
|
+
# All elements must match in order
|
36
|
+
all? {|av| av.equivalent_structure?(b.shift)}
|
37
|
+
else
|
38
|
+
# Look for any element which matches
|
39
|
+
all? do |av|
|
40
|
+
other.any? {|bv| av.equivalent_structure?(bv)}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
{
|
2
|
+
"@context": {
|
3
|
+
"avatar": "http://xmlns.com/foaf/0.1/avatar",
|
4
|
+
"homepage": "http://xmlns.com/foaf/0.1/homepage",
|
5
|
+
"name": "http://xmlns.com/foaf/0.1/name"
|
6
|
+
},
|
7
|
+
"avatar": "http://twitter.com/account/profile_image/manusporny",
|
8
|
+
"homepage": "http://manu.sporny.org/",
|
9
|
+
"name": "Manu Sporny"
|
10
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
@prefix avatar: <http://xmlns.com/foaf/0.1/avatar> .
|
2
|
+
@prefix homepage: <http://xmlns.com/foaf/0.1/homepage> .
|
3
|
+
@prefix name: <http://xmlns.com/foaf/0.1/name> .
|
4
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
5
|
+
|
6
|
+
[ avatar: "http://twitter.com/account/profile_image/manusporny";
|
7
|
+
homepage: "http://manu.sporny.org/";
|
8
|
+
name: "Manu Sporny"] .
|
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"@context": {
|
3
|
+
"dc": "http://purl.org/dc/elements/1.1/",
|
4
|
+
"ex": "http://example.org/vocab#"
|
5
|
+
},
|
6
|
+
"@id": "http://example.org/library",
|
7
|
+
"@type": "ex:Library",
|
8
|
+
"ex:contains": {
|
9
|
+
"@id": "http://example.org/library/the-republic",
|
10
|
+
"@type": "ex:Book",
|
11
|
+
"dc:creator": "Plato",
|
12
|
+
"dc:title": "The Republic",
|
13
|
+
"ex:contains": {
|
14
|
+
"@id": "http://example.org/library/the-republic#introduction",
|
15
|
+
"@type": "ex:Chapter",
|
16
|
+
"dc:description": "An introductory chapter on The Republic.",
|
17
|
+
"dc:title": "The Introduction"
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
[{
|
2
|
+
"@id": "http://example.org/library",
|
3
|
+
"@type": ["http://example.org/vocab#Library"],
|
4
|
+
"http://example.org/vocab#contains": [{
|
5
|
+
"@id": "http://example.org/library/the-republic",
|
6
|
+
"@type": ["http://example.org/vocab#Book"],
|
7
|
+
"http://purl.org/dc/elements/1.1/creator": [{"@value": "Plato"}],
|
8
|
+
"http://purl.org/dc/elements/1.1/title": [{"@value": "The Republic"}],
|
9
|
+
"http://example.org/vocab#contains": [{
|
10
|
+
"@id": "http://example.org/library/the-republic#introduction",
|
11
|
+
"@type": ["http://example.org/vocab#Chapter"],
|
12
|
+
"http://purl.org/dc/elements/1.1/description": [{"@value": "An introductory chapter on The Republic."}],
|
13
|
+
"http://purl.org/dc/elements/1.1/title": [{"@value": "The Introduction"}]
|
14
|
+
}]
|
15
|
+
}]
|
16
|
+
}]
|
@@ -0,0 +1,16 @@
|
|
1
|
+
---
|
2
|
+
"@context":
|
3
|
+
dc: http://purl.org/dc/elements/1.1/
|
4
|
+
ex: http://example.org/vocab#
|
5
|
+
"@id": http://example.org/library
|
6
|
+
"@type": ex:Library
|
7
|
+
ex:contains:
|
8
|
+
"@id": http://example.org/library/the-republic
|
9
|
+
"@type": ex:Book
|
10
|
+
dc:creator: Plato
|
11
|
+
dc:title: The Republic
|
12
|
+
ex:contains:
|
13
|
+
"@id": http://example.org/library/the-republic#introduction
|
14
|
+
"@type": ex:Chapter
|
15
|
+
dc:description: An introductory chapter on The Republic.
|
16
|
+
dc:title: The Introduction
|
@@ -0,0 +1,14 @@
|
|
1
|
+
@prefix dc: <http://purl.org/dc/elements/1.1/> .
|
2
|
+
@prefix ex: <http://example.org/vocab#> .
|
3
|
+
|
4
|
+
<http://example.org/library> a ex:Library;
|
5
|
+
ex:contains <http://example.org/library/the-republic> .
|
6
|
+
|
7
|
+
<http://example.org/library/the-republic> a ex:Book;
|
8
|
+
ex:contains <http://example.org/library/the-republic#introduction>;
|
9
|
+
dc:creator "Plato";
|
10
|
+
dc:title "The Republic" .
|
11
|
+
|
12
|
+
<http://example.org/library/the-republic#introduction> a ex:Chapter;
|
13
|
+
dc:description "An introductory chapter on The Republic.";
|
14
|
+
dc:title "The Introduction" .
|
@@ -0,0 +1,11 @@
|
|
1
|
+
{
|
2
|
+
"@context": {
|
3
|
+
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
4
|
+
"name": "http://xmlns.com/foaf/0.1/name",
|
5
|
+
"age": {"@id": "http://xmlns.com/foaf/0.1/age", "@type": "xsd:integer"},
|
6
|
+
"homepage": {"@id": "http://xmlns.com/foaf/0.1/homepage", "@type": "@id"}
|
7
|
+
},
|
8
|
+
"name": "Manu Sporny",
|
9
|
+
"age": "41",
|
10
|
+
"homepage": "http://manu.sporny.org/"
|
11
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
{
|
2
|
+
"@context": {
|
3
|
+
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
4
|
+
"name": "http://xmlns.com/foaf/0.1/name",
|
5
|
+
"age": {"@id": "http://xmlns.com/foaf/0.1/age", "@type": "xsd:integer"},
|
6
|
+
"homepage": {"@id": "http://xmlns.com/foaf/0.1/homepage", "@type": "@id"}
|
7
|
+
}
|
8
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
[{
|
2
|
+
"http://xmlns.com/foaf/0.1/name": [{"@value": "Manu Sporny"}],
|
3
|
+
"http://xmlns.com/foaf/0.1/age": [{
|
4
|
+
"@type": "http://www.w3.org/2001/XMLSchema#integer",
|
5
|
+
"@value": "41"
|
6
|
+
}],
|
7
|
+
"http://xmlns.com/foaf/0.1/homepage": [{
|
8
|
+
"@id": "http://manu.sporny.org/"
|
9
|
+
}]
|
10
|
+
}]
|
@@ -0,0 +1,13 @@
|
|
1
|
+
---
|
2
|
+
"@context":
|
3
|
+
xsd: http://www.w3.org/2001/XMLSchema#
|
4
|
+
name: http://xmlns.com/foaf/0.1/name
|
5
|
+
age:
|
6
|
+
"@id": http://xmlns.com/foaf/0.1/age
|
7
|
+
"@type": xsd:integer
|
8
|
+
homepage:
|
9
|
+
"@id": http://xmlns.com/foaf/0.1/homepage
|
10
|
+
"@type": "@id"
|
11
|
+
name: Manu Sporny
|
12
|
+
age: '41'
|
13
|
+
homepage: http://manu.sporny.org/
|
@@ -0,0 +1,8 @@
|
|
1
|
+
@prefix age: <http://xmlns.com/foaf/0.1/age> .
|
2
|
+
@prefix homepage: <http://xmlns.com/foaf/0.1/homepage> .
|
3
|
+
@prefix name: <http://xmlns.com/foaf/0.1/name> .
|
4
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
5
|
+
|
6
|
+
[ age: 41;
|
7
|
+
homepage: <http://manu.sporny.org/>;
|
8
|
+
name: "Manu Sporny"] .
|