class Spira::Base

Spira::Base aims to perform similar to ActiveRecord::Base You should inherit your models from it.

Attributes

properties[R]
reflections[R]
subject[R]

This instance's URI.

@return [RDF::URI]

Public Class Methods

base_uri() click to toggle source

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
default_vocabulary() click to toggle source

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
new(props = {}, options = {}) { |self| ... } click to toggle source

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
serialize(node, options = {}) click to toggle source
# 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
types() click to toggle source
# File lib/spira/base.rb, line 38
def types
  Set.new
end
unserialize(value, options = {}) click to toggle source
# 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

inherited(child) click to toggle source
Calls superclass method
# 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
instantiate_record(subj) click to toggle source
# File lib/spira/base.rb, line 95
def instantiate_record(subj)
  new(_subject: id_for(subj))
end

Public Instance Methods

==(other) click to toggle source

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(attrs) click to toggle source

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
attributes() click to toggle source

Returns the attributes

# File lib/spira/base.rb, line 128
def attributes
  @attrs
end
copy(new_subject) click to toggle source

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
copy!(new_subject) click to toggle source

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() click to toggle source

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
frozen?() click to toggle source

Returns true if the attributes hash has been frozen.

# File lib/spira/base.rb, line 138
def frozen?
  @attrs.frozen?
end
id() click to toggle source
# File lib/spira/base.rb, line 102
def id
  new_record? ? nil : subject.path.split(/\//).last
end
inspect() click to toggle source

A developer-friendly view of this projection

# File lib/spira/base.rb, line 182
def inspect
  "<#{self.class}:#{self.object_id} @subject: #{@subject}>"
end
node?() click to toggle source

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
reload(props = {}) click to toggle source

Assign all attributes from the given hash.

Calls superclass method Spira::Persistence#reload
# File lib/spira/base.rb, line 164
def reload(props = {})
  reset_changes
  super
  assign_attributes(props)
  self
end
respond_to?(*args) click to toggle source

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.

Calls superclass method
# 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
to_node() click to toggle source

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
to_rdf() click to toggle source

Returns the RDF representation of this resource.

@return [RDF::Enumerable]

# File lib/spira/base.rb, line 175
def to_rdf
  self
end
to_uri() click to toggle source

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
type() click to toggle source

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
types() click to toggle source

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
uri() click to toggle source

Returns the RDF::URI associated with this instance if this instance's subject is an RDF::URI, and nil otherwise.

@return [RDF::URI,nil]

# File lib/spira/base.rb, line 225
def uri
  subject.respond_to?(:to_uri) ? subject : nil
end

Private Instance Methods

build_rdf_value(value, type) click to toggle source

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_value(node, type) click to toggle source

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
hash_localized_properties(values) click to toggle source
# 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
merge_localized_property(name, arg) click to toggle source

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
read_attribute(name) click to toggle source

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
reset_changes() click to toggle source
# File lib/spira/base.rb, line 289
def reset_changes
  clear_changes_information
end
serialize_hash_localized_properties(values) click to toggle source
# File lib/spira/base.rb, line 354
def serialize_hash_localized_properties(values)
  values.map { |lang, property| RDF::Literal.new(property, language: lang) }
end
serialize_localized_property(value, locale) click to toggle source
# File lib/spira/base.rb, line 338
def serialize_localized_property(value, locale)
  RDF::Literal.new(value, language: locale)
end
unserialize_localized_properties(values, locale) click to toggle source
# 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
valid_object?(node) click to toggle source
# File lib/spira/base.rb, line 378
def valid_object?(node)
  node && (!node.literal? || node.valid?)
end
write_attribute(name, value) click to toggle source
# 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