lib/active_triples/rdf_source.rb in active-triples-0.10.2 vs lib/active_triples/rdf_source.rb in active-triples-0.11.0
- old
+ new
@@ -73,12 +73,12 @@
end
define_model_callbacks :persist
end
- delegate :query, :each, :load!, :count, :has_statement?, :to => :graph
- delegate :to_base, :term?, :escape, :to => :to_term
+ delegate :query, :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
@@ -91,25 +91,23 @@
# @todo move this logic out to a Builder?
def initialize(*args, &block)
resource_uri = args.shift unless args.first.is_a?(Hash)
@rdf_subject = get_uri(resource_uri) if resource_uri
- unless args.first.is_a?(Hash) || args.empty?
+ if args.first.is_a?(Hash) || args.empty?
+ set_persistence_strategy(RepositoryStrategy)
+ else
set_persistence_strategy(ParentStrategy)
persistence_strategy.parent = args.shift
- else
- set_persistence_strategy(RepositoryStrategy)
end
- @graph = RDF::Graph.new(*args, &block)
+ persistence_strategy.graph = RDF::Graph.new(*args, &block)
reload
# Append type to graph if necessary.
Array.wrap(self.class.type).each do |type|
- unless self.get_values(:type).include?(type)
- self.get_values(:type) << type
- end
+ get_values(:type) << type unless get_values(:type).include?(type)
end
end
##
# Compares self to other for {RDF::Term} equality.
@@ -126,16 +124,16 @@
end
##
# Gives a hash containing both the registered and unregistered attributes of
# the resource. Unregistered attributes are given with full URIs.
- #
- # @example
+ #
+ # @example
# class WithProperties
# include ActiveTriples::RDFSource
# property :title, predicate: RDF::Vocab::DC.title
- # property :creator, predicate: RDF::Vocab::DC.creator,
+ # property :creator, predicate: RDF::Vocab::DC.creator,
# class_name: 'Agent'
# end
#
# class Agent; include ActiveTriples::RDFSource; end
#
@@ -144,11 +142,11 @@
# resource.attributes
# # => {"id"=>"g47123700054720", "title"=>[], "creator"=>[]}
#
# resource.creator.build
# resource.title << ['Comet in Moominland', 'Christmas in Moominvalley']
-
+ #
# resource.attributes
# # => {"id"=>"g47123700054720",
# # "title"=>["Comet in Moominland", "Christmas in Moominvalley"],
# # "creator"=>[#<Agent:0x2adbd76f1a5c(#<Agent:0x0055b7aede34b8>)>]}
#
@@ -169,29 +167,37 @@
unregistered_predicates.map { |uri| attrs[uri.to_s] = get_values(uri) }
attrs
end
def attributes=(values)
- raise ArgumentError, "values must be a Hash, you provided #{values.class}" unless values.kind_of? Hash
+ 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?
+ set_subject!(id) if node? && id && get_uri(id).uri?
+
values.each do |key, value|
if reflections.has_property?(key)
- set_value(rdf_subject, key, value)
- elsif nested_attributes_options.keys.map { |k| "#{k}_attributes" }.include?(key)
+ set_value(key, value)
+ elsif nested_attributes_options
+ .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?"
+ raise ArgumentError, "No association found for name `#{key}'. " \
+ 'Has it been defined yet?'
end
end
end
- def serializable_hash(options = nil)
- attrs = (fields.map { |f| f.to_s }) << 'id'
- hash = super(:only => attrs)
+ ##
+ # @return [Hash]
+ 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
##
# Returns a serialized string representation of self.
@@ -202,31 +208,37 @@
# @see RDF::Enumerable#dump
#
# @param args [Array<Object>]
# @return [String]
def dump(*args)
- if args.first == :jsonld and respond_to?(:jsonld_context)
+ if args.first == :jsonld && respond_to?(:jsonld_context)
args << {} unless args.last.is_a?(Hash)
args.last[:context] ||= jsonld_context
end
super
end
##
# Delegate parent to the persistence strategy if possible
#
- # @todo establish a better pattern for this. `#parent` has been a public
+ # @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
+ return persistence_strategy.parent if
+ persistence_strategy.respond_to?(:parent)
+
+ nil
end
##
# @todo deprecate/remove
# @see #parent
def parent=(parent)
- persistence_strategy.respond_to?(:parent=) ? (persistence_strategy.parent = parent) : nil
+ return persistence_strategy.parent = parent if
+ persistence_strategy.respond_to?(:parent=)
+
+ nil
end
##
# Gives the representation of this RDFSource as an RDF::Term
#
@@ -235,27 +247,27 @@
#
# @see RDF::Term#to_term
def rdf_subject
@rdf_subject ||= RDF::Node.new
end
- alias_method :to_term, :rdf_subject
+ alias to_term rdf_subject
##
# 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
+ # @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
+ # For RDF.rb's purposes, however, it has a nil graph name: when
# enumerating statements, we treat them as triples.
#
# @return [nil]
# @sse RDF::Graph.graph_name
def graph_name
nil
end
-
+
##
# @return [String] A string identifier for the resource; '' if the
# resource is a node
def humanize
node? ? '' : rdf_subject.to_s
@@ -297,16 +309,19 @@
def base_uri
self.class.base_uri
end
def type
- self.get_values(:type)
+ get_values(:type)
end
def type=(type)
- raise "Type must be an RDF::URI" unless type.kind_of? RDF::URI
- self.update(RDF::Statement.new(rdf_subject, RDF.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
##
# Looks for labels in various default fields, prioritizing
# configured label fields.
@@ -321,20 +336,10 @@
end
node? ? [] : [rdf_subject.to_s]
end
##
- # @return [Array<RDF::URI>] a group of properties to use for default labels.
- def default_labels
- [RDF::SKOS.prefLabel,
- RDF::DC.title,
- RDF::RDFS.label,
- RDF::SKOS.altLabel,
- RDF::SKOS.hiddenLabel]
- 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.
#
@@ -351,19 +356,19 @@
# @yield gives self to block if this is a node, or an error is raised during
# load
# @yieldparam [ActiveTriples::RDFSource] resource self
#
# @return [ActiveTriples::RDFSource] self
- def fetch(*args, &block)
+ 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 "#{self} is a blank node; " \
+ 'Cannot fetch a resource without a URI' if node?
raise e
end
end
self
end
@@ -402,16 +407,16 @@
# or a single value. If not an {RDF::Resource}, the values will be
# coerced to an {RDF::Literal} or {RDF::Node} by {RDF::Statement}
#
# @overload set_value(subject, property, values)
# Updates the values for the property, using the given term as the subject
- #
- # @param [RDF::Term] subject the term representing the
+ #
+ # @param [RDF::Term] subject the term representing the
# @param [RDF::Term, #to_sym] property a symbol with the property name
# or an RDF::Term to use as a predicate.
# @param [Array<RDF::Resource>, RDF::Resource] values an array of values
- # or a single value. If not an {RDF::Resource}, the values will be
+ # or a single value. If not an {RDF::Resource}, the values will be
# coerced to an {RDF::Literal} or {RDF::Node} by {RDF::Statement}
#
# @return [ActiveTriples::Relation] an array {Relation} containing the
# values of the property
#
@@ -419,20 +424,21 @@
# coerced into an acceptable `RDF::Term`.
#
# @note This method will delete existing statements with the given
# subject and predicate from the graph
#
- # @see http://www.rubydoc.info/github/ruby-rdf/rdf/RDF/Statement For
- # documentation on {RDF::Statement} and the handling of
+ # @see http://www.rubydoc.info/github/ruby-rdf/rdf/RDF/Statement For
+ # documentation on {RDF::Statement} and the handling of
# non-{RDF::Resource} values.
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)"
+ raise ArgumentError,
+ "wrong number of arguments (#{args.length} for 2-3)"
end
values = args.pop
- get_relation(args).set(values)
+ get_values(*args).set(values)
end
##
# Returns an array of values belonging to the property
# requested. Elements in the array may RdfResource objects or a
@@ -440,60 +446,61 @@
#
# Handles two argument patterns. The recommended pattern, which accesses
# properties directly on this RDFSource, is:
# get_values(property)
#
- # @overload get_values(property)
+ # @overload get_values(property)
# Gets values on the RDFSource for the given property
# @param [String, #to_term] property the property for the values
#
# @overload get_values(uri, property)
# For backwards compatibility, explicitly passing the term used as the
# subject {ActiveTriples::Relation#rdf_subject} of the returned relation.
# @param [RDF::Term] uri the term to use as the subject
# @param [String, #to_term] property the property for the values
#
- # @return [ActiveTriples::Relation] an array {Relation} containing the
+ # @return [ActiveTriples::Relation] an array {Relation} containing the
# values of the property
#
- # @todo should this raise an error when the property argument is not an
+ # @todo should this raise an error when the property argument is not an
# {RDF::Term} or a registered property key?
def get_values(*args)
- 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}"]
end
##
# Returns an array of values belonging to the property requested. Elements
# in the array may RdfResource objects or a valid datatype.
- #
+ #
# @param [RDF::Term, :to_s] term_or_property
def [](term_or_property)
- get_relation([term_or_property])
+ get_values(term_or_property)
end
##
# Adds or updates a property with supplied values.
#
# @param [RDF::Term, :to_s] term_or_property
# @param [Array<RDF::Resource>, RDF::Resource] values an array of values
- # or a single value to set the property to.
+ # or a single value to set the property to.
#
- # @note This method will delete existing statements with the correct
+ # @note This method will delete existing statements with the correct
# subject and predicate from the graph
def []=(term_or_property, value)
self[term_or_property].set(value)
end
##
+ # @deprecated for removal in 1.0; use `#get_values` instead.
# @see #get_values
- # @todo deprecate and remove? this is an alias to `#get_values`
def get_relation(args)
- reload if (persistence_strategy.respond_to? :loaded?) && !persistence_strategy.loaded?
- @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}"]
+ warn 'DEPRECATION: `ActiveTriples::RDFSource#get_relation` will be' \
+ 'removed in 1.0; use `#get_values` instead.'
+ get_values(*args)
end
##
# Set a new rdf_subject for the resource.
#
@@ -506,17 +513,16 @@
# @raise if the current subject is not a blank node,
# and returns false if it can't figure out how to make a URI from
# the param. Otherwise it creates a URI for the resource and
# rebuilds the graph with the updated URI.
def set_subject!(uri_or_str)
- raise "Refusing update URI when one is already assigned!" unless
+ raise 'Refusing to update URI when one is already assigned!' unless
node? || rdf_subject == RDF::URI(nil)
-
- return false if uri_or_str.nil? ||
- (uri_or_str.to_s.empty? &&
- !uri_or_str.kind_of?(RDF::URI))
-
+
+ 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
@@ -524,11 +530,11 @@
##
# Indicates if the record is 'new' (has not yet been persisted).
#
# @return [Boolean]
def new_record?
- not persisted?
+ !persisted?
end
def mark_for_destruction
@marked_for_destruction = true
end
@@ -537,130 +543,77 @@
@marked_for_destruction
end
private
- ##
- # This gives the {RDF::Graph} which represents the current state of this
- # resource.
- #
- # @return [RDF::Graph] the underlying graph representation of the
- # `RDFSource`.
- #
- # @see http://www.w3.org/TR/2014/REC-rdf11-concepts-20140225/#change-over-time
- # RDF Concepts and Abstract Syntax comment on "RDF source"
- def graph
- @graph
- end
+ ##
+ # @return [Array<RDF::URI>] a group of properties to use for default labels.
+ def default_labels
+ [RDF::Vocab::SKOS.prefLabel,
+ RDF::Vocab::DC.title,
+ RDF::RDFS.label,
+ RDF::Vocab::SKOS.altLabel,
+ RDF::Vocab::SKOS.hiddenLabel]
+ end
- ##
+ ##
+ # Rewrites the subject and object of each statement containing
+ # `old_subject` in either position. Used when setting the subject to
+ # remove the placeholder blank node subjects.
+ #
+ # @param [RDF::Term] old_subject
+ # @param [RDF::Term] new_subject
+ # @return [void]
+ def rewrite_statement_uris(old_subject, new_subject)
+ graph.query(subject: old_subject).each do |st|
+ graph.delete(st)
- # 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 }
+ st.subject = new_subject
+ st.object = new_subject if st.object == old_subject
+ graph.insert(st)
end
- ##
- # Returns the properties registered and their configurations.
- #
- # @return [ActiveSupport::HashWithIndifferentAccess{String => ActiveTriples::NodeConfig}]
- def properties
- _active_triples_config
- end
+ graph.query(object: old_subject).each do |st|
+ graph.delete(st)
- ##
- # List of RDF predicates registered as properties on the object.
- #
- # @return [Array<RDF::URI>]
- def registered_predicates
- properties.values.map { |config| config.predicate }
+ st.object = new_subject
+ graph.insert(st)
end
+ end
- ##
- # List of RDF predicates used in the Resource's triples, but not
- # mapped to any property or accessor methods.
- #
- # @return [Array<RDF::URI>]
- def unregistered_predicates
- registered_preds = registered_predicates
- registered_preds << RDF.type
- unregistered_preds = []
-
- query(subject: rdf_subject) do |stmt|
- unregistered_preds << stmt.predicate unless
- registered_preds.include? stmt.predicate
- 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.
+ #
+ # The argument must be an RDF::Node, an object that responds to
+ # #to_uri, a String that represents a valid URI, or a String that
+ # appends to the Resource's base_uri to create a valid URI.
+ #
+ # @TODO: URI.scheme_list is naive and incomplete. Find a better
+ # way to check for an existing scheme.
+ #
+ # @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_term if uri_or_str.respond_to? :to_term
- unregistered_preds
- end
+ uri_or_node = RDF::Resource.new(uri_or_str)
+ return uri_or_node if uri_or_node.valid?
- def default_labels
- [RDF::Vocab::SKOS.prefLabel,
- RDF::Vocab::DC.title,
- RDF::RDFS.label,
- RDF::Vocab::SKOS.altLabel,
- RDF::Vocab::SKOS.hiddenLabel]
- end
+ uri_or_str = uri_or_str.to_s
+ return RDF::URI.intern(base_uri.to_s) / uri_or_str if
+ base_uri && !uri_or_str.start_with?(base_uri.to_s)
- ##
- # Rewrites the subject and object of each statement containing
- # `old_subject` in either position. Used when setting the subject to
- # remove the placeholder blank node subjects.
- #
- # @param [RDF::Term] old_subject
- # @param [RDF::Term] new_subject
- # @return [void]
- def rewrite_statement_uris(old_subject, new_subject)
- graph.transaction(mutable: true) do |tx|
- tx.query(subject: old_subject).each do |st|
- tx.delete(st)
+ raise "could not make a valid RDF::URI from #{uri_or_str}"
+ end
- st.subject = new_subject
- st.object = new_subject if st.object == old_subject
- tx.insert(st)
- end
-
- tx.query(object: old_subject).each do |st|
- tx.delete(st)
-
- st.object = new_subject
- tx.insert(st)
- end
- 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.
- #
- # The argument must be an RDF::Node, an object that responds to
- # #to_uri, a String that represents a valid URI, or a String that
- # appends to the Resource's base_uri to create a valid URI.
- #
- # @TODO: URI.scheme_list is naive and incomplete. Find a better
- # way to check for an existing scheme.
- #
- # @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_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)
- raise RuntimeError, "could not make a valid RDF::URI from #{uri_or_str}"
- end
-
- public
-
+ ##
+ # Class methods for RDFSource, included via ActiveSupport
module ClassMethods
##
# 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.
@@ -677,14 +630,12 @@
# Apply a predicate mapping using a given strategy.
#
# @param [ActiveTriples::Schema, #properties] schema A schema to apply.
# @param [#apply!] strategy A strategy for applying. Defaults
# to ActiveTriples::ExtensionStrategy
- def apply_schema(schema, strategy=ActiveTriples::ExtensionStrategy)
- schema.properties.each do |property|
- strategy.apply(self, property)
- end
+ def apply_schema(schema, strategy = ActiveTriples::ExtensionStrategy)
+ schema.properties.each { |property| strategy.apply(self, property) }
end
##
# Test if the rdf_subject that would be generated using a
# specific ID is already in use in the triplestore.
@@ -695,11 +646,12 @@
# use in the triplestore; otherwise, false.
# NOTE: If the ID is in use in an object not yet
# persisted, false will be returned presenting
# a window of opportunity for an ID clash.
def id_persisted?(test_id)
- rdf_subject = self.new(test_id).rdf_subject
+ rdf_subject = new(test_id).rdf_subject
+
ActiveTriples::Repositories.has_subject?(rdf_subject)
end
##
# Test if the rdf_subject that would be generated using a
@@ -711,11 +663,12 @@
# use in the triplestore; otherwise, false.
# NOTE: If the URI is in use in an object not yet
# persisted, false will be returned presenting
# a window of opportunity for an ID clash.
def uri_persisted?(test_uri)
- rdf_subject = test_uri.kind_of?(RDF::URI) ? test_uri : RDF::URI(test_uri)
- ActiveTriples::Repositories.has_subject?(rdf_subject)
+ test_uri = RDF::URI.intern(test_uri) unless test_uri.is_a?(RDF::URI)
+
+ ActiveTriples::Repositories.has_subject?(test_uri)
end
end
end
end