rdf 1.99.1 → 2.0.0.beta1

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/{README → README.md} +9 -44
  3. data/VERSION +1 -1
  4. data/bin/rdf +1 -1
  5. data/lib/rdf.rb +40 -49
  6. data/lib/rdf/changeset.rb +161 -0
  7. data/lib/rdf/cli.rb +195 -33
  8. data/lib/rdf/cli/vocab-loader.rb +13 -3
  9. data/lib/rdf/format.rb +44 -26
  10. data/lib/rdf/mixin/enumerable.rb +133 -97
  11. data/lib/rdf/mixin/enumerator.rb +8 -0
  12. data/lib/rdf/mixin/indexable.rb +1 -1
  13. data/lib/rdf/mixin/mutable.rb +101 -22
  14. data/lib/rdf/mixin/queryable.rb +21 -32
  15. data/lib/rdf/mixin/transactable.rb +94 -0
  16. data/lib/rdf/mixin/writable.rb +12 -3
  17. data/lib/rdf/model/dataset.rb +48 -0
  18. data/lib/rdf/model/graph.rb +73 -43
  19. data/lib/rdf/model/list.rb +61 -33
  20. data/lib/rdf/model/literal.rb +20 -19
  21. data/lib/rdf/model/literal/double.rb +20 -4
  22. data/lib/rdf/model/literal/numeric.rb +15 -13
  23. data/lib/rdf/model/node.rb +15 -16
  24. data/lib/rdf/model/statement.rb +1 -43
  25. data/lib/rdf/model/term.rb +10 -8
  26. data/lib/rdf/model/uri.rb +35 -34
  27. data/lib/rdf/model/value.rb +1 -1
  28. data/lib/rdf/nquads.rb +2 -11
  29. data/lib/rdf/ntriples.rb +1 -1
  30. data/lib/rdf/ntriples/reader.rb +33 -46
  31. data/lib/rdf/ntriples/writer.rb +42 -5
  32. data/lib/rdf/query.rb +6 -40
  33. data/lib/rdf/query/pattern.rb +4 -17
  34. data/lib/rdf/query/solutions.rb +6 -6
  35. data/lib/rdf/reader.rb +65 -14
  36. data/lib/rdf/repository.rb +365 -229
  37. data/lib/rdf/transaction.rb +211 -84
  38. data/lib/rdf/util.rb +1 -0
  39. data/lib/rdf/util/cache.rb +5 -5
  40. data/lib/rdf/util/file.rb +12 -9
  41. data/lib/rdf/util/logger.rb +272 -0
  42. data/lib/rdf/version.rb +2 -2
  43. data/lib/rdf/vocab/owl.rb +82 -77
  44. data/lib/rdf/vocab/rdfs.rb +22 -17
  45. data/lib/rdf/vocab/xsd.rb +5 -0
  46. data/lib/rdf/vocabulary.rb +50 -56
  47. data/lib/rdf/writer.rb +104 -52
  48. metadata +45 -90
  49. data/lib/rdf/mixin/inferable.rb +0 -5
  50. data/lib/rdf/vocab/cc.rb +0 -128
  51. data/lib/rdf/vocab/cert.rb +0 -245
  52. data/lib/rdf/vocab/dc.rb +0 -948
  53. data/lib/rdf/vocab/dc11.rb +0 -167
  54. data/lib/rdf/vocab/dcat.rb +0 -214
  55. data/lib/rdf/vocab/doap.rb +0 -337
  56. data/lib/rdf/vocab/exif.rb +0 -941
  57. data/lib/rdf/vocab/foaf.rb +0 -614
  58. data/lib/rdf/vocab/geo.rb +0 -157
  59. data/lib/rdf/vocab/gr.rb +0 -1501
  60. data/lib/rdf/vocab/ht.rb +0 -236
  61. data/lib/rdf/vocab/ical.rb +0 -528
  62. data/lib/rdf/vocab/ma.rb +0 -513
  63. data/lib/rdf/vocab/mo.rb +0 -2412
  64. data/lib/rdf/vocab/og.rb +0 -222
  65. data/lib/rdf/vocab/ogc.rb +0 -58
  66. data/lib/rdf/vocab/prov.rb +0 -1550
  67. data/lib/rdf/vocab/rsa.rb +0 -72
  68. data/lib/rdf/vocab/rss.rb +0 -66
  69. data/lib/rdf/vocab/schema.rb +0 -10569
  70. data/lib/rdf/vocab/sioc.rb +0 -669
  71. data/lib/rdf/vocab/skos.rb +0 -238
  72. data/lib/rdf/vocab/skosxl.rb +0 -57
  73. data/lib/rdf/vocab/v.rb +0 -383
  74. data/lib/rdf/vocab/vcard.rb +0 -841
  75. data/lib/rdf/vocab/vmd.rb +0 -383
  76. data/lib/rdf/vocab/void.rb +0 -186
  77. data/lib/rdf/vocab/vs.rb +0 -28
  78. data/lib/rdf/vocab/wdrs.rb +0 -134
  79. data/lib/rdf/vocab/wot.rb +0 -167
  80. data/lib/rdf/vocab/xhtml.rb +0 -8
  81. data/lib/rdf/vocab/xhv.rb +0 -505
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d489709cc4b64608bfb4a353a63d918f012732ff
4
- data.tar.gz: f9efffc401e1ba7cc000fe3fc737608487e0d7c1
3
+ metadata.gz: d6b5ce1d8969fb6463efd82a7fe9826589bac799
4
+ data.tar.gz: e0c0066693ddc81d5089bf63f7502527750d1380
5
5
  SHA512:
6
- metadata.gz: 4eeabcffc5ccdb6db0d0ff2d79bf1e25dae0d4d33e07d4a561e970497430991ae70a6e8a56450b746c69e0a26f68c930e244f7e4416d5647cd25dd69cb2e159c
7
- data.tar.gz: 2c9d36a52c4e56bc7032aaf2a584bb90ee131dc36256f2cf10af2698affdb70d8e21ab6e70969fff021b3d7a11bca67e235525831fcadeb59dd150e3aebeb57d
6
+ metadata.gz: f866ac2a48e98e154bd786f6e0c30f5298e70c01de47847c660fded022f14ef58f19b0a727e20a53704d0d46f610cae4f53a08b5b2b94dc5f82d85da006e9d0b
7
+ data.tar.gz: ccede9fff108419d7d0df702df4dd8e8a24bd53be1e0aa0bd1a1986158ff5dcdbfd7bb824eb36cbe1dc0ea41243732bb49f0aec5dbbf869721595911e7f39709
@@ -11,6 +11,8 @@ This is a pure-Ruby library for working with [Resource Description Framework
11
11
 
12
12
  [![Gem Version](https://badge.fury.io/rb/rdf.png)](http://badge.fury.io/rb/rdf)
13
13
  [![Build Status](https://travis-ci.org/ruby-rdf/rdf.png?branch=master)](http://travis-ci.org/ruby-rdf/rdf)
14
+ [![Code Climate](https://codeclimate.com/github/ruby-rdf/rdf/badges/gpa.svg)](https://codeclimate.com/github/ruby-rdf/rdf)
15
+ [![Coverage Status](https://coveralls.io/repos/ruby-rdf/rdf/badge.svg)](https://coveralls.io/r/ruby-rdf/rdf)
14
16
  [![Join the chat at https://gitter.im/ruby-rdf/rdf](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ruby-rdf/rdf?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
15
17
 
16
18
  ## Features
@@ -28,8 +30,7 @@ This is a pure-Ruby library for working with [Resource Description Framework
28
30
  not modify any of Ruby's core classes or standard library.
29
31
  * Based entirely on Ruby's autoloading, meaning that you can generally make
30
32
  use of any one part of the library without needing to load up the rest.
31
- * Compatible with Ruby Ruby 1.9.2, Ruby 2.0, Rubinius and JRuby 1.7+.
32
- * Compatible with older Ruby versions with the help of the [Backports][] gem.
33
+ * Compatible with Ruby Ruby 2.x, Rubinius and JRuby 1.7+ (in Ruby 2.0 mode).
33
34
  * Performs auto-detection of input to select appropriate Reader class if one
34
35
  cannot be determined from file characteristics.
35
36
 
@@ -50,12 +51,12 @@ the 1.1 release of RDF.rb:
50
51
  * Introduces {RDF::IRI}, as a synonym for {RDF::URI} either {RDF::IRI} or {RDF::URI} can be used interchangeably. Versions of RDF.rb prior to the 1.1 release were already compatible with IRIs. Internationalized Resource Identifiers (see [RFC3987][]) are a super-set of URIs (see [RFC3986][]) which allow for characters other than standard US-ASCII.
51
52
  * {RDF::URI} no longer uses the `Addressable` gem. As URIs typically don't need to be parsed, this provides a substantial performance improvement when enumerating or querying graphs and repositories.
52
53
  * {RDF::List} no longer emits a `rdf:List` type. However, it will now recognize any subjects that are {RDF::Node} instances as being list elements, as long as they have both `rdf:first` and `rdf:rest` predicates.
53
- * {RDF::Graph} adding a `context` to a graph may only be done when the underlying storage model supports contexts (the default {RDF::Repository} does). The notion of `context` in RDF.rb is treated equivalently to [Named Graphs](http://www.w3.org/TR/rdf11-concepts/#dfn-named-graph) within an RDF Dataset, and graphs on their own are not named.
54
+ * {RDF::Graph} adding a `graph_name` to a graph may only be done when the underlying storage model supports graph_names (the default {RDF::Repository} does). The notion of `graph_name` in RDF.rb is treated equivalently to [Named Graphs](http://www.w3.org/TR/rdf11-concepts/#dfn-named-graph) within an RDF Dataset, and graphs on their own are not named.
54
55
  * {RDF::Graph}, {RDF::Statement} and {RDF::List} now include {RDF::Value}, and not {RDF::Resource}. Made it clear that using {RDF::Graph} does not mean that it may be used within an {RDF::Statement}, for this see {RDF::Term}.
55
56
  * {RDF::Statement} now is stricter about checking that all elements are valid when validating.
56
57
  * {RDF::NTriples::Writer} and {RDF::NQuads::Writer} now default to validate output, only allowing valid statements to be emitted. This may disabled by setting the `:validate` option to `false`.
57
58
  * {RDF::Dataset} is introduced as a class alias of {RDF::Repository}. This allows closer alignment to the RDF concept of [Dataset](http://www.w3.org/TR/rdf11-concepts/#dfn-dataset).
58
- * The `context` (or `name`) of a named graph within a Dataset or Repository may be either an {RDF::IRI} or {RDF::Node}. Implementations of repositories may restrict this to being only {RDF::IRI}.
59
+ * The `graph_name` of a graph within a Dataset or Repository may be either an {RDF::IRI} or {RDF::Node}. Implementations of repositories may restrict this to being only {RDF::IRI}.
59
60
  * There are substantial and somewhat incompatible changes to {RDF::Literal}. In [RDF 1.1][], all literals are typed, including plain literals and language tagged literals. Internally, plain literals are given the `xsd:string` datatype and language tagged literals are given the `rdf:langString` datatype. Creating a plain literal, without a datatype or language, will automatically provide the `xsd:string` datatype; similar for language tagged literals. Note that most serialization formats will remove this datatype. Code which depends on a literal having the `xsd:string` datatype being different from a plain literal (formally, without a datatype) may break. However note that the `#has\_datatype?` will continue to return `false` for plain or language-tagged literals.
60
61
  * {RDF::Query#execute} now accepts a block and returns {RDF::Query::Solutions}. This allows `enumerable.query(query)` to behave like `query.execute(enumerable)` and either return an enumerable or yield each solution.
61
62
  * {RDF::Queryable#query} now returns {RDF::Query::Solutions} instead of an Enumerator if it's argument is an {RDF::Query}.
@@ -146,7 +147,7 @@ appropriate writer to use.
146
147
 
147
148
  RDF::Writer.open("hello.nq", format: :nquads) do |writer|
148
149
  writer << RDF::Repository.new do |repo|
149
- repo << RDF::Statement.new(:hello, RDF::RDFS.label, "Hello, world!", context: RDF::URI("context"))
150
+ repo << RDF::Statement.new(:hello, RDF::RDFS.label, "Hello, world!", graph_name: RDF::URI("http://example/graph_name"))
150
151
  end
151
152
  end
152
153
 
@@ -154,7 +155,7 @@ A specific sub-type of Writer can also be invoked directly:
154
155
 
155
156
  require 'rdf/nquads'
156
157
 
157
- repo = RDF::Repository.new << RDF::Statement.new(:hello, RDF::RDFS.label, "Hello, world!", context: RDF::URI("context"))
158
+ repo = RDF::Repository.new << RDF::Statement.new(:hello, RDF::RDFS.label, "Hello, world!", graph_name: RDF::URI("http://example/graph_name"))
158
159
  File.open("hello.nq", "w") {|f| f << repo.dump(:nquads)}
159
160
 
160
161
  ## Reader/Writer convenience methods
@@ -298,7 +299,6 @@ from BNode identity (i.e., they each entail the other)
298
299
  * {RDF::Countable}
299
300
  * {RDF::Enumerable}
300
301
  * {RDF::Indexable}
301
- * {RDF::Inferable}
302
302
  * {RDF::Queryable}
303
303
  * {RDF::Mutable}
304
304
  * {RDF::Durable}
@@ -327,44 +327,10 @@ from BNode identity (i.e., they each entail the other)
327
327
  * {RDF::RDFV} - RDF Vocabulary (RDFV)
328
328
  * {RDF::XSD} - XML Schema (XSD)
329
329
 
330
- #### Deprecated Vocabularies
331
-
332
- The following vocabularies will be deprecated in RDF.rb 2.0 and moved to the rdf-vocab gem.
333
-
334
- * {RDF::CC} - Creative Commons (CC)
335
- * {RDF::CERT} - W3 Authentication Certificate (CERT)
336
- * {RDF::DC} - Dublin Core (DC)
337
- * {RDF::DC11} - Dublin Core 1.1 (DC11) _deprecated_
338
- * {RDF::DOAP} - Description of a Project (DOAP)
339
- * {RDF::EXIF} - Exchangeable Image File Format (EXIF)
340
- * {RDF::FOAF} - Friend of a Friend (FOAF)
341
- * {RDF::GEO} - WGS84 Geo Positioning (GEO)
342
- * {RDF::GR} - GoodRelations (GR)
343
- * {RDF::HT} - Hypertext Transfer Protocol (HT)
344
- * {RDF::ICAL} - RDF Calendar Workspace (ICAL)
345
- * {RDF::MA} - Media Resources (MA)
346
- * {RDF::MO} - Music Ontology (MO)
347
- * {RDF::OG} - Open Graph protocol (OG)
348
- * {RDF::PROV} - Provenance on the web (PROV)
349
- * {RDF::RSA} - W3 RSA Keys (RSA)
350
- * {RDF::RSS} - RDF Site Summary (RSS)
351
- * {RDF::SCHEMA} - Schema.org (SCHEMA)
352
- * {RDF::SIOC} - Semantically-Interlinked Online Communities (SIOC)
353
- * {RDF::SKOS} - Simple Knowledge Organization System (SKOS)
354
- * {RDF::SKOSXL} - SKOS eXtension for Labels (SKOSXL)
355
- * {RDF::V} - RDF data vocabulary (V)
356
- * {RDF::VCARD} - Ontology for vCards (VCARD)
357
- * {RDF::VMD} - Data-Vocabulary.org (VMD)
358
- * {RDF::VOID} - Vocabulary of Interlinked Datasets (VOID)
359
- * {RDF::VS} - SemWeb Vocab Status ontology (VS)
360
- * {RDF::WDRS} - Protocol for Web Description Resources (WDRS)
361
- * {RDF::WOT} - Web of Trust (WOT)
362
- * {RDF::XHTML} - Extensible HyperText Markup Language (XHTML)
363
- * {RDF::XHV} - XHTML Vocabulary (XHV)
364
330
 
365
331
  ## Dependencies
366
332
 
367
- * [Ruby](http://ruby-lang.org/) (>= 1.9.2)
333
+ * [Ruby](http://ruby-lang.org/) (>= 2.0)
368
334
  * [LinkHeader][] (>= 0.0.8)
369
335
  * Soft dependency on [RestClient][] (>= 1.7)
370
336
 
@@ -373,7 +339,7 @@ The following vocabularies will be deprecated in RDF.rb 2.0 and moved to the rdf
373
339
  The recommended installation method is via [RubyGems](http://rubygems.org/).
374
340
  To install the latest official release of RDF.rb, do:
375
341
 
376
- % [sudo] gem install rdf # Ruby 1.9.2+
342
+ % [sudo] gem install rdf # Ruby 2+
377
343
 
378
344
  ## Download
379
345
 
@@ -452,7 +418,6 @@ see <http://unlicense.org/> or the accompanying {file:UNLICENSE} file.
452
418
  [YARD]: http://yardoc.org/
453
419
  [YARD-GS]: http://rubydoc.info/docs/yard/file/docs/GettingStarted.md
454
420
  [PDD]: http://lists.w3.org/Archives/Public/public-rdf-ruby/2010May/0013.html
455
- [Backports]: http://rubygems.org/gems/backports
456
421
  [JSONLD doc]: http://rubydoc.info/github/ruby-rdf/json-ld/frames
457
422
  [LinkedData doc]: http://rubydoc.info/github/datagraph/linkeddata/master/frames
458
423
  [Microdata doc]: http://rubydoc.info/github/ruby-rdf/rdf-microdata/frames
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.99.1
1
+ 2.0.0.beta1
data/bin/rdf CHANGED
@@ -5,7 +5,7 @@ require 'rdf/cli'
5
5
 
6
6
  options = RDF::CLI.options do
7
7
  self.on('-v', '--verbose', 'Enable verbose output. May be given more than once.') do
8
- $VERBOSE = true
8
+ self.options[:logger].level = Logger::INFO
9
9
  end
10
10
 
11
11
  self.on('-V', '--version', 'Display the RDF.rb version and exit.') do
data/lib/rdf.rb CHANGED
@@ -7,72 +7,61 @@ require 'rdf/version'
7
7
 
8
8
  module RDF
9
9
  # RDF mixins
10
- autoload :Countable, 'rdf/mixin/countable'
11
- autoload :Durable, 'rdf/mixin/durable'
12
- autoload :Enumerable, 'rdf/mixin/enumerable'
13
- autoload :Indexable, 'rdf/mixin/indexable'
14
- autoload :Inferable, 'rdf/mixin/inferable'
15
- autoload :Mutable, 'rdf/mixin/mutable'
16
- autoload :Queryable, 'rdf/mixin/queryable'
17
- autoload :Readable, 'rdf/mixin/readable'
18
- autoload :TypeCheck, 'rdf/mixin/type_check'
19
- autoload :Writable, 'rdf/mixin/writable'
10
+ autoload :Countable, 'rdf/mixin/countable'
11
+ autoload :Durable, 'rdf/mixin/durable'
12
+ autoload :Enumerable, 'rdf/mixin/enumerable'
13
+ autoload :Indexable, 'rdf/mixin/indexable'
14
+ autoload :Mutable, 'rdf/mixin/mutable'
15
+ autoload :Queryable, 'rdf/mixin/queryable'
16
+ autoload :Readable, 'rdf/mixin/readable'
17
+ autoload :TypeCheck, 'rdf/mixin/type_check'
18
+ autoload :Transactable, 'rdf/mixin/transactable'
19
+ autoload :Writable, 'rdf/mixin/writable'
20
20
 
21
21
  # RDF objects
22
- autoload :Graph, 'rdf/model/graph'
23
- autoload :IRI, 'rdf/model/uri'
24
- autoload :Literal, 'rdf/model/literal'
25
- autoload :Node, 'rdf/model/node'
26
- autoload :Resource, 'rdf/model/resource'
27
- autoload :Statement, 'rdf/model/statement'
28
- autoload :URI, 'rdf/model/uri'
29
- autoload :Value, 'rdf/model/value'
30
- autoload :Term, 'rdf/model/term'
22
+ autoload :Graph, 'rdf/model/graph'
23
+ autoload :IRI, 'rdf/model/uri'
24
+ autoload :Literal, 'rdf/model/literal'
25
+ autoload :Node, 'rdf/model/node'
26
+ autoload :Resource, 'rdf/model/resource'
27
+ autoload :Statement, 'rdf/model/statement'
28
+ autoload :URI, 'rdf/model/uri'
29
+ autoload :Value, 'rdf/model/value'
30
+ autoload :Term, 'rdf/model/term'
31
31
 
32
32
  # RDF collections
33
- autoload :List, 'rdf/model/list'
33
+ autoload :List, 'rdf/model/list'
34
34
 
35
35
  # RDF serialization
36
- autoload :Format, 'rdf/format'
37
- autoload :Reader, 'rdf/reader'
38
- autoload :ReaderError, 'rdf/reader'
39
- autoload :Writer, 'rdf/writer'
40
- autoload :WriterError, 'rdf/writer'
36
+ autoload :Format, 'rdf/format'
37
+ autoload :Reader, 'rdf/reader'
38
+ autoload :ReaderError, 'rdf/reader'
39
+ autoload :Writer, 'rdf/writer'
40
+ autoload :WriterError, 'rdf/writer'
41
41
 
42
42
  # RDF serialization formats
43
- autoload :NTriples, 'rdf/ntriples'
44
- autoload :NQuads, 'rdf/nquads'
43
+ autoload :NTriples, 'rdf/ntriples'
44
+ autoload :NQuads, 'rdf/nquads'
45
45
 
46
46
  # RDF storage
47
- autoload :Dataset, 'rdf/model/repository'
48
- autoload :Repository, 'rdf/repository'
49
- autoload :Transaction, 'rdf/transaction'
47
+ autoload :Changeset, 'rdf/changeset'
48
+ autoload :Dataset, 'rdf/model/dataset'
49
+ autoload :Repository, 'rdf/repository'
50
+ autoload :Transaction, 'rdf/transaction'
50
51
 
51
52
  # RDF querying
52
- autoload :Query, 'rdf/query'
53
+ autoload :Query, 'rdf/query'
53
54
 
54
55
  # RDF vocabularies
55
- autoload :Vocabulary, 'rdf/vocabulary'
56
+ autoload :Vocabulary, 'rdf/vocabulary'
56
57
  autoload :StrictVocabulary, 'rdf/vocabulary'
57
58
  VOCABS = Dir.glob(File.join(File.dirname(__FILE__), 'rdf', 'vocab', '*.rb')).map { |f| File.basename(f)[0...-(File.extname(f).size)].to_sym } rescue []
58
59
 
59
- # Ruby versions < 2.0 are deprecated in RDF.rb 2.0
60
- warn %([DEPRECATION] Ruby versions before 2.0 are no longer supported starting with RDF.rb 2.0.) if RUBY_VERSION < "2.0"
61
-
62
60
  # Use const_missing instead of autoload to load most vocabularies so we can provide deprecation messages
63
61
  def self.const_missing(constant)
64
62
  if VOCABS.include?(constant.to_s.downcase.to_sym)
65
- @vocab_fault ||= {}
66
- raise "Class not found: RDF::#{constant}" if @vocab_fault[constant.to_s]
67
- @vocab_fault[constant.to_s] = true
68
- warn %([DEPRECATION] the #{constant} vocabulary will be moved to the rdf-vocab gem
69
- for the RDF.rb 2.0 release. Use as RDF::Vocab::#{constant}, or include RDF::Vocab in the RDF module.
70
- Called from #{Gem.location_of_caller.join(':')}
71
- ).gsub(/^\s+/, '') unless [:OWL, :RDFS, :RDFV, :XSD].include?(constant)
72
63
  require "rdf/vocab/#{constant.to_s.downcase}"
73
- klass = const_get(constant)
74
- return klass if klass
75
- raise "Class not found: RDF::#{constant}"
64
+ const_get(constant)
76
65
  else
77
66
  super
78
67
  end
@@ -131,8 +120,12 @@ module RDF
131
120
  #
132
121
  # @param (see RDF::Graph#initialize)
133
122
  # @return [RDF::Graph]
134
- def self.Graph(*args, &block)
135
- Graph.new(*args, &block)
123
+ def self.Graph(*args, **options, &block)
124
+ unless args.empty?
125
+ warn "[DEPRECATION] Graph#initialize now uses keyword arguments. Called from #{Gem.location_of_caller.join(':')}"
126
+ options[:graph_name] ||= args.first
127
+ end
128
+ Graph.new(options, &block)
136
129
  end
137
130
 
138
131
  ##
@@ -172,8 +165,6 @@ module RDF
172
165
  # @option options [RDF::Resource] :subject (nil)
173
166
  # @option options [RDF::URI] :predicate (nil)
174
167
  # @option options [RDF::Term] :object (nil)
175
- # @option options [RDF::Resource] :context (nil)
176
- # Alias for :graph_name, :context is deprecated in RDF.rb.
177
168
  # @option options [RDF::Resource] :graph_name (nil)
178
169
  # Note, a graph_name MUST be an IRI or BNode.
179
170
  # @return [RDF::Statement]
@@ -0,0 +1,161 @@
1
+ module RDF
2
+ ##
3
+ # An RDF changeset that can be applied to an {RDF::Mutable}.
4
+ #
5
+ # Changesets consist of a sequence of RDF statements to delete from and a
6
+ # sequence of RDF statements to insert into a target dataset.
7
+ #
8
+ # @example Applying a Changeset with block syntax
9
+ # graph = RDF::Graph.new
10
+ # graph << [RDF::URI('s_del'), RDF::URI('p_del'), RDF::URI('o_del')]
11
+ #
12
+ # RDF::Changeset.apply(graph) do |c|
13
+ # c.insert [RDF::URI('s1'), RDF::URI('p1'), RDF::URI('o1')]
14
+ # c.insert [RDF::URI('s2'), RDF::URI('p2'), RDF::URI('o2')]
15
+ # c.delete [RDF::URI('s_del'), RDF::URI('p_del'), RDF::URI('o_del')]
16
+ # end
17
+ #
18
+ # @example Defining a changeset for later application to a Mutable
19
+ # changes = RDF::Changeset.new do |c|
20
+ # c.insert [RDF::URI('s1'), RDF::URI('p1'), RDF::URI('o1')]
21
+ # c.insert [RDF::URI('s2'), RDF::URI('p2'), RDF::URI('o2')]
22
+ # c.delete [RDF::URI('s_del'), RDF::URI('p_del'), RDF::URI('o_del')]
23
+ # end
24
+ #
25
+ # graph = RDF::Graph.new
26
+ # graph << [RDF::URI('s_del'), RDF::URI('p_del'), RDF::URI('o_del')]
27
+ #
28
+ # changes.apply(graph) # or graph.apply_changeset(changes)
29
+ #
30
+ # @note When applying a Changeset, deletes are resolved before inserts.
31
+ #
32
+ # @since 2.0.0
33
+ class Changeset
34
+ include RDF::Mutable
35
+
36
+ ##
37
+ # Applies a changeset to the given mutable RDF::Enumerable .
38
+ #
39
+ # @param [RDF::Mutable] mutable
40
+ # @param [Hash{Symbol => Object}] options
41
+ # @yield [changes]
42
+ # @yieldparam [RDF::Changeset] changes
43
+ # @return [void]
44
+ def self.apply(mutable, options = {}, &block)
45
+ self.new(&block).apply(mutable, options)
46
+ end
47
+
48
+ ##
49
+ # RDF statements to delete when applied.
50
+ #
51
+ # @return [RDF::Enumerable]
52
+ attr_reader :deletes
53
+
54
+ ##
55
+ # RDF statements to insert when applied.
56
+ #
57
+ # @return [RDF::Enumerable]
58
+ attr_reader :inserts
59
+
60
+ ##
61
+ # Any additional options for this changeset.
62
+ #
63
+ # @return [Hash{Symbol => Object}]
64
+ attr_reader :options
65
+
66
+ ##
67
+ # Initializes this changeset.
68
+ #
69
+ # @param [RDF::Enumerable] insert (RDF::Graph.new)
70
+ # @param [RDF::Enumerable] delete (RDF::Graph.new)
71
+ # @yield [changes]
72
+ # @yieldparam [RDF::Changeset] changes
73
+ def initialize(insert: [], delete: [], &block)
74
+ @inserts = insert
75
+ @deletes = delete
76
+
77
+ @inserts.extend(RDF::Enumerable) unless @inserts.kind_of?(RDF::Enumerable)
78
+ @deletes.extend(RDF::Enumerable) unless @deletes.kind_of?(RDF::Enumerable)
79
+
80
+ if block_given?
81
+ case block.arity
82
+ when 1 then block.call(self)
83
+ else self.instance_eval(&block)
84
+ end
85
+ end
86
+ end
87
+
88
+ ##
89
+ # Returns `false` to indicate that this changeset is append-only.
90
+ #
91
+ # Changesets do not support the `RDF::Enumerable` protocol directly.
92
+ # To enumerate the RDF statements to be inserted or deleted, use the
93
+ # {RDF::Changeset#inserts} and {RDF::Changeset#deletes} accessors.
94
+ #
95
+ # @return [Boolean]
96
+ # @see RDF::Readable#readable?
97
+ def readable?
98
+ false
99
+ end
100
+
101
+ ##
102
+ # Applies this changeset to the given mutable RDF::Enumerable.
103
+ #
104
+ # This operation executes as a single write transaction.
105
+ #
106
+ # @param [RDF::Mutable] mutable
107
+ # @param [Hash{Symbol => Object}] options
108
+ # @return [void]
109
+ def apply(mutable, options = {})
110
+ mutable.apply_changeset(self)
111
+ end
112
+
113
+ ##
114
+ # @return [Boolean] `true` iff inserts and deletes are both empty
115
+ def empty?
116
+ deletes.empty? && inserts.empty?
117
+ end
118
+
119
+ ##
120
+ # Returns a developer-friendly representation of this changeset.
121
+ #
122
+ # @return [String]
123
+ def inspect
124
+ sprintf("#<%s:%#0x(deletes: %d, inserts: %d)>", self.class.name,
125
+ self.__id__, self.deletes.count, self.inserts.count)
126
+ end
127
+
128
+ ##
129
+ # Outputs a developer-friendly representation of this changeset to
130
+ # `stderr`.
131
+ #
132
+ # @return [void]
133
+ def inspect!
134
+ $stderr.puts(self.inspect)
135
+ end
136
+
137
+ protected
138
+
139
+ ##
140
+ # Appends an RDF statement to the sequence to insert when applied.
141
+ #
142
+ # @param [RDF::Statement] statement
143
+ # @return [void]
144
+ # @see RDF::Writable#insert_statement
145
+ def insert_statement(statement)
146
+ self.inserts << statement
147
+ end
148
+
149
+ ##
150
+ # Appends an RDF statement to the sequence to delete when applied.
151
+ #
152
+ # @param [RDF::Statement] statement
153
+ # @return [void]
154
+ # @see RDF::Mutable#delete_statement
155
+ def delete_statement(statement)
156
+ self.deletes << statement
157
+ end
158
+
159
+ undef_method :load, :update, :clear
160
+ end # Changeset
161
+ end # RDF
@@ -1,6 +1,7 @@
1
1
  require 'rdf'
2
2
  require 'rdf/ntriples'
3
3
  require 'rdf/nquads'
4
+ require 'logger'
4
5
  require 'optparse'
5
6
  begin
6
7
  gem 'linkeddata'
@@ -13,6 +14,11 @@ rescue LoadError
13
14
  rescue LoadError
14
15
  end
15
16
  end
17
+
18
+ begin
19
+ require 'json/ld'
20
+ rescue LoadError
21
+ end
16
22
  end
17
23
 
18
24
  class OptionParser
@@ -21,10 +27,87 @@ class OptionParser
21
27
  end
22
28
 
23
29
  module RDF
30
+ # Individual formats can modify options by updating {Reader.options} or {Writer.options}. Format-specific commands are taken from {Format.cli_commands} for each loaded format, which returns an array of lambdas taking arguments and options.
31
+ #
32
+ # @example Creating Reader-specific options:
33
+ # class Reader
34
+ # def self.options
35
+ # [
36
+ # RDF::CLI::Option.new(
37
+ # symbol: :canonicalize,
38
+ # datatype: TrueClass,
39
+ # on: ["--canonicalize"],
40
+ # description: "Canonicalize input/output.") {true},
41
+ # ...
42
+ # ]
43
+ # end
44
+ #
45
+ # @example Creating Format-specific commands:
46
+ # class Format
47
+ # def self.cli_commands
48
+ # {
49
+ # count: ->(argv, opts) do
50
+ # count = 0
51
+ # RDF::CLI.parse(argv, opts) do |reader|
52
+ # reader.each_statement do |statement|
53
+ # count += 1
54
+ # end
55
+ # end
56
+ # $stdout.puts "Parsed #{count} statements"
57
+ # end,
58
+ # }
59
+ # end
60
+ #
61
+ # Format-specific commands should verify that the reader and/or output format are appropriate for the command.
24
62
  class CLI
25
63
 
64
+ # Option description for use within Readers/Writers. See {RDF::Reader.options} and {RDF::Writer.options} for example usage.
65
+ class Option
66
+ # Symbol used for this option when calling `Reader.new`
67
+ # @return [Symbol]
68
+ attr_reader :symbol
69
+
70
+ # Arguments passed to OptionParser#on
71
+ # @return [Array<String>]
72
+ attr_reader :on
73
+
74
+ # Description of this option (optional)
75
+ # @return [String]
76
+ attr_reader :description
77
+
78
+ # Argument datatype, which may be enumerated string values
79
+ # @return [Class, Array<String>]
80
+ attr_reader :datatype
81
+
82
+ # Allows multiple comma-spearated values.
83
+ # @return [Boolean]
84
+ attr_reader :multiple
85
+
86
+ ##
87
+ # Create a new option with optional callback.
88
+ #
89
+ # @param [Symbol] symbol
90
+ # @param [Array<String>] on
91
+ # @param [String] description
92
+ # @param [Class, Array<String>] datatype of value
93
+ # @param [Boolean] multiple can have multiple comma-separated values
94
+ # @yield value which may be used within `OptionParser#on`
95
+ # @yieldparam [Object] value The option value as parsed using `on` argument
96
+ # @yieldreturn [Object] a possibly modified input value
97
+ def initialize(symbol: nil, on: nil, description: nil, datatype: String, multiple: false, &block)
98
+ raise ArgumentError, "symbol is a required argument" unless symbol
99
+ raise ArgumentError, "on is a required argument" unless on
100
+ @symbol, @on, @description, @datatype, @multiple, @callback = symbol.to_sym, Array(on), description, datatype, multiple, block
101
+ end
102
+
103
+ def call(arg)
104
+ @callback ? @callback.call(arg) : arg
105
+ end
106
+ end
107
+
108
+ # @private
26
109
  COMMANDS = {
27
- "count" => lambda do |argv, opts|
110
+ count: ->(argv, opts) do
28
111
  start = Time.new
29
112
  count = 0
30
113
  self.parse(argv, opts) do |reader|
@@ -35,14 +118,17 @@ module RDF
35
118
  secs = Time.new - start
36
119
  $stdout.puts "Parsed #{count} statements with #{@readers.join(', ')} in #{secs} seconds @ #{count/secs} statements/second."
37
120
  end,
38
- "lenghts" => lambda do |argv, opts|
121
+ help: ->(argv, opts) do
122
+ self.usage(self.options)
123
+ end,
124
+ lenghts: ->(argv, opts) do
39
125
  self.parse(argv, opts) do |reader|
40
126
  reader.each_statement do |statement|
41
127
  $stdout.puts statement.to_s.size
42
128
  end
43
129
  end
44
130
  end,
45
- "objects" => lambda do |argv, opts|
131
+ objects: ->(argv, opts) do
46
132
  $stdout.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
47
133
  self.parse(argv, opts) do |reader|
48
134
  reader.each_statement do |statement|
@@ -50,7 +136,7 @@ module RDF
50
136
  end
51
137
  end
52
138
  end,
53
- "predicates" => lambda do |argv, opts|
139
+ predicates: ->(argv, opts) do
54
140
  $stdout.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
55
141
  self.parse(argv, opts) do |reader|
56
142
  reader.each_statement do |statement|
@@ -58,7 +144,7 @@ module RDF
58
144
  end
59
145
  end
60
146
  end,
61
- "serialize" => lambda do |argv, opts|
147
+ serialize: ->(argv, opts) do
62
148
  writer_class = RDF::Writer.for(opts[:output_format]) || RDF::NTriples::Writer
63
149
  out = opts[:output] || $stdout
64
150
  out.set_encoding(Encoding::UTF_8) if out.respond_to?(:set_encoding) && RUBY_PLATFORM == "java"
@@ -70,13 +156,26 @@ module RDF
70
156
  end
71
157
  end
72
158
  end,
73
- "subjects" => lambda do |argv, opts|
159
+ subjects: ->(argv, opts) do
74
160
  $stdout.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
75
161
  self.parse(argv, opts) do |reader|
76
162
  reader.each_statement do |statement|
77
163
  $stdout.puts statement.subject.to_ntriples
78
164
  end
79
165
  end
166
+ end,
167
+ validate: ->(argv, opts) do
168
+ start = Time.new
169
+ count = 0
170
+ valid = true
171
+ self.parse(argv, opts) do |reader|
172
+ reader.each_statement do |statement|
173
+ count += 1
174
+ valid = false if statement.invalid?
175
+ end
176
+ end
177
+ secs = Time.new - start
178
+ $stdout.puts "Validated #{count} statements with #{@readers.join(', ')} in #{secs} seconds @ #{count/secs} statements/second."
80
179
  end
81
180
  }
82
181
 
@@ -90,17 +189,36 @@ module RDF
90
189
  # @return [OptionParser]
91
190
  def self.options(&block)
92
191
  options = OptionParser.new
192
+ logger = Logger.new($stderr)
193
+ logger.level = Logger::ERROR
194
+ logger.formatter = lambda {|severity, datetime, progname, msg| "#{severity} #{msg}\n"}
93
195
  opts = options.options = {
94
- base_uri: nil,
95
- canonicalize: false,
96
196
  debug: false,
97
197
  evaluate: nil,
98
198
  format: nil,
99
199
  output: $stdout,
100
200
  output_format: :ntriples,
101
- validate: false,
201
+ logger: logger
102
202
  }
103
203
 
204
+ # Add default Reader and Writer options
205
+ RDF::Reader.options.each do |cli_opt|
206
+ next if opts.has_key?(cli_opt.symbol)
207
+ on_args = cli_opt.on || []
208
+ on_args << cli_opt.description if cli_opt.description
209
+ options.on(*on_args) do |arg|
210
+ opts[cli_opt.symbol] = cli_opt.call(arg)
211
+ end
212
+ end
213
+ RDF::Writer.options.each do |cli_opt|
214
+ next if opts.has_key?(cli_opt.symbol)
215
+ on_args = cli_opt.on || []
216
+ on_args << cli_opt.description if cli_opt.description
217
+ options.on(*on_args) do |arg|
218
+ opts[cli_opt.symbol] = cli_opt.call(arg)
219
+ end
220
+ end
221
+
104
222
  # Command-specific options
105
223
  if block_given?
106
224
  case block.arity
@@ -110,19 +228,28 @@ module RDF
110
228
  end
111
229
  options.banner ||= "Usage: #{self.basename} [options] command [args...]"
112
230
 
113
- options.on('--canonicalize', 'Canonicalize input.') do
114
- opts[:canonicalize] = true
115
- end
116
-
117
231
  options.on('-d', '--debug', 'Enable debug output for troubleshooting.') do
118
- opts[:debug] = $DEBUG = true
232
+ opts[:logger].level = Logger::DEBUG
119
233
  end
120
234
 
121
235
  options.on("-e", "--evaluate STRING", "Evaluate argument as RDF input, if no files are specified") do |arg|
122
236
  opts[:evaluate] = arg
123
237
  end
124
238
 
125
- options.on("--input-format FORMAT", "Format of input file, uses heuristic if not specified") do |arg|
239
+ options.on("--input-format FORMAT", "--format FORMAT", "Format of input file, uses heuristic if not specified") do |arg|
240
+ unless reader = RDF::Reader.for(arg.downcase.to_sym)
241
+ self.abort "No reader found for #{arg.downcase.to_sym}. Available readers:\n #{self.formats(reader: true).join("\n ")}"
242
+ end
243
+
244
+ # Add format-specific reader options
245
+ reader.options.each do |cli_opt|
246
+ next if opts.has_key?(cli_opt.symbol)
247
+ on_args = cli_opt.on || []
248
+ on_args << cli_opt.description if cli_opt.description
249
+ options.on(*on_args) do |arg|
250
+ opts[cli_opt.symbol] = cli_opt.call(arg)
251
+ end
252
+ end
126
253
  opts[:format] = arg.downcase.to_sym
127
254
  end
128
255
 
@@ -131,21 +258,24 @@ module RDF
131
258
  end
132
259
 
133
260
  options.on("--output-format FORMAT", "Format of output file, defaults to NTriples") do |arg|
134
- opts[:output_format] = arg.downcase.to_sym
135
- end
136
-
137
- options.on('--uri URI', 'Base URI of input file, defaults to the filename.') do |arg|
138
- opts[:base_uri] = arg
139
- end
261
+ unless writer = RDF::Writer.for(arg.downcase.to_sym)
262
+ self.abort "No writer found for #{arg.downcase.to_sym}. Available writers:\n #{self.formats(writer: true).join("\n ")}"
263
+ end
140
264
 
141
- options.on('--validate', 'Validate input file.') do
142
- opts[:validate] = true
265
+ # Add format-specific writer options
266
+ writer.options.each do |cli_opt|
267
+ next if opts.has_key?(cli_opt.symbol)
268
+ on_args = cli_opt.on || []
269
+ on_args << cli_opt.description if cli_opt.description
270
+ options.on(*on_args) do |arg|
271
+ opts[cli_opt.symbol] = cli_opt.call(arg)
272
+ end
273
+ end
274
+ opts[:output_format] = arg.downcase.to_sym
143
275
  end
144
276
 
145
277
  options.on_tail("-h", "--help", "Show this message") do
146
- $stdout.puts options
147
- $stdout.puts "Available commands:\n\t#{self.commands.join("\n\t")}"
148
- exit
278
+ self.usage(options)
149
279
  end
150
280
 
151
281
  begin
@@ -158,25 +288,57 @@ module RDF
158
288
  end
159
289
 
160
290
  ##
161
- # @param [String] command
291
+ # Output usage message
292
+ def self.usage(options)
293
+ $stdout.puts options
294
+ $stdout.puts "Note: available commands and options may be different depending on selected --input-format and/or --output-format."
295
+ $stdout.puts "Available commands:\n\t#{self.commands.join("\n\t")}"
296
+ $stdout.puts "Available formats:\n\t#{(self.formats).join("\n\t")}"
297
+ end
298
+
299
+ ##
300
+ # @param [#to_sym] command
162
301
  # @param [Array<String>] args
163
302
  # @return [Boolean]
164
303
  def self.exec_command(command, args, options = {})
165
- unless COMMANDS.has_key?(command)
166
- abort "#{File.basename($0)}: unknown command `#{command}'"
304
+ unless commands.include?(command.to_s)
305
+ abort "unknown command `#{command}'"
167
306
  end
168
307
 
169
- COMMANDS[command].call(args, options)
308
+ COMMANDS[command.to_sym].call(args, options)
309
+ rescue ArgumentError => e
310
+ abort e.message
170
311
  end
171
312
 
172
313
  ##
173
314
  # @return [Array<String>] list of executable commands
174
315
  def self.commands
175
- COMMANDS.keys
316
+ # First, load commands from other formats
317
+ unless @commands_loaded
318
+ RDF::Format.each do |format|
319
+ format.cli_commands.each do |command, lambda|
320
+ COMMANDS[command] ||= lambda
321
+ end
322
+ end
323
+ @commands_loaded = true
324
+ end
325
+ COMMANDS.keys.map(&:to_s).sort
326
+ end
327
+
328
+ ##
329
+ # @return [Array<String>] list of available formats
330
+ def self.formats(reader: false, writer: false)
331
+ f = RDF::Format.each.
332
+ select {|f| (reader ? f.reader : (writer ? f.writer : true))}.
333
+ inject({}) do |memo, reader|
334
+ memo.merge(reader.to_sym => reader.name)
335
+ end
336
+ sym_len = f.keys.map {|k| k.to_s.length}.max
337
+ f.map {|s, t| "%*s: %s" % [sym_len, s, t]}
176
338
  end
177
339
 
178
340
  ##
179
- # Parse each file, STDIN or specified string in `options[:evaluate]`
341
+ # Parse each file, $stdin or specified string in `options[:evaluate]`
180
342
  # yielding a reader
181
343
  #
182
344
  # @param [Array<String>] files
@@ -186,7 +348,7 @@ module RDF
186
348
  def self.parse(files, options = {}, &block)
187
349
  if files.empty?
188
350
  # If files are empty, either use options[:execute]
189
- input = options[:evaluate] ? StringIO.new(options[:evaluate]) : STDIN
351
+ input = options[:evaluate] ? StringIO.new(options[:evaluate]) : $stdin
190
352
  input.set_encoding(options.fetch(:encoding, Encoding::UTF_8))
191
353
  RDF::Reader.for(options[:format] || :ntriples).new(input, options) do |reader|
192
354
  yield(reader)