Грядущий релиз Ruby On Rails 4 принесёт с собой множество интересных изменений. У Andy Lindeman есть отличная презентация под названием "Rails 4 Whirlwind Tour" на Vimeo, но не у всех есть 40 минут на просмотр, поэтому я собрал заметки Cliff'а воедино и дополнил их своими комментариями.

Подробности

Rails.queue

На самом деле, это изменение содержит немного кода — по большей части это всего-лишь обертка уровня фреймворка над существующими реализациями очередей. Данное изменение добавляет некий класс с методом #run, для взаимодействия с большинством систем очередей (такими как delayed_job, resque и т.д.). По умолчанию, события в очереди будут просто выполняться синхронно как и остальные запросы. Но это можно будет изменить в файлах окружения, установив переменной config.queue значение async, или, в будущем, delayed_job, resque и т.п. Это позволит легко сменить систему очередей при необходимости.

Примечание: На самом деле, очереди обещают добавить в Rails ближе к релизу 4.1.

strong_parameters

attr_accessible и attr_protected будут убраны из моделей. Логика защиты mass-assignment переменных будет немного отличаться и перемещена в контроллер. Кажется, это хорошее изменение, так как авторизация/аутентификация имеет больше смысла на уровне контроллера. Это также решит проблему, когда есть некий скрипт (или при работе в консоли rails), который изменяет переменные, защищенные через mass-assignment. Это очень логично, так как должна быть возможность делать это, так как никакого пользовательского ввода не было; но текущая реализация защиты от mass-assignment запрещает делать это.

Пример работы со strong_parameters:

class PostsController…

  def create
    @post = Post.create(post_params)
  end

  private

  def post_params
    # params[:post] обязателен, и может содержать только params[:post][:title] и params[:post][:body]
    # Это также очистит запрещенные параметры
    params.require(:post).permit(:title, :body)
  end

end

Более того, в примере выше, при попытке передать запрещенные параметры из контроллера в модель Post, будет возбуждено исключение ActiveModel::ForbiddenAttributesError.

Turbolinks

С включенными tubrbolinks, в браузерах с поддержкой этой технологии, обычные ссылки будут загружаться через ajax, title и body текущего документа будет обновлен из полученных через ajax title и body, а URL будет вручную обновлен через pushstate. Подобную технологию использует jquery-mobile. Плюсы этого - это то, что можно использовать asset pipeline для вывода больших, скомпилированных js и css файлов при первом запросе, а затем просто использовать их в приложении без разбора любых js или css файлов при последующих запросах. Выглядит здорово, но есть вероятность, что это может вызвать некоторые проблемы. Мне пришлось столкнуться с несколькью странными багами в jquery-mobile, которые я смог решить только напрямую указав jquery-mobile, что это обычные, не ajax, ссылки.

cache_digests

Теперь в Rails не будет кэширования страниц и action'ов. В будущем будет возможно использование только кэширования фрагментов во view's приложения. Если Вам требуется кэширование более высокого уровня - используйте Varnish или другой, основанный на http, кеширующий прокси (прим. перев.: например, nginx). Более того, кэш фрагментов теперь будет именоваться в зависимости от хеша его содержимого, поэтому Rails будет автоматически очищать кэш, если Вы изменили запись, так как это автоматически изменяет содержимое кэша, но также и если вы изменили текущую разметку исходного кода фрагмента (чего не было раньше).

ActionController::Live

Теперь есть возможность отдавать данные браузеру в реальном времени. Это позволит получать отдавать страниц по частям или писать в браузер по мере получения данных. Внимание: скорее всего, Вы захотите использовать сервер, подобный puma, для того, чтобы хорошо работать с многопоточностью.

class MyController…

  include ActionController::Live

  def index
    100.times {
      response.stream.write "hello\n"
    }
  end

end

Теперь можно использовать websocket и javascript (или нечто подобное) на фронтенде для обработки данных в режиме реального времени. На самом деле, это могло бы быть в Rails 3.2, но только для HTML, а не для любых данных, таких как строки, json, видео и т.п.

Метод PATCH (REST)

В REST, метод PUT предполагается использовать для передачи полной копии объекта с изменениями. В Rails он обычно используется для передачи лишь нескольких обновленных атрибутов, для чего, по идее, существует метод PATCH. Как и PUT, большинство браузеров его не поддерживают, поэтому он реализован также -- в форме передается параметр _method=PATCH. Метод PATCH будет использоваться по умолчанию для обновлений. PUT будет по прежнему работать.

Relation#all

Post.all использовался для выполнения запроса к базе данных и возвращения массива. Теперь Post.all будет возвращать ActiveRecord::Relation, как другие скопы (scopes). Для выполнения запроса и получения массива возможно использование Post.to_a.

Также, Post.scoped использовался для возврата используемого в текущем скопе (currently scoped) объекта Relation. Теперь, .scoped объявлен устаревшим, вместо него рекоммендуется использовать .all.

Object#id

Ранее, при обращении к nil.id (обычно ошибочном, думая, что переменная будет определена как запись базы данных), вызывалось исключение RuntimeError: Called id for nil, which mistakenly be 4.... Теперь его заменит более верное NoMethodError: undefined method 'id' for nil:NilClass, так как Rails 4 требует ruby 1.9.3, а ruby 1.9.3 более не использует #id.

Эта неразбериха из-за того, что Rails использует "id" как основной ключ записи, что конфликтует с методом, используемым ruby для получения уникального идентификатора объекта. К счастью, теперь всё немного проще, но ведь этого можно было бы избежать гораздо раньше.

Relation#none

Если, по каким-либо причинам, требуется, чтобы следующее звено скоупа не возвращало записей, можно использовать вызов .none на нём. Например, Post.some_scope.none вернет объект ActiveRecord::Relation, с которым можно обращаться как с пустым массивом. Ранее у меня было несколько моделей, где было необходимо так сделать, и мне всегда приходилось определять скоуп или что-нибудь подобное, что добавляло .where("1=0") или что-то в этом духе. Данное новшество –– это именно та вещь, которую не знаешь, как применить, пока однажды она тебе не потребуется.

Relation#first и Relation#last

Вызов .first и .last на Relation теперь будет добавлять ORDER BY id ASC к выполняемому запросу. Ранее, никакая сортировка к возвращаемым записям не применялась, поэтому в Rails 3, если выполнять запрос без явного указания, как сортировать, сортировка выполнялась по усмотрению базы данных, что иногда могло приносить неожиданные результаты.

Плагины Rails

Теперь их нету.

Вы можете включить их вручную, разместив их в директории lib и добавив в инициализатор. Но лучше всего просто использовать gem'ы.

Синтаксис Model.find(:[all|:first:last]) из Rails 2

Объявлен устаревшим.

Relation#references

В Rails 3, если встречалась конструкция вида:

Post.includes(:comments).where("comments.created_at > ?", …)

то подразумевалось, что where-часть содержит условия для таблицы comments, и поэтому Rails должны были реализовать SQL запрос с использованием LEFT OUTER JOIN, чтобы WHERE работал (вместо реализации двух отдельных запросов, одного для постов и второго для комментариев). Теперь Rails больше не будет делать это предположение (фактически, на самом деле, будет, но с предупреджением о том, что функциональность устарела), а требовать напрямую указывать, что WHERE зависит от ассоциированной таблицы с комментариями:

Post.includes(:comments).where("comments.created_at > ?", …).references(:comments)

Динамические методы поиска (Relation#find_)

Старый => Новый:

  • find_all_by_… => where(…)
  • scoped_by_… => where(…)
  • find_last_by_… => where(…).last
  • find_or_initialize_by… => where(…).first_or_initialize
  • find_or_create_by… => where(…).first_or_create
  • find_or_create_by_…! => where(…).first_or_create!

Это большой шаг в сторону единообразия синтаксиса запросов.

Скоупы, вычисляемые при загрузке

Более не поддерживаются.

scope :published, where(:published => true)

…вычислялся сразу же, как только был прочитан файл с моделью (при инициализации Rails). Это очень здорово, но только до тех пор, пока не использовалось нечто вроде:

scope :published, where("published_at <= ?", Time.now)

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

authenticity_token теперь не добавляется в формы по умолчанию

При отправке формы, Rails требует наличия токена аутентификации в параметрах для предотвращения XSS атак. Если ajax-запрос или форма не включала его, rails.js автоматически добавлял его. Впрочем, Rails 3 автоматически включал параметр authenticity_token в формы, поэтому даже при отключенном JavaScript формы работали.

Это вызывало проблемы при использовании кеширования фрагментов в формах, так как токен аутентификации был неверным при повторных запросах, когда форма извлекалась из кеша. Очевидным решением этой проблемы стал отказ от включения в форму токена аутентификации.

Лично я считаю, что это плохая идея. Людей, использующих частичное кеширование форма гораздо меньше, чем людей, для которых важнее работоспособность форм при отключенном JavaScript. Также, если задуматься, то люди, использующие частичное кеширование обычно более опытные разработчики, и могут сами разобраться с проблемой токена аутентификации, чем разработчики начинающего уровня, пытающиеся использовать удаленную отправку форм.

О версиях Rails

Rails 4.0, в основном, объявит функциональность устаревшей, в 4.1 она будет убрана (но большинство будет всё равно доступно с помощью gem'ов). В 5.0 откажутся от поддержки некоторых gem'ов. После выпуска Rails 4.1, версию 3.1 перестанут активно разрабатывать.

Перевод оригинальной статьи от Steve Schwartz специально для «Дельта Зет»

http://www.alfajango.com/blog/rails-4-whats-new/