json-ld 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/History.markdown +3 -0
- data/VERSION +1 -1
- data/bin/jsonld +134 -0
- data/lib/json/ld/api.rb +4 -4
- data/lib/json/ld/frame.rb +2 -1
- data/lib/json/ld/from_rdf.rb +1 -1
- data/spec/api_spec.rb +66 -0
- data/spec/compact_spec.rb +420 -0
- data/spec/evaluation_context_spec.rb +1039 -0
- data/spec/expand_spec.rb +625 -0
- data/spec/format_spec.rb +71 -0
- data/spec/frame_spec.rb +542 -0
- data/spec/from_rdf_spec.rb +316 -0
- data/spec/matchers.rb +67 -0
- data/spec/reader_spec.rb +79 -0
- data/spec/spec_helper.rb +56 -0
- data/spec/suite_helper.rb +184 -0
- data/spec/suite_spec.rb +104 -0
- data/spec/support/extensions.rb +10 -0
- data/spec/test-files/test-1-automatic.json +10 -0
- data/spec/test-files/test-1-compacted.json +10 -0
- data/spec/test-files/test-1-context.json +7 -0
- data/spec/test-files/test-1-expanded.json +5 -0
- data/spec/test-files/test-1-input.json +10 -0
- data/spec/test-files/test-1-normalized.json +8 -0
- data/spec/test-files/test-1-rdf.ttl +7 -0
- data/spec/test-files/test-2-automatic.json +27 -0
- data/spec/test-files/test-2-compacted.json +20 -0
- data/spec/test-files/test-2-context.json +7 -0
- data/spec/test-files/test-2-expanded.json +16 -0
- data/spec/test-files/test-2-input.json +20 -0
- data/spec/test-files/test-2-normalized.json +32 -0
- data/spec/test-files/test-2-rdf.ttl +14 -0
- data/spec/test-files/test-3-compacted.json +11 -0
- data/spec/test-files/test-3-context.json +8 -0
- data/spec/test-files/test-3-expanded.json +10 -0
- data/spec/test-files/test-3-input.json +11 -0
- data/spec/test-files/test-3-normalized.json +13 -0
- data/spec/test-files/test-3-rdf.ttl +7 -0
- data/spec/test-files/test-4-automatic.json +10 -0
- data/spec/test-files/test-4-compacted.json +10 -0
- data/spec/test-files/test-4-context.json +7 -0
- data/spec/test-files/test-4-expanded.json +6 -0
- data/spec/test-files/test-4-input.json +10 -0
- data/spec/test-files/test-4-rdf.ttl +5 -0
- data/spec/test-files/test-5-automatic.json +13 -0
- data/spec/test-files/test-5-compacted.json +13 -0
- data/spec/test-files/test-5-context.json +7 -0
- data/spec/test-files/test-5-expanded.json +9 -0
- data/spec/test-files/test-5-input.json +13 -0
- data/spec/test-files/test-5-rdf.ttl +6 -0
- data/spec/test-files/test-6-automatic.json +10 -0
- data/spec/test-files/test-6-compacted.json +10 -0
- data/spec/test-files/test-6-context.json +7 -0
- data/spec/test-files/test-6-expanded.json +10 -0
- data/spec/test-files/test-6-input.json +10 -0
- data/spec/test-files/test-6-rdf.ttl +5 -0
- data/spec/test-files/test-7-automatic.json +20 -0
- data/spec/test-files/test-7-compacted.json +23 -0
- data/spec/test-files/test-7-context.json +4 -0
- data/spec/test-files/test-7-expanded.json +20 -0
- data/spec/test-files/test-7-input.json +23 -0
- data/spec/test-files/test-7-rdf.ttl +13 -0
- data/spec/test-files/test-8-automatic.json +1 -0
- data/spec/test-files/test-8-compacted.json +34 -0
- data/spec/test-files/test-8-context.json +11 -0
- data/spec/test-files/test-8-expanded.json +24 -0
- data/spec/test-files/test-8-frame.json +18 -0
- data/spec/test-files/test-8-framed.json +29 -0
- data/spec/test-files/test-8-input.json +30 -0
- data/spec/test-files/test-8-rdf.ttl +15 -0
- data/spec/test-files/test-9-compacted.json +20 -0
- data/spec/test-files/test-9-context.json +13 -0
- data/spec/test-files/test-9-expanded.json +14 -0
- data/spec/test-files/test-9-input.json +12 -0
- data/spec/to_rdf_spec.rb +640 -0
- data/spec/writer_spec.rb +161 -0
- metadata +150 -22
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$:.unshift File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require 'rspec'
|
6
|
+
require 'rdf'
|
7
|
+
require 'rdf/isomorphic'
|
8
|
+
require 'json/ld'
|
9
|
+
require 'rdf/nquads'
|
10
|
+
require 'rdf/turtle'
|
11
|
+
require 'rdf/trig'
|
12
|
+
require 'rdf/spec'
|
13
|
+
require 'rdf/spec/matchers'
|
14
|
+
require 'yaml'
|
15
|
+
require 'open-uri/cached'
|
16
|
+
require 'matchers'
|
17
|
+
|
18
|
+
JSON_STATE = JSON::State.new(
|
19
|
+
:indent => " ",
|
20
|
+
:space => " ",
|
21
|
+
:space_before => "",
|
22
|
+
:object_nl => "\n",
|
23
|
+
:array_nl => "\n"
|
24
|
+
)
|
25
|
+
|
26
|
+
# Create and maintain a cache of downloaded URIs
|
27
|
+
URI_CACHE = File.expand_path(File.join(File.dirname(__FILE__), "uri-cache"))
|
28
|
+
Dir.mkdir(URI_CACHE) unless File.directory?(URI_CACHE)
|
29
|
+
OpenURI::Cache.class_eval { @cache_path = URI_CACHE }
|
30
|
+
|
31
|
+
::RSpec.configure do |c|
|
32
|
+
c.filter_run :focus => true
|
33
|
+
c.run_all_when_everything_filtered = true
|
34
|
+
c.exclusion_filter = {
|
35
|
+
:ruby => lambda { |version| !(RUBY_VERSION.to_s =~ /^#{version.to_s}/) },
|
36
|
+
}
|
37
|
+
c.include(RDF::Spec::Matchers)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Heuristically detect the input stream
|
41
|
+
def detect_format(stream)
|
42
|
+
# Got to look into the file to see
|
43
|
+
if stream.is_a?(IO) || stream.is_a?(StringIO)
|
44
|
+
stream.rewind
|
45
|
+
string = stream.read(1000)
|
46
|
+
stream.rewind
|
47
|
+
else
|
48
|
+
string = stream.to_s
|
49
|
+
end
|
50
|
+
case string
|
51
|
+
when /<html/i then RDF::RDFa::Reader
|
52
|
+
when /\{\s*\"@\"/i then JSON::LD::Reader
|
53
|
+
else RDF::Turtle::Reader
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# Spira class for manipulating test-manifest style test suites.
|
2
|
+
# Used for SWAP tests
|
3
|
+
require 'spira'
|
4
|
+
require 'json/ld'
|
5
|
+
require 'open-uri'
|
6
|
+
require 'support/extensions'
|
7
|
+
|
8
|
+
# For now, override RDF::Utils::File.open_file to look for the file locally before attempting to retrieve it
|
9
|
+
module RDF::Util
|
10
|
+
module File
|
11
|
+
REMOTE_PATH = "http://json-ld.org/test-suite/"
|
12
|
+
LOCAL_PATH = ::File.expand_path("../json-ld.org/test-suite", __FILE__) + '/'
|
13
|
+
|
14
|
+
##
|
15
|
+
# Override to use Patron for http and https, Kernel.open otherwise.
|
16
|
+
#
|
17
|
+
# @param [String] filename_or_url to open
|
18
|
+
# @param [Hash{Symbol => Object}] options
|
19
|
+
# @option options [Array, String] :headers
|
20
|
+
# HTTP Request headers.
|
21
|
+
# @return [IO] File stream
|
22
|
+
# @yield [IO] File stream
|
23
|
+
def self.open_file(filename_or_url, options = {}, &block)
|
24
|
+
case filename_or_url.to_s
|
25
|
+
when /^file:/
|
26
|
+
path = filename_or_url[5..-1]
|
27
|
+
Kernel.open(path.to_s, &block)
|
28
|
+
when /^#{REMOTE_PATH}/
|
29
|
+
#puts "attempt to open #{filename_or_url} locally"
|
30
|
+
if response = ::File.open(filename_or_url.to_s.sub(REMOTE_PATH, LOCAL_PATH))
|
31
|
+
#puts "use #{filename_or_url} locally"
|
32
|
+
case filename_or_url.to_s
|
33
|
+
when /\.jsonld$/
|
34
|
+
def response.content_type; 'application/ld+json'; end
|
35
|
+
when /\.sparql$/
|
36
|
+
def response.content_type; 'application/sparql-query'; end
|
37
|
+
end
|
38
|
+
|
39
|
+
if block_given?
|
40
|
+
begin
|
41
|
+
yield response
|
42
|
+
ensure
|
43
|
+
response.close
|
44
|
+
end
|
45
|
+
else
|
46
|
+
response
|
47
|
+
end
|
48
|
+
else
|
49
|
+
Kernel.open(filename_or_url.to_s, &block)
|
50
|
+
end
|
51
|
+
else
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
module Fixtures
|
58
|
+
module JSONLDTest
|
59
|
+
SUITE = RDF::URI("http://json-ld.org/test-suite/")
|
60
|
+
class Test < RDF::Vocabulary("http://www.w3.org/2006/03/test-description#"); end
|
61
|
+
class Jld < RDF::Vocabulary("http://json-ld.org/test-suite/vocab#"); end
|
62
|
+
|
63
|
+
class Manifest < Spira::Base
|
64
|
+
type Jld.Manifest
|
65
|
+
property :name, :predicate => RDF::DC.title, :type => XSD.string
|
66
|
+
property :comment, :predicate => RDF::RDFS.comment, :type => XSD.string
|
67
|
+
property :sequence, :predicate => Jld.sequence
|
68
|
+
|
69
|
+
def entries
|
70
|
+
@entries ||= begin
|
71
|
+
repo = self.class.repository
|
72
|
+
RDF::List.new(sequence, repo).map do |entry|
|
73
|
+
results = repo.query(:subject => entry, :predicate => RDF.type)
|
74
|
+
entry_types = results.map(&:object)
|
75
|
+
|
76
|
+
# Load entry if it is not in repo
|
77
|
+
if entry_types.empty?
|
78
|
+
repo.load(entry, :format => :jsonld)
|
79
|
+
entry_types = repo.query(:subject => entry, :predicate => RDF.type).map(&:object)
|
80
|
+
end
|
81
|
+
|
82
|
+
case
|
83
|
+
when entry_types.include?(Jld.Manifest) then entry.as(Manifest)
|
84
|
+
when entry_types.include?(Jld.CompactTest) then entry.as(CompactTest)
|
85
|
+
when entry_types.include?(Jld.ExpandTest) then entry.as(ExpandTest)
|
86
|
+
when entry_types.include?(Jld.FrameTest) then entry.as(FrameTest)
|
87
|
+
when entry_types.include?(Jld.NormalizeTest) then entry.as(NormalizeTest)
|
88
|
+
when entry_types.include?(Jld.ToRDFTest) then entry.as(ToRDFTest)
|
89
|
+
when entry_types.include?(Jld.FromRDFTest) then entry.as(FromRDFTest)
|
90
|
+
when entry_types.include?(Test.TestCase) then entry.as(Entry)
|
91
|
+
else raise "Unexpected entry type: #{entry_types.inspect}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def inspect
|
98
|
+
"[#{self.class.to_s} " + %w(
|
99
|
+
subject
|
100
|
+
name
|
101
|
+
).map {|a| v = self.send(a); "#{a}='#{v}'" if v}.compact.join(", ") +
|
102
|
+
", entries=#{entries.length}" +
|
103
|
+
"]"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class Entry
|
108
|
+
attr_accessor :debug
|
109
|
+
include Spira::Resource
|
110
|
+
type Test.TestCase
|
111
|
+
|
112
|
+
property :name, :predicate => RDF::DC.title, :type => XSD.string
|
113
|
+
property :purpose, :predicate => Test.purpose, :type => XSD.string
|
114
|
+
property :expected, :predicate => Test.expectedResults
|
115
|
+
property :inputDocument, :predicate => Test.informationResourceInput
|
116
|
+
property :resultDocument, :predicate => Test.informationResourceResults
|
117
|
+
property :extraDocument, :predicate => Test.input
|
118
|
+
|
119
|
+
def information; name; end
|
120
|
+
|
121
|
+
def input
|
122
|
+
RDF::Util::File.open_file(self.inputDocument)
|
123
|
+
end
|
124
|
+
|
125
|
+
def extra
|
126
|
+
RDF::Util::File.open_file(self.extraDocument)
|
127
|
+
end
|
128
|
+
|
129
|
+
def expect
|
130
|
+
RDF::Util::File.open_file(self.resultDocument)
|
131
|
+
end
|
132
|
+
|
133
|
+
def base_uri
|
134
|
+
inputDocument.to_s
|
135
|
+
end
|
136
|
+
|
137
|
+
def trace
|
138
|
+
@debug.to_a.join("\n")
|
139
|
+
end
|
140
|
+
|
141
|
+
def inspect
|
142
|
+
"[#{self.class.to_s} " + %w(
|
143
|
+
subject
|
144
|
+
name
|
145
|
+
inputDocument
|
146
|
+
resultDocument
|
147
|
+
extraDocument
|
148
|
+
).map {|a| v = self.send(a); "#{a}='#{v}'" if v}.compact.join(", ") +
|
149
|
+
"]"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class CompactTest < Entry
|
154
|
+
type Jld.CompactTest
|
155
|
+
end
|
156
|
+
|
157
|
+
class ExpandTest < Entry
|
158
|
+
type Jld.ExpandTest
|
159
|
+
end
|
160
|
+
|
161
|
+
class FrameTest < Entry
|
162
|
+
type Jld.FameTest
|
163
|
+
end
|
164
|
+
|
165
|
+
class NormalizeTest < Entry
|
166
|
+
type Jld.NormalizeTest
|
167
|
+
end
|
168
|
+
|
169
|
+
class FromRDFTest < Entry
|
170
|
+
type Jld.FromRDFTest
|
171
|
+
end
|
172
|
+
|
173
|
+
class ToRDFTest < Entry
|
174
|
+
type Jld.ToRDFTest
|
175
|
+
|
176
|
+
def quads
|
177
|
+
RDF::Util::File.open_file(self.expected)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
repo = RDF::Repository.load(SUITE.join("manifest.jsonld"), :format => :jsonld)
|
182
|
+
Spira.add_repository! :default, repo
|
183
|
+
end
|
184
|
+
end
|
data/spec/suite_spec.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
$:.unshift "."
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe JSON::LD do
|
6
|
+
describe "test suite" do
|
7
|
+
require 'suite_helper'
|
8
|
+
|
9
|
+
m = Fixtures::JSONLDTest::Manifest.each.to_a.first
|
10
|
+
describe m.name do
|
11
|
+
m.entries.each do |m2|
|
12
|
+
describe m2.name do
|
13
|
+
m2.entries.each do |t|
|
14
|
+
next if t.is_a?(Fixtures::JSONLDTest::NormalizeTest)
|
15
|
+
specify "#{File.basename(t.inputDocument.to_s)}: #{t.name}" do
|
16
|
+
begin
|
17
|
+
t.debug = ["test: #{t.inspect}", "source: #{t.input.read}"]
|
18
|
+
case t
|
19
|
+
when Fixtures::JSONLDTest::CompactTest
|
20
|
+
t.debug << "context: #{t.extra.read}" if t.extraDocument
|
21
|
+
result = JSON::LD::API.compact(t.input, t.extra, nil,
|
22
|
+
:base => t.base_uri,
|
23
|
+
:debug => t.debug)
|
24
|
+
expected = JSON.load(t.expect)
|
25
|
+
result.should produce(expected, t.debug)
|
26
|
+
when Fixtures::JSONLDTest::ExpandTest
|
27
|
+
t.debug << "context: #{t.extra.read}" if t.extraDocument
|
28
|
+
result = JSON::LD::API.expand(t.input, nil, nil,
|
29
|
+
:base => t.base_uri,
|
30
|
+
:debug => t.debug)
|
31
|
+
expected = JSON.load(t.expect)
|
32
|
+
result.should produce(expected, t.debug)
|
33
|
+
when Fixtures::JSONLDTest::FrameTest
|
34
|
+
t.debug << "frame: #{t.extra.read}" if t.extraDocument
|
35
|
+
result = JSON::LD::API.frame(t.input, t.extra, nil,
|
36
|
+
:base => t.inputDocument,
|
37
|
+
:debug => t.debug)
|
38
|
+
expected = JSON.load(t.expect)
|
39
|
+
result.should produce(expected, t.debug)
|
40
|
+
when Fixtures::JSONLDTest::FromRDFTest
|
41
|
+
repo = RDF::Repository.load(t.inputDocument)
|
42
|
+
result = JSON::LD::API.fromRDF(repo.each_statement.to_a, nil,
|
43
|
+
:debug => t.debug)
|
44
|
+
expected = JSON.load(t.expect)
|
45
|
+
result.should produce(expected, t.debug)
|
46
|
+
when Fixtures::JSONLDTest::ToRDFTest
|
47
|
+
quads = []
|
48
|
+
JSON::LD::API.toRDF(t.input, nil, nil,
|
49
|
+
:base => t.inputDocument,
|
50
|
+
:debug => t.debug) do |statement|
|
51
|
+
quads << to_quad(statement)
|
52
|
+
end
|
53
|
+
|
54
|
+
quads.sort.join("").should produce(t.expect.read, t.debug)
|
55
|
+
else
|
56
|
+
pending("unkown test type #{t.inspect}")
|
57
|
+
end
|
58
|
+
rescue JSON::LD::ProcessingError => e
|
59
|
+
fail("Processing error: #{e.message}")
|
60
|
+
rescue JSON::LD::InvalidContext => e
|
61
|
+
fail("Invalid Context: #{e.message}")
|
62
|
+
rescue JSON::LD::InvalidFrame => e
|
63
|
+
fail("Invalid Frame: #{e.message}")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Don't use NQuads writer so that we don't escape Unicode
|
73
|
+
def to_quad(thing)
|
74
|
+
case thing
|
75
|
+
when RDF::URI
|
76
|
+
"<#{escaped(thing.to_s)}>"
|
77
|
+
when RDF::Node
|
78
|
+
escaped(thing.to_s)
|
79
|
+
when RDF::Literal::Double
|
80
|
+
quoted("%1.15e" % thing.value) + "^^<#{RDF::XSD.double}>"
|
81
|
+
when RDF::Literal
|
82
|
+
quoted(escaped(thing.value)) +
|
83
|
+
(thing.datatype? ? "^^<#{thing.datatype}>" : "") +
|
84
|
+
(thing.language? ? "@#{thing.language}" : "")
|
85
|
+
when RDF::Statement
|
86
|
+
thing.to_quad.map {|r| to_quad(r)}.compact.join(" ") + " .\n"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# @param [String] string
|
92
|
+
# @return [String]
|
93
|
+
def quoted(string)
|
94
|
+
"\"#{string}\""
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# @param [String] string
|
99
|
+
# @return [String]
|
100
|
+
def escaped(string)
|
101
|
+
string.gsub('\\', '\\\\').gsub("\t", '\\t').
|
102
|
+
gsub("\n", '\\n').gsub("\r", '\\r').gsub('"', '\\"')
|
103
|
+
end
|
104
|
+
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,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,10 @@
|
|
1
|
+
{
|
2
|
+
"@context": {
|
3
|
+
"name": "http://xmlns.com/foaf/0.1/name",
|
4
|
+
"homepage": "http://xmlns.com/foaf/0.1/homepage",
|
5
|
+
"avatar": "http://xmlns.com/foaf/0.1/avatar"
|
6
|
+
},
|
7
|
+
"name": "Manu Sporny",
|
8
|
+
"homepage": "http://manu.sporny.org/",
|
9
|
+
"avatar": "http://twitter.com/account/profile_image/manusporny"
|
10
|
+
}
|
@@ -0,0 +1,7 @@
|
|
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
|
+
|
5
|
+
[ avatar: "http://twitter.com/account/profile_image/manusporny";
|
6
|
+
homepage: "http://manu.sporny.org/";
|
7
|
+
name: "Manu Sporny"] .
|
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"@context": [
|
3
|
+
{
|
4
|
+
"dc": "http://purl.org/dc/elements/1.1/",
|
5
|
+
"ex": "http://example.org/vocab#"
|
6
|
+
},
|
7
|
+
{
|
8
|
+
"ex:contains": {
|
9
|
+
"@coerce": "@id"
|
10
|
+
}
|
11
|
+
}
|
12
|
+
],
|
13
|
+
"@id": "http://example.org/library",
|
14
|
+
"@type": "ex:Library",
|
15
|
+
"ex:contains": {
|
16
|
+
"@id": "http://example.org/library/the-republic",
|
17
|
+
"@type": "ex:Book",
|
18
|
+
"dc:creator": "Plato",
|
19
|
+
"dc:title": "The Republic",
|
20
|
+
"ex:contains": {
|
21
|
+
"@id": "http://example.org/library/the-republic#introduction",
|
22
|
+
"@type": "ex:Chapter",
|
23
|
+
"dc:description": "An introductory chapter on The Republic.",
|
24
|
+
"dc:title": "The Introduction"
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|