Module: ActiveTriples::RDFSource
- Extended by:
- ActiveSupport::Concern
- Includes:
- ActiveModel::Conversion, ActiveModel::Serialization, ActiveModel::Serializers::JSON, ActiveModel::Validations, NestedAttributes, Persistable, Properties, RDF::Queryable, RDF::Value
- Included in:
- List::ListResource, Resource
- Defined in:
- lib/active_triples/rdf_source.rb
Overview
complete RDF::Value/RDF::Term/RDF::Resource interfaces
Defines a concern for managing RDF::Graph driven Resources as discrete, stateful graphs using ActiveModel-style objects.
An ‘RDFSource` models a resource (RDF::Resource) with a state that may change over time. The current state is represented by an RDF::Graph, accessible as Persistable#graph. The source is an RDF::Resource represented by #rdf_subject, which may be either an RDF::URI or an RDF::Node.
The graph of a source may contain contain arbitrary triples, including full representations of the state of other sources. The triples in the graph should be limited to statements that have bearing on the resource’s state.
Properties may be defined on inheriting classes to configure accessor methods for predicates.
An ‘RDFSource` is an RDF::Term—it can be used as a subject, predicate, object, or context in an RDF::Statement.
Defined Under Namespace
Modules: ClassMethods
Class Method Summary collapse
Instance Method Summary collapse
-
#==(other) ⇒ Object
Compares self to other for RDF::Term equality.
-
#[](term_or_property) ⇒ Object
Returns an array of values belonging to the property requested.
-
#[]=(term_or_property, value) ⇒ Object
Adds or updates a property with supplied values.
- #add_observer(observer) ⇒ Object
-
#attributes ⇒ Hash<String, Array<Object>>
Gives a hash containing both the registered and unregistered attributes of the resource.
- #attributes=(values) ⇒ Object
-
#base_uri ⇒ String?
The base URI the resource will use when setting its subject.
- #count ⇒ Object
-
#default_labels ⇒ Array<RDF::URI>
A group of properties to use for default labels.
-
#delete_observer(observer) ⇒ #notify?
The deleted observer; nil if the observer was not registered.
-
#dump(*args) ⇒ String
Returns a serialized string representation of self.
- #each ⇒ Object
- #escape ⇒ Object
-
#fetch(**args) {|resource| ... } ⇒ ActiveTriples::RDFSource
Load data from the #rdf_subject URI.
-
#get_relation(args) ⇒ Object
deprecated
Deprecated.
for removal in 1.0; use ‘#get_values` insctead.
-
#get_values(*args) ⇒ ActiveTriples::Relation
Returns an array of values belonging to the property requested.
-
#graph_name ⇒ nil
Returns ‘nil` as the `graph_name`.
- #has_statement? ⇒ Object
-
#humanize ⇒ String
A string identifier for the resource; ” if the resource is a node.
- #id ⇒ String
-
#initialize(*args, &block) ⇒ Object
Initialize an instance of this resource class.
- #inspect ⇒ String
- #load! ⇒ Object
- #mark_for_destruction ⇒ Object
- #marked_for_destruction? ⇒ Boolean
-
#new_record? ⇒ Boolean
Indicates if the record is ‘new’ (has not yet been persisted).
-
#node? ⇒ Boolean
True if the Term is a node.
-
#notify_observers(property) ⇒ void
Sends ‘#notify` messages with the property symbol and the current values for the property to each observer.
-
#parent ⇒ Object
Delegate parent to the persistence strategy if possible.
- #parent=(parent) ⇒ Object
- #query ⇒ Object
-
#rdf_label ⇒ Object
Looks for labels in various default fields, prioritizing configured label fields.
-
#rdf_subject ⇒ RDF::URI, RDF::Node
(also: #to_term)
Gives the representation of this RDFSource as an RDF::Term.
- #serializable_hash ⇒ Hash
-
#set_subject!(uri_or_str) ⇒ void
Set a new rdf_subject for the resource.
-
#set_value(*args) ⇒ ActiveTriples::Relation
Adds or updates a property by creating triples for each of the supplied values.
-
#term?(*args) ⇒ Object
See RDF::Enumerable#term?.
- #to_base ⇒ Object
-
#to_uri ⇒ RDF::URI
The uri.
- #type ⇒ Object
- #type=(type) ⇒ Object
-
#uri? ⇒ Boolean
True if the Term is a uri.
Methods included from Persistable
#delete_statement, #destroy, #destroyed?, #graph, #insert_statement, #persist!, #persisted?, #persistence_strategy, #reload, #set_persistence_strategy
Class Method Details
.type_registry ⇒ Object
58 59 60 |
# File 'lib/active_triples/rdf_source.rb', line 58 def type_registry @@type_registry ||= {} end |
Instance Method Details
#==(other) ⇒ Object
Compares self to other for RDF::Term equality.
Delegates the check to ‘other#==` passing it the term version of `self`.
147 148 149 |
# File 'lib/active_triples/rdf_source.rb', line 147 def ==(other) other == to_term end |
#[](term_or_property) ⇒ Object
Returns an array of values belonging to the property requested. Elements in the array may RdfResource objects or a valid datatype.
521 522 523 |
# File 'lib/active_triples/rdf_source.rb', line 521 def [](term_or_property) get_values(term_or_property) end |
#[]=(term_or_property, value) ⇒ Object
This method will delete existing statements with the correct subject and predicate from the graph
Adds or updates a property with supplied values.
534 535 536 |
# File 'lib/active_triples/rdf_source.rb', line 534 def []=(term_or_property, value) self[term_or_property].set(value) end |
#add_observer(observer) ⇒ Object
613 614 615 |
# File 'lib/active_triples/rdf_source.rb', line 613 def add_observer(observer) @observers.add(observer) end |
#attributes ⇒ Hash<String, Array<Object>>
Gives a hash containing both the registered and unregistered attributes of the resource. Unregistered attributes are given with full URIs.
@todo: should this, ‘#attributes=`, and `#serializable_hash` be moved out
into a dedicated `Serializer` object?
188 189 190 191 192 193 194 |
# File 'lib/active_triples/rdf_source.rb', line 188 def attributes attrs = {} attrs['id'] = id fields.map { |f| attrs[f.to_s] = get_values(f) } unregistered_predicates.map { |uri| attrs[uri.to_s] = get_values(uri) } attrs end |
#attributes=(values) ⇒ Object
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/active_triples/rdf_source.rb', line 196 def attributes=(values) raise(ArgumentError, "values must be a Hash. Got: #{values.class}") unless values.is_a? Hash values = values.with_indifferent_access id = values.delete(:id) set_subject!(id) if node? && id && get_uri(id).uri? values.each do |key, value| if reflections.has_property?(key) set_value(key, value) elsif .keys.any? { |k| key == "#{k}_attributes" } send("#{key}=".to_sym, value) else raise ArgumentError, "No association found for name `#{key}'. " \ 'Has it been defined yet?' end end end |
#base_uri ⇒ String?
Returns the base URI the resource will use when setting its subject. ‘nil` if none is used.
352 353 354 |
# File 'lib/active_triples/rdf_source.rb', line 352 def base_uri self.class.base_uri end |
#count ⇒ Object
90 |
# File 'lib/active_triples/rdf_source.rb', line 90 delegate :query, :each, :load!, :count, :has_statement?, to: :graph |
#default_labels ⇒ Array<RDF::URI>
Returns a group of properties to use for default labels.
219 220 221 222 223 224 225 |
# File 'lib/active_triples/rdf_source.rb', line 219 def default_labels [RDF::Vocab::SKOS.prefLabel, RDF::Vocab::DC.title, RDF::RDFS.label, RDF::Vocab::SKOS.altLabel, RDF::Vocab::SKOS.hiddenLabel] end |
#delete_observer(observer) ⇒ #notify?
Returns the deleted observer; nil if the observer was not registered.
622 623 624 |
# File 'lib/active_triples/rdf_source.rb', line 622 def delete_observer(observer) @observers.delete?(observer) end |
#dump(*args) ⇒ String
Returns a serialized string representation of self. Extends the base implementation builds a JSON-LD context if the specified format is :jsonld and a context is provided by #jsonld_context
247 248 249 250 251 252 253 |
# File 'lib/active_triples/rdf_source.rb', line 247 def dump(*args) if args.first == :jsonld && respond_to?(:jsonld_context) args << {} unless args.last.is_a?(Hash) args.last[:context] ||= jsonld_context end super end |
#each ⇒ Object
90 |
# File 'lib/active_triples/rdf_source.rb', line 90 delegate :query, :each, :load!, :count, :has_statement?, to: :graph |
#escape ⇒ Object
97 |
# File 'lib/active_triples/rdf_source.rb', line 97 delegate :to_base, :escape, to: :to_term |
#fetch(**args) {|resource| ... } ⇒ ActiveTriples::RDFSource
Load data from the #rdf_subject URI. Retrieved data will be parsed into the Resource’s graph from available RDF::Readers and available from property accessors if if predicates are registered.
404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
# File 'lib/active_triples/rdf_source.rb', line 404 def fetch(**args, &_block) begin load(rdf_subject, **args) rescue => e if block_given? yield(self) else raise "#{self} is a blank node; " \ 'Cannot fetch a resource without a URI' if node? raise e end end self end |
#get_relation(args) ⇒ Object
for removal in 1.0; use ‘#get_values` insctead.
541 542 543 544 545 |
# File 'lib/active_triples/rdf_source.rb', line 541 def get_relation(args) warn 'DEPRECATION: `ActiveTriples::RDFSource#get_relation` will be' \ 'removed in 1.0; use `#get_values` instead.' get_values(*args) end |
#get_values(property) ⇒ ActiveTriples::Relation #get_values(uri, property) ⇒ ActiveTriples::Relation
should this raise an error when the property argument is not an RDF::Term or a registered property key?
Returns an array of values belonging to the property requested. Elements in the array may RdfResource objects or a valid datatype.
Handles two argument patterns. The recommended pattern, which accesses properties directly on this RDFSource, is:
get_values(property)
509 510 511 512 513 514 |
# File 'lib/active_triples/rdf_source.rb', line 509 def get_values(*args) @relation_cache ||= {} rel = Relation.new(self, args) @relation_cache["#{rel.send(:rdf_subject)}/#{rel.property}/#{rel.rel_args}"] ||= rel @relation_cache["#{rel.send(:rdf_subject)}/#{rel.property}/#{rel.rel_args}"] end |
#graph_name ⇒ nil
Returns ‘nil` as the `graph_name`. This behavior mimics an `RDF::Graph` with no graph name, or one without named graph support.
@note: it’s possible to think of an ‘RDFSource` as “supporting named
graphs" in the sense that the `#rdf_subject` is an implied graph name.
For RDF.rb's purposes, however, it has a nil graph name: when
enumerating statements, we treat them as triples.
300 301 302 |
# File 'lib/active_triples/rdf_source.rb', line 300 def graph_name nil end |
#has_statement? ⇒ Object
90 |
# File 'lib/active_triples/rdf_source.rb', line 90 delegate :query, :each, :load!, :count, :has_statement?, to: :graph |
#humanize ⇒ String
Returns A string identifier for the resource; ” if the resource is a node.
307 308 309 |
# File 'lib/active_triples/rdf_source.rb', line 307 def humanize node? ? '' : rdf_subject.to_s end |
#id ⇒ String
321 322 323 |
# File 'lib/active_triples/rdf_source.rb', line 321 def id node? ? rdf_subject.id : rdf_subject.to_s end |
#initialize(*args, &block) ⇒ Object
move this logic out to a Builder?
Initialize an instance of this resource class. Defaults to a blank node subject. In addition to RDF::Graph parameters, you can pass in a URI and/or a parent to build a resource from a existing data.
You can pass in only a parent with:
new(nil, parent)
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/active_triples/rdf_source.rb', line 110 def initialize(*args, &block) @observers = Set.new resource_uri = args.shift unless args.first.is_a?(Hash) @rdf_subject = get_uri(resource_uri) if resource_uri if args.first.is_a?(Hash) || args.empty? set_persistence_strategy(RepositoryStrategy) else set_persistence_strategy(ParentStrategy) persistence_strategy.parent = args.shift end graph_params = if args.empty? || args.first.nil? {} else args.shift end persistence_strategy.graph = RDF::Graph.new(**graph_params, &block) reload # Append type to graph if necessary. Array.wrap(self.class.type).each do |type| get_values(:type) << type unless get_values(:type).include?(type) end end |
#inspect ⇒ String
Without a custom #inspect, we inherit from RDF::Value.
329 330 331 |
# File 'lib/active_triples/rdf_source.rb', line 329 def inspect sprintf("#<%s:%#0x ID:%s>", self.class.to_s, self.object_id, self.to_base) end |
#load! ⇒ Object
90 |
# File 'lib/active_triples/rdf_source.rb', line 90 delegate :query, :each, :load!, :count, :has_statement?, to: :graph |
#mark_for_destruction ⇒ Object
601 602 603 |
# File 'lib/active_triples/rdf_source.rb', line 601 def mark_for_destruction @marked_for_destruction = true end |
#marked_for_destruction? ⇒ Boolean
605 606 607 |
# File 'lib/active_triples/rdf_source.rb', line 605 def marked_for_destruction? @marked_for_destruction end |
#new_record? ⇒ Boolean
Indicates if the record is ‘new’ (has not yet been persisted).
577 578 579 |
# File 'lib/active_triples/rdf_source.rb', line 577 def new_record? !persisted? end |
#node? ⇒ Boolean
Returns true if the Term is a node.
337 338 339 |
# File 'lib/active_triples/rdf_source.rb', line 337 def node? rdf_subject.node? end |
#notify_observers(property) ⇒ void
We short circuit to avoid query costs if no observers are present. If there are regisetred observers, values are returned as an array. This means that we incur query costs immediately and only once.
This method returns an undefined value.
Sends ‘#notify` messages with the property symbol and the current values for the property to each observer.
650 651 652 653 654 |
# File 'lib/active_triples/rdf_source.rb', line 650 def notify_observers(property) return if @observers.empty? values = get_values(property).to_a @observers.each { |o| o.notify(property, values) } end |
#parent ⇒ Object
establish a better pattern for this. ‘#parent` has been a public method in the past, but it’s probably time to deprecate it.
Delegate parent to the persistence strategy if possible
260 261 262 263 264 265 |
# File 'lib/active_triples/rdf_source.rb', line 260 def parent return persistence_strategy.parent if persistence_strategy.respond_to?(:parent) nil end |
#parent=(parent) ⇒ Object
deprecate/remove
270 271 272 273 274 275 |
# File 'lib/active_triples/rdf_source.rb', line 270 def parent=(parent) return persistence_strategy.parent = parent if persistence_strategy.respond_to?(:parent=) nil end |
#query ⇒ Object
90 |
# File 'lib/active_triples/rdf_source.rb', line 90 delegate :query, :each, :load!, :count, :has_statement?, to: :graph |
#rdf_label ⇒ Object
Looks for labels in various default fields, prioritizing configured label fields.
373 374 375 376 377 378 379 380 381 |
# File 'lib/active_triples/rdf_source.rb', line 373 def rdf_label labels = Array.wrap(self.class.rdf_label) labels += default_labels labels.each do |label| values = get_values(label) return values unless values.empty? end node? ? [] : [rdf_subject.to_s] end |
#rdf_subject ⇒ RDF::URI, RDF::Node Also known as: to_term
Gives the representation of this RDFSource as an RDF::Term
284 285 286 |
# File 'lib/active_triples/rdf_source.rb', line 284 def rdf_subject @rdf_subject ||= RDF::Node.new end |
#serializable_hash ⇒ Hash
229 230 231 232 233 234 235 |
# File 'lib/active_triples/rdf_source.rb', line 229 def serializable_hash(*) attrs = fields.map(&:to_s) << 'id' hash = super(only: attrs) unregistered_predicates.map { |uri| hash[uri.to_s] = get_values(uri) } hash end |
#set_subject!(uri_or_str) ⇒ void
This method returns an undefined value.
Set a new rdf_subject for the resource.
Will try to build a uri as an extension of the class’s base_uri if appropriate.
560 561 562 563 564 565 566 567 568 569 570 571 |
# File 'lib/active_triples/rdf_source.rb', line 560 def set_subject!(uri_or_str) raise 'Refusing to update URI when one is already assigned!' unless node? || rdf_subject == RDF::URI(nil) return if uri_or_str.nil? || (uri_or_str.to_s.empty? && !uri_or_str.is_a?(RDF::URI)) new_subject = get_uri(uri_or_str) rewrite_statement_uris(rdf_subject, new_subject) @rdf_subject = new_subject end |
#set_value(property, values) ⇒ ActiveTriples::Relation #set_value(subject, property, values) ⇒ ActiveTriples::Relation
This method will delete existing statements with the given subject and predicate from the graph
Adds or updates a property by creating triples for each of the supplied values.
The ‘property` argument may be either a symbol representing a registered property name, or an RDF::Term to use as the predicate.
The recommended pattern, which sets properties directly on this RDFSource, is: ‘set_value(property, values)`
475 476 477 478 479 480 481 482 483 |
# File 'lib/active_triples/rdf_source.rb', line 475 def set_value(*args) # Add support for legacy 3-parameter syntax if args.length > 3 || args.length < 2 raise ArgumentError, "wrong number of arguments (#{args.length} for 2-3)" end values = args.pop get_values(*args).set(values) end |
#term? ⇒ Boolean #term?(value) ⇒ Boolean
See RDF::Enumerable#term?
593 594 595 596 597 598 599 |
# File 'lib/active_triples/rdf_source.rb', line 593 def term?(*args) case args.length when 0 then to_term.term? when 1 then args.first && graph.term?(args.first) else raise ArgumentError("wrong number of arguments (given #{args.length}, expected 0 or 1)") end end |
#to_base ⇒ Object
97 |
# File 'lib/active_triples/rdf_source.rb', line 97 delegate :to_base, :escape, to: :to_term |
#to_uri ⇒ RDF::URI
Returns the uri.
313 314 315 |
# File 'lib/active_triples/rdf_source.rb', line 313 def to_uri rdf_subject if uri? end |
#type ⇒ Object
356 357 358 |
# File 'lib/active_triples/rdf_source.rb', line 356 def type get_values(:type) end |
#type=(type) ⇒ Object
360 361 362 363 364 365 366 |
# File 'lib/active_triples/rdf_source.rb', line 360 def type=(type) raise(ArgumentError, "Type must be an RDF::URI. Got: #{type.class}, #{type}") unless type.is_a? RDF::URI update(RDF::Statement.new(rdf_subject, RDF.type, type)) end |
#uri? ⇒ Boolean
Returns true if the Term is a uri.
345 346 347 |
# File 'lib/active_triples/rdf_source.rb', line 345 def uri? rdf_subject.uri? end |