Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.
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
3 changes: 3 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ Bug Fixes:
`and_yield`, `and_raise`, `and_return` or `and_throw`. This got fixed
in 2.13.1 but failed to get merged into master for the 2.14.0.rc1
release (Myron Marston).
* `Marshal.dump` does not unnecessarily duplicate objects when rspec-mocks has
not been fully initialized. This could cause errors when using `spork` or
similar preloading gems (Andy Lindeman).

### 2.14.0.rc1 / 2013-05-27
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.14.0.rc1)
Expand Down
14 changes: 7 additions & 7 deletions lib/rspec/mocks/extensions/marshal.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
module Marshal
class << self
def dump_with_mocks(*args)
object = args.shift

if ( ::RSpec::Mocks.space && !::RSpec::Mocks.space.registered?(object) ) || NilClass === object
return dump_without_mocks(*args.unshift(object))
# Duplicates any mock objects before serialization. Otherwise,
# serialization will fail because methods exist on the singleton class.
def dump_with_mocks(object, *rest)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making object a separate arg is so much clearer than the stuff we had before. Not sure why I didn't think of it. Nice work!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks :) I blame Erlang, Clojure, etc... for keeping even this simple sort of destructuring on my mind.

if ::RSpec::Mocks.space.nil? || !::RSpec::Mocks.space.registered?(object) || NilClass === object
dump_without_mocks(object, *rest)
else
dump_without_mocks(object.dup, *rest)
end

dump_without_mocks(*args.unshift(object.dup))
end

alias_method :dump_without_mocks, :dump
Expand Down
54 changes: 54 additions & 0 deletions spec/rspec/mocks/extensions/marshal_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
require 'spec_helper'

describe Marshal, 'extensions' do
# An object that raises when code attempts to dup it.
#
# Because we manipulate the internals of RSpec::Mocks.space below, we need
# an object that simply blows up when #dup is called without using any
# partial mocking or stubbing from rspec-mocks itself.
class UndupableObject
def dup
raise NotImplementedError
end
end

describe '#dump' do
context 'when rspec-mocks has not been fully initialized' do
def without_space
stashed_space, RSpec::Mocks.space = RSpec::Mocks.space, nil
yield
ensure
RSpec::Mocks.space = stashed_space
end

it 'does not duplicate the object before serialization' do
obj = UndupableObject.new
without_space do
serialized = Marshal.dump(obj)
expect(Marshal.load(serialized)).to be_an(UndupableObject)
end
end
end

context 'when rspec-mocks has been fully initialized' do
it 'duplicates objects with stubbed or mocked implementations before serialization' do
obj = double(:foo => "bar")

serialized = Marshal.dump(obj)
expect(Marshal.load(serialized)).to be_an(obj.class)
end

it 'does not duplicate other objects before serialization' do
obj = UndupableObject.new

serialized = Marshal.dump(obj)
expect(Marshal.load(serialized)).to be_an(UndupableObject)
end

it 'does not duplicate nil before serialization' do
serialized = Marshal.dump(nil)
expect(Marshal.load(serialized)).to be_nil
end
end
end
end