module Spira::Persistence::ClassMethods
Public Instance Methods
# File lib/spira/persistence.rb, line 36 def all(*args) find(:all, *args) end
The number of URIs projectable as a given class in the repository. This method is only valid for classes which declare a `type` with the `type` method in the Resource
.
@raise [Spira::NoTypeError] if the resource class does not have an RDF
type declared @return [Integer] the count
# File lib/spira/persistence.rb, line 100 def count each.count end
Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.
The attributes
parameter can be either be a Hash or an Array of Hashes. These Hashes describe the attributes on the objects that are to be created.
create
respects mass-assignment security and accepts either :as
or :without_protection
options in the options
parameter.
Examples¶ ↑
# Create a single new object User.create(first_name: 'Jamie') # Create a single new object using the :admin mass-assignment security role User.create({ first_name: 'Jamie', is_admin: true }, as: :admin) # Create a single new object bypassing mass-assignment security User.create({ first_name: 'Jamie', is_admin: true }, without_protection: true) # Create an Array of new objects User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) # Create a single object and pass it into a block to set other attributes. User.create(first_name: 'Jamie') do |u| u.is_admin = false end # Creating an Array of new objects using a block, where the block is executed for each object: User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u| u.is_admin = false end
# File lib/spira/persistence.rb, line 135 def create(attributes = nil, options = {}, &block) if attributes.is_a?(Array) attributes.collect { |attr| create(attr, options, &block) } else object = new(attributes, options, &block) object.save object end end
Enumerate over all resources projectable as this class. This method is only valid for classes which declare a `type` with the `type` method in the Resource
.
Note that the instantiated records are “promises” not real instances.
@raise [Spira::NoTypeError] if the resource class does not have an RDF
type declared @overload each
@yield [instance] A block to perform for each available projection of this class @yieldparam [self] instance @yieldreturn [Void] @return [Void]
@overload each
@return [Enumerator]
# File lib/spira/persistence.rb, line 60 def each(*args) raise Spira::NoTypeError, "Cannot count a #{self} without a reference type URI" unless type options = args.extract_options! conditions = options.delete(:conditions) || {} raise Spira::SpiraError, "Cannot accept :type in query conditions" if conditions.delete(:type) || conditions.delete("type") if block_given? limit = options[:limit] || -1 offset = options[:offset] || 0 # TODO: ideally, all types should be joined in a conjunction # within "conditions_to_query", but since RDF::Query # cannot handle such patterns, we iterate across types "manually" types.each do |tp| break if limit.zero? q = conditions_to_query(conditions.merge(type: tp)) repository.query(q) do |solution| break if limit.zero? if offset.zero? yield unserialize(solution[:subject]) limit -= 1 else offset -= 1 end end end else enum_for(:each, *args) end end
Simple finder method.
@param [Symbol, ID] scope
scope can be :all, :first or an ID
@param [Hash] args
args can contain: :conditions - Hash of properties and values :limit - Integer, limiting the amount of returned records
@return [Spira::Base, Array]
# File lib/spira/persistence.rb, line 25 def find(scope, *args) case scope when :first find_each(*args).first when :all find_all(*args) else instantiate_record(scope) end end
# File lib/spira/persistence.rb, line 40 def first(*args) find(:first, *args) end
Create a new projection instance of this class for the given URI. If a class has a base_uri given, and the argument is not an `RDF::URI`, the given identifier will be appended to the base URI.
Spira
does not have 'find' or 'create' functions. As RDF
identifiers are globally unique, they all simply 'are'.
On calling `for`, a new projection is created for the given URI. The first time access is attempted on a field, the repository will be queried for existing attributes, which will be used for the given URI. Underlying repositories are not accessed at the time of calling `for`.
A class with a base URI may still be projected for any URI, whether or not it uses the given resource class' base URI.
@raise [TypeError] if an RDF
type is given in the attributes and one is given in the attributes. @raise [ArgumentError] if a non-URI is given and the class does not have a base URI. @overload for(uri, attributes = {})
@param [RDF::URI] uri The URI to create an instance for @param [Hash{Symbol => Any}] attributes Initial attributes
@overload for(identifier, attributes = {})
@param [Any] uri The identifier to append to the base URI for this class @param [Hash{Symbol => Any}] attributes Initial attributes
@yield [self] Executes a given block and calls `#save!` @yieldparam [self] self The newly created instance @return [Spira::Base] The newly created instance @see rdf.rubyforge.org/RDF/URI.html
# File lib/spira/persistence.rb, line 175 def for(identifier, attributes = {}, &block) self.project(id_for(identifier), attributes, &block) end
Creates a URI or RDF::Node
based on a potential base_uri and string, URI, or Node, or Addressable::URI. If not a URI or Node, the given identifier should be a string representing an absolute URI, or something responding to to_s which can be appended to a base URI, which this class must have.
@param [Any] identifier @return [RDF::URI, RDF::Node] @raise [ArgumentError] If this class cannot create an identifier from the given argument @see rdf.rubyforge.org/RDF/URI.html @see Spira.base_uri @see Spira.for
# File lib/spira/persistence.rb, line 211 def id_for(identifier) case # Absolute URI's go through unchanged when identifier.is_a?(RDF::URI) && identifier.absolute? identifier # We don't have a base URI to join this fragment with, so go ahead and instantiate it as-is. when identifier.is_a?(RDF::URI) && self.base_uri.nil? identifier # Blank nodes go through unchanged when identifier.respond_to?(:node?) && identifier.node? identifier # Anything that can be an RDF::URI, we re-run this case statement # on it for the fragment logic above. when identifier.respond_to?(:to_uri) && !identifier.is_a?(RDF::URI) id_for(identifier.to_uri) # see comment with #to_uri above, this might be a fragment else uri = identifier.is_a?(RDF::URI) ? identifier : RDF::URI.intern(identifier.to_s) case when uri.absolute? uri when self.base_uri.nil? raise ArgumentError, "Cannot create identifier for #{self} by String without base_uri; an RDF::URI is required" else separator = self.base_uri.to_s[-1,1] =~ /(\/|#)/ ? '' : '/' RDF::URI.intern(self.base_uri.to_s + separator + identifier.to_s) end end end
Create a new instance with the given subject without any modification to the given subject at all. This method exists to provide an entry point for implementing classes that want to create a more intelligent .for and/or .id_for for their given use cases, such as simple string appending to base URIs or calculated URIs from other representations.
@example Using simple string concatentation with base_uri in .for instead of joining delimiters
def for(identifier, attributes = {}, &block) self.project(RDF::URI(self.base_uri.to_s + identifier.to_s), attributes, &block) end
@param [RDF::URI, RDF::Node] subject @param [Hash{Symbol => Any}] attributes Initial attributes @return [Spira::Base] the newly created instance
# File lib/spira/persistence.rb, line 194 def project(subject, attributes = {}, &block) new(attributes.merge(_subject: subject), &block) end
The current repository for this class
@return [RDF::Repository, nil]
# File lib/spira/persistence.rb, line 11 def repository Spira.repository || raise(NoRepositoryError) end
Private Instance Methods
# File lib/spira/persistence.rb, line 252 def conditions_to_query(conditions) patterns = [] conditions.each do |name, value| if name.to_s == "type" patterns << [:subject, RDF.type, value] else patterns << [:subject, properties[name][:predicate], value] end end RDF::Query.new do patterns.each { |pat| pattern(pat) } end end
# File lib/spira/persistence.rb, line 244 def find_all(*args) [].tap do |records| find_each(*args) do |record| records << record end end end