codeclimate/minidoc

View on GitHub
lib/minidoc/finders.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
94%
require "active_support/concern"
require "forwardable"

module Minidoc::Finders
  extend ActiveSupport::Concern

  DocumentNotFoundError = Class.new(StandardError)

  module ClassMethods
    def all
      find({})
    end

    def first
      find_one({})
    end

    def count(selector = {})
      collection.count_documents(selector)
    end

    def exists?(selector = {})
      find_one(selector).present?
    end

    def find(id_or_selector, options = {})
      if id_or_selector.is_a?(Hash)
        ResultSet.new(collection.find(id_or_selector, options), wrapper)
      else
        raise ArgumentError unless options.empty?
        id = BSON::ObjectId(id_or_selector.to_s)
        wrapper.call(collection.find(_id: id).first)
      end
    end

    def find_one(selector = {}, options = {})
      # running collection.find({}).first will leave the cursor open unless we iterate across all of the documents
      # so in order to do not let a cusror open we want to kill the cursor after having grabbed the first document
      view = collection.find(selector, options)
      wrapped_doc = wrapper.call(view.first)
      view.close_query
      wrapped_doc
    end

    def find_one!(selector = {}, options = {})
      find_one(selector, options) or raise DocumentNotFoundError
    end

    def find_one_or_initialize(attributes = {}, options = {})
      raise ArgumentError unless attributes.is_a?(Hash)
      find_one(attributes, options) || new(attributes)
    end

    private

    def from_db(attrs)
      doc = new(attrs)
      doc.instance_variable_set("@new_record", false)
      doc
    end

    def wrapper
      @wrapper ||= proc do |doc|
        if doc
          if doc.is_a?(Array) || doc.is_a?(Mongo::Cursor)
            doc.map { |d| from_db(d) }
          else
            from_db(doc)
          end
        end
      end
    end
  end

  class ResultSet
    include Enumerable

    def initialize(view, doc_wrapper)
      @view = view
      @doc_wrapper = doc_wrapper
    end

    def each(&_block)
      if block_given?
        @view.each do |doc|
          yield @doc_wrapper.call(doc)
        end
      else
        to_enum
      end
    end

    def count
      @view.count_documents
    end
  end
end