Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора? |
Обновление, демонстрация и удаление пользователей
Провальное редактирование
В этом разделе мы обработаем случай провального редактирования и получим прохождение теста сообщения об ошибке Листинг 9.1. Код приложения создает действие update которое использует update_attributes (Раздел 6.1.5) для обновления пользователя на основе отправленного хэша params, как это показано в Листинге 9.8. С невалидной информацией, попытка обновления вернет false и ветка else заново отрендерит страницу редактирования. Мы видели этот способ ранее; структура очень похожа на первую версию действия create (Листинге 7.21).
class UsersController < ApplicationController . . . def edit @user = User.find(params[:id]) end def update @user = User.find(params[:id]) if @user.update_attributes(user_params) # Handle a successful update. else render 'edit' end end . . . endЛистинг 9.8. Начальное действие update контроллера Users. app/controllers/users_controller.rb
Обратите внимание на использование user_params в вызове update_attributes, который использует строгие параметры для предотвращения уязвимости массового назначения (как было описано в Разделе 7.3.2).
Получившееся в результате сообщение об ошибке ( рис. 9.3) - именно то что нам нужно для прохождения теста, что вам следует проверить запустив набор тестов:
$ bundle exec rspec spec/
Успешное редактирование
Теперь пришло время заставить работать форму редактирования. Редактирование профильных изображений уже работает поскольку мы переложили загрузку изображений на Gravatar; мы можем отредактировать граватар, кликнув по ссылке "change", показанной на рис. 9.2, как это показано на рис. 9.4. Давайте заставим работать остальной функционал редактирования пользователя.
Тесты для update действия похожи на аналогичные для create действия. Листинг 9.9 показывает как использовать Capybara для заполнения полей формы валидной информацией, а затем тестирует, что результирующее поведение корректно. Это большой кусок кода; посмотрим, сможете ли вы проработать его, опираясь на тесты из Главы 7.
require 'spec_helper' describe "User pages" do . . . describe "edit" do let(:user) { FactoryGirl.create(:user) } before do sign_in user visit edit_user_path(user) end . . . describe "with valid information" do let(:new_name) { "New Name" } let(:new_email) { "new@example.com" } before do fill_in "Name", with: new_name fill_in "Email", with: new_email fill_in "Password", with: user.password fill_in "Confirm Password", with: user.password click_button "Save changes" end it { should have_title(new_name) } it { should have_selector('div.alert.alert-success') } it { should have_link('Sign out', href: signout_path) } specify { expect(user.reload.name).to eq new_name } specify { expect(user.reload.email).to eq new_email } end end endЛистинг 9.9. Тесты для update действия контроллера Users. spec/requests/user_pages_spec.rb
Обратите внимание на то что Листинг 9.9 добавляет метод sign_in из Листинга 9.6 в блок before, что необходимо для прохождения тестов ссылки "Sign out" и также предполагает защиту действия edit от невошедших пользователей (Раздел 9.2.1).
Единственной новинкой в Листинг 9.9 является метод reload, который появляется в тесте для изменения атрибутов пользователя:
specify { expect(user.reload.name).to eq new_name } specify { expect(user.reload.email).to eq new_email }
Этот код перезагружает user переменную из (тестовой) базы данных используя user.reload, а затем проверяет, что новые имя пользователя и email совпадают с новыми значениями.
Действие update, необходимое для прохождения тестов в Листинге 9.9 аналогично финальной форме create действия (Листин 8.27), как видно в Листинг 9.10. Единственное что он делает, это добавляет
flash[:success] = "Profile updated" redirect_to @user
к коду в Листинг 9.8.
class UsersController < ApplicationController . . . def update @user = User.find(params[:id]) if @user.update_attributes(user_params) flash[:success] = "Profile updated" redirect_to @user else render 'edit' end end . . . endЛистинг 9.10. Дейcтвие update контроллера Users. app/controllers/users_controller.rb
Обратите внимание, что сейчас каждое редактирование требует от пользователя повторного подтверждения пароля (как следует из пустого текстового поля подтверждения на рис. 9.2), что делает обновление более безопасным, но вызывающим незначительное раздражение.
С кодом из этого раздела, страница редактирования пользователя должна работать, что вы можете проверить, перезапустив набор тестов, который в данный момент должен быть полностью зеленым:
$ bundle exec rspec spec/
Авторизация
Одним из приятных эффектов построения механизма аутентификации в Главе 8 является то, что мы теперь готовы к реализации авторизации: аутентификация позволяет нам идентифицировать пользователей нашего сайта, а авторизация позволяет нам контролировать предоставляемые им возможности.
Хотя edit и update действия из Раздела 9.1 функционально завершены, они страдают от нелепой бреши в безопасности: они позволяют любым (даже незарегистрированным) пользователям иметь доступ к любому действию, и любой зарегистрированный пользователь может изменять информацию любого другого пользователя. В этом разделе мы реализуем модель безопасности, которая будет требовать от пользователей входа в систему и будет предотвращать обновление любой информации, кроме их собственной. Незарегистрированные пользователи, пытающиеся получить доступ к защищенным страницам будут перенаправлены на страницу входа с полезным сообщением, как это показано на рис. 9.5.
Требование входа пользователей
Поскольку ограничения безопасности для edit и update действий идентичны, мы будем обрабатывать их в одном RSpec describe блоке. Начав с требования входа, наши первоначальные тесты затем проверяют, что не вошедшие пользователи, пытающиеся получить доступ к какому либо из действий, просто перенаправляеются на страницу входа, как показано в Листинге 9.11.
require 'spec_helper' describe "Authentication" do . . . describe "authorization" do describe "for non-signed-in users" do let(:user) { FactoryGirl.create(:user) } describe "in the Users controller" do describe "visiting the edit page" do before { visit edit_user_path(user) } it { should have_title('Sign in') } end describe "submitting to the update action" do before { patch user_path(user) } specify { expect(response).to redirect_to(signin_path) } end end end end endЛистинг 9.11. Тестирование того, что edit и update действия защищены. spec/requests/authentication_pages_spec.rb
Код в Листинге 9.11 вводит второй способ, отличающийся от метода visit предоставляемого Capybara, для доступа к действию контроллера: выдавая соответствующий HTTP запрос непосредственно, в данном случае, с помощью метода patch для выдачи запроса PATCH:
describe "submitting to the update action" do before { patch user_path(user) } specify { expect(response).to redirect_to(signin_path) } end
Это выдает запрос PATCH непосредственно к /users/1, который направляет к update действию контроллера Users (Таблица 7.1, лекция 7). Это необходимо из-за того, что браузер не может посетить непосредственно само действие update — он может лишь попасть туда через отправку формы редактирования — так что Capybara тоже не может этого сделать. Но посещение страницы редактирования тестирует только авторизацию для действия edit, но не для update. В результате, единственный способ как следует протестировать авторизацию для самого действия update это выдать непосредственный запрос. (Как вы можете догадаться, в дополнение к patch Rails тесты поддерживают также get, post, и delete.)
При использовании одного из способов непосредственной выдачи HTTP запросов, мы получаем доступ к низкоуровневому объекту response. В отличие от объекта Capybara page, response позволяет нам тестировать сам ответ сервера, в данном случае, проверяя что действие update отвечает переадресацией на страницу входа:
specify { expect(response).to redirect_to(signin_path) }
Авторизационный код приложения использует предфильтр, который использует before_action команду для указания конкретному методу быть вызванным до данных действий. (Раньше команда для предфильтров называлась before_filter, но ядро разработчиков Rails решило переименовать его для того чтобы подчеркнуть что фильтр исполняется перед заданным действием контроллера.) Для того чтобы требовать от пользователей входа, мы определяем signed_in_user метод и вызываем его с помощью before_action :signed_in_user, как это показано в Листинге 9.12.
class UsersController < ApplicationController before_action :signed_in_user, only: [:edit, :update] . . . private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end # Before filters def signed_in_user redirect_to signin_url, notice: "Please sign in." unless signed_in? end endЛистинг 9.12. Добавление предфильтра signed_in_user. app/controllers/users_controller.rb
По умолчанию, предфильтры применяются ко всем действиям контроллера, поэтому мы ограничиваем действие фильтра только :edit и :update действиями, посредством передачи соответствующего хэша опций :only.
Обратите внимание, что Листинге 9.12 использует сокращение для установки flash[:notice] передавая хэш опций в функцию redirect_to. Код в Листинге 9.12 эквивалентен более многословному
unless signed_in? flash[:notice] = "Please sign in." redirect_to signin_url end
(К сожалению данная конструкция не работает для ключей :error и :success.)
Совместно с :success и :error, ключ :notice завершает наш триумвират отстиленных flash, поддерживаемых Bootstrap CSS фреймворком. Выйдя из сайта и попытавшись получить доступ к странице редактирования пользователя /users/1/edit, мы можем увидеть результирующий желтый блок "notice", как это показано на рис. 9.6.
В этой точке наш набор тестов должен решительно позеленеть:
$ bundle exec rspec spec/