rdf 3.0.11 → 3.1.2

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/AUTHORS +1 -1
  3. data/README.md +127 -95
  4. data/UNLICENSE +1 -1
  5. data/VERSION +1 -1
  6. data/etc/doap.nt +79 -85
  7. data/lib/rdf.rb +35 -23
  8. data/lib/rdf/changeset.rb +80 -19
  9. data/lib/rdf/cli.rb +7 -7
  10. data/lib/rdf/format.rb +17 -10
  11. data/lib/rdf/mixin/enumerable.rb +4 -3
  12. data/lib/rdf/mixin/mutable.rb +5 -15
  13. data/lib/rdf/mixin/queryable.rb +12 -4
  14. data/lib/rdf/mixin/transactable.rb +2 -2
  15. data/lib/rdf/mixin/writable.rb +9 -14
  16. data/lib/rdf/model/dataset.rb +1 -1
  17. data/lib/rdf/model/graph.rb +7 -4
  18. data/lib/rdf/model/list.rb +5 -5
  19. data/lib/rdf/model/literal.rb +3 -3
  20. data/lib/rdf/model/statement.rb +32 -9
  21. data/lib/rdf/model/uri.rb +53 -32
  22. data/lib/rdf/nquads.rb +6 -6
  23. data/lib/rdf/ntriples.rb +7 -5
  24. data/lib/rdf/ntriples/reader.rb +29 -7
  25. data/lib/rdf/ntriples/writer.rb +10 -1
  26. data/lib/rdf/query.rb +27 -35
  27. data/lib/rdf/query/hash_pattern_normalizer.rb +14 -12
  28. data/lib/rdf/query/pattern.rb +51 -18
  29. data/lib/rdf/query/solution.rb +20 -1
  30. data/lib/rdf/query/solutions.rb +15 -5
  31. data/lib/rdf/query/variable.rb +17 -5
  32. data/lib/rdf/reader.rb +76 -25
  33. data/lib/rdf/repository.rb +32 -18
  34. data/lib/rdf/transaction.rb +1 -1
  35. data/lib/rdf/util.rb +6 -5
  36. data/lib/rdf/util/cache.rb +2 -2
  37. data/lib/rdf/util/coercions.rb +60 -0
  38. data/lib/rdf/util/file.rb +20 -10
  39. data/lib/rdf/util/logger.rb +6 -6
  40. data/lib/rdf/util/uuid.rb +4 -4
  41. data/lib/rdf/vocab/owl.rb +401 -86
  42. data/lib/rdf/vocab/rdfs.rb +81 -18
  43. data/lib/rdf/vocab/rdfv.rb +147 -1
  44. data/lib/rdf/vocab/writer.rb +41 -3
  45. data/lib/rdf/vocab/xsd.rb +203 -2
  46. data/lib/rdf/vocabulary.rb +73 -15
  47. data/lib/rdf/writer.rb +33 -11
  48. metadata +34 -28
@@ -19,7 +19,7 @@ module RDF
19
19
  #
20
20
  # @example Querying for statements having a given predicate
21
21
  # queryable.query([nil, RDF::Vocab::DOAP.developer, nil])
22
- # queryable.query(predicate: RDF::Vocab::DOAP.developer) do |statement|
22
+ # queryable.query({predicate: RDF::Vocab::DOAP.developer}) do |statement|
23
23
  # puts statement.inspect
24
24
  # end
25
25
  #
@@ -50,7 +50,7 @@ module RDF
50
50
  solutions = RDF::Query::Solutions.new
51
51
  block = lambda {|solution| solutions << solution} unless block_given?
52
52
  before_query(pattern) if respond_to?(:before_query)
53
- query_execute(pattern, options, &block)
53
+ query_execute(pattern, **options, &block)
54
54
  after_query(pattern) if respond_to?(:after_query)
55
55
  # Returns the solutions, not an enumerator
56
56
  solutions
@@ -85,7 +85,7 @@ module RDF
85
85
 
86
86
  # Otherwise, we delegate to `#query_pattern`:
87
87
  else # pattern.variable?
88
- query_pattern(pattern, options, &block)
88
+ query_pattern(pattern, **options, &block)
89
89
  end
90
90
  after_query(pattern) if respond_to?(:after_query)
91
91
  enum
@@ -116,7 +116,7 @@ module RDF
116
116
  # query execution by breaking down the query into its constituent
117
117
  # triple patterns and invoking `RDF::Query::Pattern#execute` on each
118
118
  # pattern.
119
- query.execute(self, options, &block)
119
+ query.execute(self, **options, &block)
120
120
  end
121
121
  protected :query_execute
122
122
 
@@ -128,6 +128,14 @@ module RDF
128
128
  # method in order to provide for storage-specific optimized triple
129
129
  # pattern matching.
130
130
  #
131
+ # ## RDFStar (RDF*)
132
+ #
133
+ # Statements may have embedded statements as either a subject or object, recursively.
134
+ #
135
+ # Patterns may also have embedded patterns as either a subject or object, recursively.
136
+ #
137
+ # When matching, match an embedded pattern against embedded statements, recursively. (see {RDF::Query::Pattern#eql?})
138
+ #
131
139
  # @param [RDF::Query::Pattern] pattern
132
140
  # the query pattern to match
133
141
  # @param [Hash{Symbol => Object}] options ({})
@@ -21,14 +21,14 @@ module RDF
21
21
  #
22
22
  # @example running a transaction
23
23
  # repository.transaction(mutable: true) do |tx|
24
- # tx.insert [RDF::URI("http://rubygems.org/gems/rdf"), RDF::RDFS.label, "RDF.rb"]
24
+ # tx.insert [RDF::URI("https://rubygems.org/gems/rdf"), RDF::RDFS.label, "RDF.rb"]
25
25
  # end
26
26
  #
27
27
  # Raising an error within the transaction block causes automatic rollback.
28
28
  #
29
29
  # @example manipulating a live transaction
30
30
  # tx = repository.transaction(mutable: true)
31
- # tx.insert [RDF::URI("http://rubygems.org/gems/rdf"), RDF::RDFS.label, "RDF.rb"]
31
+ # tx.insert [RDF::URI("https://rubygems.org/gems/rdf"), RDF::RDFS.label, "RDF.rb"]
32
32
  # tx.execute
33
33
  #
34
34
  # @overload transaction(mutable: false)
@@ -7,6 +7,7 @@ module RDF
7
7
  # @see RDF::Repository
8
8
  module Writable
9
9
  extend RDF::Util::Aliasing::LateBound
10
+ include RDF::Util::Coercions
10
11
 
11
12
  ##
12
13
  # Returns `true` if `self` is writable.
@@ -53,31 +54,20 @@ module RDF
53
54
  # @overload insert(*statements)
54
55
  # @param [Array<RDF::Statement>] statements
55
56
  # @return [self]
57
+ # @raise [ArgumentError] on an attempt to insert an embedded statement when it is not supported
56
58
  #
57
59
  # @overload insert(statements)
58
60
  # @param [Enumerable<RDF::Statement>] statements
59
61
  # @return [self]
62
+ # @raise [ArgumentError] on an attempt to insert an embedded statement when it is not supported
60
63
  def insert(*statements)
61
- statements.map! do |value|
62
- case
63
- when value.respond_to?(:each_statement)
64
- insert_statements(value)
65
- nil
66
- when (statement = Statement.from(value))
67
- statement
68
- else
69
- raise ArgumentError.new("not a valid statement: #{value.inspect}")
70
- end
71
- end
72
- statements.compact!
73
- insert_statements(statements) unless statements.empty?
64
+ coerce_statements(statements) { |value| insert_statements value }
74
65
 
75
66
  return self
76
67
  end
77
68
  alias_method :insert!, :insert
78
69
 
79
70
  protected
80
-
81
71
  ##
82
72
  # Inserts statements from the given RDF reader into the underlying
83
73
  # storage or output stream.
@@ -132,10 +122,14 @@ module RDF
132
122
  #
133
123
  # @param [RDF::Enumerable] statements
134
124
  # @return [void]
125
+ # @raise [ArgumentError] on an attempt to insert an embedded statement when it is not supported
135
126
  # @since 0.1.6
136
127
  def insert_statements(statements)
137
128
  each = statements.respond_to?(:each_statement) ? :each_statement : :each
138
129
  statements.__send__(each) do |statement|
130
+ if statement.embedded? && respond_to?(:supports?) && !supports?(:rdfstar)
131
+ raise ArgumentError, "Wriable does not support embedded statements"
132
+ end
139
133
  insert_statement(statement)
140
134
  end
141
135
  end
@@ -150,6 +144,7 @@ module RDF
150
144
  #
151
145
  # @param [RDF::Statement] statement
152
146
  # @return [void]
147
+ # @raise [ArgumentError] on an attempt to insert an embedded statement when it is not supported
153
148
  # @abstract
154
149
  def insert_statement(statement)
155
150
  raise NotImplementedError.new("#{self.class}#insert_statement")
@@ -104,7 +104,7 @@ module RDF
104
104
  # @private
105
105
  # @see RDF::Enumerable#supports?
106
106
  def supports?(feature)
107
- return true if feature == :graph_name
107
+ return true if [:graph_name, :rdfstar].include?(feature)
108
108
  super
109
109
  end
110
110
 
@@ -211,7 +211,7 @@ module RDF
211
211
  # @return [Integer]
212
212
  # @see RDF::Enumerable#count
213
213
  def count
214
- @data.query(graph_name: graph_name || false).count
214
+ @data.query({graph_name: graph_name || false}).count
215
215
  end
216
216
 
217
217
  ##
@@ -237,7 +237,7 @@ module RDF
237
237
  # @see RDF::Enumerable#each_statement
238
238
  def each(&block)
239
239
  if @data.respond_to?(:query)
240
- @data.query(graph_name: graph_name || false, &block)
240
+ @data.query({graph_name: graph_name || false}, &block)
241
241
  elsif @data.respond_to?(:each)
242
242
  @data.each(&block)
243
243
  else
@@ -259,11 +259,11 @@ module RDF
259
259
  ##
260
260
  # Graph equivalence based on the contents of each graph being _exactly_
261
261
  # the same. To determine if the have the same _meaning_, consider
262
- # [rdf-isomorphic](http://rubygems.org/gems/rdf-isomorphic).
262
+ # [rdf-isomorphic](https://rubygems.org/gems/rdf-isomorphic).
263
263
  #
264
264
  # @param [RDF::Graph] other
265
265
  # @return [Boolean]
266
- # @see http://rubygems.org/gems/rdf-isomorphic
266
+ # @see https://rubygems.org/gems/rdf-isomorphic
267
267
  def ==(other)
268
268
  other.is_a?(RDF::Graph) &&
269
269
  graph_name == other.graph_name &&
@@ -283,6 +283,9 @@ module RDF
283
283
  # @private
284
284
  # @see RDF::Mutable#insert
285
285
  def insert_statement(statement)
286
+ if statement.embedded? && !@data.supports?(:rdfstar)
287
+ raise ArgumentError, "Graph does not support embedded statements"
288
+ end
286
289
  statement = statement.dup
287
290
  statement.graph_name = graph_name
288
291
  @data.insert(statement)
@@ -59,7 +59,7 @@ module RDF
59
59
  def initialize(subject: nil, graph: nil, values: nil, &block)
60
60
  @subject = subject || RDF.nil
61
61
  @graph = graph || RDF::Graph.new
62
- is_empty = @graph.query(subject: subject, predicate: RDF.first).empty?
62
+ is_empty = @graph.query({subject: subject, predicate: RDF.first}).empty?
63
63
 
64
64
  if subject && is_empty
65
65
  # An empty list with explicit subject and value initializers
@@ -115,7 +115,7 @@ module RDF
115
115
  list_nodes << li
116
116
  rest = nil
117
117
  firsts = rests = 0
118
- @graph.query(subject: li) do |st|
118
+ @graph.query({subject: li}) do |st|
119
119
  return false unless st.subject.node?
120
120
  case st.predicate
121
121
  when RDF.first
@@ -136,7 +136,7 @@ module RDF
136
136
 
137
137
  # All elements other than the head must be referenced exactly once
138
138
  return list_nodes.all? do |li|
139
- refs = @graph.query(object: li).count
139
+ refs = @graph.query({object: li}).count
140
140
  case refs
141
141
  when 0 then li == subject
142
142
  when 1 then true
@@ -479,7 +479,7 @@ module RDF
479
479
  # @return [Boolean]
480
480
  # @see http://ruby-doc.org/core-2.2.2/Array.html#method-i-empty-3F
481
481
  def empty?
482
- graph.query(subject: subject, predicate: RDF.first).empty?
482
+ graph.query({subject: subject, predicate: RDF.first}).empty?
483
483
  end
484
484
 
485
485
  ##
@@ -822,7 +822,7 @@ module RDF
822
822
  return enum_statement unless block_given?
823
823
 
824
824
  each_subject do |subject|
825
- graph.query(subject: subject, &block)
825
+ graph.query({subject: subject}, &block)
826
826
  end
827
827
  end
828
828
  alias_method :to_rdf, :each_statement
@@ -49,7 +49,7 @@ module RDF
49
49
  # RDF::Literal.new(123).datatype #=> XSD.integer
50
50
  # RDF::Literal.new(9223372036854775807).datatype #=> XSD.integer
51
51
  # RDF::Literal.new(3.1415).datatype #=> XSD.double
52
- # RDF::Literal.new(Time.now).datatype #=> XSD.time
52
+ # RDF::Literal.new(Time.now).datatype #=> XSD.dateTime
53
53
  # RDF::Literal.new(Date.new(2010)).datatype #=> XSD.date
54
54
  # RDF::Literal.new(DateTime.new(2010)).datatype #=> XSD.dateTime
55
55
  #
@@ -119,8 +119,8 @@ module RDF
119
119
  when ::Float then RDF::Literal::Double
120
120
  when ::BigDecimal then RDF::Literal::Decimal
121
121
  when ::DateTime then RDF::Literal::DateTime
122
+ when ::Time then RDF::Literal::DateTime
122
123
  when ::Date then RDF::Literal::Date
123
- when ::Time then RDF::Literal::Time # FIXME: Ruby's Time class can represent datetimes as well
124
124
  when ::Symbol then RDF::Literal::Token
125
125
  else self
126
126
  end
@@ -360,7 +360,7 @@ module RDF
360
360
  # @return [Boolean] `true` or `false`
361
361
  # @since 0.2.1
362
362
  def valid?
363
- return false if language? && language.to_s !~ /^[a-zA-Z]+(-[a-zA-Z0-9]+)*$/
363
+ return false if language? && language.to_s !~ /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/
364
364
  return false if datatype? && datatype.invalid?
365
365
  grammar = self.class.const_get(:GRAMMAR) rescue nil
366
366
  grammar.nil? || value.match?(grammar)
@@ -3,7 +3,7 @@ module RDF
3
3
  # An RDF statement.
4
4
  #
5
5
  # @example Creating an RDF statement
6
- # s = RDF::URI.new("http://rubygems.org/gems/rdf")
6
+ # s = RDF::URI.new("https://rubygems.org/gems/rdf")
7
7
  # p = RDF::Vocab::DC.creator
8
8
  # o = RDF::URI.new("http://ar.to/#self")
9
9
  # RDF::Statement(s, p, o)
@@ -14,7 +14,7 @@ module RDF
14
14
  #
15
15
  # @example Creating an RDF statement from a `Hash`
16
16
  # RDF::Statement({
17
- # subject: RDF::URI.new("http://rubygems.org/gems/rdf"),
17
+ # subject: RDF::URI.new("https://rubygems.org/gems/rdf"),
18
18
  # predicate: RDF::Vocab::DC.creator,
19
19
  # object: RDF::URI.new("http://ar.to/#self"),
20
20
  # })
@@ -26,7 +26,7 @@ module RDF
26
26
  # RDF::Statement(s, p, "o")
27
27
  #
28
28
  class Statement
29
- include RDF::Value
29
+ include RDF::Resource
30
30
 
31
31
  ##
32
32
  # @private
@@ -112,7 +112,7 @@ module RDF
112
112
  elsif @subject.nil?
113
113
  nil
114
114
  else
115
- raise ArgumentError, "expected subject to be nil or a term, was #{@subject.inspect}"
115
+ raise ArgumentError, "expected subject to be nil or a resource, was #{@subject.inspect}"
116
116
  end
117
117
  @predicate = Node.intern(@predicate) if @predicate.is_a?(Symbol)
118
118
  @object = if @object.is_a?(Value)
@@ -124,6 +124,15 @@ module RDF
124
124
  else
125
125
  Literal.new(@object)
126
126
  end
127
+ @graph_name = if @graph_name.is_a?(Value)
128
+ @graph_name.to_term
129
+ elsif @graph_name.is_a?(Symbol)
130
+ Node.intern(@graph_name)
131
+ elsif !@graph_name
132
+ @graph_name
133
+ else
134
+ raise ArgumentError, "expected graph_name to be nil or a resource, was #{@graph_name.inspect}"
135
+ end
127
136
  end
128
137
 
129
138
  ##
@@ -140,10 +149,18 @@ module RDF
140
149
  #
141
150
  # @return [Boolean]
142
151
  def variable?
143
- !(has_subject? && subject.resource? &&
144
- has_predicate? && predicate.resource? &&
145
- has_object? && (object.resource? || object.literal?) &&
146
- (has_graph? ? graph_name.resource? : true))
152
+ !(has_subject? && subject.constant? &&
153
+ has_predicate? && predicate.constant? &&
154
+ has_object? && object.constant? &&
155
+ (has_graph? ? graph_name.constant? : true))
156
+ end
157
+
158
+ ##
159
+ # Returns `true` if any element of the statement is, itself, a statement.
160
+ #
161
+ # @return [Boolean]
162
+ def embedded?
163
+ subject && subject.statement? || object && object.statement?
147
164
  end
148
165
 
149
166
  ##
@@ -386,7 +403,13 @@ module RDF
386
403
  # @return [String]
387
404
  def to_s
388
405
  (graph_name ? to_quad : to_triple).map do |term|
389
- term.respond_to?(:to_base) ? term.to_base : term.inspect
406
+ if term.is_a?(Statement)
407
+ "<<#{term.to_s[0..-3]}>>"
408
+ elsif term.respond_to?(:to_base)
409
+ term.to_base
410
+ else
411
+ term.inspect
412
+ end
390
413
  end.join(" ") + " ."
391
414
  end
392
415
 
@@ -7,23 +7,23 @@ module RDF
7
7
  # Also compatible with International Resource Identifier (IRI)
8
8
  #
9
9
  # @example Creating a URI reference (1)
10
- # uri = RDF::URI.new("http://rubygems.org/gems/rdf")
10
+ # uri = RDF::URI.new("https://rubygems.org/gems/rdf")
11
11
  #
12
12
  # @example Creating a URI reference (2)
13
13
  # uri = RDF::URI.new(scheme: 'http', host: 'rubygems.org', path: '/gems/rdf')
14
- # #=> RDF::URI.new("http://rubygems.org/gems/rdf")
14
+ # #=> RDF::URI.new("https://rubygems.org/gems/rdf")
15
15
  #
16
16
  # @example Creating an interned URI reference
17
- # uri = RDF::URI.intern("http://rubygems.org/gems/rdf")
17
+ # uri = RDF::URI.intern("https://rubygems.org/gems/rdf")
18
18
  #
19
19
  # @example Getting the string representation of a URI
20
- # uri.to_s #=> "http://rubygems.org/gems/rdf"
20
+ # uri.to_s #=> "https://rubygems.org/gems/rdf"
21
21
  #
22
- # http://en.wikipedia.org/wiki/Internationalized_Resource_Identifier
23
- # @see http://en.wikipedia.org/wiki/Uniform_Resource_Identifier
24
- # @see http://www.ietf.org/rfc/rfc3986.txt
25
- # @see http://www.ietf.org/rfc/rfc3987.txt
26
- # @see http://www.rubydoc.info/gems/addressable
22
+ # @see https://en.wikipedia.org/wiki/Internationalized_Resource_Identifier
23
+ # @see https://en.wikipedia.org/wiki/Uniform_Resource_Identifier
24
+ # @see https://www.ietf.org/rfc/rfc3986.txt
25
+ # @see https://www.ietf.org/rfc/rfc3987.txt
26
+ # @see https://rubydoc.info/gems/addressable
27
27
  class URI
28
28
  include RDF::Resource
29
29
 
@@ -141,8 +141,8 @@ module RDF
141
141
  #
142
142
  # (see #initialize)
143
143
  # @return [RDF::URI] an immutable, frozen URI object
144
- def self.intern(str, *args)
145
- (cache[(str = str.to_s).to_sym] ||= self.new(str, *args)).freeze
144
+ def self.intern(str, *args, **options)
145
+ (cache[(str = str.to_s).to_sym] ||= self.new(str, *args, **options)).freeze
146
146
  end
147
147
 
148
148
  ##
@@ -225,11 +225,9 @@ module RDF
225
225
  @mutex = Mutex.new
226
226
  uri = args.first
227
227
  if uri
228
- @value = uri.to_s
229
- if @value.encoding != Encoding::UTF_8
230
- @value.dup.force_encoding(Encoding::UTF_8)
231
- @value.freeze
232
- end
228
+ @value = uri.to_s.dup
229
+ @value.dup.force_encoding(Encoding::UTF_8) if @value.encoding != Encoding::UTF_8
230
+ @value.freeze
233
231
  else
234
232
  %w(
235
233
  scheme
@@ -402,7 +400,7 @@ module RDF
402
400
  # @example Joining two URIs
403
401
  # RDF::URI.new('http://example.org/foo/bar').join('/foo')
404
402
  # #=> RDF::URI('http://example.org/foo')
405
- # @see <http://github.com/ruby-rdf/rdf-spec/blob/master/lib/rdf/spec/uri.rb>
403
+ # @see <https://github.com/ruby-rdf/rdf-spec/blob/master/lib/rdf/spec/uri.rb>
406
404
  # @see <http://tools.ietf.org/html/rfc3986#section-5.2>
407
405
  # @see RDF::URI#/
408
406
  # @see RDF::URI#+
@@ -431,7 +429,9 @@ module RDF
431
429
  joined_parts[:query] = uri.query
432
430
  else
433
431
  # Merge path segments from section 5.2.3
434
- base_path = path.to_s.sub(/\/[^\/]*$/, '/')
432
+ # Note that if the path includes no segments, the entire path is removed
433
+ # > return a string consisting of the reference's path component appended to all but the last segment of the base URI's path (i.e., excluding any characters after the right-most "/" in the base URI path, or excluding the entire base URI path if it does not contain any "/" characters).
434
+ base_path = path.to_s.include?('/') ? path.to_s.sub(/\/[^\/]*$/, '/') : ''
435
435
  joined_parts[:path] = self.class.normalize_path(base_path + uri.path)
436
436
  joined_parts[:query] = uri.query
437
437
  end
@@ -439,7 +439,7 @@ module RDF
439
439
  end
440
440
 
441
441
  # Return joined URI
442
- RDF::URI.new(joined_parts)
442
+ RDF::URI.new(**joined_parts)
443
443
  end
444
444
 
445
445
  ##
@@ -471,7 +471,7 @@ module RDF
471
471
  # @see RDF::URI#+
472
472
  # @see RDF::URI#join
473
473
  # @see <http://tools.ietf.org/html/rfc3986#section-5.2>
474
- # @see <http://github.com/ruby-rdf/rdf-spec/blob/master/lib/rdf/spec/uri.rb>
474
+ # @see <https://github.com/ruby-rdf/rdf-spec/blob/master/lib/rdf/spec/uri.rb>
475
475
  # @example Building a HTTP URL
476
476
  # RDF::URI.new('http://example.org') / 'jhacker' / 'foaf.ttl'
477
477
  # #=> RDF::URI('http://example.org/jhacker/foaf.ttl')
@@ -507,7 +507,7 @@ module RDF
507
507
  case fragment.to_s[0,1]
508
508
  when '#'
509
509
  # Base ending with '/', fragment beginning with '#'. The fragment wins, we use '#'.
510
- res.path = res.path.to_s.sub!(/\/*$/, '')
510
+ res.path = res.path.to_s.sub(/\/*$/, '')
511
511
  # Add fragment
512
512
  res.fragment = fragment.to_s.sub(/^#+/,'')
513
513
  else
@@ -572,7 +572,7 @@ module RDF
572
572
  self
573
573
  else
574
574
  RDF::URI.new(
575
- object.merge(path: '/').
575
+ **object.merge(path: '/').
576
576
  keep_if {|k, v| [:scheme, :authority, :path].include?(k)})
577
577
  end
578
578
  end
@@ -657,7 +657,7 @@ module RDF
657
657
  #
658
658
  # @return [RDF::URI]
659
659
  def dup
660
- self.class.new((@value || @object).dup)
660
+ self.class.new(@value, **(@object || {}))
661
661
  end
662
662
 
663
663
  ##
@@ -846,7 +846,7 @@ module RDF
846
846
  parts[:user] = (user.dup.force_encoding(Encoding::UTF_8) if user)
847
847
  parts[:password] = (password.dup.force_encoding(Encoding::UTF_8) if password)
848
848
  parts[:host] = (host.dup.force_encoding(Encoding::UTF_8) if host)
849
- parts[:port] = (::URI.decode(port).to_i if port)
849
+ parts[:port] = (URI.decode(port).to_i if port)
850
850
  parts[:path] = (path.to_s.dup.force_encoding(Encoding::UTF_8) unless path.empty?)
851
851
  parts[:query] = (query[1..-1].dup.force_encoding(Encoding::UTF_8) if query)
852
852
  parts[:fragment] = (fragment[1..-1].dup.force_encoding(Encoding::UTF_8) if fragment)
@@ -902,7 +902,7 @@ module RDF
902
902
  # Normalized version of user
903
903
  # @return [String]
904
904
  def normalized_user
905
- ::URI.encode(::URI.decode(user), /[^#{IUNRESERVED}|#{SUB_DELIMS}]/) if user
905
+ URI.encode(URI.decode(user), /[^#{IUNRESERVED}|#{SUB_DELIMS}]/) if user
906
906
  end
907
907
 
908
908
  ##
@@ -928,7 +928,7 @@ module RDF
928
928
  # Normalized version of password
929
929
  # @return [String]
930
930
  def normalized_password
931
- ::URI.encode(::URI.decode(password), /[^#{IUNRESERVED}|#{SUB_DELIMS}]/) if password
931
+ URI.encode(URI.decode(password), /[^#{IUNRESERVED}|#{SUB_DELIMS}]/) if password
932
932
  end
933
933
 
934
934
  HOST_FROM_AUTHORITY_RE = /(?:[^@]+@)?([^:]+)(?::.*)?$/.freeze
@@ -937,7 +937,7 @@ module RDF
937
937
  # @return [String]
938
938
  def host
939
939
  object.fetch(:host) do
940
- @object[:host] = ($1 if HOST_FROM_AUTHORITY_RE.match(@object[:authority]))
940
+ @object[:host] = ($1 if @object[:authority] && HOST_FROM_AUTHORITY_RE.match(@object[:authority]))
941
941
  end
942
942
  end
943
943
 
@@ -965,7 +965,7 @@ module RDF
965
965
  # @return [String]
966
966
  def port
967
967
  object.fetch(:port) do
968
- @object[:port] = ($1 if PORT_FROM_AUTHORITY_RE.match(@object[:authority]))
968
+ @object[:port] = ($1 if @object[:authority] && PORT_FROM_AUTHORITY_RE.match(@object[:authority]))
969
969
  end
970
970
  end
971
971
 
@@ -1180,8 +1180,8 @@ module RDF
1180
1180
  inject(return_type == Hash ? {} : []) do |memo,kv|
1181
1181
  k,v = kv.to_s.split('=', 2)
1182
1182
  next if k.to_s.empty?
1183
- k = ::URI.decode(k)
1184
- v = ::URI.decode(v) if v
1183
+ k = URI.decode(k)
1184
+ v = URI.decode(v) if v
1185
1185
  if return_type == Hash
1186
1186
  case memo[k]
1187
1187
  when nil then memo[k] = v
@@ -1293,9 +1293,9 @@ module RDF
1293
1293
  def normalize_segment(value, expr, downcase = false)
1294
1294
  if value
1295
1295
  value = value.dup.force_encoding(Encoding::UTF_8)
1296
- decoded = ::URI.decode(value)
1296
+ decoded = URI.decode(value)
1297
1297
  decoded.downcase! if downcase
1298
- ::URI.encode(decoded, /[^(?:#{expr})]/)
1298
+ URI.encode(decoded, /[^(?:#{expr})]/)
1299
1299
  end
1300
1300
  end
1301
1301
 
@@ -1314,6 +1314,27 @@ module RDF
1314
1314
  ""
1315
1315
  end
1316
1316
  end
1317
+
1318
+ # URI encode matching characters in value
1319
+ # From URI gem, as this is now generally deprecated
1320
+ def self.encode(str, expr)
1321
+ str.gsub(expr) do
1322
+ us = $&
1323
+ tmp = ''
1324
+ us.each_byte do |uc|
1325
+ tmp << sprintf('%%%02X', uc)
1326
+ end
1327
+ tmp
1328
+ end.force_encoding(Encoding::US_ASCII)
1329
+ end
1330
+
1331
+ # URI decode escape sequences in value
1332
+ # From URI gem, as this is now generally deprecated
1333
+ def self.decode(str)
1334
+ enc = str.encoding
1335
+ enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
1336
+ str.gsub(PCT_ENCODED) { [$&[1, 2]].pack('H2').force_encoding(enc) }
1337
+ end
1317
1338
  end
1318
1339
 
1319
1340
  # RDF::IRI is a synonym for RDF::URI