19529


こういうクラスがあったとして、

[code]

    class Book
      attr_reader :author
    end
    
[/code]

以下のspecを書くと、

[code]

    let(:author){ build :author, name: '田中'}
    
    it 'test_a' do  
      book = Book.new
      book.instance_eval do
        @author = author
      end
      expect(book.author.name).to eq '田中'
    end
    
[/code]

undefined method or variables 'name' for nil class  
になってしまいます。  
しかし、

[code]

    let(:author){ build :author, name: '田中'}
    
    it 'test_a' do  
      # 一度ローカル変数に代入する。
      var_author = author
      book = Book.new
      book.instance_eval do
        @author = var_author
      end
      expect(book.author.name).to eq '田中'
    end
    
[/code]

にすれば解決します。  
ブロックの中身ではletの値は取れずに、ブロックの外ではletの中身を取れます。  
しかし、ブロックの中身も外もスコープの違いはないはずです。  
letとはそもそもどういう働きなのでしょうか？？なぜブロックの中身からletで定義した変数?を参照する事ができないのでしょうか。

※注意 Bookクラスは直面している問題をわかりやすくするためのモデルで、実際にはBookクラスでテストをしているわけではありません。


CC BY-SA 3.0
2015-12-01T16:15:44.163
19525
2015-12-01T21:44:19.227
12758
question
ruby-on-rails ruby rspec
letのスコープについて


一言で言うと、「instance_evalの仕様が特殊だから」ということになります。  
以下は詳細です。

[instance method BasicObject#instance_eval (Ruby 2.2.0)](http://docs.ruby-
lang.org/ja/2.2.0/method/BasicObject/i/instance_eval.html)

> ただし、ローカル変数だけは、文字列 expr の評価では instance_eval
> の外側のスコープと、ブロックの評価ではそのブロックの外側のスコープと、共有します。

上の説明にあるように、instance_evalを使った場合、ローカル変数はスコープが共有されますが、メソッドは共有されません。  
そしてRSpecのletは変数のように見えますが、実際にはメソッドです。

[code]

    defined? author # => method
    
[/code]

また、

[code]

    book.instance_eval do
      @author = author
    end
    
[/code]

で代入している`author`はRSpecではなく、Bookクラスのゲッターです。  
つまり以下のコードと同じ意味です。

[code]

    book.instance_eval do
      @author = self.author
    end
    
[/code]

というわけで結局、instance_evalの中ではletで定義した`author`を参照できていないことになります。

一方、質問の後半にある`var_author`はローカル変数なのでinstance_evalの中でも参照できます。  
よって、instance_evalの中で値をセットできるわけです。

ちなみに、 instance_eval でなければブロック内でもletの値を参照することは可能です。

[code]

    let(:x) { 1 }
    example do
      y = [].tap do |a|
        a << x
      end
      expect(y).to [1]
    end
    
[/code]


CC BY-SA 3.0
2015-12-01T21:44:19.227
19529
2015-12-01T21:44:19.227
85
19525
answer