Skip to content

Commit 6bbd621

Browse files
committed
Release 1.1.8
* Use RDF::Util::File.open_file for retrieving remote contexts; this enables the use of HTTP caching if RestClient is included. * When opening a context, make sure options are passed to documentLoader, but exclude 'Cache-Control' directive. This ensures that frequently accessed contexts can be cached, even if the referencing document isn't cached. * Added :stream option to JSON::LD::Writer to write out blocks where context and subject are the same. If quads come sorted by context and subject, this dramatically improves write performance by avoiding core algorithms. (use --stream option to jsonld CLI). * For API#initialize, set context to link header only if the document doesn't define a context, and one is not passed. * Define Context#merge and Context#merge! * Relax absolute IRI validation in context unless :validate flag is set. * Recover from bad JSON/RDF serialization in debug messages, typically due to UTF-8 errors. * Ignore input header options when requesting remote files to ensure property Accept sequence is used.
2 parents 9d4ddc0 + 1445102 commit 6bbd621

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1175
-746
lines changed

.travis.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ rvm:
77
- 1.9.3
88
- 2.0
99
- 2.1
10-
- jruby-19mode
10+
- 2.2
11+
- jruby
1112
- rbx-2
1213
cache: bundler
14+
matrix:
15+
allow_failures:
16+
- rvm: 2.2
17+

Gemfile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
source "https://rubygems.org"
22

33
gemspec
4-
gem 'rdf', :git => "git://github.com/ruby-rdf/rdf.git", :branch => "develop"
5-
gem 'rdf-spec', :git => "git://github.com/ruby-rdf/rdf-spec.git", :branch => "develop"
4+
gem 'rdf', git: "git://github.com/ruby-rdf/rdf.git", branch: "develop"
5+
gem 'rdf-spec', git: "git://github.com/ruby-rdf/rdf-spec.git", branch: "develop"
66

77
group :development do
8-
gem 'rdf-turtle', :git => "git://github.com/ruby-rdf/rdf-turtle.git", :branch => "develop"
9-
gem 'rdf-trig', :git => "git://github.com/ruby-rdf/rdf-trig.git", :branch => "develop"
8+
gem 'rdf-turtle', git: "git://github.com/ruby-rdf/rdf-turtle.git", branch: "develop"
9+
gem 'rdf-trig', git: "git://github.com/ruby-rdf/rdf-trig.git", branch: "develop"
1010
end
1111

1212
group :debug do
1313
gem "wirble"
1414
gem "linkeddata"
15-
gem "byebug", :platforms => [:mri_20, :mri_21]
15+
gem "byebug", platforms: [:mri_20, :mri_21]
1616
end
1717

1818
platforms :rbx do

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ JSON::LD can now be used to create a _context_ from an RDFS/OWL definition, and
1313

1414
Install with `gem install json-ld`
1515

16+
### JSON-LD Streaming Profile
17+
This gem implements an optimized streaming writer used for generating JSON-LD from large repositories. Such documents result in the JSON-LD Streaming Profile:
18+
19+
* Each statement written as a separate node in expanded/flattened form.
20+
* RDF Lists are written as separate nodes using `rdf:first` and `rdf:rest` properties.
21+
1622
## Examples
1723

1824
require 'rubygems'
@@ -165,7 +171,7 @@ Install with `gem install json-ld`
165171
graph = RDF::Graph.new << JSON::LD::API.toRdf(input)
166172

167173
require 'rdf/turtle'
168-
graph.dump(:ttl, :prefixes => {:foaf => "http://xmlns.com/foaf/0.1/"})
174+
graph.dump(:ttl, prefixes: {foaf: "http://xmlns.com/foaf/0.1/"})
169175
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
170176

171177
<http://example.org/people#joebob> a foaf:Person;
@@ -213,8 +219,8 @@ Install with `gem install json-ld`
213219
## RDF Reader and Writer
214220
{JSON::LD} also acts as a normal RDF reader and writer, using the standard RDF.rb reader/writer interfaces:
215221

216-
graph = RDF::Graph.load("etc/doap.jsonld", :format => :jsonld)
217-
graph.dump(:jsonld, :standard_prefixes => true)
222+
graph = RDF::Graph.load("etc/doap.jsonld", format: :jsonld)
223+
graph.dump(:jsonld, standard_prefixes: true)
218224

219225
`RDF::GRAPH#dump` can also take a `:context` option to use a separately defined context
220226

Rakefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
require 'rubygems'
22

3-
task :default => [ :spec ]
3+
task default: [ :spec ]
44

55
namespace :gem do
66
desc "Build the json-ld-#{File.read('VERSION').chomp}.gem file"
@@ -50,7 +50,7 @@ namespace :presentation do
5050
end
5151

5252
desc "Build presentation files"
53-
task :build => %w(
53+
task build: %w(
5454
presentation/dbpedia/expanded.jsonld
5555
presentation/dbpedia/compacted.jsonld
5656
presentation/dbpedia/framed.jsonld
@@ -95,5 +95,5 @@ namespace :doc do
9595
YARD::Rake::YardocTask.new
9696
end
9797

98-
task :default => :spec
99-
task :specs => :spec
98+
task default: :spec
99+
task specs: :spec

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.1.7
1+
1.1.8

bin/jsonld

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#!/usr/bin/env ruby
22
require 'rubygems'
3-
$:.unshift(File.expand_path("../../lib", __FILE__))
43
begin
54
require 'linkeddata'
65
rescue LoadError
76
end
7+
$:.unshift(File.expand_path("../../lib", __FILE__))
88
require 'json/ld'
99
require 'getoptlong'
1010
require 'open-uri'
@@ -17,42 +17,49 @@ def run(input, options)
1717
options[:format] = :jsonld if options[:compact] || options[:frame]
1818

1919
# If input format is not JSON-LD, transform input to JSON-LD first
20-
if options[:format] == :jsonld && options[:input_format] != :jsonld
21-
r = reader_class.new(input, options[:parser_options])
22-
g = RDF::Repository.new << r
23-
input = JSON::LD::API.fromRdf(g)
20+
reader = if options[:input_format] != :jsonld
21+
reader_class.new(input, options[:parser_options])
2422
end
2523

2624
prefixes = {}
2725
start = Time.new
2826
if options[:expand]
29-
options = options.merge(:expandContext => options.delete(:context)) if options.has_key?(:context)
27+
options = options.merge(expandContext: options.delete(:context)) if options.has_key?(:context)
28+
input = JSON::LD::API.fromRdf(reader) if reader
3029
output = JSON::LD::API.expand(input, options)
3130
secs = Time.new - start
3231
options[:output].puts output.to_json(JSON::LD::JSON_STATE)
3332
STDERR.puts "Expanded in #{secs} seconds." unless options[:quiet]
3433
elsif options[:compact]
34+
input = JSON::LD::API.fromRdf(reader) if reader
3535
output = JSON::LD::API.compact(input, options[:context], options)
3636
secs = Time.new - start
3737
options[:output].puts output.to_json(JSON::LD::JSON_STATE)
3838
STDERR.puts "Compacted in #{secs} seconds." unless options[:quiet]
3939
elsif options[:flatten]
40+
input = JSON::LD::API.fromRdf(reader) if reader
4041
output = JSON::LD::API.flatten(input, options[:context], options)
4142
secs = Time.new - start
4243
options[:output].puts output.to_json(JSON::LD::JSON_STATE)
4344
STDERR.puts "Flattened in #{secs} seconds." unless options[:quiet]
4445
elsif options[:frame]
46+
input = JSON::LD::API.fromRdf(reader) if reader
4547
output = JSON::LD::API.frame(input, options[:frame], options)
4648
secs = Time.new - start
4749
options[:output].puts output.to_json(JSON::LD::JSON_STATE)
4850
STDERR.puts "Framed in #{secs} seconds." unless options[:quiet]
4951
else
50-
options = options.merge(:expandContext => options.delete(:context)) if options.has_key?(:context)
51-
g = JSON::LD::API.toRdf(input, options)
52+
options = options.merge(expandContext: options.delete(:context)) if options.has_key?(:context)
53+
parser_options = options[:parser_options].merge(standard_prefixes: true)
54+
reader ||= JSON::LD::Reader.new(input, parser_options)
55+
num = 0
56+
RDF::Writer.for(options[:output_format]).new(options[:output], parser_options) do |w|
57+
reader.each do |statement|
58+
num += 1
59+
w << statement
60+
end
61+
end
5262
secs = Time.new - start
53-
num = g.count
54-
parser_options = options[:parser_options].merge(:standard_prefixes => true)
55-
options[:output].puts g.dump(options[:output_format], parser_options)
5663
STDERR.puts "\nParsed #{num} statements in #{secs} seconds @ #{num/secs} statements/second." unless options[:quiet]
5764
end
5865
rescue
@@ -62,17 +69,18 @@ rescue
6269
end
6370

6471
parser_options = {
65-
:base => nil,
66-
:progress => false,
67-
:validate => false,
68-
:strict => false,
72+
base: nil,
73+
progress: false,
74+
validate: false,
75+
stream: false,
76+
strict: false,
6977
}
7078

7179
options = {
72-
:parser_options => parser_options,
73-
:output => STDOUT,
74-
:output_format => :turtle,
75-
:input_format => :jsonld,
80+
parser_options: parser_options,
81+
output: STDOUT,
82+
output_format: :jsonld,
83+
input_format: :jsonld,
7684
}
7785
input = nil
7886

@@ -89,8 +97,9 @@ OPT_ARGS = [
8997
["--output", "-o", GetoptLong::REQUIRED_ARGUMENT,"Output to the specified file path"],
9098
["--parse-only", GetoptLong::NO_ARGUMENT, "Parse the document for well-formedness only"],
9199
["--quiet", GetoptLong::NO_ARGUMENT, "Supress most output other than progress indicators"],
100+
["--stream", GetoptLong::NO_ARGUMENT, "Use Streaming reader/writer"],
92101
["--uri", GetoptLong::REQUIRED_ARGUMENT,"URI to be used as the document base"],
93-
["--verbose", GetoptLong::NO_ARGUMENT, "Detail on execution"],
102+
["--validate", GetoptLong::NO_ARGUMENT, "Validate while processing"],
94103
["--help", "-?", GetoptLong::NO_ARGUMENT, "This message"]
95104
]
96105
def usage
@@ -108,7 +117,6 @@ def usage
108117
exit(1)
109118
end
110119

111-
112120
opts = GetoptLong.new(*OPT_ARGS.map {|o| o[0..-2]})
113121

114122
opts.each do |opt, arg|
@@ -125,18 +133,29 @@ opts.each do |opt, arg|
125133
when '--output' then options[:output] = File.open(arg, "w")
126134
when '--parse-only' then options[:parse_only] = true
127135
when '--quiet' then options[:quiet] = true
136+
when '--stream' then parser_options[:stream] = true
128137
when '--uri' then parser_options[:base] = arg
129-
when '--verbose' then $verbose = true
138+
when '--validate' then parser_options[:validate] = true
130139
when '--help' then usage
131140
end
132141
end
133142

143+
# Hack
144+
options[:parser_options][:context] = options[:context] if parser_options[:stream]
145+
146+
if !(options.keys & [:expand, :compact, :flatten, :frame]).empty? &&
147+
(parser_options[:stream] || options[:output_format] != :jsonld)
148+
STDERR.puts "Incompatible options"
149+
exit(1)
150+
end
151+
134152
if ARGV.empty?
135153
s = input ? input : $stdin.read
136154
run(StringIO.new(s), options)
137155
else
138156
ARGV.each do |file|
139-
run(file, options)
157+
# Call with opened files
158+
RDF::Util::File.open_file(file, options) {|f| run(f, options)}
140159
end
141160
end
142161
puts

example-files/bench.rb

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/usr/bin/env ruby
2+
require 'rubygems'
3+
$:.unshift(File.expand_path("../../lib", __FILE__))
4+
require 'json/ld'
5+
require 'getoptlong'
6+
7+
def run(options)
8+
parser_options = options[:parser_options].merge(standard_prefixes: true)
9+
statement = RDF::Statement(RDF::URI("http://example/a"), RDF::URI("http://example/b"), RDF::Literal("c"))
10+
start = Time.new
11+
num = 100_000
12+
RDF::Writer.for(options[:output_format]).new(options[:output], parser_options) do |w|
13+
(1..num).each do
14+
w << statement
15+
end
16+
end
17+
secs = Time.new - start
18+
STDERR.puts "\nProcessed #{num} statements in #{secs} seconds @ #{num/secs} statements/second." unless options[:quiet]
19+
end
20+
21+
parser_options = {
22+
base: nil,
23+
progress: false,
24+
validate: false,
25+
stream: false,
26+
strict: false,
27+
}
28+
29+
options = {
30+
parser_options: parser_options,
31+
output: STDOUT,
32+
output_format: :jsonld,
33+
input_format: :jsonld,
34+
}
35+
input = nil
36+
37+
OPT_ARGS = [
38+
["--output", "-o", GetoptLong::REQUIRED_ARGUMENT,"Output to the specified file path"],
39+
["--stream", GetoptLong::NO_ARGUMENT, "Use Streaming reader/writer"],
40+
["--help", "-?", GetoptLong::NO_ARGUMENT, "This message"]
41+
]
42+
def usage
43+
STDERR.puts %{Usage: #{$0} [options] file ...}
44+
width = OPT_ARGS.map do |o|
45+
l = o.first.length
46+
l += o[1].length + 2 if o[1].is_a?(String)
47+
l
48+
end.max
49+
OPT_ARGS.each do |o|
50+
s = " %-*s " % [width, (o[1].is_a?(String) ? "#{o[0,2].join(', ')}" : o[0])]
51+
s += o.last
52+
STDERR.puts s
53+
end
54+
exit(1)
55+
end
56+
57+
58+
opts = GetoptLong.new(*OPT_ARGS.map {|o| o[0..-2]})
59+
60+
opts.each do |opt, arg|
61+
case opt
62+
when '--output' then options[:output] = File.open(arg, "w")
63+
when '--quiet' then options[:quiet] = true
64+
when '--stream' then parser_options[:stream] = true
65+
when '--help' then usage
66+
end
67+
end
68+
69+
run(options)

json-ld.gemspec

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ Gem::Specification.new do |gem|
2727

2828
gem.required_ruby_version = '>= 1.9.2'
2929
gem.requirements = []
30-
gem.add_runtime_dependency 'rdf', '~> 1.1', '>= 1.1.4'
31-
gem.add_development_dependency 'open-uri-cached', '~> 0.0', '>= 0.0.5'
30+
gem.add_runtime_dependency 'rdf', '~> 1.1', '>= 1.1.7'
3231
gem.add_development_dependency 'yard' , '~> 0.8'
32+
gem.add_development_dependency "rack-cache", '~> 1.2'
33+
gem.add_development_dependency "rest-client", '~> 1.7'
34+
gem.add_development_dependency "rest-client-components", '~> 1.3'
3335
gem.add_development_dependency 'rspec', '~> 3.0'
3436
gem.add_development_dependency 'rspec-its', '~> 1.0'
3537
gem.add_development_dependency 'rdf-spec', '~> 1.1'

lib/json/ld.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ module LD
2525
require 'json/ld/format'
2626
require 'json/ld/utils'
2727
autoload :API, 'json/ld/api'
28-
autoload :Context, 'json/ld/context'
28+
autoload :Context, 'json/ld/context'
2929
autoload :Normalize, 'json/ld/normalize'
3030
autoload :Reader, 'json/ld/reader'
3131
autoload :Resource, 'json/ld/resource'
@@ -77,11 +77,11 @@ module LD
7777
NATIVE_DATATYPES = [RDF::XSD.integer.to_s, RDF::XSD.boolean.to_s, RDF::XSD.double.to_s]
7878

7979
JSON_STATE = JSON::State.new(
80-
:indent => " ",
81-
:space => " ",
82-
:space_before => "",
83-
:object_nl => "\n",
84-
:array_nl => "\n"
80+
indent: " ",
81+
space: " ",
82+
space_before: "",
83+
object_nl: "\n",
84+
array_nl: "\n"
8585
)
8686

8787
def self.debug?; @debug; end

0 commit comments

Comments
 (0)