Skip to content

Commit

Permalink
Merge pull request #833 from rspec/fix-constant-original-for-bad-cons…
Browse files Browse the repository at this point in the history
…t-names

Make `RSpec::Mocks::Constant.original(name)` handle invalid names gracefully.
  • Loading branch information
JonRowe committed Dec 2, 2014
2 parents a454e6f + 689878e commit 8018b83
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 8 deletions.
3 changes: 3 additions & 0 deletions Changelog.md
Expand Up @@ -30,6 +30,9 @@ Bug Fixes:
`receive_message_chain`. (Myron Marston, #828)
* Validate invocation args for null object verified doubles.
(Myron Marston, #829)
* Fix `RSpec::Mocks::Constant.original` when called with an invalid
constant to return an object indicating the constant name is invalid,
rather than blowing up. (Myron Marston, #833)

### 3.1.3 / 2014-10-08
[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...v3.1.3)
Expand Down
27 changes: 19 additions & 8 deletions lib/rspec/mocks/mutate_const.rb
Expand Up @@ -13,6 +13,8 @@ def initialize(name)
@previously_defined = false
@stubbed = false
@hidden = false
@valid_name = true
yield self if block_given?
end

# @return [String] The fully qualified name of the constant.
Expand All @@ -24,7 +26,7 @@ def initialize(name)
attr_accessor :original_value

# @private
attr_writer :previously_defined, :stubbed, :hidden
attr_writer :previously_defined, :stubbed, :hidden, :valid_name

# @return [Boolean] Whether or not the constant was defined
# before the current example.
Expand All @@ -50,6 +52,12 @@ def hidden?
@hidden
end

# @return [Boolean] Whether or not the provided constant name
# is a valid Ruby constant name.
def valid_name?
@valid_name
end

# The default `to_s` isn't very useful, so a custom version is provided.
def to_s
"#<#{self.class.name} #{name}>"
Expand All @@ -58,13 +66,16 @@ def to_s

# @private
def self.unmutated(name)
const = new(name)
const.previously_defined = recursive_const_defined?(name)
const.stubbed = false
const.hidden = false
const.original_value = recursive_const_get(name) if const.previously_defined?

const
previously_defined = recursive_const_defined?(name)
rescue NameError
new(name) do |c|
c.valid_name = false
end
else
new(name) do |const|
const.previously_defined = previously_defined
const.original_value = recursive_const_get(name) if previously_defined
end
end

# Queries rspec-mocks to find out information about the named constant.
Expand Down
23 changes: 23 additions & 0 deletions spec/rspec/mocks/mutate_const_spec.rb
Expand Up @@ -439,6 +439,7 @@ def change_const_value_to(value)
let(:const) { Constant.original("TestClass::M") }

it("exposes its name") { expect(const.name).to eq("TestClass::M") }
it("indicates the name is valid") { expect(const).to be_valid_name }
it("indicates it was previously defined") { expect(const).to be_previously_defined }
it("indicates it has not been mutated") { expect(const).not_to be_mutated }
it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
Expand All @@ -451,6 +452,7 @@ def change_const_value_to(value)
let(:const) { Constant.original("TestClass::M") }

it("exposes its name") { expect(const.name).to eq("TestClass::M") }
it("indicates the name is valid") { expect(const).to be_valid_name }
it("indicates it was previously defined") { expect(const).to be_previously_defined }
it("indicates it has been mutated") { expect(const).to be_mutated }
it("indicates it has been stubbed") { expect(const).to be_stubbed }
Expand All @@ -463,6 +465,7 @@ def change_const_value_to(value)
let(:const) { Constant.original("TestClass::Undefined") }

it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") }
it("indicates the name is valid") { expect(const).to be_valid_name }
it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
it("indicates it has been mutated") { expect(const).to be_mutated }
it("indicates it has been stubbed") { expect(const).to be_stubbed }
Expand All @@ -475,6 +478,7 @@ def change_const_value_to(value)
let(:const) { Constant.original("TestClass::UndefinedModule") }

it("exposes its name") { expect(const.name).to eq("TestClass::UndefinedModule") }
it("indicates the name is valid") { expect(const).to be_valid_name }
it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
it("indicates it has been mutated") { expect(const).to be_mutated }
it("indicates it has been stubbed") { expect(const).to be_stubbed }
Expand All @@ -486,6 +490,7 @@ def change_const_value_to(value)
let(:const) { Constant.original("TestClass::Undefined") }

it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") }
it("indicates the name is valid") { expect(const).to be_valid_name }
it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
it("indicates it has not been mutated") { expect(const).not_to be_mutated }
it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
Expand All @@ -499,6 +504,7 @@ def change_const_value_to(value)
let(:const) { Constant.original("TestClass::M") }

it("exposes its name") { expect(const.name).to eq("TestClass::M") }
it("indicates the name is valid") { expect(const).to be_valid_name }
it("indicates it was previously defined") { expect(const).to be_previously_defined }
it("indicates it has been mutated") { expect(const).to be_mutated }
it("indicates it has been stubbed") { expect(const).to be_stubbed }
Expand All @@ -512,6 +518,7 @@ def change_const_value_to(value)
let(:const) { Constant.original("TestClass::Undefined") }

it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") }
it("indicates the name is valid") { expect(const).to be_valid_name }
it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
it("indicates it has been mutated") { expect(const).to be_mutated }
it("indicates it has been stubbed") { expect(const).to be_stubbed }
Expand All @@ -524,6 +531,7 @@ def change_const_value_to(value)
let(:const) { Constant.original("SomeUndefinedConst") }

it("exposes its name") { expect(const.name).to eq("SomeUndefinedConst") }
it("indicates the name is valid") { expect(const).to be_valid_name }
it("indicates it was previously undefined") { expect(const).not_to be_previously_defined }
it("indicates it has not been mutated") { expect(const).not_to be_mutated }
it("indicates it has not not been stubbed") { expect(const).not_to be_stubbed }
Expand All @@ -536,6 +544,7 @@ def change_const_value_to(value)
let(:const) { Constant.original("TestClass::M") }

it("exposes its name") { expect(const.name).to eq("TestClass::M") }
it("indicates the name is valid") { expect(const).to be_valid_name }
it("indicates it was previously defined") { expect(const).to be_previously_defined }
it("indicates it has been mutated") { expect(const).to be_mutated }
it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
Expand All @@ -549,12 +558,26 @@ def change_const_value_to(value)
let(:const) { Constant.original("TestClass::M") }

it("exposes its name") { expect(const.name).to eq("TestClass::M") }
it("indicates the name is valid") { expect(const).to be_valid_name }
it("indicates it was previously defined") { expect(const).to be_previously_defined }
it("indicates it has been mutated") { expect(const).to be_mutated }
it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
it("indicates it has been hidden") { expect(const).to be_hidden }
it("exposes its original value") { expect(const.original_value).to eq(:m) }
end

context "for an invalid const name (such as an anonymous module's `inspect` output)" do
let(:mod) { Module.new }
let(:const) { Constant.original(mod.inspect) }

it("exposes the provided string as the name") { expect(const.name).to eq(mod.inspect) }
it("indicates the name is invalid") { expect(const).not_to be_valid_name }
it("indicates it was not previously defined") { expect(const).not_to be_previously_defined }
it("indicates it has not been mutated") { expect(const).not_to be_mutated }
it("indicates it has not been stubbed") { expect(const).not_to be_stubbed }
it("indicates it has not been hidden") { expect(const).not_to be_hidden }
it("returns nil for its original value") { expect(const.original_value).to be_nil }
end
end
end
end
Expand Down

0 comments on commit 8018b83

Please sign in to comment.