lib/active_triples/rdf_source.rb in active-triples-0.7.6 vs lib/active_triples/rdf_source.rb in active-triples-0.8.0
- old
+ new
@@ -1,6 +1,5 @@
-require 'deprecation'
require 'active_model'
require 'active_support/core_ext/hash'
module ActiveTriples
##
@@ -30,40 +29,39 @@
# @see http://www.w3.org/TR/2014/REC-rdf11-concepts-20140225/#change-over-time
# RDF Concepts and Abstract Syntax comment on "RDF source"
# @see http://www.w3.org/TR/ldp/#dfn-linked-data-platform-rdf-source an
# example of the RDF source concept as defined in the LDP specification
#
+ # An `RDFSource` is an {RDF::Term}---it can be used as a subject, predicate,
+ # object, or context in an {RDF::Statement}.
+ #
+ # @todo complete RDF::Value/RDF::Term/RDF::Resource interfaces
+ #
# @see ActiveModel
# @see RDF::Resource
# @see RDF::Queryable
module RDFSource
extend ActiveSupport::Concern
include NestedAttributes
+ include Persistable
include Properties
include Reflection
include RDF::Value
- include RDF::Countable
- include RDF::Durable
- include RDF::Enumerable
include RDF::Queryable
- include RDF::Mutable
+ include ActiveModel::Validations
include ActiveModel::Conversion
include ActiveModel::Serialization
include ActiveModel::Serializers::JSON
- include ActiveModel::Validations
- attr_accessor :parent
-
def type_registry
@@type_registry ||= {}
end
module_function :type_registry
included do
extend Configurable
- extend Deprecation
extend ActiveModel::Naming
extend ActiveModel::Translation
extend ActiveModel::Callbacks
validate do
@@ -71,32 +69,15 @@
rdf_subject.valid?
errors.add(:base, 'The underlying graph must be valid') unless
graph.valid?
end
- delegate :query, :each, :load!, :count, :has_statement?, :to => :@graph
-
define_model_callbacks :persist
-
- protected
-
- def insert_statement(*args)
- @graph.send(:insert_statement, *args)
- end
-
- def delete_statement(*args)
- @graph.send(:delete_statement, *args)
- end
end
- ##
- # Specifies whether the object is currently writable.
- #
- # @return [true, false]
- def writable?
- !frozen?
- end
+ delegate :each, :load!, :count, :has_statement?, :to => :graph
+ delegate :to_base, :term?, :escape, :to => :to_term
##
# 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
@@ -107,33 +88,59 @@
#
# @see RDF::Graph
# @todo move this logic out to a Builder?
def initialize(*args, &block)
resource_uri = args.shift unless args.first.is_a?(Hash)
- self.parent = args.shift unless args.first.is_a?(Hash)
+ unless args.first.is_a?(Hash) || args.empty?
+ set_persistence_strategy(ParentStrategy)
+ persistence_strategy.parent = args.shift
+ else
+ set_persistence_strategy(RepositoryStrategy)
+ end
@graph = RDF::Graph.new(*args, &block)
set_subject!(resource_uri) if resource_uri
reload
+
# Append type to graph if necessary.
Array(self.class.type).each do |type|
unless self.get_values(:type).include?(type)
self.get_values(:type) << type
end
end
end
- def final_parent
- @final_parent ||= begin
- parent = self.parent
- while parent && parent.parent && parent.parent != parent
- parent = parent.parent
- end
- parent
- end
+ ##
+ # Compares self to other for {RDF::Term} equality.
+ #
+ # Delegates the check to `other#==` passing it the term version of `self`.
+ #
+ # @param other [Object]
+ #
+ # @see RDF::Term#==
+ # @see RDF::Node#==
+ # @see RDF::URI#==
+ def ==(other)
+ other == to_term
end
+ ##
+ # Delegate parent to the persistence strategy if possible
+ #
+ # @todo establish a better pattern for this. `#parent` has been a public method
+ # in the past, but it's probably time to deprecate it.
+ def parent
+ persistence_strategy.respond_to?(:parent) ? persistence_strategy.parent : nil
+ end
+
+ ##
+ # @todo deprecate/remove
+ # @see #parent
+ def parent=(parent)
+ persistence_strategy.respond_to?(:parent=) ? (persistence_strategy.parent = parent) : nil
+ end
+
def attributes
attrs = {}
attrs['id'] = id if id
fields.map { |f| attrs[f.to_s] = get_values(f) }
unregistered_predicates.map { |uri| attrs[uri.to_s] = get_values(uri) }
@@ -145,10 +152,12 @@
hash = super(:only => attrs)
unregistered_predicates.map { |uri| hash[uri.to_s] = get_values(uri) }
hash
end
+ ##
+ # @return [Class] gives `self#class`
def reflections
self.class
end
def attributes=(values)
@@ -184,31 +193,59 @@
end
super
end
##
- # @return [RDF::URI, RDF::Node] a URI or Node which the resource's
- # properties are about.
+ # Gives the representation of this
+ #
+ # @return [RDF::URI, RDF::Node] the URI that identifies this `RDFSource`;
+ # or a bnode identifier
+ #
+ # @see RDF::Term#to_term
def rdf_subject
@rdf_subject ||= RDF::Node.new
end
alias_method :to_term, :rdf_subject
##
- # A string identifier for the resource
+ # @return [String] A string identifier for the resource; '' if the
+ # resource is a node
+ def humanize
+ node? ? '' : rdf_subject.to_s
+ end
+
+ ##
+ # @return [RDF::URI] the uri
+ def to_uri
+ uri? ? rdf_subject : NullURI.new
+ end
+
+ ##
+ # @return [String]
+ #
+ # @see RDF::Node#id
def id
- node? ? nil : rdf_subject.to_s
+ node? ? rdf_subject.id : rdf_subject.to_s
end
##
- # @return [Boolean]
+ # @return [Boolean] true if the Term is a node
+ #
# @see RDF::Term#node?
def node?
rdf_subject.node?
end
##
+ # @return [Boolean] true if the Term is a uri
+ #
+ # @see RDF::Term#uri?
+ def uri?
+ rdf_subject.uri?
+ end
+
+ ##
# @return [String, nil] the base URI the resource will use when
# setting its subject. `nil` if none is used.
def base_uri
self.class.base_uri
end
@@ -234,18 +271,10 @@
end
node? ? [] : [rdf_subject.to_s]
end
##
- # Lists fields registered as properties on the object.
- #
- # @return [Array<Symbol>] the list of registered properties.
- def fields
- properties.keys.map(&:to_sym).reject{|x| x == :type}
- end
-
- ##
# 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.
#
@@ -253,55 +282,16 @@
# osu.fetch
# osu.rdf_label.first
# # => "Oregon State University"
#
# @return [ActiveTriples::Entity] self
- def fetch(*args)
- load(rdf_subject, *args)
+ def fetch
+ load(rdf_subject)
self
end
- def persist!(opts={})
- return if @persisting
- return false if opts[:validate] && !valid?
- @persisting = true
- run_callbacks :persist do
- raise "failed when trying to persist to non-existant repository or parent resource" unless repository
- erase_old_resource
- repository << self
- @persisted = true
- end
- @persisting = false
- true
- end
-
##
- # Indicates if the resource is persisted.
- #
- # @see #persist
- # @return [true, false]
- def persisted?
- @persisted ||= false
- return (@persisted and parent.persisted?) if parent
- @persisted
- end
-
- ##
- # Repopulates the graph from the repository or parent resource.
- #
- # @return [true, false]
- def reload
- @relation_cache ||= {}
- return false if (node? && self.class.repository != :parent) || !repository
- self << repository.query(subject: rdf_subject)
- unless empty?
- @persisted = true
- end
- true
- end
-
- ##
# Adds or updates a property with supplied values.
#
# Handles two argument patterns. The recommended pattern is:
# set_value(property, values)
#
@@ -348,11 +338,10 @@
# valid datatype.
def [](uri_or_term_property)
get_relation([uri_or_term_property])
end
-
def get_relation(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}"]
@@ -387,36 +376,20 @@
self << RDF::Statement.new(statement.subject, statement.predicate, rdf_subject)
end
end
end
- def destroy
- clear
- persist! if repository
- parent.destroy_child(self) if parent
- @destroyed = true
- end
- alias_method :destroy!, :destroy
-
- ##
- # Indicates if the Resource has been destroyed.
- #
- # @return [true, false]
- def destroyed?
- @destroyed ||= false
- end
-
def destroy_child(child)
statements.each do |statement|
delete_statement(statement) if statement.subject == child.rdf_subject || statement.object == child.rdf_subject
end
end
##
# Indicates if the record is 'new' (has not yet been persisted).
#
- # @return [true, false]
+ # @return [Boolean]
def new_record?
not persisted?
end
def mark_for_destruction
@@ -425,31 +398,25 @@
def marked_for_destruction?
@marked_for_destruction
end
- protected
-
- #Clear out any old assertions in the repository about this node or statement
- # thus preparing to receive the updated assertions.
- def erase_old_resource
- if node?
- repository.statements.each do |statement|
- repository.send(:delete_statement, statement) if statement.subject == rdf_subject
- end
- else
- repository.delete [rdf_subject, nil, nil]
- end
- end
-
private
def graph
@graph
end
##
+ # Lists fields registered as properties on the object.
+ #
+ # @return [Array<Symbol>] the list of registered properties.
+ def fields
+ properties.keys.map(&:to_sym).reject{ |x| x == :type }
+ end
+
+ ##
# Returns the properties registered and their configurations.
#
# @return [ActiveSupport::HashWithIndifferentAccess{String => ActiveTriples::NodeConfig}]
def properties
_active_triples_config
@@ -472,51 +439,19 @@
preds = registered_predicates
preds << RDF.type
predicates.select { |p| !preds.include? p }
end
- ##
- # Given a predicate which has been registered to a property,
- # returns the name of the matching property.
- #
- # @param predicate [RDF::URI]
- #
- # @return [String, nil] the name of the property mapped to the
- # predicate provided
- def property_for_predicate(predicate)
- properties.each do |property, values|
- return property if values[:predicate] == predicate
- end
- return nil
- end
-
def default_labels
[RDF::SKOS.prefLabel,
RDF::DC.title,
RDF::RDFS.label,
RDF::SKOS.altLabel,
RDF::SKOS.hiddenLabel]
end
##
- # Return the repository (or parent) that this resource should
- # write to when persisting.
- #
- # @return [RDF::Repository, ActiveTriples::Entity] the target
- # repository
- def repository
- @repository ||=
- if self.class.repository == :parent
- final_parent
- else
- repo = Repositories.repositories[self.class.repository]
- raise RepositoryNotFoundError, "The class #{self.class} expects a repository called #{self.class.repository}, but none was declared" unless repo
- repo
- end
- end
-
- ##
# Takes a URI or String and aggressively tries to convert it into
# an RDF term. If a String is given, first tries to interpret it
# as a valid URI, then tries to append it to base_uri. Finally,
# raises an error if no valid term can be built.
#
@@ -530,11 +465,11 @@
# @param uri_or_str [RDF::Resource, String]
#
# @return [RDF::Resource] A term
# @raise [RuntimeError] no valid RDF term could be built
def get_uri(uri_or_str)
- return uri_or_str.to_uri if uri_or_str.respond_to? :to_uri
+ return uri_or_str.to_term if uri_or_str.respond_to? :to_term
return uri_or_str if uri_or_str.is_a? RDF::Node
uri_or_node = RDF::Resource.new(uri_or_str)
return uri_or_node if uri_or_node.valid?
uri_or_str = uri_or_str.to_s
return RDF::URI(base_uri.to_s) / uri_or_str if base_uri && !uri_or_str.start_with?(base_uri.to_s)
@@ -548,14 +483,14 @@
# Adapter for a consistent interface for creating a new Resource
# from a URI. Similar functionality should exist in all objects
# which can become a Resource.
#
# @param uri [#to_uri, String]
- # @param vals values to pass as arguments to ::new
+ # @param args values to pass as arguments to ::new
#
# @return [ActiveTriples::Entity] a Resource with the given uri
- def from_uri(uri, vals = nil)
- new(uri, vals)
+ def from_uri(uri, *args)
+ new(uri, *args)
end
##
# Apply a predicate mapping using a given strategy.
#