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

Rails Console

Наверное, все разработчики на Rails используют консоль, или хотя бы слышали о ней. Запускается она из папки проекта:

./script/console

На самом деле, консоль Rails — не что иное, как простой irb (Interactive Ruby — интерактивный интерпретатор Руби), в который подгружено окружение нашего проекта. В основном консоль используется для отладки приложений.

Режим “песочница”

Если нам нужно проверить что-то на рабочей базе, либо при наших дейсвиях возможно сильно испортить существующие данные, нам нужна возможность отката всех наших действий после выхода из консоли. И она у нас есть! Это и называется режимом “песочница” и включается он указанием параметра —sandbox при запуске консоли:

./script/console --sandbox

Читабельный вывод атрибутов модели

Как известно, для вывода на консоль содержимого какого-либо объекта в irb используется метод inspect. При выводе коллекции моделей это не сильно помогает, так как все атрибуты начинают рябить в глазах. Можно упростить себе жизнь, используя метод “y”. Запись

y my_model

Эквивалентна

puts my_model.to_yaml

То есть мы получаем модель, сериализованную в YAML, что заметно улучшает читабельность.
Если же вас не устраивает и это, то нично не мешает переопределить метод inspect для ActiveRecord. Сделать это можно в файлике ~/.irbrc (если его нет — нужно создать). Это файл выполняется каждый раз при запуске irb и содержит обычный руби-код. То есть нам нужно поместить туда что-то вроде

class ActiveRecord::Base def inspect # тут ваш код end end

Лог в консоль

Иногда нужно знать какие запросы к базе данных делаются при вызове того или иного метода (особенно полезно при обучении и отладке кода). Как известно, выводом всяческой информации заведует экземпляр класса Logger, находящийся в классе ActiveRecord::Base. Следовательно,

ActiveRecord::Base.logger = Logger.new(STDOUT)

даёт нам желаемый результат. Можем при необходимости также положить эту строчку в ~/.irbrc

Mass assignment

Очень удобно где-то в контроллере далать что-то подобное:

@my_model = MyModel.create params[:my_model]

Собственно, это и называется mass assignment — “множественное присвоение”. Всё хорошо. Но рассмотрим такую ситуацию: у нас в модели есть поля, которые могут устанавливаться только в определённых ситуациях, например, есть модель Post, у которой могут быть комментарии и рейтинг (помимо, естественно, тела и заголовка). И есть контроллер PostsController, у которого в экшене new написано что-то вроде

@post = Post.create params[:post]

Тогда кто-то с нехорошими намерениями может сделать ручками запрос на нашу страничку и спокойно поставить любой рейтинг посту. Более того, он сможет добавлять комментарии к посту. Как же нам этого избежать? Неужели, придётся в каждом месте писать что-то типа

@my_model = MyModel.new @my_model.attr1 = params[:my_model][:attr1] @my_model.attr2 = params[:my_model][:attr2] # и ещё 25 параметров

К счастью, нет. В ActiveRecord есть два замечательных метода: attr_protected и attr_accessible. Первый защищает переданные ему атрибуты модели от mass assignment, а второй, наоборот, принимает список атрибутов, которые могут быть присвоены через mass assignment.
Проще говоря, если мы пишем в модели

attr_protected :attr1, :attr2

То при выполнении строки

@my_model = MyModel.create { :attr1 => 1, :attr2 => 2, :attr3 => 3 }

Значения атрибутов attr1 и attr2 будут игнорированы. А если напишем так:

attr_accessible :attr3

То присвоить через mass_assignment можно будет только attr3.

ActiveRecord#find

В большинстве случаев ограничиваюсь

MyModel.find :all, :conditions => { :filed1 => val1 }

или

MyModel.find :all, :conditions => ['field1 = ? or field1 = ?', val1, val2]

Но недавно открыл для себя такую интересную возможность: к примеру, нам нужно сгруппировать модели по какому-то полю. Делается это примерно так:

MyModel.find :all, :group => 'group_field'

А как быть, если нам нужно не только группировать, но и знать, к примеру, количество сгруппированных записей? Оказыватся, при выполнении кода:

MyModel.find :all, :group => 'group_field', :select => '*, count(*) as group_size'

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

MyModel.find(:all, :group => 'group_field', :select => '*, count(*) as group_size').each do |my_model| my_model.group_size # => вернёт вам размер группы end