Skip to content

Commit caf2895

Browse files
committed
Finish 3.0.10
2 parents 0dee10d + 00d0776 commit caf2895

23 files changed

+733
-224
lines changed

Gemfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ group :debug do
2222
gem 'psych', platforms: [:mri, :rbx]
2323
gem "redcarpet", platforms: :ruby
2424
gem "byebug", platforms: :mri
25-
gem 'ruby-debug', platform: :jruby
2625
gem 'guard-rspec'
2726
end
2827

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.0.9
1+
3.0.10

lib/rdf/model/literal/datetime.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module RDF; class Literal
77
class DateTime < Literal
88
DATATYPE = RDF::URI("http://www.w3.org/2001/XMLSchema#dateTime")
99
GRAMMAR = %r(\A(-?(?:\d{4}|[1-9]\d{4,})-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?)((?:[\+\-]\d{2}:\d{2})|UTC|GMT|Z)?\Z).freeze
10-
FORMAT = '%Y-%m-%dT%H:%M:%S%:z'.freeze
10+
FORMAT = '%Y-%m-%dT%H:%M:%S.%L%:z'.freeze
1111

1212
##
1313
# @param [DateTime] value
@@ -31,9 +31,9 @@ def initialize(value, datatype: nil, lexical: nil, **options)
3131
def canonicalize!
3232
if self.valid?
3333
@string = if has_timezone?
34-
@object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z')
34+
@object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z').sub('.000', '')
3535
else
36-
@object.strftime(FORMAT[0..-4])
36+
@object.strftime(FORMAT[0..-4]).sub('.000', '')
3737
end
3838
end
3939
self
@@ -80,6 +80,16 @@ def valid?
8080
super && object && value !~ %r(\A0000)
8181
end
8282

83+
##
84+
# Does the literal representation include millisectonds?
85+
#
86+
# @return [Boolean]
87+
# @since 1.1.6
88+
def has_milliseconds?
89+
self.format("%L").to_i > 0
90+
end
91+
alias_method :has_ms?, :has_milliseconds?
92+
8393
##
8494
# Does the literal representation include a timezone? Note that this is only possible if initialized using a string, or `:lexical` option.
8595
#
@@ -98,7 +108,7 @@ def has_timezone?
98108
#
99109
# @return [String]
100110
def to_s
101-
@string || @object.strftime(FORMAT).sub("+00:00", 'Z')
111+
@string || @object.strftime(FORMAT).sub("+00:00", 'Z').sub('.000', '')
102112
end
103113

104114
##

lib/rdf/model/literal/time.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module RDF; class Literal
1212
class Time < Literal
1313
DATATYPE = RDF::URI("http://www.w3.org/2001/XMLSchema#time")
1414
GRAMMAR = %r(\A(\d{2}:\d{2}:\d{2}(?:\.\d+)?)((?:[\+\-]\d{2}:\d{2})|UTC|GMT|Z)?\Z).freeze
15-
FORMAT = '%H:%M:%S%:z'.freeze
15+
FORMAT = '%H:%M:%S.%L%:z'.freeze
1616

1717
##
1818
# @param [String, DateTime, #to_datetime] value
@@ -43,9 +43,9 @@ def initialize(value, datatype: nil, lexical: nil, **options)
4343
def canonicalize!
4444
if self.valid?
4545
@string = if has_timezone?
46-
@object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z')
46+
@object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z').sub('.000', '')
4747
else
48-
@object.strftime(FORMAT[0..-4])
48+
@object.strftime(FORMAT[0..-4]).sub('.000', '')
4949
end
5050
end
5151
self
@@ -91,7 +91,7 @@ def has_timezone?
9191
#
9292
# @return [String]
9393
def to_s
94-
@string || @object.strftime(FORMAT).sub("+00:00", 'Z')
94+
@string || @object.strftime(FORMAT).sub("+00:00", 'Z').sub('.000', '')
9595
end
9696

9797
##
@@ -122,7 +122,7 @@ def ==(other)
122122
return super unless other.valid?
123123
# Compare as strings, as time includes a date portion, and adjusting for UTC
124124
# can create a mismatch in the date portion.
125-
self.object.new_offset.strftime('%H%M%S') == other.object.new_offset.strftime('%H%M%S')
125+
self.object.new_offset.strftime('%H%M%S.%L') == other.object.new_offset.strftime('%H%M%S.%L')
126126
when Literal::DateTime, Literal::Date
127127
false
128128
else

lib/rdf/model/statement.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ def self.from(statement, graph_name: nil, **options)
5757
# @return [RDF::Term]
5858
attr_accessor :object
5959

60+
# @return [Hash{Symbol => Object}]
61+
attr_accessor :options
62+
6063
##
6164
# @overload initialize(**options)
6265
# @param [Hash{Symbol => Object}] options

lib/rdf/model/uri.rb

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,7 @@ def self.parse(str)
169169
def self.normalize_path(path)
170170
output, input = "", path.to_s
171171
if input.encoding != Encoding::ASCII_8BIT
172-
input = input.dup if input.frozen?
173-
input = input.force_encoding(Encoding::ASCII_8BIT)
172+
input = input.dup.force_encoding(Encoding::ASCII_8BIT)
174173
end
175174
until input.empty?
176175
if input.match(RDS_2A)
@@ -228,8 +227,7 @@ def initialize(*args, validate: false, canonicalize: false, **options)
228227
if uri
229228
@value = uri.to_s
230229
if @value.encoding != Encoding::UTF_8
231-
@value = @value.dup if @value.frozen?
232-
@value.force_encoding(Encoding::UTF_8)
230+
@value.dup.force_encoding(Encoding::UTF_8)
233231
@value.freeze
234232
end
235233
else
@@ -842,16 +840,16 @@ def parse(value)
842840
user, password = userinfo.to_s.split(':', 2)
843841
host, port = hostport.to_s.split(':', 2)
844842

845-
parts[:scheme] = (scheme.force_encoding(Encoding::UTF_8) if scheme)
846-
parts[:authority] = (authority.force_encoding(Encoding::UTF_8) if authority)
847-
parts[:userinfo] = (userinfo.force_encoding(Encoding::UTF_8) if userinfo)
848-
parts[:user] = (user.force_encoding(Encoding::UTF_8) if user)
849-
parts[:password] = (password.force_encoding(Encoding::UTF_8) if password)
850-
parts[:host] = (host.force_encoding(Encoding::UTF_8) if host)
843+
parts[:scheme] = (scheme.dup.force_encoding(Encoding::UTF_8) if scheme)
844+
parts[:authority] = (authority.dup.force_encoding(Encoding::UTF_8) if authority)
845+
parts[:userinfo] = (userinfo.dup.force_encoding(Encoding::UTF_8) if userinfo)
846+
parts[:user] = (user.dup.force_encoding(Encoding::UTF_8) if user)
847+
parts[:password] = (password.dup.force_encoding(Encoding::UTF_8) if password)
848+
parts[:host] = (host.dup.force_encoding(Encoding::UTF_8) if host)
851849
parts[:port] = (::URI.decode(port).to_i if port)
852-
parts[:path] = (path.to_s.force_encoding(Encoding::UTF_8) unless path.empty?)
853-
parts[:query] = (query[1..-1].force_encoding(Encoding::UTF_8) if query)
854-
parts[:fragment] = (fragment[1..-1].force_encoding(Encoding::UTF_8) if fragment)
850+
parts[:path] = (path.to_s.dup.force_encoding(Encoding::UTF_8) unless path.empty?)
851+
parts[:query] = (query[1..-1].dup.force_encoding(Encoding::UTF_8) if query)
852+
parts[:fragment] = (fragment[1..-1].dup.force_encoding(Encoding::UTF_8) if fragment)
855853
end
856854

857855
parts
@@ -869,7 +867,7 @@ def scheme
869867
# @param [String, #to_s] value
870868
# @return [RDF::URI] self
871869
def scheme=(value)
872-
object[:scheme] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
870+
object[:scheme] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
873871
@value = nil
874872
self
875873
end
@@ -893,7 +891,7 @@ def user
893891
# @param [String, #to_s] value
894892
# @return [RDF::URI] self
895893
def user=(value)
896-
object[:user] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
894+
object[:user] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
897895
@object[:userinfo] = format_userinfo("")
898896
@object[:authority] = format_authority
899897
@value = nil
@@ -919,7 +917,7 @@ def password
919917
# @param [String, #to_s] value
920918
# @return [RDF::URI] self
921919
def password=(value)
922-
object[:password] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
920+
object[:password] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
923921
@object[:userinfo] = format_userinfo("")
924922
@object[:authority] = format_authority
925923
@value = nil
@@ -947,7 +945,7 @@ def host
947945
# @param [String, #to_s] value
948946
# @return [RDF::URI] self
949947
def host=(value)
950-
object[:host] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
948+
object[:host] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
951949
@object[:authority] = format_authority
952950
@value = nil
953951
self
@@ -1010,7 +1008,7 @@ def path=(value)
10101008
if value
10111009
# Always lead with a slash
10121010
value = "/#{value}" if host && value.to_s.match?(/^[^\/]/)
1013-
object[:path] = value.to_s.force_encoding(Encoding::UTF_8)
1011+
object[:path] = value.to_s.dup.force_encoding(Encoding::UTF_8)
10141012
else
10151013
object[:path] = nil
10161014
end
@@ -1069,7 +1067,7 @@ def query
10691067
# @param [String, #to_s] value
10701068
# @return [RDF::URI] self
10711069
def query=(value)
1072-
object[:query] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
1070+
object[:query] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
10731071
@value = nil
10741072
self
10751073
end
@@ -1093,7 +1091,7 @@ def fragment
10931091
# @param [String, #to_s] value
10941092
# @return [RDF::URI] self
10951093
def fragment=(value)
1096-
object[:fragment] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
1094+
object[:fragment] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
10971095
@value = nil
10981096
self
10991097
end
@@ -1118,7 +1116,7 @@ def authority
11181116
# @return [RDF::URI] self
11191117
def authority=(value)
11201118
object.delete_if {|k, v| [:user, :password, :host, :port, :userinfo].include?(k)}
1121-
object[:authority] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
1119+
object[:authority] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
11221120
user; password; userinfo; host; port
11231121
@value = nil
11241122
self
@@ -1148,7 +1146,7 @@ def userinfo
11481146
# @return [RDF::URI] self
11491147
def userinfo=(value)
11501148
object.delete_if {|k, v| [:user, :password, :authority].include?(k)}
1151-
object[:userinfo] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
1149+
object[:userinfo] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
11521150
user; password; authority
11531151
@value = nil
11541152
self
@@ -1263,6 +1261,26 @@ def request_uri
12631261
return res
12641262
end
12651263

1264+
##
1265+
# Dump of data needed to reconsitute this object using Marshal.load
1266+
# This override is needed to avoid serializing @mutex.
1267+
#
1268+
# @param [Integer] level The maximum depth of objects to dump.
1269+
# @return [String] The dump of data needed to reconsitute this object.
1270+
def _dump(level)
1271+
value
1272+
end
1273+
1274+
##
1275+
# Load dumped data to reconsitute marshaled object
1276+
# This override is needed to avoid serializing @mutex.
1277+
#
1278+
# @param [String] data The dump of data needed to reconsitute this object.
1279+
# @return [RDF::URI] The reconsituted object.
1280+
def self._load(data)
1281+
new(data)
1282+
end
1283+
12661284
private
12671285

12681286
##
@@ -1274,8 +1292,7 @@ def request_uri
12741292
# @return [String]
12751293
def normalize_segment(value, expr, downcase = false)
12761294
if value
1277-
value = value.dup if value.frozen?
1278-
value = value.force_encoding(Encoding::UTF_8)
1295+
value = value.dup.force_encoding(Encoding::UTF_8)
12791296
decoded = ::URI.decode(value)
12801297
decoded.downcase! if downcase
12811298
::URI.encode(decoded, /[^(?:#{expr})]/)

lib/rdf/ntriples/reader.rb

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class Reader < RDF::Reader
3232
format RDF::NTriples::Format
3333

3434
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
35-
ESCAPE_CHARS = ["\b", "\f", "\t", "\n", "\r", "\"", "\\"].freeze
35+
ESCAPE_CHARS = ["\b", "\f", "\t", "\n", "\r", "\"", "'", "\\"].freeze
3636
UCHAR4 = /\\u([0-9A-Fa-f]{4,4})/.freeze
3737
UCHAR8 = /\\U([0-9A-Fa-f]{8,8})/.freeze
3838
UCHAR = Regexp.union(UCHAR4, UCHAR8).freeze
@@ -60,7 +60,7 @@ class Reader < RDF::Reader
6060
# 166s
6161
PN_CHARS = /-|[0-9]|#{PN_CHARS_U}|#{U_CHARS2}/.freeze
6262
# 159s
63-
ECHAR = /\\[tbnrf\\"]/.freeze
63+
ECHAR = /\\[tbnrf"'\\]/.freeze
6464
# 18
6565
IRIREF = /<((?:#{IRI_RANGE}|#{UCHAR})*)>/.freeze
6666
# 141s
@@ -135,7 +135,6 @@ def self.parse_node(input, **options)
135135
# @return [RDF::URI]
136136
def self.parse_uri(input, intern: false, **options)
137137
if input =~ URIREF
138-
uri_str = unescape($1)
139138
RDF::URI.send(intern ? :intern : :new, unescape($1))
140139
end
141140
end
@@ -155,9 +154,16 @@ def self.parse_literal(input, **options)
155154
end
156155

157156
# cache constants to optimize escaping the escape chars in self.unescape
158-
ESCAPE_CHARS_ESCAPED = ESCAPE_CHARS.each_with_object({}) do |escape, memo|
159-
memo[escape.inspect[1...-1]] = escape
160-
end.freeze
157+
ESCAPE_CHARS_ESCAPED = {
158+
"\\b" => "\b",
159+
"\\f" => "\f",
160+
"\\t" => "\t",
161+
"\\n" => "\n",
162+
"\\r" => "\r",
163+
"\\\"" => "\"",
164+
"\\'" => "'",
165+
"\\\\" => "\\"
166+
} .freeze
161167
ESCAPE_CHARS_ESCAPED_REGEXP = Regexp.union(
162168
ESCAPE_CHARS_ESCAPED.keys
163169
).freeze
@@ -171,26 +177,23 @@ def self.parse_literal(input, **options)
171177
def self.unescape(string)
172178
# Note: avoiding copying the input string when no escaping is needed
173179
# greatly reduces the number of allocations and the processing time.
174-
unless string.encoding == Encoding::UTF_8
175-
string = string.dup.force_encoding(Encoding::UTF_8)
176-
end
177-
178-
has_escape_chars = ESCAPE_CHARS_ESCAPED_REGEXP.match?(string)
179-
has_uchar = UCHAR.match?(string)
180-
181-
string = string.dup if has_escape_chars || has_uchar
180+
string = string.dup.force_encoding(Encoding::UTF_8) unless string.encoding == Encoding::UTF_8
181+
scanner = StringScanner.new(string)
182182

183-
# Decode \t|\n|\r|\"|\\ character escapes using Regexp:
184-
string.gsub!(ESCAPE_CHARS_ESCAPED_REGEXP) do
185-
ESCAPE_CHARS_ESCAPED.fetch($~[0])
186-
end if has_escape_chars
183+
buffer = ""
187184

188-
# Decode \uXXXX and \UXXXXXXXX code points:
189-
string.gsub!(UCHAR) do
190-
[($1 || $2).hex].pack('U*')
191-
end if has_uchar
185+
while !scanner.eos?
186+
buffer << if scanner.scan(ESCAPE_CHARS_ESCAPED_REGEXP)
187+
ESCAPE_CHARS_ESCAPED[scanner.matched]
188+
elsif scanner.scan(UCHAR)
189+
scanner.matched.sub(UCHAR) {[($1 || $2).hex].pack('U*')}
190+
else
191+
# Scan one character
192+
scanner.getch
193+
end
194+
end
192195

193-
string
196+
buffer
194197
end
195198

196199
##
@@ -250,15 +253,15 @@ def read_uriref(intern: false, **options)
250253
uri.canonicalize! if canonicalize?
251254
uri
252255
end
253-
rescue ArgumentError => e
256+
rescue ArgumentError
254257
log_error("Invalid URI (found: \"<#{uri_str}>\")", lineno: lineno, token: "<#{uri_str}>", exception: RDF::ReaderError)
255258
end
256259

257260
##
258261
# @return [RDF::Node]
259262
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (nodeID)
260263
def read_node
261-
if node_id = match(NODEID)
264+
if node_id = match(NODEID)
262265
@nodes ||= {}
263266
@nodes[node_id] ||= RDF::Node.new(node_id)
264267
end

0 commit comments

Comments
 (0)