|
Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На 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/



