class Spira::Base
Spira::Base
aims to perform similar to ActiveRecord::Base You should inherit your models from it.
Attributes
This instance's URI.
@return [RDF::URI]
Public Class Methods
The base URI for this class. Attempts to create instances for non-URI objects will be appended to this base URI.
@return [Void]
# File lib/spira/base.rb, line 47 def base_uri # should be redefined in children, if required # see also Spira::Resource.configure :base_uri option nil end
The default vocabulary for this class. Setting a default vocabulary will allow properties to be defined without a `:predicate` option. Predicates will instead be created by appending the property name to the given string.
@return [Void]
# File lib/spira/base.rb, line 60 def default_vocabulary # should be redefined in children, if required # see also Spira::Resource.configure :default_vocabulary option nil end
Initialize a new Spira::Base
instance of this resource class using a new blank node subject. Accepts a hash of arguments for initial attributes. To use a URI or existing blank node as a subject, use {Spira.for} instead.
@param [Hash{Symbol => Any}] props Default attributes for this instance @yield [self] Executes a given block @yieldparam [self] self The newly created instance @see Spira.for @see RDF::URI#as
@see RDF::Node#as
# File lib/spira/base.rb, line 118 def initialize(props = {}, options = {}) @subject = props.delete(:_subject) || RDF::Node.new @attrs = {} reload props yield self if block_given? end
# File lib/spira/base.rb, line 66 def serialize(node, options = {}) if node.respond_to?(:subject) node.subject elsif node.respond_to?(:blank?) && node.blank? nil else raise TypeError, "cannot serialize #{node.inspect} as a Spira resource" end end
# File lib/spira/base.rb, line 38 def types Set.new end
# File lib/spira/base.rb, line 76 def unserialize(value, options = {}) if value.respond_to?(:blank?) && value.blank? nil else # Spira resources are instantiated as "promised" # to avoid instantiation loops in case of resource-to-resource relations. promise { instantiate_record(value) } end end
Private Class Methods
# File lib/spira/base.rb, line 89 def inherited(child) child.instance_variable_set :@properties, @properties.dup child.instance_variable_set :@reflections, @reflections.dup super end
# File lib/spira/base.rb, line 95 def instantiate_record(subj) new(_subject: id_for(subj)) end
Public Instance Methods
Compare this instance with another instance. The comparison is done on an RDF
level, and will work across subclasses as long as the attributes are the same.
@see rdf.rubyforge.org/isomorphic/
# File lib/spira/base.rb, line 192 def ==(other) # TODO: define behavior for equality on subclasses. # TODO: should we compare attributes here? if self.class == other.class subject == other.uri elsif other.is_a?(RDF::Enumerable) self.isomorphic_with?(other) else false end end
Assign attributes to the resource without persisting it.
# File lib/spira/base.rb, line 279 def assign_attributes(attrs) attrs.each do |name, value| attribute_will_change!(name.to_s) send "#{name}=", value end end
Returns the attributes
# File lib/spira/base.rb, line 128 def attributes @attrs end
Returns a new instance of this class with the new subject instead of self.subject
@param [RDF::Resource] new_subject @return [Spira::Base] copy
# File lib/spira/base.rb, line 262 def copy(new_subject) self.class.new(@attrs.merge(_subject: new_subject)) end
Returns a new instance of this class with the new subject instead of self.subject after saving the new copy to the repository.
@param [RDF::Resource] new_subject @return [Spira::Base, String] copy
# File lib/spira/base.rb, line 272 def copy!(new_subject) copy(new_subject).save! end
Freeze the attributes hash such that associations are still accessible, even on destroyed records.
# File lib/spira/base.rb, line 133 def freeze @attrs.freeze; self end
Returns true
if the attributes hash has been frozen.
# File lib/spira/base.rb, line 138 def frozen? @attrs.frozen? end
# File lib/spira/base.rb, line 102 def id new_record? ? nil : subject.path.split(/\//).last end
A developer-friendly view of this projection
# File lib/spira/base.rb, line 182 def inspect "<#{self.class}:#{self.object_id} @subject: #{@subject}>" end
Returns true if the subject associated with this instance is a blank node.
@return [true, false]
# File lib/spira/base.rb, line 243 def node? subject.node? end
Assign all attributes from the given hash.
Spira::Persistence#reload
# File lib/spira/base.rb, line 164 def reload(props = {}) reset_changes super assign_attributes(props) self end
Returns true for :to_uri if this instance's subject is a URI, and false if it is not. Returns true for :to_node if this instance's subject is a Node, and false if it is not. Calls super otherwise.
# File lib/spira/base.rb, line 209 def respond_to?(*args) case args[0] when :to_uri subject.respond_to?(:to_uri) when :to_node subject.node? else super(*args) end end
Returns the Node subject of this resource, if available. If this resource's subject is a URI, raises a NoMethodError.
@return [RDF::Node] @raise [NoMethodError]
# File lib/spira/base.rb, line 253 def to_node subject.node? ? subject : (raise NoMethodError, "No such method: :to_uri (this instance's subject is not a URI)") end
Returns the RDF
representation of this resource.
@return [RDF::Enumerable]
# File lib/spira/base.rb, line 175 def to_rdf self end
Returns the URI representation of this resource, if available. If this resource's subject is a BNode, raises a NoMethodError.
@return [RDF::URI] @raise [NoMethodError]
# File lib/spira/base.rb, line 235 def to_uri uri || (raise NoMethodError, "No such method: :to_uri (this instance's subject is not a URI)") end
The `RDF.type` associated with this class.
This just takes a first type from “types” list, so make sure you know what you're doing if you use it.
@return [nil,RDF::URI] The RDF
type associated with this instance's class.
# File lib/spira/base.rb, line 149 def type self.class.type end
All `RDF.type` nodes associated with this class.
@return [nil,RDF::URI] The RDF
type associated with this instance's class.
# File lib/spira/base.rb, line 157 def types self.class.types end
Private Instance Methods
Build an RDF
value from a Ruby value for a property
# File lib/spira/base.rb, line 369 def build_rdf_value(value, type) klass = classize_resource(type) if klass.respond_to?(:serialize) klass.serialize(value) else raise TypeError, "Unable to serialize #{value} as #{type}" end end
Build a Ruby value from an RDF
value.
# File lib/spira/base.rb, line 359 def build_value(node, type) klass = classize_resource(type) if klass.respond_to?(:unserialize) klass.unserialize(node) else raise TypeError, "Unable to unserialize #{node} as #{type}" end end
# File lib/spira/base.rb, line 347 def hash_localized_properties(values) values.inject({}) do |out, v| out[v.language] = v.object out end end
Localized properties functions
# File lib/spira/base.rb, line 331 def merge_localized_property(name, arg) values = read_attribute("#{name}_native") values.delete_if { |s| s.language == I18n.locale } values << serialize_localized_property(arg, I18n.locale) if arg values end
Get the current value for the given attribute
# File lib/spira/base.rb, line 313 def read_attribute(name) value = @attrs[name.to_s] refl = self.class.reflections[name] if refl && !value # yield default values for empty reflections case refl.macro when :has_many # TODO: this should be actually handled by the reflection class [] end else value end end
# File lib/spira/base.rb, line 289 def reset_changes clear_changes_information end
# File lib/spira/base.rb, line 354 def serialize_hash_localized_properties(values) values.map { |lang, property| RDF::Literal.new(property, language: lang) } end
# File lib/spira/base.rb, line 338 def serialize_localized_property(value, locale) RDF::Literal.new(value, language: locale) end
# File lib/spira/base.rb, line 342 def unserialize_localized_properties(values, locale) v = values.detect { |s| s.language == locale || s.simple? } v && v.object end
# File lib/spira/base.rb, line 378 def valid_object?(node) node && (!node.literal? || node.valid?) end
# File lib/spira/base.rb, line 293 def write_attribute(name, value) name = name.to_s if self.class.properties[name] if @attrs[name].is_a?(Promise) changed_attributes[name] = @attrs[name] unless changed_attributes.include?(name) @attrs[name] = value else if value != read_attribute(name) attribute_will_change!(name) @attrs[name] = value end end else raise Spira::PropertyMissingError, "attempt to assign a value to a non-existing property '#{name}'" end end