RDR3R: Callbacks (коллбеки — обратные вызовы) в Rails
октября 5, 2011 | Published in Ruby on Rails, Ruby on Rails 3 | 7 Comments
Callbacks — колбеки (обратные вызовы) — это вызов методов как реакция на какое-нибудь действие объекта. Коллбеки привязываются к определенным методом и могут быть выполнены до или после вызова метода к которому они прикреплены. Например, когда в Rails вы используете ассоциацию с зависимостью, например такую:
Post has_many :comments, :dependent => :destroy
То после действия destroy для объекта — записи запускается соответствующий after_destroy коллбек, который выполняет удаление всех принадлежащищ Post’у комментариев.
В Rails существуют коллбеки модели и колбеки контроллеров, в Sinatra (Rack) существуют коллбеки обработчиков запроса, даже в RSpec существуют коллбеки before и after для подготовки и удаления данных для спецификаций.
В ActiveRecord существует следующий набор предопределенных коллбеков (рядом с колбеком стоит его номер в порядке выполнения):
1 before_validation — выполняется перед валидацией
1 before_validation_on_create — перед валидацией, но только для новой записи. Deprecated, следует использовать before_validation :on => [:create].
2 after_validation — после валидации
2 after_validation_on_create — после валидации, но только для новой записи. Deprecated, следует использовать after_validation :on => [:create].
3 before_save — перед сохранением
4 before_create — перед созданием
5 after_create — после создания
6 after_save — после сохранения
Чем отличаются, скажем before_save и before_create так это тем,что before_save выполняется при каждом сохранении записи, а before_create только при первом, т.е. для новой записи.
Также сущесвуют коллбеки порядок которых нельза так просто указать:
Удаление записи:
before_destroy
after_destroy
Обновление записи:
before_validation_on_updateDeprecated, следует использовать before_validation :on => [:update].
after_validation_on_updateDeprecated, следует использовать after_validation :on => [:update].
before_update
after_update
Существуют также around-коллбеки, которые совмещают в себе before и after коллбеки. Вот они:
around_save
around_create
around_update
around_destroy
По некоторым коллбекам почти невозможно найти документацию. Оправдать это можно тем, что они используются крайне редко, и вообще не рекомендуются к использованию.
Редко используемые коллбеки:
after_initialize — коллбек выполняется при инициализации экземпляра модели. Этот коллбек нерекомендуется использовать так как after_initialize ухудшает производительность.
after_find — коллбек выполняется при поиске при помощи метода #find, также не рекомендуется использовать из-за ухудшения производительности.
after_touch — выполняется после метода touch который выполняет сохранение записи со значением updated_at установленным в текущий момент времени.
after_commit и after_rollback — коллбеки, что выполняются при успешной и при неудачной транзакции.
Лично мне ни разу не приходилось сталкиваться с использованием этих коллбеков, в смысле, не только я их не использовал, но и не видел их использования в некоторых проектах исходники которых я изучах.
Как использовать коллбеки?
Существует 3 способа использование: декларативный, выполнение блока и переопределение метода. Все их я продемонстрирую на примере after_save. Смысла показывать пример для всех коллбеков не вижу, они абсолютно идентичны, разница только в том, когда они выполняются.
Декларативный способ:
class RubyDev < ActiveRecord::Base after_save :do_something def do_something #do something end end
В декларативном способе в метод after_save мы передаем симво, который потом внутри метода преобразуется в процедуру с вызовом внутри метода с соответствующим символу именем. Этот метод должен быть определен в коде вашей модели.
Выполнение блока кода:
class RubyDev < ActiveRecord::Base after_save { #do something } end
Переопределение метода:
class RubyDev < ActiveRecord::Base def after_save #do something end end
Способ с переопределением метода считается плохим стилем и вроде даже будет deprecated, что значит, что эта возможность должна ищезнуть.
С коллбеками в моделях вроде все понятно, перейдем к коллбекам в контролерах!
В контроллерах коллбеки называются фильтрами, не знаю почему, но могу предположить, что это потому, что они могут «отфильтровывать» некоторый повторяющийся в контроллерах код. В контроллерах имеется всего три коллбека: before_filter, after_filter и around_filter которые выполняются соответственно до, после и до и после выполнения определенного экшена. Рассмотримпростой пример использования:
class RubyDevController < ApplicationController before_filter :my_method def index #do something here end def show #do something else end private def my_method #do something before actions end end
В примере выше мы использовали фильтр before_filter в который через символ передали имя метода, который необходимо выполнить соответственно перед выполнением каждого экшена. Заходит пользователь на …/rubydev/ или на …/rubydev/1, перед любым его запросом к контроллеру будет выполнен метод before_filter.
Для ограничения того, до, после и «вокруг» каких экшенов будет выполняться фильтр, мы можем использовать два параметра:
after_filter :some_method, :except => :index after_filter :some_method, :only => :index
Параметр :except указывает на то, для каких экшенов следует игнорировать текущий коллбек, а параметр :only наоборот, указывает для каких экшенов необходимо применить текущий коллбек.
:except — Все кроме
:only — Только
В коде контроллера, как и в коде модели вы можете использовать сколько угодно коллбеков:
class RubyDevController < ApplicationController before_filter :first before_filter :second, :except => :index before_filter :third, :only => [:index, :show] after_filter :fourth around_filter :fifth, :except => :show #... end
Очень часто фильтры в контроллерах используются для выDRYивания кода контроллера:
class PostsController < ApplicationController before_filter(:only => [:show, :edit, :update, :destroy]) do @post = Post.params[:id] end #... end
В примере выше мы делаем так, что нам больше не нужно в определенных контроллерах делать выборку из БД записи для показа, редактирования и удаления так как эта выборка для всех трех случаев вынесена в before_filter. Более явно демонстрирующий выгоды от фильтров пример использования before_filter — это использования его для проверки того, зарегистрирован ли пользователь, проверки IP-адреса пользователя, прав доступа и т.д.
С тем, как использовать коллбеки в контроллерах и моделях мы разобрались — это достаточно простая и скучная тема. Действительно интересной темой будет то, что Rails предоставляет средства для создания собственных коллбеков! Сейчас мы научимся создавать собственные коллбеки!
В Rails код отвечающий за работу коллбеков находится в классе ActiveSupport::Callbacks для того, чтобы реализовать собственные колбеки, вам необходимо подключить этот класс в ваш код (для ActiveRecord и ActionController он уже подключен).
Пример объявления коллбеков:
require 'active_support/callbacks' class CallbacksExample include ActiveSupport::Callbacks define_callbacks :hello set_callback :hello, :after, :bye, :bye def hello run_callbacks :hello do puts 'Hello!' end end def bye puts 'Bye!' end end CallbacksExample.new.hello #Hello! #Bye! #Bye!
Если метод определен в какой-то библиотеке и вы не хотите лезть в код библиотеки и исправлять метод (что безусловно правильно), то можете воспользоваться таким простым приемом:
def method_from_some_lib run_callbacks :lib_for_some_method { super } end
Нашли в статьи ошибки и неточности, или просто хотите узнать о чем-то более подробно? — Пишите об этом в комментариях.
Лучшая благодарность автору — ваши комментарии!
октября 5, 2011 at 13:23 (#)
Эти методы уже объвлены deprecated. Вместо них следует использовать методы before_validation и after_validation с параметрами.
октября 7, 2011 at 07:56 (#)
Игорь Александров, спасибо за замечание, все исправил.
марта 26, 2012 at 12:17 (#)
а как предотвратить запись в БД через callback? Грубо говоря не выполнились какие нибудь условия
марта 27, 2012 at 16:57 (#)
marat, для этого существует валидация свойств. Например можно проверять передано ли какое-либо свойство, соответствует ли оно регулярному выражению, какую длину имеет и т.д. Также можно собственные валидации создавать.
августа 6, 2012 at 11:49 (#)
А есть ли статья о After_commit — про транзакционные колбеки?
августа 6, 2012 at 19:10 (#)
Alex, нет, но напишу в скором времени.
декабря 11, 2013 at 21:19 (#)
Здравствуйте.
Хотя и ерунда, наверное, но все же:
«… перед любым его запросом к контроллеру будет выполнен метод before_filter» — наверное, опечатка? Должно быть: «… будет выполнен метод my_method»?
Спасибо за статью.