redirect_to :back

Илья • 22 мая 2008 г.

Оказывается метод redirect_to может принимать аргумент :back.

def destroy
  …
  redirect_to :back
end

Теперь пользователь будет переброшен обратно на ту страницу, с которой пришёл запрос на destroy.

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

Профилируем Rails-приложение

Илья • 8 апреля 2008 г.

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

Читать дальше…

Изучая Lisp, я столкнулся с проблемой поиска подходящих задачек для практики. Хотелось чего-нибудь компактного, интересного и нетривиального.

Как раз для этих целей и был создан сайт Project Euler — решение интересных задач по математике и программированию.

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

Если зарегистрироваться на сайте, то можно отправлять свои ответы на задачи и, таким образом, закреплять свой прогресс. По количеству решённых задач на сайте строится Top-список пользователей.

Вот пример одной из самых простых задач с сайта:

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

Find the sum of all the multiples of 3 or 5 below 1000.

Несколько интересных фактов: всего на сайте зарегистрировано более 20 тыс. программистов, из которых 290 русских и 580 Рубистов. Самый популярный язык программирования C. На втором месте Питон, затем Джава, Хаскель и — сюрприз — Руби на пятом месте.

Издательство Питер наконец-то завершило русский перевод второго издания книги-бестселлера Agile Web Development With Rails.

AWDWR это культовая книга, которая содержит в себе достаточно информации о рельсах, чтобы написать своё первое приложение с нуля. Просто незаменима для новичков и стажёров.

Уже сейчас книжку можно пред-заказать на Озоне, а доставлена она будет в феврале.

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

$ rake -T clear
(in /Users/ilya/work/application)
rake db:sessions:clear   # Clear the sessions table
rake log:clear           # Truncates all *.log files in log/ to zero bytes
rake tmp:cache:clear     # Clears all files and directories in tmp/cache
rake tmp:clear           # Clear session, cache, and socket files from tmp/
rake tmp:pids:clear      # Clears all files in tmp/pids
rake tmp:sessions:clear  # Clears all files in tmp/sessions
rake tmp:sockets:clear   # Clears all files in tmp/sockets

То есть, после rake -T можно написать любое интересуемое вас слово, в данном случае clear. И тогда на экран выведутся только задачи в названии которых это слово встречается.

За открытие спасибо Антонюку Стасу ;)

Upd: Переписал описание и пример. Как оказалось позднее, выводятся не только задачи, у которых именная область совпадает с введённым словом, но и вообще все задачи, в название которых это слово включено.

Приятное обновление error_messages_for

Илья • 14 октября 2007 г.

В Edge Rails и Rails 2.0 теперь можно указывать допонительные параметры методу error messages for, который выводит ошибки валидации во вьюшках:

  1. :header_message По умолчанию: “<count> errors prohibited this <object_name> from being saved”
  2. :message По умолчанию: “There were problems with the following fields:"

<%= error_messages_for :user, 
  :header_message => "#{@user.errors.size} ошибок сохранения этого пользователя", 
  :message => "Следующие поля заполнены не верно:" %>

Так что теперь можно спокойно использовать эту удобную функцию в русских проектах.

Changeset #7870

Не так давно в распоряжении Rails-программистов появился метод form for, который позволяет сэкономить пару строчек кода, во время написания шаблонов форм. Используя этот метод, мы можем превратить такой стандартный шаблон:


<% form_tag users_path do %>
  <%= text_field :user, :name %>
  <%= text_field :user, :email %>
<% end %>

в такой:


<% form_for :user, :url => users_path do |f| %>
  <%= f.text_field, :name %>
  <%= f.text_field, :email %>
<% end %>

Используя form_for, больше нету необходимости указывать более чем один раз кому принадлежит то или иное поле. Это уменьшает количество повторений в вашем коде, то есть делает его более DRY.

Но что делать, если вы хотите использовать сразу несколько моделей в рамках одной формы? В такой ситуации нам на помощь придёт метод fields for, который переопределяет объект, к которому будут принадлежать поля, находящиеся в его блоке.

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


<% form_for :user, :url => users_path do |f| %>
  <%= f.text_field, :name %>
  <%= f.text_field, :email %>

  <% fields_for :credit_card do |cf| %>
   <%= cf.text_field :number %>
   <%= cf.text_field :holder_name %>
  <% end %>

<% end %>

Используя fields_for в примере выше, мы получили возможность описать сразу две модели в одной форме. Теперь в контроллере у нас доступны и params[:user] и params[:credit_card].

Эта тема так же подробно раскрывается в эпизоде 73 от Railscasts.

Безопасность Rails: фильтрация логов

Илья • 25 сентября 2007 г.

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

Сделать это очень просто. Достаточно вставить следующую строку в ApplicationController:


class ApplicationController < ActionController::Base
  filter_parameter_logging :password
end

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

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

Вот как выглядят логи с отфильтрованным параметром :password:

Processing AccountsController#login (for 127.0.0.1 at 2007-09-25 03:56:02) [POST]
  Session ID: 66e0c98d03cc80433cecfe3cf76f65aa
  Parameters: {"login"=>"vasya", "password"=> "[FILTERED]", 
               "password_confirmation"=> "[FILTERED]"}

Берегитесь операторов and, or и not в Ruby

Илья • 21 сентября 2007 г.

С виду безобидные операторы and,  or  и not могут привести к очень загадочным ошибкам в вашем приложении.

Вот две строчки одинакового кода. В первой строке используются операторы ||  и &&, а во второй операторы  or  и and. Как видите, результат не совпадает:

Всё потому-что в таблице приоритетов операторов в Ruby, оператор && стоит выше оператора ||, в то время как операторы and  и  or находятся на одном уровне.

Что самое неприятное, операторы and  и  or стоят по приоритету даже ниже чем оператор =, а это может создать в коде ошибки следующего типа:

По-скольку оператор = имеет больший приоритет, чем оператор  or, руби сначала присваивает значение false переменной value, а затем сравнивает её со значением true. Получается, что вместо значения true в переменной value, мы получаем значение false.

Такой же код, но с оператором || вместо  or:

Теперь всё работает как надо.

Итак, старайтесь заменять операторы and,  or  и not  на &&, ||  и !. Конечно, вторая тройка не такая красивая и читабельная, но читабельность того не стоит.

Один маленький нюанс :counter_cache

Илья • 18 июля 2007 г.

Если вы хотите использовать :counter_cache для своих ассоциаций, не забудьте что поле, в котором будет хранится количество связей, должно иметь дефолтовое значение 0, а не NULL, как это иногда бывает по-стандарту.

class AddCounterCaches < ActiveRecord::Migration
  def self.up
    add_column :posts, :comments_counter, :integer, :default => 0
  end

  def self.down
    remove_column :posts, :comments_size
  end
end

Часто бывает, что вы создаёте новый рельсовый проект, несколько моделей для начала и даже пару миграций, но, ничего не спасёт вас от того, чтобы создать базу данных самостоятельно. Теперь же, достаточно только написать rake db:create и всё готово! А если вы хотите наоборот удалить базу данных, то rake db:drop ваш помощник. Но самое интересное начинается тогда, когда вы хотите полностью пересоздать базу данных и перезапустить для неё миграции — rake db:reset и вуаля!

Итак, три новых рейка для баз данных:

rake db:create
rake db:drop
rake db:reset

Ну и, разумеется, вы можете указывать над какой именно базой данных будут производится действия, указывая переменную RAILS_ENV перед запуском рейка:

RAILS_ENV=test rake db:create

Так же, когда вы используете rake db:reset, вы можете указать до какой версии базу нужно мигрировать:

rake db:reset VERSION=15

Регэкспы для валидаций

Илья • 3 мая 2007 г.

Каждый раз пишу валидации и каждый раз приходится искать в инете регэкспы или писать самому (ну нет…). Спасибо Рику Олсену за наше счастливое детство — теперь для меня эта проблема навсегда снята.

Я откопал в его Мефисто маленький файлик format.rb, в котором описаны все, нужные для валидаций, регэкспы. Теперь этот файлик кочует из одного моего проекта в другой.

Итак, копируем файл в папку lib/ и используем его во время написания валидаций:

Ляпота? :)

Представим что у нас есть модель User со следующими валидациями:

class User < ActiveRecord::Base 

  validates_presence_of :username
  validates_presence_of :email
  validates_presence_of :password 
  validates_length_of :password, :in => 6..12

  attr_accessor :password 

end

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

Решается всё элементарно: добавляем флаг :allow_nil => :true для валидации длины пароля:

class User < ActiveRecord::Base 

  validates_presence_of :username
  validates_presence_of :email
  validates_presence_of :password 
  validates_length_of :password, :in => 6..12, :allow_nil => true

  attr_accessor :password 

end

Теперь, если пользователь не введёт пароль, то сработает только валидация присутствия, а валидация на длину будет молчать как партизан.

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

Update: Поправка опечатки.

Belongs_to :counter_cache

Илья • 24 апреля 2007 г.

Как всё-таки иногда полезно почитать чужой код. Особенно, если его писал Рик Олсен ;)

Ковыряясь в Beast, наткнулся на интересную штуку, которую раньше не замечал:

class Topic < ActiveRecord::Base
  belongs_to :forum, :counter_cache => true
  …

Флаг counter_cache сделан для автоматического сохранения в базе количество ассоциированных объектов. Будет их автоматически наращивать и уменьшать при добавлении и удалении соответственно.

Для того чтобы вся эта красота работала, в таблице бд модели нужно добавить поле с именем типа «#{table_name}_count». Или, если вы хотите собственное название, достаточно просто его прописать вместо true:

class Topic < ActiveRecord::Base
  belongs_to :forum, :counter_cache => :super_puper_counter
  …

Делается это всё для быстроты. Чтобы каждый раз, когда вам нужно узнать сколько у форумов топиков (к примеру), вам не нужно было бы делать SQL-запрос, считая все топики.

Как приятно узнать что-то новое.

Update: добавил информацию о том, как нужно назвать поле в базе данных

Используем символ как Proc

Илья • 29 января 2007 г.

Просто замечательный пример элегантности Руби:

Вместо:
@people.collect{|human| human.name }
Делаем так:
@people.collect(&:name)

Dave Thomas подробно описал как это работает.