Permalink
Browse files

Refactor `subject` to be implemented in terms of `let`.

This is far simpler, and paves the way for supporting
`super` in both `let` and `subject`.

I had to rewrite a few of the subject specs; they were coupled
to ExampleGroup#subject returning a proc, but that was an
implementation detail of the old implementation.
  • Loading branch information...
1 parent cc2e56f commit 19dd6b2d4b25da0042f80bb8ff854672b913ba30 @myronmarston myronmarston committed Dec 24, 2012
Showing with 60 additions and 48 deletions.
  1. +6 −23 lib/rspec/core/subject.rb
  2. +54 −25 spec/rspec/core/subject_spec.rb
View
@@ -42,11 +42,10 @@ module ExampleMethods
# @see ExampleGroupMethods#subject
# @see #should
def subject
- if defined?(@original_subject)
- @original_subject
- else
- @original_subject = instance_eval(&self.class.subject)
- end
+ # This logic defines an implicit subject.
+ # Explicit `subject` declarations re-define this method.
+ described = described_class || self.class.description
+ Class === described ? described.new : described
end
# When `should` is called with no explicit receiver, the call is
@@ -197,7 +196,7 @@ def subject(name=nil, &block)
let(name, &block)
subject { send name }
else
- block ? @explicit_subject_block = block : explicit_subject || implicit_subject
+ let(:subject, &block)
end
end
@@ -258,24 +257,8 @@ def subject!(name=nil, &block)
subject(name, &block)
before { __send__(:subject) }
end
-
- attr_reader :explicit_subject_block
-
- private
-
- def explicit_subject
- group = self
- while group.respond_to?(:explicit_subject_block)
- return group.explicit_subject_block if group.explicit_subject_block
- group = group.superclass
- end
- end
-
- def implicit_subject
- described = described_class || description
- Class === described ? proc { described.new } : proc { described }
- end
end
end
end
end
+
@@ -5,28 +5,36 @@ module RSpec::Core
describe Subject do
before(:each) { RSpec.configuration.configure_expectation_framework }
+ def subject_value_for(describe_arg, &block)
+ group = ExampleGroup.describe(describe_arg, &block)
+ subject_value = nil
+ group.example { subject_value = subject }
+ group.run
+ subject_value
+ end
+
describe "implicit subject" do
describe "with a class" do
it "returns an instance of the class" do
- ExampleGroup.describe(Array).subject.call.should eq([])
+ expect(subject_value_for(Array)).to eq([])
end
end
describe "with a Module" do
it "returns the Module" do
- ExampleGroup.describe(Enumerable).subject.call.should eq(Enumerable)
+ expect(subject_value_for(Enumerable)).to eq(Enumerable)
end
end
describe "with a string" do
it "return the string" do
- ExampleGroup.describe("Foo").subject.call.should eq("Foo")
+ expect(subject_value_for("Foo")).to eq("Foo")
end
end
describe "with a number" do
it "returns the number" do
- ExampleGroup.describe(15).subject.call.should eq(15)
+ expect(subject_value_for(15)).to eq(15)
end
end
@@ -54,53 +62,74 @@ module RSpec::Core
describe "defined in a top level group" do
it "replaces the implicit subject in that group" do
- group = ExampleGroup.describe(Array)
- group.subject { [1,2,3] }
- group.subject.call.should eq([1,2,3])
+ subject_value = subject_value_for(Array) do
+ subject { [1, 2, 3] }
+ end
+ expect(subject_value).to eq([1, 2, 3])
end
end
describe "defined in a top level group" do
let(:group) do
ExampleGroup.describe do
- subject{ [4,5,6] }
+ subject{ [4, 5, 6] }
end
end
it "is available in a nested group (subclass)" do
- nested_group = group.describe("I'm nested!") { }
- nested_group.subject.call.should eq([4,5,6])
+ subject_value = nil
+ group.describe("I'm nested!") do
+ example { subject_value = subject }
+ end.run
+
+ expect(subject_value).to eq([4, 5, 6])
end
it "is available in a doubly nested group (subclass)" do
- nested_group = group.describe("Nesting level 1") { }
- doubly_nested_group = nested_group.describe("Nesting level 2") { }
- doubly_nested_group.subject.call.should eq([4,5,6])
+ subject_value = nil
+ group.describe("Nesting level 1") do
+ describe("Nesting level 2") do
+ example { subject_value = subject }
+ end
+ end.run
+
+ expect(subject_value).to eq([4, 5, 6])
end
end
describe "with a name" do
it "defines a method that returns the memoized subject" do
- group = ExampleGroup.describe do
- subject(:list) { [1,2,3] }
+ list_value_1 = list_value_2 = subject_value_1 = subject_value_2 = nil
+
+ ExampleGroup.describe do
+ subject(:list) { [1, 2, 3] }
example do
- list.should equal(list)
- subject.should equal(subject)
- subject.should equal(list)
+ list_value_1 = list
+ list_value_2 = list
+ subject_value_1 = subject
+ subject_value_2 = subject
end
- end
- group.run.should be_true
+ end.run
+
+ expect(list_value_1).to eq([1, 2, 3])
+ expect(list_value_1).to equal(list_value_2)
+
+ expect(subject_value_1).to equal(subject_value_2)
+ expect(subject_value_1).to equal(list_value_1)
end
it "is referred from inside subject by the name" do
- group = ExampleGroup.describe do
- subject(:list) { [1,2,3] }
+ inner_subject_value = nil
+
+ ExampleGroup.describe do
+ subject(:list) { [1, 2, 3] }
describe 'first' do
subject(:first_element) { list.first }
- it { should eq(1) }
+ example { inner_subject_value = subject }
end
- end
- group.run.should be_true
+ end.run
+
+ expect(inner_subject_value).to eq(1)
end
end
end

0 comments on commit 19dd6b2

Please sign in to comment.