Skip to content

Instantly share code, notes, and snippets.

@bcardarella
Created September 22, 2009 16:22
Show Gist options
  • Save bcardarella/191210 to your computer and use it in GitHub Desktop.
Save bcardarella/191210 to your computer and use it in GitHub Desktop.
Should Have Named Scope matcher for Rspec
module Shoulda # :nodoc:
module ActiveRecord # :nodoc:
# Examples:
# class Book < ActiveRecord::Base
# named_scope :test, :conditions => { :name => "test" }
# named_scope :by_name, lambda { |n| { :conditions => { :name => n} } }
# end
#
# RSpec:
# describe Book
# describe "Named Scope" do
# before(:each) do
# @expected = [Factory(:book, :name => "test")]
# Factory(:book, :name => "blah")
# Factory(:book, :name => "heynow")
# @by_test_param = "test"
# end
#
# it { should have_named_scope(:test).to_return(@expected) }
# it { should have_named_scope("by_name(@by_test_param)").to_return(@expected) }
# end
# end
#
# Test::Unit:
# class BookTest < ActiveSupport::TestCase
# context "Named Scope" do
# setup do
# @expected = [Factory(:book, :name => "test")]
# Factory(:book, :name => "blah")
# Factory(:book, :name => "heynow")
# @by_test_param = "test"
# end
#
# should_have_named_scope(:test, "@expected")
# should_have_named_scope("by_name(@by_test_param)", "@expected")
# end
# end
module NamedScopeMatcher
def have_named_scope(scope_call)
HaveNamedScopeMatcher.new(scope_call).in_context(self)
end
class HaveNamedScopeMatcher
def initialize(scope_call)
@scope_call = scope_call
end
def matches?(subject)
@subject = subject.class
if @expected
return is_named_scope? && match_expected?
else
return is_named_scope?
end
end
def to_return(expected)
@expected = get_expected(expected)
self
end
def in_context(context)
@context = context
self
end
def failure_message
if @not_named_scope
@not_named_scope
else
"Named scope failed for #{@subject.class.name}.#{@scope_call}\n" +
"Expected: #{@expected.inspect}\n" +
"Got: #{@result.inspect}"
end
end
def description(scope_call = nil, expected = nil, subject = nil)
@scope_call ||= scope_call
@expected ||= expected
@subject ||= subject
message = "have #{@subject}.#{@scope_call}"
message = "#{message} result in #{@expected.inspect}" if @expected
return message
end
private
def is_named_scope?
scope_sym = @scope_call.to_s.split("(").first.to_sym
if @subject.scopes.key?(scope_sym)
return true
else
@not_named_scope = "Named scope '#{scope_sym}' does not exist for #{@subject}"
return false
end
end
def match_expected?
@result = @context.instance_eval("#{@subject}.#{@scope_call}")
@result == @expected
end
def get_expected(expectation)
if expectation.is_a?(String)
return @context.instance_eval(expectation)
else
return expectation
end
end
end
end
module NamedScopeMacro
include NamedScopeMatcher
def should_have_named_scope(scope_call, expected = nil)
subject = (described_type rescue model_class).new
matcher = HaveNamedScopeMatcher.new(scope_call)
should matcher.description(scope_call, expected, subject.class) do
assert matcher.in_context(self).to_return(expected).matches?(subject),
matcher.failure_message
end
end
end
end
end
if defined? Spec
Spec::Runner.configure do |config|
config.include(Shoulda::ActiveRecord::NamedScopeMatcher)
end
else
Test::Unit::TestCase.class_eval { extend Shoulda::ActiveRecord::NamedScopeMacro }
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment