Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoid dynamic finder with Mongoid::Errors::DocumentNotFound exception raised

I'm building a REST api for this project that uses Mongoid.

I've setup the following to catch the Mongoid::Errors::DocumentNotFound exception:

rescue_from Mongoid::Errors::DocumentNotFound in my base controller

In my controller I've this query code:

@current_account.users.find(:first, :conditions => {:name => "some_name"})

The above query just returns nil. It doesn't raise the exception. Tried with another syntax as well:

User.find(:conditions => {:name => "same"}).first

All those methods just runs where internally and afaik where doesn't raise exception, its simply returns []

So what can be the solution to this? I want partially dynamic finder but should raise the exception too?

like image 984
Autodidact Avatar asked Jul 12 '11 09:07

Autodidact


2 Answers

I've met same problem today, and found another solution.

Set raise_not_found_error to false. so your config/mongoid.yml should be

development:
  host: localhost
  port: 10045
  username: ...
  password: ...
  database: ...
  raise_not_found_error: false

from http://mongoid.org/docs/installation/configuration.html

like image 111
jaypark Avatar answered Oct 17 '22 04:10

jaypark


I believe that Mongoid will only raise a DocumentNotFound exception when using the find method by passing in an object's id (and not with conditions). Otherwise it will return nil. From the Mongoid source:

# lib/mongoid/errors/document_not_found.rb

# Raised when querying the database for a document by a specific id which
# does not exist. If multiple ids were passed then it will display all of
# those.

You will have to check manually to see if you got any results and either raise the DocumentNotFound exception yourself (not great), or raise your own custom exception (better solution).

An example of the former would be something like this:

raise Mongoid::Errors::DocumentNotFound.new(User, params[:name]) unless @current_account.users.first(:conditions => {:name => params[:name]})

Update: I haven't tested any of this, but it should allow you to make calls like (or at least point you in the right direction - i hope!):

@current_account.users.where!(:conditions => {:name => params[:name]})

Which will throw a custom Mongoid::CollectionEmpty error, if the collection returned from the query is empty. Note that it's not the most efficient solution, since in order to find out if the returned collection is empty - it has to actually process the query.

Then all you need to do is rescue from Mongoid::CollectionEmpty instead (or as well).

# lib/mongoid_criterion_with_errors.rb
module Mongoid
  module Criterion
    module WithErrors
      extend ActiveSupport::Concern

      module ClassMethods
        def where!(*args)
          criteria = self.where(args)
          raise Mongoid::EmptyCollection(criteria) if criteria.empty?
          criteria
        end
      end
    end
  end

  class EmptyCollection < StandardError
    def initialize(criteria)
      @class_name = criteria.class
      @selector = criteria.selector
    end

    def to_s
      "Empty collection found for #{@class_name}, using selector: #{@selector}"
    end
  end
end

# config/application.rb
module ApplicationName
  class Application < Rails::Application
    require 'mongoid_criterion_with_errors'
    #...snip...
  end
end

# app/models/user.rb
class User
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongoid::Criterion::WithErrors
  #...snip...
end
like image 23
theTRON Avatar answered Oct 17 '22 03:10

theTRON