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. #