Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Move in code from RSpec::Mocks::RecursiveConstantMethods #76

Merged
merged 1 commit into from
Jun 6, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions lib/rspec/support/recursive_const_methods.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
module RSpec
module Support
# Provides recursive constant lookup methods useful for
# constant stubbing.
module RecursiveConstMethods
# We only want to consider constants that are defined directly on a
# particular module, and not include top-level/inherited constants.
# Unfortunately, the constant API changed between 1.8 and 1.9, so
# we need to conditionally define methods to ignore the top-level/inherited
# constants.
#
# Given:
# class A; B = 1; end
# class C < A; end
#
# On 1.8:
# - C.const_get("Hash") # => ::Hash
# - C.const_defined?("Hash") # => false
# - C.constants # => ["B"]
# - None of these methods accept the extra `inherit` argument
# On 1.9:
# - C.const_get("Hash") # => ::Hash
# - C.const_defined?("Hash") # => true
# - C.const_get("Hash", false) # => raises NameError
# - C.const_defined?("Hash", false) # => false
# - C.constants # => [:B]
# - C.constants(false) #=> []
if Module.method(:const_defined?).arity == 1
def const_defined_on?(mod, const_name)
mod.const_defined?(const_name)
end

def get_const_defined_on(mod, const_name)
if const_defined_on?(mod, const_name)
return mod.const_get(const_name)
end

raise NameError, "uninitialized constant #{mod.name}::#{const_name}"
end

def constants_defined_on(mod)
mod.constants.select { |c| const_defined_on?(mod, c) }
end
else
def const_defined_on?(mod, const_name)
mod.const_defined?(const_name, false)
end

def get_const_defined_on(mod, const_name)
mod.const_get(const_name, false)
end

def constants_defined_on(mod)
mod.constants(false)
end
end

def recursive_const_get(const_name)
normalize_const_name(const_name).split('::').inject(Object) do |mod, name|
get_const_defined_on(mod, name)
end
end

def recursive_const_defined?(const_name)
normalize_const_name(const_name).split('::').inject([Object, '']) do |(mod, full_name), name|
yield(full_name, name) if block_given? && !(Module === mod)
return false unless const_defined_on?(mod, name)
[get_const_defined_on(mod, name), [mod, name].join('::')]
end
end

def normalize_const_name(const_name)
const_name.sub(/\A::/, '')
end
end
end
end
50 changes: 50 additions & 0 deletions spec/rspec/support/recursive_const_methods_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require 'spec_helper'
require 'rspec/support/recursive_const_methods'

module RSpec
module Support
describe RecursiveConstMethods do
include described_class

module Foo
class Parent
UNDETECTED = 'Not seen when looking up constants in Bar'
end

class Bar < Parent
VAL = 10
end
end

describe '#recursive_const_defined?' do
it 'finds constants' do
const, _name = recursive_const_defined?('::RSpec::Support::Foo::Bar::VAL')

expect(const).to eq(10)
end

it 'returns the fully qualified name of the constant' do
_const, name = recursive_const_defined?('::RSpec::Support::Foo::Bar::VAL')

expect(name).to eq('RSpec::Support::Foo::Bar::VAL')
end

it 'does not find constants in ancestors' do
expect(recursive_const_defined?('::RSpec::Support::Foo::Bar::UNDETECTED')).to be_falsy
end
end

describe '#recursive_const_get' do
it 'gets constants' do
expect(recursive_const_get('::RSpec::Support::Foo::Bar::VAL')).to eq(10)
end

it 'does not get constants in ancestors' do
expect do
recursive_const_get('::RSpec::Support::Foo::Bar::UNDETECTED')
end.to raise_error(NameError)
end
end
end
end
end