json-ld 0.1.3 → 0.1.4

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.
Files changed (78) hide show
  1. data/History.markdown +3 -0
  2. data/VERSION +1 -1
  3. data/bin/jsonld +134 -0
  4. data/lib/json/ld/api.rb +4 -4
  5. data/lib/json/ld/frame.rb +2 -1
  6. data/lib/json/ld/from_rdf.rb +1 -1
  7. data/spec/api_spec.rb +66 -0
  8. data/spec/compact_spec.rb +420 -0
  9. data/spec/evaluation_context_spec.rb +1039 -0
  10. data/spec/expand_spec.rb +625 -0
  11. data/spec/format_spec.rb +71 -0
  12. data/spec/frame_spec.rb +542 -0
  13. data/spec/from_rdf_spec.rb +316 -0
  14. data/spec/matchers.rb +67 -0
  15. data/spec/reader_spec.rb +79 -0
  16. data/spec/spec_helper.rb +56 -0
  17. data/spec/suite_helper.rb +184 -0
  18. data/spec/suite_spec.rb +104 -0
  19. data/spec/support/extensions.rb +10 -0
  20. data/spec/test-files/test-1-automatic.json +10 -0
  21. data/spec/test-files/test-1-compacted.json +10 -0
  22. data/spec/test-files/test-1-context.json +7 -0
  23. data/spec/test-files/test-1-expanded.json +5 -0
  24. data/spec/test-files/test-1-input.json +10 -0
  25. data/spec/test-files/test-1-normalized.json +8 -0
  26. data/spec/test-files/test-1-rdf.ttl +7 -0
  27. data/spec/test-files/test-2-automatic.json +27 -0
  28. data/spec/test-files/test-2-compacted.json +20 -0
  29. data/spec/test-files/test-2-context.json +7 -0
  30. data/spec/test-files/test-2-expanded.json +16 -0
  31. data/spec/test-files/test-2-input.json +20 -0
  32. data/spec/test-files/test-2-normalized.json +32 -0
  33. data/spec/test-files/test-2-rdf.ttl +14 -0
  34. data/spec/test-files/test-3-compacted.json +11 -0
  35. data/spec/test-files/test-3-context.json +8 -0
  36. data/spec/test-files/test-3-expanded.json +10 -0
  37. data/spec/test-files/test-3-input.json +11 -0
  38. data/spec/test-files/test-3-normalized.json +13 -0
  39. data/spec/test-files/test-3-rdf.ttl +7 -0
  40. data/spec/test-files/test-4-automatic.json +10 -0
  41. data/spec/test-files/test-4-compacted.json +10 -0
  42. data/spec/test-files/test-4-context.json +7 -0
  43. data/spec/test-files/test-4-expanded.json +6 -0
  44. data/spec/test-files/test-4-input.json +10 -0
  45. data/spec/test-files/test-4-rdf.ttl +5 -0
  46. data/spec/test-files/test-5-automatic.json +13 -0
  47. data/spec/test-files/test-5-compacted.json +13 -0
  48. data/spec/test-files/test-5-context.json +7 -0
  49. data/spec/test-files/test-5-expanded.json +9 -0
  50. data/spec/test-files/test-5-input.json +13 -0
  51. data/spec/test-files/test-5-rdf.ttl +6 -0
  52. data/spec/test-files/test-6-automatic.json +10 -0
  53. data/spec/test-files/test-6-compacted.json +10 -0
  54. data/spec/test-files/test-6-context.json +7 -0
  55. data/spec/test-files/test-6-expanded.json +10 -0
  56. data/spec/test-files/test-6-input.json +10 -0
  57. data/spec/test-files/test-6-rdf.ttl +5 -0
  58. data/spec/test-files/test-7-automatic.json +20 -0
  59. data/spec/test-files/test-7-compacted.json +23 -0
  60. data/spec/test-files/test-7-context.json +4 -0
  61. data/spec/test-files/test-7-expanded.json +20 -0
  62. data/spec/test-files/test-7-input.json +23 -0
  63. data/spec/test-files/test-7-rdf.ttl +13 -0
  64. data/spec/test-files/test-8-automatic.json +1 -0
  65. data/spec/test-files/test-8-compacted.json +34 -0
  66. data/spec/test-files/test-8-context.json +11 -0
  67. data/spec/test-files/test-8-expanded.json +24 -0
  68. data/spec/test-files/test-8-frame.json +18 -0
  69. data/spec/test-files/test-8-framed.json +29 -0
  70. data/spec/test-files/test-8-input.json +30 -0
  71. data/spec/test-files/test-8-rdf.ttl +15 -0
  72. data/spec/test-files/test-9-compacted.json +20 -0
  73. data/spec/test-files/test-9-context.json +13 -0
  74. data/spec/test-files/test-9-expanded.json +14 -0
  75. data/spec/test-files/test-9-input.json +12 -0
  76. data/spec/to_rdf_spec.rb +640 -0
  77. data/spec/writer_spec.rb +161 -0
  78. metadata +150 -22
@@ -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
@@ -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
+ class Hash
2
+ def diff(other)
3
+ self.keys.inject({}) do |memo, key|
4
+ unless self[key] == other[key]
5
+ memo[key] = [self[key], other[key]]
6
+ end
7
+ memo
8
+ end
9
+ end
10
+ 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,7 @@
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
+ }
@@ -0,0 +1,5 @@
1
+ [{
2
+ "http://xmlns.com/foaf/0.1/name": [{"@value": "Manu Sporny"}],
3
+ "http://xmlns.com/foaf/0.1/homepage": [{"@value": "http://manu.sporny.org/"}],
4
+ "http://xmlns.com/foaf/0.1/avatar": [{"@value": "http://twitter.com/account/profile_image/manusporny"}]
5
+ }]
@@ -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,8 @@
1
+ [{
2
+ "@id": {
3
+ "@id": "_:c14n0"
4
+ },
5
+ "http://xmlns.com/foaf/0.1/avatar": "http://twitter.com/account/profile_image/manusporny",
6
+ "http://xmlns.com/foaf/0.1/homepage": "http://manu.sporny.org/",
7
+ "http://xmlns.com/foaf/0.1/name": "Manu Sporny"
8
+ }]
@@ -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
+ }