Skip to content

Commit

Permalink
Restore original implementation of its(attribute) from rspec-1.
Browse files Browse the repository at this point in the history
- original implementation by Stephen Touset
- Closes rspec#90.
  • Loading branch information
dchelimsky committed Jul 23, 2010
1 parent e516329 commit 3025753
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 28 deletions.
44 changes: 44 additions & 0 deletions features/subject/attribute_of_subject.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Feature: attribute of subject

Scenario: simple attribute
Given a file named "example_spec.rb" with:
"""
describe Array do
its(:size) { should == 0 }
end
"""
When I run "rspec example_spec.rb --format documentation"
Then the output should contain:
"""
Array
size
should == 0
"""

Scenario: nested attribute
Given a file named "example_spec.rb" with:
"""
class Person
attr_reader :phone_numbers
def initialize
@phone_numbers = []
end
end
describe Person do
subject do
person = Person.new
person.phone_numbers << "555-1212"
person
end
its("phone_numbers.first") { should == "555-1212" }
end
"""
When I run "rspec example_spec.rb --format documentation"
Then the output should contain:
"""
Person
phone_numbers.first
should == "555-1212"
"""
2 changes: 1 addition & 1 deletion features/subject/implicit_subject.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Feature: implicit subject
The first argument to the outermost example group block is
made available to each example as an implicit subject of
that example.

Scenario: subject in top level group
Given a file named "top_level_subject_spec.rb" with:
"""
Expand Down
1 change: 0 additions & 1 deletion lib/rspec/core/example_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ class << self
end

alias_example_to :it
alias_example_to :its, :attribute_of_subject => true
alias_example_to :specify
alias_example_to :focused, :focused => true
alias_example_to :pending, :pending => true
Expand Down
89 changes: 69 additions & 20 deletions lib/rspec/core/subject.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,32 @@ def self.included(kls)
end
end

# Returns the subject defined by the example group. The subject block is
# only executed once per example, the result of which is cached and
# returned by any subsequent calls to +subject+.
#
# If a class is passed to +describe+ and no subject is explicitly
# declared in the example group, then +subject+ will return a new
# instance of that class.
#
# == Examples
#
# # explicit subject defined by the subject method
# describe Person do
# subject { Person.new(:birthdate => 19.years.ago) }
# it "should be eligible to vote" do
# subject.should be_eligible_to_vote
# end
# end
#
# # implicit subject => { Person.new }
# describe Person do
# it "should be eligible to vote" do
# subject.should be_eligible_to_vote
# end
# end
def subject
using_attribute? ? attribute_of_subject : original_subject
@original_subject ||= instance_eval(&self.class.subject)
end

# When +should+ is called with no explicit receiver, the call is
Expand Down Expand Up @@ -41,6 +65,50 @@ def should_not(matcher=nil, message=nil)
end

module ClassMethods
# Creates a nested example group named by the submitted +attribute+,
# and then generates an example using the submitted block.
#
# # This ...
# describe Array do
# its(:size) { should == 0 }
# end
#
# # ... generates the same runtime structure as this:
# describe Array do
# describe "size" do
# it "should == 0" do
# subject.size.should == 0
# end
# end
# end
#
# The attribute can be a +Symbol+ or a +String+. Given a +String+
# with dots, the result is as though you concatenated that +String+
# onto the subject in an expression.
#
# describe Person
# let(:person) do
# person = Person.new
# person.phone_numbers << "555-1212"
# end
#
# its("phone_numbers.first") { should == "555-1212" }
# end
def its(attribute, &block)
describe(attribute) do
example do
self.class.class_eval do
define_method(:subject) do
attribute.to_s.split('.').inject(super) do |target, method|
target.send(method)
end
end
end
instance_eval(&block)
end
end
end

# Defines an explicit subject for an example group which can then be the
# implicit receiver (through delegation) of calls to +should+.
#
Expand Down Expand Up @@ -75,25 +143,6 @@ def implicit_subject
end
end

private

def original_subject
@original_subject ||= instance_eval(&self.class.subject)
end

def attribute_of_subject
if using_attribute?
example.description.split('.').inject(original_subject) do |target, method|
target.send(method)
end
end
end

def using_attribute?
example.in_block? &&
example.metadata[:attribute_of_subject]
end

end
end
end
24 changes: 18 additions & 6 deletions spec/rspec/core/example_group_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -374,12 +374,6 @@ module RSpec::Core
group.examples.size.should == 1
end

it "allows adding an example using 'its'" do
group = ExampleGroup.describe
group.its(:some_method) { }
group.examples.size.should == 1
end

it "exposes all examples at examples" do
group = ExampleGroup.describe
group.it("should do something 1") { }
Expand Down Expand Up @@ -547,6 +541,24 @@ def name
its("name.size") { should == 4 }
its("name.size.class") { should == Fixnum }
end

context "calling and overriding super" do
it "calls to the subject defined in the parent group" do
group = ExampleGroup.describe(Array) do
subject { [1, 'a'] }

its(:last) { should == 'a' }

describe '.first' do
def subject; super.first; end

its(:next) { should == 2 }
end
end

group.run_all.should be_true
end
end
end

describe "#top_level_description" do
Expand Down

0 comments on commit 3025753

Please sign in to comment.