|
Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора? |
Регистрация
Изображение Gravatar и боковая панель
Определив базовую страницу пользователя в предыдущем разделе, теперь мы немного улучшим ее, добавив изображение пользователя и начальную реализацию боковой панели пользователя. При создании страницы мы сфокусируемся на ее внешнем виде, не особо заботясь о ее структуре, это означает, что (по крайней мере пока) мы не будем писать тесты. Когда мы подойдем к аспектам представления, более подверженным ошибкам, таким как пагинация (Раздел 9.3.3), мы продолжим разработку через тестирование.
Мы начнем с добавления "глобально распознаваемого аватара" (также известного как Граватар) к профилю пользователя.6В индуизме, аватар это форма проявление божества в человеке или животном. В более широком смысле термин avatar обычно используется для обозначения персонализации пользователя, особенно в виртуальной среде. Но вы уже видели фильм, и почти наверняка знаете об этом. Автором Граватар является Tom Preston-Werner (сооснователь GitHub), впоследствии его (Граватар) приобрела компания Automattic (создатели WordPress), это бесплатный сервис который позволяет пользователям загружать изображения и связывать их с подконтрольными им адресами электронной почты. Gravatar это удобный способ добавить изображения пользователей не связываясь с проблемами загрузки изображений, их обрезкой и хранением; все что нам нужно, это создать правильный URL Gravatar изображения используя адрес электронной почты пользователя и соответствующее изображение Gravatar появится автоматич ески.7Если вашему приложению потребуется обработка кастомных изображений или загрузки других файлов, я рекомендую использовать для этих целей гем Paperclip.
Мы планируем определить вспомогательную функцию gravatar_for которая будет возвращать изображение Граватар для данного пользователя, как это показано в Листинге 7.12.
<% provide(:title, @user.name) %> <h1> <%= gravatar_for @user %> <%= @user.name %> </h1>Листинг 7.12. Представление показывающее пользователя с именем и Граватаром. app/views/users/show.html.erb
В этой точке вы можете проверить что набор тестов не проходит:
$ bundle exec rspec spec/
Из-за того что метод gravatar_for неопределен, страница показывающая пользователя в настоящий момент не работает. (Отлов подобных ошибок это, возможно самый полезный аспект тестов представлений. Вот почему так важно наличие хоть каких-то тестов представлений, даже минималистичных.)
По умолчанию, методы определеннные в любом файле хелпера являются автоматически доступным в любом представлении, но для удобства мы поместим метод gravatar_for в файл для хелперов, связанных с контроллером Users. Как отмечено на главной странице Граватар, URL Граватара основывается на MD5 хэше адреса электронной почты пользователя. В Ruby, алгоритм MD5 хэширования реализутся с помощью метода hexdigest, который является частью библиотеки Digest:
>> email = "MHARTL@example.COM". >> Digest::MD5::hexdigest(email.downcase) => "1fda4469bcbec3badf5418269ffc5968"
Поскольку адреса электронной почты нечувствительны к регистру (Раздел 6.2.4), в отличие от MD5 хэшей, мы используем метод downcase для того чтобы быть уверенными в том, что аргумент передаваемый в hexdigest находится в нижнем регистре. Получившийся хелпер gravatar_for представлен в Листинге 7.13.
module UsersHelper
# Returns the Gravatar (http://gravatar.com/) for the given user.
def gravatar_for(user)
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}"
image_tag(gravatar_url, alt: user.name, class: "gravatar")
end
end
Листинг
7.13.
Определение вспомогательного метода gravatar_for. app/helpers/users_helper.rb
Код в Листинге 7.13 возвращает тег img для Граватара с классом "gravatar" и альтернативным текстом эквивалентным имени пользователя (что особенно удобно для браузеров используемых слабовидящими людьми которые работают с помощью считывателей экрана). Вы можете проверить что теперь набор тестов проходит:
$ bundle exec rspec spec/
Страница профиля представленная на рис. 7.7, показывающая дефолтное изображение Граватара, выглядит подобным образом т.к. user@example.com это невалидный email адрес (домен example.com зарезервирован для примеров (examples).
Для того чтобы отобразить кастомный Граватар в нашем приложении, мы будем использовать update_attributes (Раздел 6.1.5) для обновления пользователя в базе данных:
$ rails console >> user = User.first >> user.update_attributes(name: "Example User", ?> email: "example@railstutorial.org", ?> password: "foobar", ?> password_confirmation: "foobar") => true
Здесь мы назначили пользователю адрес электронной почты example@railstutorial.org, который я связал с логотипом Rails Tutorial, как это видно на рис. 7.8.
Последний элемент, необходимый для завершения наброска из рис. 7.1 это начальная версия пользовательской боковой панели. Мы реализуем ее с помощью тега aside, который используется для элементов (таких как сайдбары), которые дополняют страницу, но также могут быть использованы отдельно. Мы включаем row и span4 классы, которые являются частью Bootstrap. Код для измененной страницы показывающей пользователя представлен в Листинге 7.14.
<% provide(:title, @user.name) %>
<div class="row">
<aside class="span4">
<section>
<h1>
<%= gravatar_for @user %>
<%= @user.name %>
</h1>
</section>
</aside>
</div>
Листинг
7.14.
Добавление боковой панели к представлению user show. app/views/users/show.html.erb
Имея соответствующие HTML элементы и CSS классы, мы можем отстилить страницу профиля (включая боковую панель и Gravatar) с SCSS показанным в Листинге 7.15. (Обратите внимание на наследование CSS правил, которое работает только благодаря препроцессору Sass, используемому файлопроводом.) Получившаяся страница показана на рис. 7.9.
.
.
.
/* sidebar */
aside {
section {
padding: 10px 0;
border-top: 1px solid $grayLighter;
&:first-child {
border: 0;
padding-top: 0;
}
span {
display: block;
margin-bottom: 3px;
line-height: 1;
}
h1 {
font-size: 1.4em;
text-align: left;
letter-spacing: -1px;
margin-bottom: 3px;
margin-top: 0px;
}
}
}
.gravatar {
float: left;
margin-right: 10px;
}
Листинг
7.15.
SCSS для стилизации страницы показывающей пользователя, включая боковую панель. app/assets/stylesheets/custom.css.scss
Форма регистрации
Теперь, когда у нас есть рабочая (хоть и незавершенная) страница профиля пользователя, мы готовы приступить к созданию формы для регистрации на нашем сайте. Мы видели на рис. 5.9 (дублированном на рис. 7.10) что страница регистрации в настоящий момент пуста и совершенно бесполезна для регистрации новых пользователей. Целью этого раздела является разработка формы регистрации из рис. 7.11.
Поскольку мы говорим о создании пользователей через веб-интерфейс, давайте удалим пользователя созданного через консоль в Разделе 6.3.5. Простейшим способом сделать это является очистка базы данных с помощью Rake-задачи db:reset:
$ bundle exec rake db:reset
После очистки базы данных на некоторых системах также необходимо заново подготовить тестовую базу данных:
$ bundle exec rake test:prepare
Наконец, на некоторых системах вам может потребоваться перезагрузка сервера для того чтобы изменения вступили в силу.8Странно, не правда ли? Я тоже этого не понимаю.
Тесты для регистрации пользователя
До появления мощных веб-фреймворков с полным набором тестировочных средств, тестирование часто доставляло много хлопот. Например, для тестирования страницы регистрации вручную, нам бы пришлось посещать страницу в браузере, затем заполнять форму различными валидными и невалидными данными и проверять что в каждом случае поведение приложения соответствует ожидаемому. Кроме того, нам бы пришлось проделывать это каждый раз при изменении приложения. С помощью RSpec и Capybara мы сможем написать выразительные тесты, которые автоматизируют задачи, которые раньше делались вручную.
Мы уже видели как Capybara поддерживает интуитивно понятный синтаксис веб-навигации. До сих пор мы в основном использовали visit для посещения конкретных страниц, но Capybara может гораздо больше, включая заполнение полей, вроде тех что мы видели на рис. 7.11 и кликанья по кнопке. Синтаксис выглядит примерно так:
visit signup_path fill_in "Name", with: "Example User" . . . click_button "Create my account"
Сейчас нашей целью является написание тестов для правильного поведения при предоставлении невалидной и валидной регистрационной информации. Поскольку эти тесты довольно продвинутые, мы будем писать их постепенно. Если вам интересно посмотреть как они работают (включая файл в который они должны быть помещены), вы можете перейти непосредственно к Листингу 7.16.
Наша первая задача это тестирование провальной отправки регистрационной формы, и мы можем симулировать отправку невалидных данных посетив страницу и кликнув по кнопке с помощью click_button:
visit signup_path click_button "Create my account"
Это эквивалентно посещению страницы регистрации и отправке формы незаполненной регистрационной информацией. Аналогично, для симуляции отправки валидных данных, мы заполняем форму валидными данными с помощью fill_in:
visit signup_path fill_in "Name", with: "Example User" fill_in "Email", with: "user@example.com" fill_in "Password", with: "foobar" fill_in "Confirmation", with: "foobar" click_button "Create my account"
Эти тесты проверяют что поведение приложения после клика по кнопке "Create my account" соответствует нашим ожиданиям: создается новый пользователь если информация валидна и не создается новый пользователь в случае когда она невалидна. Для того чтобы сделать это мы проверяем количество пользователей и наши тесты будут использовать метод count, доступный для каждого класса Active Record, включая User:
$ rails console >> User.count => 0
Здесь User.count является 0 поскольку мы очистили базу данных в начале этого раздела.
При отправке невалидных данных, мы ожидаем что количество пользователей не будет изменено; при отправке валидных данных мы ожидаем что оно изменится на 1. Мы можем выразить это в RSpec скомбинировав метод expect с методом to или методом not_to. Мы начнем со случая с невалидными данными поскольку он проще; мы посетим страницу регистрации и кликнем по кнопке, и мы ожидаем что это не изменит количества пользователей:
visit signup_path
expect { click_button "Create my account" }.not_to change(User, :count)Обратите внимание: expect обворачивает click_button в блок (Раздел 4.3.2). Что необходимо для работы метода change, который принимает в качестве аргумента объект и символ, а затем вычисляет результат вызова этого символа в качестве метода на объекте до и после блока. Другими словами, код
expect { click_button "Create my account" }.not_to change(User, :count)вычисляет
User.count
до и после выполнения
click_button "Create my account"
В данном случае, мы хотим чтобы данный код не изменял количества, что мы можем выразить с помощью метода not_to. В результате, закрыв клик по кнопке в блоке, мы можем заменить
initial = User.count click_button "Create my account" final = User.count expect(initial).to eq final
на одну строку
expect { click_button "Create my account" }.not_to change(User, :count)которая читается как естественный язык и является намного более компактной (eq это RSpec метод для тестирования на равенство).
Случай с валидными данными аналогичен, но вместо проверки того, что количество пользователей не изменилось, мы проверяем что клик по кнопке изменяет их количество на 1:
visit signup_path fill_in "Name", with: "Example User" fill_in "Email", with: "user@example.com" fill_in "Password", with: "foobar" fill_in "Confirmation", with: "foobar" expect do click_button "Create my account" end.to change(User, :count).by(1)
Здесь используется метод to поскольку мы ожидаем что клик по кнопке (вкупе с валидными данными) изменит количество пользователей на единицу.
Комбинирование двух случаев с соответствующими блоками describe и выталкивание общего кода в блоки before, приводит нас к хорошим базовым тестам для регистрации пользователей, как это показано в Листинге 7.16. Здесь мы вынесли общий текст для кнопки регистрации в переменную submit, которую мы определили с помощью метода let.
require 'spec_helper'
describe "User pages" do
subject { page }
.
.
.
describe "signup page" do
before { visit signup_path }
let(:submit) { "Create my account" }
describe "with invalid information" do
it "should not create a user" do
expect { click_button submit }.not_to change(User, :count)
end
end
describe "with valid information" do
before do
fill_in "Name", with: "Example User"
fill_in "Email", with: "user@example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
end
end
end
Листинг
7.16.
Хорошие базовые тесты для регистрации пользователей. spec/requests/user_pages_spec.rb
Мы будем добавлять тесты по мере надобности в последующих разделах, но базовые тесты в Листинге 7.16 уже покрывают внушительный объем функционала. Для того чтобы получить их прохождение, мы должны создать страницу регистрации с необходимым минимумом правильных элементов, предопределить направление пользователя в правильное место после отправки формы и создание нового пользователя в базе данных только в случае если полученная информация валидна.
Конечно же, в этой точке тесты должны быть провальными:
$ bundle exec rspec spec/




