@@ -43,8 +43,8 @@ module RDF
4343 # EX = Class.new(RDF::StrictVocabulay("http://example/ns#")) do
4444 # # Ontology definition
4545 # ontology :"http://example/ns#",
46- # label: "The RDF Example Vocablary".freeze ,
47- # type: "http://www.w3.org/2002/07/owl#Ontology".freeze
46+ # label: "The RDF Example Vocablary",
47+ # type: "http://www.w3.org/2002/07/owl#Ontology"
4848 #
4949 # # Class definitions
5050 # term :Class,
@@ -56,12 +56,12 @@ module RDF
5656 #
5757 # # Property definitions
5858 # property :prop,
59- # comment: "A description of the property".freeze ,
60- # label: "property".freeze ,
61- # domain: "http://example/ns#Class".freeze ,
62- # range: "rdfs:Literal".freeze ,
63- # isDefinedBy: %(ex:).freeze ,
64- # type: "rdf:Property".freeze
59+ # comment: "A description of the property",
60+ # label: "property",
61+ # domain: "http://example/ns#Class",
62+ # range: "rdfs:Literal",
63+ # isDefinedBy: %(ex:),
64+ # type: "rdf:Property"
6565 # end
6666 #
6767 # @example Method calls are converted to the typical RDF camelcase convention
@@ -203,12 +203,24 @@ def strict?; false; end
203203 #
204204 # @example A simple term definition
205205 # property :domain,
206- # comment: %(A domain of the subject property.).freeze,
207- # domain: "rdf:Property".freeze,
208- # label: "domain".freeze,
209- # range: "rdfs:Class".freeze,
210- # isDefinedBy: %(rdfs:).freeze,
211- # type: "rdf:Property".freeze
206+ # comment: %(A domain of the subject property.),
207+ # domain: "rdf:Property",
208+ # label: "domain",
209+ # range: "rdfs:Class",
210+ # isDefinedBy: %(rdfs:),
211+ # type: "rdf:Property"
212+ #
213+ # @example A term definition with tagged values
214+ # property :actor,
215+ # comment: {en: "Subproperty of as:attributedTo that identifies the primary actor"},
216+ # domain: "https://www.w3.org/ns/activitystreams#Activity",
217+ # label: {en: "actor"},
218+ # range: term(
219+ # type: "http://www.w3.org/2002/07/owl#Class",
220+ # unionOf: list("https://www.w3.org/ns/activitystreams#Object", "https://www.w3.org/ns/activitystreams#Link")
221+ # ),
222+ # subPropertyOf: "https://www.w3.org/ns/activitystreams#attributedTo",
223+ # type: "http://www.w3.org/2002/07/owl#ObjectProperty"
212224 #
213225 # @example A SKOS term with anonymous values
214226 # term: :af,
@@ -227,76 +239,8 @@ def strict?; false; end
227239 # "foaf:name": "Aland Islands"
228240 #
229241 # @param [String, #to_s] name
230- # @param [Hash{Symbol=>String,Array<String,Term>}] options
231- # Any other values are expected to expands to a {URI} using built-in vocabulary prefixes. The value is a `String`, `Array<String>` or `Array<Term>` which is interpreted according to the `range` of the associated property.
232- # @option options [String, Array<String,Term>] :type
233- # Shortcut for `rdf:type`, values are interpreted as a {Term}.
234- # @option options [String, Array<String>] :comment
235- # Shortcut for `rdfs:comment`, values are interpreted as a {Literal}.
236- # @option options [String, Array<String,Term>] :domain
237- # Shortcut for `rdfs:domain`, values are interpreted as a {Term}.
238- # @option options [String, Array<String,Term>] :isDefinedBy
239- # Shortcut for `rdfs:isDefinedBy`, values are interpreted as a {Term}.
240- # @option options [String, Array<String>] :label
241- # Shortcut for `rdfs:label`, values are interpreted as a {Literal}.
242- # @option options [String, Array<String,Term>] :range
243- # Shortcut for `rdfs:range`, values are interpreted as a {Term}.
244- # @option options [String, Array<String,Term>] :subClassOf
245- # Shortcut for `rdfs:subClassOf`, values are interpreted as a {Term}.
246- # @option options [String, Array<String,Term>] :subPropertyOf
247- # Shortcut for `rdfs:subPropertyOf`, values are interpreted as a {Term}.
248- # @option options [String, Array<String,Term>] :allValuesFrom
249- # Shortcut for `owl:allValuesFrom`, values are interpreted as a {Term}.
250- # @option options [String, Array<String,Term>] :cardinality
251- # Shortcut for `owl:cardinality`, values are interpreted as a {Literal}.
252- # @option options [String, Array<String,Term>] :equivalentClass
253- # Shortcut for `owl:equivalentClass`, values are interpreted as a {Term}.
254- # @option options [String, Array<String,Term>] :equivalentProperty
255- # Shortcut for `owl:equivalentProperty`, values are interpreted as a {Term}.
256- # @option options [String, Array<String,Term>] :intersectionOf
257- # Shortcut for `owl:intersectionOf`, values are interpreted as a {Term}.
258- # @option options [String, Array<String,Term>] :inverseOf
259- # Shortcut for `owl:inverseOf`, values are interpreted as a {Term}.
260- # @option options [String, Array<String,Term>] :maxCardinality
261- # Shortcut for `owl:maxCardinality`, values are interpreted as a {Literal}.
262- # @option options [String, Array<String,Term>] :minCardinality
263- # Shortcut for `owl:minCardinality`, values are interpreted as a {Literal}.
264- # @option options [String, Array<String,Term>] :onProperty
265- # Shortcut for `owl:onProperty`, values are interpreted as a {Term}.
266- # @option options [String, Array<String,Term>] :someValuesFrom
267- # Shortcut for `owl:someValuesFrom`, values are interpreted as a {Term}.
268- # @option options [String, Array<String,Term>] :unionOf
269- # Shortcut for `owl:unionOf`, values are interpreted as a {Term}.
270- # @option options [String, Array<String,Term>] :domainIncludes
271- # Shortcut for `schema:domainIncludes`, values are interpreted as a {Term}.
272- # @option options [String, Array<String,Term>] :rangeIncludes
273- # Shortcut for `schema:rangeIncludes`, values are interpreted as a {Term}.
274- # @option options [String, Array<String>] :altLabel
275- # Shortcut for `skos:altLabel`, values are interpreted as a {Literal}.
276- # @option options [String, Array<String,Term>] :broader
277- # Shortcut for `skos:broader`, values are interpreted as a {Term}.
278- # @option options [String, Array<String>] :definition
279- # Shortcut for `skos:definition`, values are interpreted as a {Literal}.
280- # @option options [String, Array<String>] :editorialNote
281- # Shortcut for `skos:editorialNote`, values are interpreted as a {Literal}.
282- # @option options [String, Array<String,Term>] :exactMatch
283- # Shortcut for `skos:exactMatch`, values are interpreted as a {Term}.
284- # @option options [String, Array<String,Term>] :hasTopConcept
285- # Shortcut for `skos:hasTopConcept`, values are interpreted as a {Term}.
286- # @option options [String, Array<String,Term>] :inScheme
287- # Shortcut for `skos:inScheme`, values are interpreted as a {Term}.
288- # @option options [String, Array<String,Term>] :member
289- # Shortcut for `skos:member`, values are interpreted as a {Term}.
290- # @option options [String, Array<String,Term>] :narrower
291- # Shortcut for `skos:narrower`, values are interpreted as a {Term}.
292- # @option options [String, Array<String>] :notation
293- # Shortcut for `skos:notation`, values are interpreted as a {Literal}.
294- # @option options [String, Array<String>] :note
295- # Shortcut for `skos:note`, values are interpreted as a {Literal}.
296- # @option options [String, Array<String>] :prefLabel
297- # Shortcut for `skos:prefLabel`, values are interpreted as a {Literal}.
298- # @option options [String, Array<String,Term>] :related
299- # Shortcut for `skos:related`, values are interpreted as a {Term}.
242+ # @param [Hash{Symbol=>String,Array<String>}] options
243+ # Any other values are expected to expands to a {URI} using built-in vocabulary prefixes. The value is a `String`, 'Hash{Symbol=>String,Array<String>}' or `Array<String,Hash{Symbol=>Array<String>}>` which is interpreted according to the `range` of the associated property and by heuristically determining the value datatype. See `attributes` argument to {Term#initialize}.
300244 # @return [RDF::Vocabulary::Term]
301245 def property ( *args )
302246 case args . length
@@ -309,15 +253,6 @@ def property(*args)
309253 uri_str = [ to_s , name . to_s ] . join ( '' )
310254 URI . cache . delete ( uri_str . to_sym ) # Clear any previous entry
311255
312- # Transform attribute keys that are PNames with a warning
313- # FIXME: add back later
314- #if !@is_deprecated && options.is_a?(Hash) &&
315- # options.keys.map(&:to_s).any? {|k| k.include?(':') && !k.match?(/^https?:/)}
316- #
317- # @is_deprecated = true
318- # warn "[DEPRECATION] Vocabulary #{to_uri} includes pname attribute keys, regenerate"
319- #end
320-
321256 # Term attributes passed in a block for lazy evaluation. This helps to avoid load-time circular dependencies
322257 prop = Term . intern ( uri_str , vocab : self , attributes : options || { } )
323258 props [ name . to_sym ] = prop
@@ -985,6 +920,10 @@ module Term
985920 #
986921 # Symbols which are accessors may also be looked up by their associated URI.
987922 #
923+ # Values may be Strings, Hash (Map), or Terms, or an Array including a combination of these. A Hash (Map) is used to create a datatype/language map to one or more string values which represent either datatyped literals, or language-tagged literals as interpreted by {#attribute_value}.
924+ #
925+ # In general, this accessor is used internally. The {#properties} method interprets these values as {RDF::Value}.
926+ #
988927 # @note lookup by PName is DEPRECATED and will be removed in a future version.
989928 #
990929 # @example looking up term label
@@ -993,23 +932,23 @@ module Term
993932 # RDF::RDFS.Literal.attributes[RDF::RDFS.label] #=> "Literal"
994933 # RDF::RDFS.Literal.attributes["http://www.w3.org/2000/01/rdf-schema#label"] #=> "Literal"
995934 # RDF::RDFS.Literal.attributes[:"http://www.w3.org/2000/01/rdf-schema#label"] #=> "Literal"
996- # @return [Hash{Symbol,Resource => Term, #to_s}]
935+ # @return [Hash{Symbol => String, Term, Hash{Symbol => String}, Array<String, Term, Hash{Symbol => String}>}]
936+ # @see #properties
997937 attr_reader :attributes
998938
999939 ##
1000- # @overload new(uri, attributes:, **options)
940+ # @overload new(uri, attributes:, vocab:, **options)
1001941 # @param [URI, String, #to_s] uri
1002942 # @param [Vocabulary] vocab Vocabulary of this term.
1003- # @param [Hash{Symbol => Symbol,Array<String,Term >}] attributes ({})
943+ # @param [Hash{Symbol => String,Term,Hash{ Symbol=>String ,Array<String>},Array<String >}] attributes ({})
1004944 # Attributes of this vocabulary term, used for finding `label` and `comment` and to serialize the term back to RDF. See {#attributes} and {#properties} for other ways to access.
1005945 # @param [Hash{Symbol => Object}] options
1006946 # Options from {URI#initialize}
1007947 #
1008- # @overload new(attributes:, **options)
1009- # @param [Hash{Symbol => Object}] options
948+ # @overload new(attributes:, vocab:, **options)
1010949 # @param [Vocabulary] vocab Vocabulary of this term.
1011- # @param [Hash{Symbol => Symbol,Array<String,Term >}] attributes ({})
1012- # Attributes of this vocabulary term, used for finding `label` and `comment` and to serialize the term back to RDF. See {#attributes} and {#properties} for other ways to access.
950+ # @param [Hash{Symbol => String,Term,Hash{ Symbol=>String ,Array<String>},Array<String >}] attributes ({})
951+ # Attributes of this vocabulary term, used for finding `label`, `comment` and other term properties, and to serialize the term back to RDF. See {#attributes} and {#properties} for other ways to access.
1013952 # @param [Hash{Symbol => Object}] options
1014953 # Options from {URI#initialize}
1015954 def self . new ( *args , vocab : nil , attributes : { } , **options )
@@ -1149,6 +1088,7 @@ def other?
11491088 # RDF::RDFS.Literal.properties[:"http://www.w3.org/2000/01/rdf-schema#label"] #=> RDF::Literal("Literal")
11501089 #
11511090 # @return [Hash{Symbol => Array<RDF::Value>}]
1091+ # @see #attribute_value
11521092 def properties
11531093 Hash . new { |hash , key | attribute_value ( key ) }
11541094 end
@@ -1161,7 +1101,8 @@ def properties
11611101 # Attribute values which are not already a {RDF::Value} (including strings and symbols) are converted by a heuristic loookup as follows:
11621102 #
11631103 # * An {RDF::URI} if it can be turned into a valid IRI using {RDF::Vocabulary.expand_pname}. This includes IRIs already in non-relative form.
1164- # * {RDF::Literal::Date} if valud,
1104+ # * A {Hash{Symbol=>String,Array<String>}} is interpreted as a datatype/language map. If the key contains a ':', it is treated as a PName or IRI datatype applied to the values. Otherwise, it is treated as a language-tag applied to the values.
1105+ # * {RDF::Literal::Date} if valid,
11651106 # * {RDF::Literal::DateTime} if valid,
11661107 # * {RDF::Literal::Integer} if valid,
11671108 # * {RDF::Literal::Decimal} if valid,
@@ -1179,9 +1120,22 @@ def attribute_value(prop)
11791120 v = value . is_a? ( Symbol ) ? value . to_s : value
11801121 value = ( RDF ::Vocabulary . expand_pname ( v ) rescue nil ) if v . is_a? ( String ) && v . include? ( ':' )
11811122 value = value . to_uri if value . respond_to? ( :to_uri )
1182- unless value . is_a? ( RDF ::Value ) && value . valid?
1123+ value = if value . is_a? ( RDF ::Value ) && value . valid?
1124+ value
1125+ elsif value . is_a? ( Hash )
1126+ # type/language map
1127+ value . inject ( [ ] ) do |memo , ( k , v ) |
1128+ vv = [ v ] unless v . is_a? ( Array )
1129+ memo << if k . to_s . include? ( ':' )
1130+ dt = RDF ::Vocabulary . expand_pname ( v ) rescue nil
1131+ vv . map { |val | RDF ::Literal ( val , datatype : dt ) }
1132+ else
1133+ vv . map { |val | RDF ::Literal ( val , language : k ) }
1134+ end
1135+ end . flatten . compact . select ( &:valid? )
1136+ else
11831137 # Use as most appropriate literal
1184- value = [
1138+ [
11851139 RDF ::Literal ::Date ,
11861140 RDF ::Literal ::DateTime ,
11871141 RDF ::Literal ::Integer ,
@@ -1196,9 +1150,7 @@ def attribute_value(prop)
11961150 end
11971151 end
11981152 end
1199-
1200- value
1201- end
1153+ end . flatten
12021154
12031155 prop_values . length <= 1 ? prop_values . first : prop_values
12041156 end
@@ -1296,24 +1248,24 @@ def to_ruby(indent: "")
12961248 values = [ values ] . compact unless values . is_a? ( Array )
12971249 values = values . map do |value |
12981250 if value . is_a? ( Literal ) && %w( : comment definition notation note editorialNote ) . include? ( k . to_s )
1299- "%(#{ value . to_s . gsub ( '(' , '\(' ) . gsub ( ')' , '\)' ) } ).freeze "
1251+ "%(#{ value . to_s . gsub ( '(' , '\(' ) . gsub ( ')' , '\)' ) } )"
13001252 elsif value . node? && value . is_a? ( RDF ::Vocabulary ::Term )
1301- "#{ value . to_ruby ( indent : indent + " " ) } .freeze "
1253+ "#{ value . to_ruby ( indent : indent + " " ) } "
13021254 elsif value . is_a? ( RDF ::Term )
1303- "#{ value . to_s . inspect } .freeze "
1255+ "#{ value . to_s . inspect } "
13041256 elsif value . is_a? ( RDF ::List )
13051257 list_elements = value . map do |u |
13061258 if u . uri?
1307- "#{ u . to_s . inspect } .freeze "
1259+ "#{ u . to_s . inspect } "
13081260 elsif u . node? && u . respond_to? ( :to_ruby )
13091261 u . to_ruby ( indent : indent + " " )
13101262 else
1311- "#{ u . to_s . inspect } .freeze "
1263+ "#{ u . to_s . inspect } "
13121264 end
13131265 end
13141266 "list(#{ list_elements . join ( ', ' ) } )"
13151267 else
1316- "#{ value . inspect } .freeze "
1268+ "#{ value . inspect } "
13171269 end
13181270 end
13191271 "#{ k . to_s . include? ( ':' ) ? k . to_s . inspect : k } : " +
0 commit comments