rdf-xsd 3.2.0 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 171331f779259dbadcce5879cc66d99684b13bee789bb681621aad87c72d0baa
4
- data.tar.gz: 4b043323c4f66549b0dbeec293bdf462b753960836f23e6c1b33c43d7a519ac3
3
+ metadata.gz: 8a344fd7adc4bd8ca84e939dcf26daf656c19e5f60d04b2026ed18393b07a6d9
4
+ data.tar.gz: fd035fb32fb4719ebcc72f7a133d64c690d20c9f8a8276b18d801625317119b2
5
5
  SHA512:
6
- metadata.gz: 1d9d455005767e17091c7c20721fd72058d5520e47676cced291ff81824a083886ae49913830d0a404bc3ea3098bdcc6ab02638b0d3098c73753d95bf2aab81d
7
- data.tar.gz: cfb0f759ef10a18d4036671f9ee5ab90630d58b55233b2f9ab9b1be775921d77d6045100a1aef0a97ae21d3ba085eda063269ada7d88f31fad9ffc82b9e812a2
6
+ metadata.gz: 303a3003b754327c637caf02ca34c27fc52e50b25302dee1f2cf0d2f820d61e431814c92a5e6af4b9af8511de699f5f841c789a1719dbbe0f36a1a3c96db29ce
7
+ data.tar.gz: 63cf8178d9118864fc09b26539e95a33062309e10de38eed5b1f0c769d7bd6be47eca8a51d77e77d2faf2545bcd7e032f020a6e326400c804941d0ebc7b0f34f
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Extended XSD Datatypes for RDF.rb
1
+ # Extended XSD Datatypes and XQuery functions for RDF.rb
2
2
 
3
- This gem adds additional RDF::Literal subclasses for extended [XSD datatypes][]
3
+ This gem adds additional RDF::Literal subclasses for extended [XSD datatypes][] along with methods implementing many [XPath and XQuery Functions][]
4
4
 
5
5
  [![Gem Version](https://badge.fury.io/rb/rdf-xsd.png)](https://badge.fury.io/rb/rdf-xsd)
6
6
  [![Build Status](https://github.com/ruby-rdf/rdf-xsd/workflows/CI/badge.svg?branch=develop)](https://github.com/ruby-rdf/rdf-xsd/actions?query=workflow%3ACI)
@@ -11,10 +11,10 @@ This gem adds additional RDF::Literal subclasses for extended [XSD datatypes][]
11
11
 
12
12
  * Additional xsd:integer subtypes
13
13
  * xsd:float based on xsd:double
14
- * xsd:duration
14
+ * xsd:duration, xsd:yearMonthDuration, and xsd:dayTimeDuration.
15
15
  * rdf:XMLLiteral
16
16
  * XML Exclusive Canonicalization (Nokogiri & REXML)
17
- * XML Literal comparisions (EquivalentXml, ActiveSupport or String)
17
+ * XML Literal comparisons (EquivalentXml, ActiveSupport or String)
18
18
 
19
19
  ## Examples
20
20
 
@@ -29,25 +29,27 @@ This gem adds additional RDF::Literal subclasses for extended [XSD datatypes][]
29
29
  * Soft dependency on [ActiveSupport](https://rubygems.org/gems/activesupport) (~> 6.2)
30
30
 
31
31
  ## Documentation
32
- Full documentation available on [Rubydoc.info][XSD doc]
32
+ Full documentation available on [GitHub][XSD doc]
33
33
 
34
34
  ### Principle Classes
35
35
  * {RDF::Literal::Base64Binary}
36
36
  * {RDF::Literal::Duration}
37
+ * {RDF::Literal::YearMonthDuration}
38
+ * {RDF::Literal::DayTimeDuration}
37
39
  * {RDF::Literal::Float}
38
40
  * {RDF::Literal::HexBinary}
39
41
  * {RDF::Literal::NonPositiveInteger}
40
- * {RDF::Literal::NegativeInteger}
42
+ * {RDF::Literal::NegativeInteger}
41
43
  * {RDF::Literal::Long}
42
- * {RDF::Literal::Int}
43
- * {RDF::Literal::Short}
44
- * {RDF::Literal::Byte}
44
+ * {RDF::Literal::Int}
45
+ * {RDF::Literal::Short}
46
+ * {RDF::Literal::Byte}
45
47
  * {RDF::Literal::NonNegativeInteger}
46
- * {RDF::Literal::PositiveInteger}
47
- * {RDF::Literal::UnsignedLong}
48
- * {RDF::Literal::UnsignedInt}
49
- * {RDF::Literal::UnsignedShort}
50
- * {RDF::Literal::UnsignedByte}
48
+ * {RDF::Literal::PositiveInteger}
49
+ * {RDF::Literal::UnsignedLong}
50
+ * {RDF::Literal::UnsignedInt}
51
+ * {RDF::Literal::UnsignedShort}
52
+ * {RDF::Literal::UnsignedByte}
51
53
  * {RDF::Literal::YearMonth}
52
54
  * {RDF::Literal::Year}
53
55
  * {RDF::Literal::MonthDay}
@@ -100,4 +102,6 @@ Portions of tests are derived from [W3C DAWG tests](https://www.w3.org/2001/sw/D
100
102
  [YARD-GS]: https://rubydoc.info/docs/yard/file/docs/GettingStarted.md
101
103
  [PDD]: https://unlicense.org/#unlicensing-contributions
102
104
  [Backports]: https://rubygems.org/gems/backports
103
- [XSD Datatypes]: https://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#built-in-datatypes
105
+ [XSD Datatypes]: https://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#built-in-datatypes
106
+ [XPath and XQuery Functions]: https://www.w3.org/TR/xpath-functions/
107
+ [XSD Doc]: https://ruby-rdf.github.io/rdf-xsd
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.2.0
1
+ 3.2.1
data/lib/rdf/xsd/date.rb CHANGED
@@ -11,7 +11,7 @@ module RDF; class Literal
11
11
  class DateTimeStamp < RDF::Literal::DateTime
12
12
  DATATYPE = RDF::XSD.dateTimeStamp
13
13
  GRAMMAR = %r(\A(-?\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?)((?:[\+\-]\d{2}:\d{2})|UTC|GMT|Z)\Z).freeze
14
- FORMAT = '%Y-%m-%dT%H:%M:%SZ'.freeze
14
+ FORMAT = '%Y-%m-%dT%H:%M:%S'.freeze
15
15
  end
16
16
 
17
17
  ##
@@ -24,7 +24,7 @@ module RDF; class Literal
24
24
  class YearMonth < RDF::Literal::Date
25
25
  DATATYPE = RDF::XSD.gYearMonth
26
26
  GRAMMAR = %r(\A(-?\d{4,}-\d{2})((?:[\+\-]\d{2}:\d{2})|UTC|Z)?\Z).freeze
27
- FORMAT = '%Y-%m%Z'.freeze
27
+ FORMAT = '%Y-%m'.freeze
28
28
 
29
29
  def initialize(value, datatype: nil, lexical: nil, **options)
30
30
  @string = lexical || value.to_s
@@ -42,7 +42,7 @@ module RDF; class Literal
42
42
  class Year < RDF::Literal::Date
43
43
  DATATYPE = RDF::XSD.gYear
44
44
  GRAMMAR = %r(\A(-?\d{4,})((?:[\+\-]\d{2}:\d{2})|UTC|Z)?\Z).freeze
45
- FORMAT = '%Y%Z'.freeze
45
+ FORMAT = '%Y'.freeze
46
46
 
47
47
  def initialize(value, datatype: nil, lexical: nil, **options)
48
48
  @string = lexical || value.to_s
@@ -60,7 +60,7 @@ module RDF; class Literal
60
60
  class MonthDay < RDF::Literal::Date
61
61
  DATATYPE = RDF::XSD.gMonthDay
62
62
  GRAMMAR = %r(\A--(\d{2}-\d{2})((?:[\+\-]\d{2}:\d{2})|UTC|Z)?\Z).freeze
63
- FORMAT = '%m-%d%Z'.freeze
63
+ FORMAT = '%m-%d'.freeze
64
64
 
65
65
  def initialize(value, datatype: nil, lexical: nil, **options)
66
66
  @string = lexical || value.to_s
@@ -78,7 +78,7 @@ module RDF; class Literal
78
78
  class Day < RDF::Literal::Date
79
79
  DATATYPE = RDF::XSD.gDay
80
80
  GRAMMAR = %r(\A---(\d{2})((?:[\+\-]\d{2}:\d{2})|UTC|Z)?\Z).freeze
81
- FORMAT = '%d%Z'.freeze
81
+ FORMAT = '%d'.freeze
82
82
 
83
83
  def initialize(value, datatype: nil, lexical: nil, **options)
84
84
  @string = lexical || value.to_s
@@ -95,7 +95,7 @@ module RDF; class Literal
95
95
  class Month < RDF::Literal::Date
96
96
  DATATYPE = RDF::XSD.gMonth
97
97
  GRAMMAR = %r(\A--(\d{2})((?:[\+\-]\d{2}:\d{2})|UTC|Z)?\Z).freeze
98
- FORMAT = '%m%Z'.freeze
98
+ FORMAT = '%m'.freeze
99
99
 
100
100
  def initialize(value, datatype: nil, lexical: nil, **options)
101
101
  @string = lexical || value.to_s
@@ -5,6 +5,8 @@ module RDF; class Literal
5
5
  ##
6
6
  # A duration literal.
7
7
  #
8
+ # `duration` is a datatype that represents durations of time. The concept of duration being captured is drawn from those of [ISO 8601](https://www.w3.org/TR/xmlschema11-2/#ISO8601), specifically durations without fixed endpoints.
9
+ #
8
10
  # @see https://www.w3.org/TR/xmlschema11-2/#duration
9
11
  class Duration < Literal
10
12
  DATATYPE = RDF::XSD.duration
@@ -14,12 +16,12 @@ module RDF; class Literal
14
16
  | (?:(?:(?<mo>\d+)M)(?:(?<da>\d+)D)?)
15
17
  | (?:(?<da>\d+)D)
16
18
  )
17
- (?:T(?:(?:(?:(?<hr>\d+)H)(?:(?<mi>\d+)M)?(?<se>\d+(?:\.\d+)?S)?)
19
+ (?:T(?:(?:(?:(?<hr>\d+)H)(?:(?<mi>\d+)M)?(?:(?<se>\d+(?:\.\d+)?)S)?)
18
20
  | (?:(?:(?<mi>\d+)M)(?:(?<se>\d+(?:\.\d+)?)S)?)
19
21
  | (?:(?<se>\d+(?:\.\d+)?)S)
20
22
  )
21
23
  )?
22
- |(?:T(?:(?:(?:(?<hr>\d+)H)(?:(?<mi>\d+)M)?(?<se>\d+(?:\.\d+)?S)?)
24
+ |(?:T(?:(?:(?:(?<hr>\d+)H)(?:(?<mi>\d+)M)?(?:(?<se>\d+(?:\.\d+)?)S)?)
23
25
  | (?:(?:(?<mi>\d+)M)(?:(?<se>\d+(?:\.\d+)?)S)?)
24
26
  | (?:(?<se>\d+(?:\.\d+)?)S)
25
27
  )
@@ -28,66 +30,59 @@ module RDF; class Literal
28
30
  \z)x.freeze
29
31
 
30
32
  ##
31
- # * Given a Numeric, assumes that it is milliseconds
32
- # * Given a String, parse as xsd:duration
33
- # * Hash form is used for internal representation
34
- # @param [Duration, Hash, Numeric, #to_s] value
35
- # @option options [String] :lexical (nil)
33
+ # Creates a new Duration instance.
34
+ #
35
+ # * Given a `String`, parse as `xsd:duration` into months and seconds
36
+ # * Given a `Hash` containing any of `:yr`, `:mo`, :da`, `:hr`, `:mi` and `:si`, it is transformed into months and seconds
37
+ # * Given a Rational, the result is interpreted as days, hours, minutes, and seconds.
38
+ # * Given an Integer, the result is interpreted as years and months.
39
+ # * Object representation is the `Array(months, seconds)`
40
+ #
41
+ # @param [Literal::Duration, Hash, Array, Literal::Numeric, #to_s] value
42
+ # If provided an Array, it is the same as the object form of this literal, an array of two integers, the first of which may be negative.
43
+ # @param [String] lexical (nil)
44
+ # Supplied lexical representation of this literal,
45
+ # otherwise it comes from transforming `value` to a string form..
46
+ # @param [URI] datatype (nil)
47
+ # @param [Hash{Symbol => Object}] options other options passed to `RDF::Literal#initialize`.
48
+ # @option options [Boolean] :validate (false)
49
+ # @option options [Boolean] :canonicalize (false)
36
50
  def initialize(value, datatype: nil, lexical: nil, **options)
37
51
  super
38
52
  @object = case value
39
53
  when Hash
40
- value = value.dup
41
- value[:yr] ||= value[:years]
42
- value[:mo] ||= value[:months]
43
- value[:da] ||= value[:days]
44
- value[:hr] ||= value[:hours]
45
- value[:mi] ||= value[:minutes]
46
- value[:se] ||= value[:seconds]
47
-
48
- value
49
- when Duration, Numeric
50
- {:se => value.to_f}
51
- else
52
- parse(value.to_s)
54
+ months = value[:yr].to_i * 12 + value[:mo].to_i
55
+ seconds = value[:da].to_i * 3600 * 24 +
56
+ value[:hr].to_i * 3600 +
57
+ value[:mi].to_i * 60 +
58
+ value[:se].to_f
59
+
60
+ if value[:si]
61
+ if months != 0
62
+ months = -months
63
+ else
64
+ seconds = -seconds
65
+ end
66
+ end
67
+ [months, seconds]
68
+ when Rational
69
+ [0, value * 24 * 3600]
70
+ when Integer, ::Integer
71
+ [value.to_i, 0]
72
+ when Literal::Duration then value.object
73
+ when Array then value
74
+ else parse(value.to_s)
53
75
  end
54
76
  end
55
77
 
56
78
  ##
57
79
  # Converts this literal into its canonical lexical representation.
58
80
  #
59
- # Also normalizes elements
60
- #
61
- # @return [RDF::Literal] `self`
81
+ # @return [Literal] `self`
62
82
  # @see https://www.w3.org/TR/xmlschema11-2/#dateTime
63
83
  def canonicalize!
64
- @string = @humanize = nil
65
- if @object[:se].to_i > 60
66
- m_r = (@object[:se].to_f / 60) - 1
67
- @object[:se] -= m_r * 60
68
- @object[:mi] = @object[:mi].to_i + m_r
69
- end
70
- if @object[:mi].to_i > 60
71
- h_r = (@object[:mi].to_i / 60) - 1
72
- @object[:mi] -= h_r * 60
73
- @object[:hr] = @object[:hr].to_i + h_r
74
- end
75
- if @object[:hr].to_i > 24
76
- d_r = (@object[:hr].to_i / 24) - 1
77
- @object[:hr] -= d_r * 24
78
- @object[:da] = @object[:da].to_i + d_r
79
- end
80
- if @object[:da].to_i > 30
81
- m_r = (@object[:da].to_i / 30) - 1
82
- @object[:da] -= m_r * 30
83
- @object[:mo] = @object[:mo].to_i + m_r
84
- end
85
- if @object[:mo].to_i > 12
86
- y_r = (@object[:mo].to_i / 12) - 1
87
- @object[:mo] -= y_r * 12
88
- @object[:yr] = @object[:yr].to_i + y_r
89
- end
90
- @object.to_s # side-effect
84
+ @string = @humanize = @hash = nil
85
+ self.to_s # side-effect
91
86
  self
92
87
  end
93
88
 
@@ -99,7 +94,23 @@ module RDF; class Literal
99
94
  #
100
95
  # @return [Boolean]
101
96
  def valid?
102
- !!(value =~ GRAMMAR)
97
+ !!value.match?(self.class.const_get(:GRAMMAR))
98
+ end
99
+
100
+ ##
101
+ # Returns a hash representation.
102
+ #
103
+ # @return [Hash]
104
+ def to_h
105
+ @hash ||= {
106
+ si: ('-' if (@object.first == 0 ? @object.last : @object.first) < 0),
107
+ yr: (@object.first.abs / 12),
108
+ mo: (@object.first.abs % 12),
109
+ da: (@object.last.abs.to_i / (3600 * 24)),
110
+ hr: ((@object.last.abs.to_i / 3600) % 24),
111
+ mi: ((@object.last.abs.to_i / 60) % 60),
112
+ se: sec_str.to_f
113
+ }
103
114
  end
104
115
 
105
116
  ##
@@ -108,14 +119,22 @@ module RDF; class Literal
108
119
  # @return [String]
109
120
  def to_s
110
121
  @string ||= begin
111
- str = @object[:si] == '-' ? "-P" : "P"
112
- str << "%dY" % @object[:yr].to_i if @object[:yr]
113
- str << "%dM" % @object[:mo].to_i if @object[:mo]
114
- str << "%dD" % @object[:da].to_i if @object[:da]
115
- str << "T" if @object[:hr] || @object[:mi] || @object[:se]
116
- str << "%dH" % @object[:hr].to_i if @object[:hr]
117
- str << "%dM" % @object[:mi].to_i if @object[:mi]
118
- str << "#{sec_str}S" if @object[:se]
122
+ hash = to_h
123
+ str = (@object.first == 0 ? @object.last : @object.first) < 0 ? '-P' : 'P'
124
+ hash = to_h
125
+ str << "%dY" % hash[:yr] if hash[:yr] > 0
126
+ str << "%dM" % hash[:mo] if hash[:mo] > 0
127
+ str << "%dD" % hash[:da] if hash[:da] > 0
128
+ str << "T" if (hash[:hr] + hash[:mi] + hash[:se]) > 0
129
+ str << "%dH" % hash[:hr] if hash[:hr] > 0
130
+ str << "%dM" % hash[:mi] if hash[:mi] > 0
131
+ str << sec_str + 'S' if hash[:se] > 0
132
+ # Ensure some legal representation
133
+ if str.end_with?('P')
134
+ is_a?(Literal::YearMonthDuration) ? 'P0M' : 'PT0S'
135
+ else
136
+ str
137
+ end
119
138
  end
120
139
  end
121
140
 
@@ -129,57 +148,85 @@ module RDF; class Literal
129
148
  @humanize ||= {}
130
149
  @humanize[lang] ||= begin
131
150
  # Just english, for now
151
+ return "Invalid duration #{value.to_s.inspect}" unless valid?
152
+
153
+ md = value.match(GRAMMAR)
132
154
  ar = []
133
- ar << plural(@object[:yr], "year")
134
- ar << plural(@object[:mo], "month")
135
- ar << plural(@object[:da], "day")
136
- ar << plural(@object[:hr], "hour")
137
- ar << plural(@object[:mi], "minute")
138
- ar << plural(sec_str, "second") if @object[:se]
139
- ar = ar.compact
155
+ ar << plural(md[:yr], "year") if md[:yr]
156
+ ar << plural(md[:mo], "month") if md[:mo]
157
+ ar << plural(md[:da], "day") if md[:da]
158
+ ar << plural(md[:hr], "hour") if md[:hr]
159
+ ar << plural(md[:mi], "minute") if md[:mi]
160
+ ar << plural(md[:se], "second") if md[:se]
140
161
  last = ar.pop
141
162
  first = ar.join(" ")
142
163
  res = first.empty? ? last : "#{first} and #{last}"
143
- @object[:si] == '-' ? "#{res} ago" : res
164
+ md[:si] == '-' ? "#{res} ago" : res
144
165
  end
145
166
  end
146
167
 
147
168
  ##
148
- # Equal compares as DateTime objects
169
+ # Returns `true` if `self` and `other` are durations of the same length.
170
+ #
171
+ # From the XQuery function [op:duration-equal](https://www.w3.org/TR/xpath-functions/#func-duration-equal).
172
+ #
173
+ # @see https://www.w3.org/TR/xpath-functions/#func-duration-equal
149
174
  def ==(other)
150
175
  # If lexically invalid, use regular literal testing
151
176
  return super unless self.valid?
152
177
 
153
- case other
154
- when Duration
155
- return super unless other.valid?
156
- self.to_f == other.to_f
157
- when String
158
- self.to_s(:xml) == other
159
- when Numeric
160
- self.to_f == other
161
- when Literal::DateTime, Literal::Time, Literal::Date
162
- false
163
- else
164
- super
165
- end
178
+ other.is_a?(Literal::Duration) && other.valid? ? @object == other.object : super
166
179
  end
167
180
 
168
- # @return [Float]
169
- def to_f
170
- ( @object[:yr].to_i * 365 * 24 * 3600 +
171
- @object[:mo].to_i * 30 * 24 * 3600 +
172
- @object[:da].to_i * 24 * 3600 +
173
- @object[:hr].to_i * 3600 +
174
- @object[:mi].to_i * 60 +
175
- @object[:se].to_f
176
- ) * (@object[:si] == '-' ? -1 : 1)
177
- end
181
+ # Years
182
+ #
183
+ # From the XQuery function [fn:years-from-duration](https://www.w3.org/TR/xpath-functions/#func-years-from-duration).
184
+ #
185
+ # @return [Integer]
186
+ # @see https://www.w3.org/TR/xpath-functions/#func-years-from-duration
187
+ def years; Integer.new(to_h[:yr] * (to_h[:si] ? -1 : 1)); end
178
188
 
189
+ # Months
190
+ #
191
+ # From the XQuery function [fn:months-from-duration](https://www.w3.org/TR/xpath-functions/#func-months-from-duration).
192
+ #
179
193
  # @return [Integer]
180
- def to_i; Integer(self.to_f); end
181
-
182
- private
194
+ # @see https://www.w3.org/TR/xpath-functions/#func-months-from-duration
195
+ def months; Integer.new(to_h[:mo] * (to_h[:si] ? -1 : 1)); end
196
+
197
+ # Days
198
+ #
199
+ # From the XQuery function [fn:days-from-duration](https://www.w3.org/TR/xpath-functions/#func-days-from-duration).
200
+ #
201
+ # @return [Integer]
202
+ # @see https://www.w3.org/TR/xpath-functions/#func-days-from-duration
203
+ def days; Integer.new(to_h[:da] * (to_h[:si] ? -1 : 1)); end
204
+
205
+ # Hours
206
+ #
207
+ # From the XQuery function [fn:hours-from-duration](https://www.w3.org/TR/xpath-functions/#func-hours-from-duration).
208
+ #
209
+ # @return [Integer]
210
+ # @see https://www.w3.org/TR/xpath-functions/#func-hours-from-duration
211
+ def hours; Integer.new(to_h[:hr] * (to_h[:si] ? -1 : 1)); end
212
+
213
+ # Minutes
214
+ #
215
+ # From the XQuery function [fn:minutes-from-duration](https://www.w3.org/TR/xpath-functions/#func-minutes-from-duration).
216
+ #
217
+ # @return [Integer]
218
+ # @see https://www.w3.org/TR/xpath-functions/#func-minutes-from-duration
219
+ def minutes; Integer.new(to_h[:mi] * (to_h[:si] ? -1 : 1)); end
220
+
221
+ # Seconds
222
+ #
223
+ # From the XQuery function [fn:seconds-from-duration](https://www.w3.org/TR/xpath-functions/#func-seconds-from-duration).
224
+ #
225
+ # @return [Decimal]
226
+ # @see https://www.w3.org/TR/xpath-functions/#func-seconds-from-duration
227
+ def seconds; Decimal.new(to_h[:se] * (to_h[:si] ? -1 : 1)); end
228
+
229
+ private
183
230
  # Reverse convert from XSD version of duration
184
231
  # XSD allows -P1111Y22M33DT44H55M66.666S with any combination in regular order
185
232
  # We assume 1M == 30D, but are out of spec in this regard
@@ -188,22 +235,137 @@ module RDF; class Literal
188
235
  # @param [String] value XSD formatted duration
189
236
  # @return [Duration]
190
237
  def parse(value)
191
- value.to_s.match(GRAMMAR)
238
+ return [0, 0] unless md = value.to_s.match(GRAMMAR)
239
+
240
+ months = md[:yr].to_i * 12 + md[:mo].to_i
241
+ seconds = md[:da].to_i * 3600 * 24 +
242
+ md[:hr].to_i * 3600 +
243
+ md[:mi].to_i * 60 +
244
+ md[:se].to_f
245
+
246
+ if md[:si]
247
+ if months != 0
248
+ months = -months
249
+ else
250
+ seconds = -seconds
251
+ end
252
+ end
253
+
254
+ [months, seconds]
192
255
  end
193
256
 
194
257
  def sec_str
195
- sec = @object[:se].to_f
258
+ sec = @object.last.abs % 60
196
259
  ((sec.truncate == sec ? "%d" : "%2.3f") % sec).sub(/(\.[1-9]+)0+$/, '\1')
197
260
  end
198
261
  end # Duration
199
262
 
263
+ ##
264
+ # A `YearMonthDuration` literal.
265
+ #
266
+ # `yearMonthDuration` is a datatype ·derived· from `xsd:duration` by restricting its ·lexical representations· to instances of `yearMonthDurationLexicalRep`. The ·value space· of `yearMonthDuration` is therefore that of `duration` restricted to those whose ·seconds· property is 0. This results in a `duration` datatype which is totally ordered.
267
+ #
268
+ # @see https://www.w3.org/TR/xmlschema11-2/#yearMonthDuration
269
+ class YearMonthDuration < Duration
270
+ DATATYPE = RDF::XSD.yearMonthDuration
271
+ GRAMMAR = %r(\A
272
+ (?<si>-)?
273
+ P(?:(?:(?:(?:(?<yr>\d+)Y)(?:(?<mo>\d+)M)?)
274
+ | (?:(?:(?<mo>\d+)M))
275
+ )
276
+ )
277
+ \z)x.freeze
278
+
279
+ ##
280
+ # Returns the sum of two xs:yearMonthDuration values.
281
+ #
282
+ # From the XQuery function [op:add-yearMonthDurations](https://www.w3.org/TR/xpath-functions/#func-add-yearMonthDurations).
283
+ #
284
+ # @param [YearMonthDuration] other
285
+ # @return [YearMonthDuration]
286
+ # @see https://www.w3.org/TR/xpath-functions/#func-add-yearMonthDurations
287
+ def +(other)
288
+ return type_error("#{other.inspect} is not a valid YearMonthDuration") unless other.is_a?(Literal::YearMonthDuration) && other.valid?
289
+ self.class.new([object.first + other.object.first, 0])
290
+ end
291
+
292
+ ##
293
+ # Returns the result of subtracting one xs:yearMonthDuration value from another.
294
+ #
295
+ # From the XQuery function [op:subtract-yearMonthDurations](https://www.w3.org/TR/xpath-functions/#func-subtract-yearMonthDurations).
296
+ #
297
+ # @param [YearMonthDuration] other
298
+ # @return [YearMonthDuration]
299
+ # @see https://www.w3.org/TR/xpath-functions/#func-subtract-yearMonthDurations
300
+ def -(other)
301
+ return type_error("#{other.inspect} is not a valid YearMonthDuration") unless other.is_a?(Literal::YearMonthDuration) && other.valid?
302
+ self.class.new([object.first - other.object.first, 0])
303
+ end
304
+
305
+ ##
306
+ # Returns the result of multiplying the value of self by `other`. The result is rounded to the nearest month.
307
+ #
308
+ # From the XQuery function [op:multiply-yearMonthDuration](https://www.w3.org/TR/xpath-functions/#func-multiply-yearMonthDuration).
309
+ #
310
+ # @param [Literal::Numeric, ::Numeric, DayTimeDuration] other
311
+ # @return [YearMonthDuration]
312
+ # @see https://www.w3.org/TR/xpath-functions/#func-multiply-yearMonthDuration
313
+ def *(other)
314
+ return type_error("#{other.inspect} is not a valid Numeric") unless (other.is_a?(::Numeric) || other.is_a?(Literal::Numeric))
315
+ self.class.new([(object.first * other.to_f).round, 0])
316
+ end
317
+
318
+ ##
319
+ # Returns the result of dividing the value of self by `other`. The result is rounded to the nearest month.
320
+ #
321
+ # From the XQuery functions [op:divide-yearMonthDuration](https://www.w3.org/TR/xpath-functions/#func-divide-yearMonthDuration) and [op:divide-yearMonthDuration-by-yearMonthDuration](https://www.w3.org/TR/xpath-functions/#func-divide-yearMonthDuration-by-yearMonthDuration).
322
+ #
323
+ # @param [Literal::Numeric, ::Numeric, YearMonthDuration] other
324
+ # @return [YearMonthDuration, Decimal]
325
+ # @see https://www.w3.org/TR/xpath-functions/#func-divide-yearMonthDuration
326
+ # @see https://www.w3.org/TR/xpath-functions/#func-divide-yearMonthDuration-by-yearMonthDuration
327
+ def /(other)
328
+ case other
329
+ when Literal::YearMonthDuration
330
+ return type_error("#{other.inspect} is not a valid YearMonthDuration or Numeric") unless other.valid?
331
+ Decimal.new(object.first / other.object.first.to_f)
332
+ when Literal::Numeric, ::Numeric
333
+ self.class.new([(object.first / other.to_f).round, 0])
334
+ else
335
+ type_error("#{other.inspect} is not a valid YearMonthDuration or Numeric")
336
+ end
337
+ end
338
+
339
+ ##
340
+ # Compares this literal to `other` for sorting purposes.
341
+ #
342
+ # From the XQuery function [op:yearMonthDuration-greater-than](https://www.w3.org/TR/xpath-functions/#func-yearMonthDuration-less-than).
343
+ #
344
+ # @param [Literal::YearMonthDuration] other
345
+ # @return [Boolean] `true` if less than other for defined datatypes
346
+ # @see https://www.w3.org/TR/xpath-functions/#func-yearMonthDuration-less-than
347
+ # @see https://www.w3.org/TR/xpath-functions/#func-yearMonthDuration-greater-than
348
+ def <=>(other)
349
+ return type_error("#{other.inspect} is not a valid YearMonthDuration") unless other.is_a?(Literal::YearMonthDuration) && other.valid?
350
+ @object.first <=> other.object.first
351
+ end
352
+
353
+ ##
354
+ # Converts the dayTimeDuration into rational seconds.
355
+ #
356
+ # @return [Rational]
357
+ def to_i
358
+ object.first.to_i
359
+ end
360
+ end # YearMonthDuration
361
+
200
362
  ##
201
363
  # A DayTimeDuration literal.
202
364
  #
203
- # dayTimeDuration is a datatype ·derived· from duration by restricting its ·lexical representations· to instances of dayTimeDurationLexicalRep. The ·value space· of dayTimeDuration is therefore that of duration restricted to those whose ·months· property is 0. This results in a duration datatype which is totally ordered.
365
+ # `dayTimeDuration` is a datatype ·derived· from `duration` by restricting its ·lexical representations· to instances of `dayTimeDurationLexicalRep`. The ·value space· of `dayTimeDuration` is therefore that of `duration` restricted to those whose ·months· property is 0. This results in a duration datatype which is totally ordered.
204
366
  #
205
367
  # @see https://www.w3.org/TR/xmlschema11-2/#dayTimeDuration
206
- class DayTimeDuration < Literal
368
+ class DayTimeDuration < Duration
207
369
  DATATYPE = RDF::XSD.dayTimeDuration
208
370
  GRAMMAR = %r(\A
209
371
  (?<si>-)?
@@ -221,22 +383,87 @@ module RDF; class Literal
221
383
  )
222
384
  )
223
385
  \z)x.freeze
224
- end # DayTimeDuration
225
386
 
226
- ##
227
- # A YearMonthDuration literal.
228
- #
229
- # yearMonthDuration is a datatype ·derived· from duration by restricting its ·lexical representations· to instances of yearMonthDurationLexicalRep. The ·value space· of yearMonthDuration is therefore that of duration restricted to those whose ·seconds· property is 0. This results in a duration datatype which is totally ordered.
230
- #
231
- # @see https://www.w3.org/TR/xmlschema11-2/#yearMonthDuration
232
- class YearMonthDuration < Literal
233
- DATATYPE = RDF::XSD.yearMonthDuration
234
- GRAMMAR = %r(\A
235
- (?<si>-)?
236
- P(?:(?:(?:(?:(?<yr>\d+)Y)(?:(?<mo>\d+)M)?)
237
- | (?:(?:(?<mo>\d+)M))
238
- )
239
- )
240
- \z)x.freeze
387
+ ##
388
+ # Returns the sum of two xs:dayTimeDuration values.
389
+ #
390
+ # From the XQuery function [op:add-dayTimeDurations](https://www.w3.org/TR/xpath-functions/#func-add-dayTimeDurations).
391
+ #
392
+ # @param [DayTimeDuration] other
393
+ # @return [DayTimeDuration]
394
+ # @see https://www.w3.org/TR/xpath-functions/#func-add-dayTimeDurations
395
+ def +(other)
396
+ return type_error("#{other.inspect} is not a valid DayTimeDuration") unless other.is_a?(Literal::DayTimeDuration) && other.valid?
397
+ self.class.new([0, object.last + other.object.last])
398
+ end
399
+
400
+ ##
401
+ # Returns the result of subtracting one xs:dayTimeDuration value from another.
402
+ #
403
+ # From the XQuery function [op:subtract-dayTimeDurationss](https://www.w3.org/TR/xpath-functions/#func-subtract-dayTimeDurations).
404
+ #
405
+ # @param [DayTimeDuration] other
406
+ # @return [DayTimeDuration]
407
+ # @see https://www.w3.org/TR/xpath-functions/#func-subtract-dayTimeDurations
408
+ def -(other)
409
+ return type_error("#{other.inspect} is not a valid DayTimeDuration") unless other.is_a?(Literal::DayTimeDuration) && other.valid?
410
+ self.class.new([0, object.last - other.object.last])
411
+ end
412
+
413
+ ##
414
+ # Returns the result of multiplying the value of self by `other`. The result is rounded to the nearest month.
415
+ #
416
+ # From the XQuery function [op:multiply-dayTimeDuration](https://www.w3.org/TR/xpath-functions/#func-multiply-dayTimeDuration).
417
+ #
418
+ # @param [Literal::Numeric, ::Numeric] other
419
+ # @return [DayTimeDuration]
420
+ # @see https://www.w3.org/TR/xpath-functions/#func-multiply-dayTimeDuration
421
+ def *(other)
422
+ return type_error("#{other.inspect} is not a valid Numeric") unless (other.is_a?(::Numeric) || other.is_a?(Literal::Numeric))
423
+ self.class.new([0, object.last * other.to_f])
424
+ end
425
+
426
+ ##
427
+ # Returns the result of dividing the value of self by `other`. The result is rounded to the nearest month.
428
+ #
429
+ # From the XQuery functions [op:divide-yearMonthDuration](https://www.w3.org/TR/xpath-functions/#func-divide-dayTimeDuration) and [op:divide-yearMonthDuration-by-yearMonthDuration](https://www.w3.org/TR/xpath-functions/#func-divide-dayTimeDuration-by-dayTimeDuration).
430
+ #
431
+ # @param [Literal::Numeric, ::Numeric, DayTimeDuration] other
432
+ # @return [DayTimeDuration, Decimal]
433
+ # @see https://www.w3.org/TR/xpath-functions/#func-divide-dayTimeDuration
434
+ # @see https://www.w3.org/TR/xpath-functions/#func-divide-dayTimeDuration-by-dayTimeDuration
435
+ def /(other)
436
+ case other
437
+ when DayTimeDuration
438
+ return type_error("#{other.inspect} is not a valid DayTimeDuration or Numeric") unless other.valid?
439
+ Decimal.new(object.last / other.object.last.to_f)
440
+ when Literal::Numeric, ::Numeric
441
+ self.class.new([0, object.last / other.to_f])
442
+ else
443
+ type_error("#{other.inspect} is not a valid DayTimeDuration or Numeric")
444
+ end
445
+ end
446
+
447
+ ##
448
+ # Compares this literal to `other` for sorting purposes.
449
+ #
450
+ # From the XQuery function [op:dayTimeDuration-less-than](https://www.w3.org/TR/xpath-functions/#func-dayTimeDuration-less-than).
451
+ #
452
+ # @param [DayTimeDuration] other
453
+ # @return [Boolean] `true` if less than other for defined datatypes
454
+ # @see https://www.w3.org/TR/xpath-functions/#func-dayTimeDuration-less-than
455
+ # @see https://www.w3.org/TR/xpath-functions/#func-dayTimeDuration-greater-than
456
+ def <=>(other)
457
+ return type_error("#{other.inspect} is not a valid DayTimeDuration") unless other.is_a?(Literal::DayTimeDuration) && other.valid?
458
+ @object.last <=> other.object.last
459
+ end
460
+
461
+ ##
462
+ # Converts the dayTimeDuration into rational seconds.
463
+ #
464
+ # @return [Rational]
465
+ def to_r
466
+ Rational(object.last) / (24 * 3600)
467
+ end
241
468
  end # DayTimeDuration
242
469
  end; end # RDF::Literal
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rdf-xsd
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gregg
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-12-06 00:00:00.000000000 Z
12
+ date: 2022-02-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rdf
@@ -109,7 +109,8 @@ dependencies:
109
109
  - - "~>"
110
110
  - !ruby/object:Gem::Version
111
111
  version: '0.9'
112
- description: Adds RDF::Literal subclasses for extended XSD datatypes.
112
+ description: Adds RDF::Literal subclasses for extended XSD datatypes with methods
113
+ for many XPath and XQuery functions.
113
114
  email: public-rdf-ruby@w3.org
114
115
  executables: []
115
116
  extensions: []
@@ -132,7 +133,12 @@ files:
132
133
  homepage: https://github.com/ruby-rdf/rdf-xsd
133
134
  licenses:
134
135
  - Unlicense
135
- metadata: {}
136
+ metadata:
137
+ documentation_uri: https://ruby-rdf.github.io/rdf-xsd
138
+ bug_tracker_uri: https://github.com/ruby-rdf/rdf-xsd/issues
139
+ homepage_uri: https://github.com/ruby-rdf/rdf-xsd
140
+ mailing_list_uri: https://lists.w3.org/Archives/Public/public-rdf-ruby/
141
+ source_code_uri: https://github.com/ruby-rdf/rdf-xsd
136
142
  post_install_message: |2
137
143
 
138
144
  For best results, use nokogiri and equivalent-xml gems as well.
@@ -154,5 +160,5 @@ requirements: []
154
160
  rubygems_version: 3.3.3
155
161
  signing_key:
156
162
  specification_version: 4
157
- summary: Extended XSD Datatypes for RDF.rb.
163
+ summary: Extended XSD Datatypes and XPath and XQuery functions for RDF.rb.
158
164
  test_files: []