Опубликован: 27.01.2016 | Уровень: для всех | Доступ: платный
Лекция 7:

Регистрация

Строгие параметры

В Разделе 4.4.5 мы коротко упомянули об идее массового назначения, которая подразумевает инициализацию Ruby-переменной с хэшем значений:

@user = User.new(params[:user])    # Not the final implementation!

Комментарий включенный в Листинг 7.21 и воспроизведенный выше указывает на то что это не финальная реализация. Причина по которой иниацилизация всего хэша params является очень опасной затеей заключается в том что это приводит к передаче в User.new всех данных отправленных пользователем. В частности, предположим что, в дополнение к текущим атрибутам, модель User включает в себя атрибут admin используемый для идентификации пользователей-администраторов сайта. (Мы реализуем именно такой атрибут в Разделе 9.4.1.) Способом присвоить этому атрибуту значение true является передача значения admin=’1’ как части params[:user], этого легко достичь с помощью такого консольного HTTP клиента как curl. Таким образом, передавая весь хэш params в User.new, мы тем самым позволяем любому пользователю сайта получить права администратора включив admin=’1’ в веб-запрос.

Для решения этой проблемы предыдущие версии Rails использовали метод attr_accessible на уровне модели, но в Rails 4.0 более предпочтительной является техника с использованием так называемых строгих параметров на уровне контроллера. Это позваляет нам прописать какие именно параметры являются обязательными, а какие разрешенными. К тому же, передача чистого хэша params (как это было показано выше) приведет к ошибке, т.е. теперь Rails приложения невосприимчивы к уязвимостям массового назначиния по умолчанию.

В данном случае мы хотим требовать от хэша params наличие атрибута :user и мы хотим позволить наличие атрибутов name, email, password и password_confirmation (но только их). Мы можем достигнуть этого следующим образом:

params.require(:user).permit(:name, :email, :password, :password_confirmation)

Этот код вернет версию хэша params содержащего только разрешенные атрибуты (при этом будет вызвана ошибка если отсутствует атрибут :user).

Для облегчения использования этих параметров, обычно вводят вспомогательный метод с названием user_params который возвращает соответствующий инициализационных хэш используемый вместо params[:user]:

@user = User.new(user_params)

Поскольку user_params будет использоваться только внутри контроллера Users и нет никакой надобности открывать к нему доступ внешним пользователям через веб, мы сделаем его приватным используя ключевое слово private, как это показано в Листинге 7.22. (Мы более детально обсудим private в Разделе 8.2.1.)

class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      # Handle a successful save.
    else
      render 'new'
    end
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end
end
Листинг 7.22. Использование строгих параметров в действии create. app/controllers/users_controller.rb

С кодом из Листинге 7.22 тесты отправки невалидных данных должны пройти:

$ bundle exec rspec spec/requests/user_pages_spec.rb \
> -e "signup with invalid information"

Сообщения об ошибках при регистрации

В качестве финального шага в реализации провального создания пользователя мы добавим полезные сообщения об ошибках для указания на проблемы которые помешали регистрации. Rails автоматически предоставляет такие сообщения, основываясь на валидациях модели User. Рассмотрим, например, попытку сохранения пользователя с неправильным адресом электронной почты и коротким паролем:

$ rails console
>> user = User.new(name: "Foo Bar", email: "foo@invalid",
?>                 password: "dude", password_confirmation: "dude")
>> user.save
=> false
>> user.errors.full_messages
=> ["Email is invalid", "Password is too short (minimum is 6 characters)"]

Здесь объект errors.full_messages (который мы видели кратко в Разделе 6.2.2) содержит массив сообщений об ошибках.

Как и в консольной сессии выше, сбой сохранения в Листинге 7.21 генерирует список сообщений об ошибках, связанных с объектом @user. Для отображения сообщения в браузере, мы рендерим партиал error_messages на странице user new как это показано в Листинге 7.23. (Написание теста на сообщения об ошибках это хорошая идея и мы оставим эти тесты в качестве упражнения; см. Раздел 7.6.) Стоит отметить что этот партиал сообщений об ошибках лишь первая попытка; конечная версия представлена в Разделе 10.3.2.

<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="span6 offset3">
    <%= form_for(@user) do |f| %>
      <%= render 'shared/error_messages' %>
      .
      .
      .
    <% end %>
  </div>
</div>
Листинг 7.23. Код для отображения сообщений об ошибках в форме регистрации. app/views/users/new.html.erb

Заметим здесь, что мы render партиал ’shared/error_messages’; что отражает общую конвенцию Rails, которая предписывает размещение частичных шаблонов которые мы планируем использовать во многих контроллерах в специально отведенном каталоге shared/. Это означает, что мы должны создать этот новый каталог вместе с файлом партиала _error_messages.html.erb. Сам партиал представлен в Листинге 7.24.

<% if @user.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-error">
      The form contains <%= pluralize(@user.errors.count, "error") %>.
    </div>
    <ul>
    <% @user.errors.full_messages.each do |msg| %>
      <li>* <%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>
Листинг 7.24. Партиал для отображения сообщений об ошибках отправки формы регистрации. app/views/shared/_error_messages.html.erb

Этот частичный шаблон вводит несколько новых Rails и Ruby конструкций, в том числе два метода для объекта ошибок Rails. Первый метод это count, который просто возвращает количество ошибок:

>> user.errors.count
=> 2

Другой новый метод это any?, который (совместно с empty?) является одним из пары взаимодополняющих методов:

>> user.errors.empty?
=> false
>> user.errors.any?
=> true

Мы видим здесь что метод empty?, который мы впервые увидели в Разделе 4.2.3 в контексте строк, также работает на объекте ошибок Rails, возваращая true для пустого объекта и false в противном случае. Метод any? это просто противоположность empty?, возвращающая true если существует какой-нибудь элемент и false в противном случае. (Кстати, все эти методы — count, empty? и any? — работают и на массивах Ruby. Мы найдем хорошее применение этому факту в Разделе 10.2.)

Другой новой идеей является текстовый хелпер pluralize. По умолчанию, он недоступен в консоли, но мы можем явно его включить через модуль ActionView::Helpers::TextHelper:9Я выяснил это путем поиска pluralize в Rails API

>> include ActionView::Helpers::TextHelper
>> pluralize(1, "error")
=> "1 error"
>> pluralize(5, "error")
=> "5 errors"

Мы видим здесь, что pluralize принимает целочисленный аргумент и возвращает число с правильной версией множественного числа его второго аргумента. В основе этого метода лежит мощный инфлектор, который знает как преобразовать во множественное число огромное количество слов (в том числе, многие с неправильным множественным числом):

>> pluralize(2, "woman")
=> "2 women"
>> pluralize(3, "erratum")
=> "3 errata"

В результате код

<%= pluralize(@user.errors.count, "error") %>

возвращает "0 errors", "1 error", "2 errors" и т.д., в зависимости от количества ошибок, тем самым позволяя нам избежать грамматически неверных фраз вроде "1 errors".

Обратите внимание: Листинг 7.24 включает CSS id error_explanation для использования в стилизации сообщений об ошибках. (Напомним из Раздела 5.1.2 что CSS использует знак решетки # для стилизации id.) Кроме того, Rails автоматически помещает поля с ошибками в divы с CSS классом field_with_errors. Эти метки затем позволят нам отредактироваь стиль сообщений об ошибках с SCSS показанным в Листинге 7.25, который использует Sass функцию @extend для включения функциональности двух классов Bootstrap control-group и error. В результате чего, при провальной регистрации сообщения об ошибках окружены красным как это показано на рис. 7.17. Поскольку сообщения генерируются валидациями модели, они автоматически изменятся, если вы когда-нибудь поменяете свое мнение о, скажем, формате адресов электронной почты или минимальной длине паролей.

.
.
.

/* forms */
.
.
.
#error_explanation {
  color: #f00;
  ul {
    list-style: none;
    margin: 0 0 18px 0;
  }
}

.field_with_errors {
  @extend .control-group;
  @extend .error;
}
Листинг 7.25. CSS для стилизации сообщений об ошибках. app/assets/stylesheets/custom.css.scss
Сбой регистрации с сообщениями об ошибках.

Рис. 7.17. Сбой регистрации с сообщениями об ошибках.

Для того чтобы увидеть результаты нашей работы в этом разделе, мы повторим шаги из теста провальной регистрации Листинга 7.16 посетив страницу регистрации и кликнув по "Sign up" с пустыми полями ввода. Результат показан на рис. 7.18. Как вы догадываетесь, глядя на рабочую страницу, в этой точке соответствующий тест тоже должен пройти:

$ bundle exec rspec spec/requests/user_pages_spec.rb \

> -e "signup with invalid information"
Результат посещения /signup и клика по “Create my account”.

Рис. 7.18. Результат посещения /signup и клика по “Create my account”.
Вадим Обозин
Вадим Обозин

Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора?

Акбар Ахвердов
Акбар Ахвердов
Россия, г. Москва
Артём Зайцев
Артём Зайцев
Украина, ДНР