module ActiveFedora class Relation include Enumerable include FinderMethods, Calculations, SpawnMethods, QueryMethods, Delegation attr_reader :loaded attr_accessor :default_scoped alias loaded? loaded attr_accessor :values, :klass def initialize(klass, _values = {}) @klass = klass @loaded = false @values = {} end # This method gets called on clone def initialize_copy(_other) # Dup the values @values = Hash[@values] reset end # Tries to create a new record with the same scoped attributes # defined in the relation. Returns the initialized object if validation fails. # # Expects arguments in the same format as +Base.create+. # # ==== Examples # users = User.where(name: 'Oscar') # users.create # # # # users.create(name: 'fxn') # users.create # # # # users.create { |user| user.name = 'tenderlove' } # # # # # users.create(name: nil) # validation on name # # # def create(*args, &block) scoping { @klass.create(*args, &block) } end def reset @first = @loaded = nil @records = [] self end def to_a return @records if loaded? args = @klass == ActiveFedora::Base ? { cast: true } : {} args[:rows] = limit_value if limit_value args[:start] = offset_value if offset_value args[:sort] = order_values if order_values @records = to_enum(:find_each, where_values, args).to_a @loaded = true @records end # Scope all queries to the current scope. # # Comment.where(post_id: 1).scoping do # Comment.first # end # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1 # # Please check unscoped if you want to remove all previous scopes (including # the default_scope) during the execution of a block. def scoping previous = klass.current_scope klass.current_scope = self yield ensure klass.current_scope = previous end def ==(other) case other when Relation other.where_values == where_values when Array to_a == other end end delegate :inspect, to: :to_a # Destroys the records matching +conditions+ by instantiating each # record and calling its +destroy+ method. Each object's callbacks are # executed (including :dependent association options and # +before_destroy+/+after_destroy+ Observer methods). Returns the # collection of objects that were destroyed; each will be frozen, to # reflect that no changes should be made (since they can't be # persisted). # # Note: Instantiation, callback execution, and deletion of each # record can be time consuming when you're removing many records at # once. It generates at least one fedora +DELETE+ query per record (or # possibly more, to enforce your callbacks). If you want to delete many # rows quickly, without concern for their associations or callbacks, use # +delete_all+ instead. # # ==== Parameters # # * +conditions+ - A string, array, or hash that specifies which records # to destroy. If omitted, all records are destroyed. See the # Conditions section in the ActiveFedora::Relation#where for # more information. # # ==== Examples # # Person.destroy_all(:status_s => "inactive") # Person.where(:age_i => 18).destroy_all def destroy_all(conditions = nil) if conditions where(conditions).destroy_all else to_a.each(&:destroy).tap { reset }.size end end def delete_all(conditions = nil) if conditions where(conditions).delete_all else to_a.each(&:delete).tap { reset }.size end end # Returns a hash of where conditions. # # User.where(name: 'Oscar').where_values_hash # # => {name: "Oscar"} def where_values_hash {} end def scope_for_create @scope_for_create ||= where_values_hash.merge(create_with_value) end private VALID_FIND_OPTIONS = [:order, :limit, :start, :conditions, :cast].freeze def apply_finder_options(options) relation = clone return relation unless options options.assert_valid_keys(VALID_FIND_OPTIONS) finders = options.dup finders.delete_if { |key, value| value.nil? && key != :limit } ([:order, :limit, :start] & finders.keys).each do |finder| relation = relation.send(finder, finders[finder]) end relation = relation.where(finders[:conditions]) if options.key?(:conditions) relation end end end