Skip to content

Commit

Permalink
Don't hit db on method_missing if array doesnt respond.
Browse files Browse the repository at this point in the history
[ fix #3081 ]
  • Loading branch information
durran committed May 31, 2013
1 parent f5ba129 commit 11e45e5
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,10 @@ For instructions on upgrading to newer versions, visit

### Resolved Issues

* \#3081 Criteria's `method_missing` now checks if an array responds to the provided
method before calling entries in order to not hit the database if a `NoMethodError`
was to get raised.

* \#3068 Fixed spec runs on non standard MongoDB ports if `MONGOID_SPEC_PORT` is
set.

Expand Down
12 changes: 10 additions & 2 deletions lib/mongoid/criteria.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ class Criteria
include Modifiable
include Scopable

# Static array used to check with method missing - we only need to ever
# instantiate once.
#
# @since 4.0.0
CHECK = []

attr_accessor :embedded, :klass

# Returns true if the supplied +Enumerable+ or +Criteria+ is equal to the results
Expand Down Expand Up @@ -322,7 +328,7 @@ def only(*args)
#
# @return [ true, false ] If the criteria responds to the method.
def respond_to?(name, include_private = false)
super || klass.respond_to?(name) || entries.respond_to?(name, include_private)
super || klass.respond_to?(name) || CHECK.respond_to?(name, include_private)
end

alias :to_ary :to_a
Expand Down Expand Up @@ -506,8 +512,10 @@ def method_missing(name, *args, &block)
klass.send(:with_scope, self) do
klass.send(name, *args, &block)
end
else
elsif CHECK.respond_to?(name)
return entries.send(name, *args, &block)
else
super
end
end

Expand Down
53 changes: 53 additions & 0 deletions spec/mongoid/criteria_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3534,6 +3534,59 @@ def self.ages; self; end
end
end

describe "#method_missing" do

let(:criteria) do
Person.all
end

context "when the method exists on the class" do

before do
Person.should_receive(:minor).and_call_original
end

it "calls the method on the class" do
expect(criteria.minor).to be_empty
end
end

context "when the method exists on the criteria" do

before do
criteria.should_receive(:to_criteria).and_call_original
end

it "calls the method on the criteria" do
expect(criteria.to_criteria).to eq(criteria)
end
end

context "when the method exists on array" do

before do
criteria.should_receive(:entries).and_call_original
end

it "calls the method on the criteria" do
expect(criteria.at(0)).to be_nil
end
end

context "when the method does not exist" do

before do
criteria.should_receive(:entries).never
end

it "raises an error" do
expect {
criteria.to_hash
}.to raise_error(NoMethodError)
end
end
end

describe "#uniq" do

let!(:band_one) do
Expand Down

0 comments on commit 11e45e5

Please sign in to comment.