rdf 1.99.1 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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)