Вопрос с переменной областью для Руби Новичок - PullRequest
4 голосов
/ 12 февраля 2011

Я изучаю книгу «Учебник по Ruby on Rails 3» и натолкнулся на ту часть, где мне нужно написать несколько базовых модульных тестов для моих статических страниц. Я заметил, что код реплицирован только с некоторыми изменениями текста, поэтому я изменил его следующим образом:

require 'spec_helper'

describe PagesController do
  render_views

  pages = ['home', 'contact', 'about', 'help']

  before(:each) do
    @base_title = "Ruby on Rails Tutorial Sample App | "
  end

  pages.each do |page|
    describe "GET '#{page}'" do
      it "should be successful" do
        get "#{page}"
        response.should be_success
      end

      it "should have the right title" do
        get "#{page}"
        response.should have_selector("title", :content => @base_title + page.capitalize)
      end
    end
  end

end

Что меня смущает в приведенном выше примере, так это то, что я могу заменить переменную 'pages' следующим:

@pages = ['home', 'contact', 'about', 'help']

И это все еще работает. Это почему? Чем отличаются «@pages» и «pages»?

Другая сбивающая с толку вещь заключается в том, что оба этих параметра приводят к сбою тестов:

pages = ['home', 'contact', 'about', 'help']
@base_title = "Ruby on Rails Tutorial Sample App | "

И

before(:each) do
  pages = ['home', 'contact', 'about', 'help']
  @base_title = "Ruby on Rails Tutorial Sample App | "
end

Почему вышеприведенные 2 примера терпят неудачу? Почему код должен выглядеть так, как я написал в своем первом фрагменте кода? Я предполагаю, что это как-то связано с переменной областью действия, но я все еще новичок в Ruby, поэтому я ищу более глубокое понимание.

FWIW, я опытный разработчик C #, поэтому получение сопоставимого кода Java или C # поможет мне понять это или хорошо написанное описание.

Спасибо за любую поддержку.

Edit: Добавлено сообщение об ошибке, когда я перемещаю @base_title за пределы блока before.

Failure/Error: response.should have_selector("title", :content => @base_title + page.capitalize)
NoMethodError:
   You have a nil object when you didn't expect it!
   You might have expected an instance of Array.
   The error occurred while evaluating nil.+
 # ./spec/controllers/pages_controller_spec.rb:21:in `block (4 levels) in <top (required)>'

Ответы [ 2 ]

4 голосов
/ 12 февраля 2011

K, я собираюсь ответить на ваши вопросы по порядку ...

1. Когда вы сделаете изменение с

pages = ['home', 'contact', 'about', 'help']

на ...

@pages = ['home', 'contact', 'about', 'help']

Вы просто изменяете локальную переменную в переменную экземпляра ... это не должно работать, и ваши тесты должны ломаться ...

2. Следующий код не должен работать.

pages = ['home', 'contact', 'about', 'help']
@base_title = "Ruby on Rails Tutorial Sample App | "

Это потому, что @base_title не будет доступен для ваших блоков 'it' 'do'.Страницы переменных будут находиться в области видимости ... но у вас есть условие ошибки @base_title.

3. Это также не будет работать.

before(:each) do
  pages = ['home', 'contact', 'about', 'help']
  @base_title = "Ruby on Rails Tutorial Sample App | "
end

определяемые здесь переменные страницы выходят за рамки каждого имеющегося у вас цикла.С @base_title все будет в порядке, и он отлично подойдет ко всем вашим методам.

- Заключение -

Последний пример, который вы разместили, верен.Вам просто нужна локальная переменная для каждого цикла и переменная экземпляра (@base_title), чтобы она была доступна всему экземпляру класса во время выполнения тестов.Надеюсь, это поможет вам.Я бы посоветовал взглянуть на несколько других онлайн-руководств по ruby, лично мне нравится отправлять людей на http://rubykoans.com/ =)

Последнее замечание: RSpec - сложный случай для определения объема, поскольку он использует много блокови немного перемещает код, чтобы сделать то, что ему нужно.В основном у вас есть блоки внутри блоков ... все может быстро стать хитрым.Я бы начал с более простых примеров.

2 голосов
/ 12 февраля 2011

В Ruby вы создаете метод доступа, который позволяет внешнему коду обращаться к переменным экземпляра:

class Foo
  def pages
    @pages
  end

  def pages=(value)
    @pages = value
  end
end

Если вы обращаетесь к @pages из класса, это будет то же самое, что и доступpages из класса, который будет вызывать self.pages, возвращая @pages.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...