module ActiveRecord module FinderMethods def find(*ids, &block) return to_a.find(&block) if block_given? expects_array = ids.first.kind_of?(Array) return ids.first if expects_array && ids.first.empty? ids = ids.flatten.compact.uniq case ids.size when 0 raise RecordNotFound, "Couldn't find #{@klass.name} without an ID" when 1 result = find_one(ids.first) expects_array ? [ result ] : result else find_some(ids) end end def exists?(id = nil) relation = select(primary_key).limit(1) relation = relation.where(primary_key.eq(id)) if id relation.first ? true : false end def first if loaded? @records.first else @first ||= limit(1).to_a[0] end end def last if loaded? @records.last else @last ||= reverse_order.limit(1).to_a[0] end end protected def find_by_attributes(match, attributes, *args) conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} result = where(conditions).send(match.finder) if match.bang? && result.blank? raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}" else result end end def find_or_instantiator_by_attributes(match, attributes, *args) guard_protected_attributes = false if args[0].is_a?(Hash) guard_protected_attributes = true attributes_for_create = args[0].with_indifferent_access conditions = attributes_for_create.slice(*attributes).symbolize_keys else attributes_for_create = conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} end record = where(conditions).first unless record record = @klass.new { |r| r.send(:attributes=, attributes_for_create, guard_protected_attributes) } yield(record) if block_given? record.save if match.instantiator == :create end record end def find_one(id) record = where(primary_key.eq(id)).first unless record conditions = where_clause(', ') conditions = " [WHERE #{conditions}]" if conditions.present? raise RecordNotFound, "Couldn't find #{@klass.name} with ID=#{id}#{conditions}" end record end def find_some(ids) result = where(primary_key.in(ids)).all expected_size = if @relation.taken && ids.size > @relation.taken @relation.taken else ids.size end # 11 ids with limit 3, offset 9 should give 2 results. if @relation.skipped && (ids.size - @relation.skipped < expected_size) expected_size = ids.size - @relation.skipped end if result.size == expected_size result else conditions = where_clause(', ') conditions = " [WHERE #{conditions}]" if conditions.present? error = "Couldn't find all #{@klass.name.pluralize} with IDs " error << "(#{ids.join(", ")})#{conditions} (found #{result.size} results, but was looking for #{expected_size})" raise RecordNotFound, error end end end end