Обзор новинок в Ruby on Rails

Для начала мы обратим внимание на Rails 2.3.8 — многие из вас слышали о нем, но не все знают, что конкретного там поменялось. К слову, основные нововведения произошли в версии 2.3.6, а .7 и .8 версии лишь исправляли ошибки.

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

1. Переработан флеш, рекомендуется использовать два базовых типа — alert и notice. А еще его можно использовать с редиректом.
Вот как было:
flash[:notice] = 'Статья добавлена'
redirect_to @article
И как можно писать теперь:
redirect_to(@article, :notice => 'Статья добавлена')
Стоит учесть, что если у вас есть свой тип флеша, например, flash[:error], то добавить его к редиректу не получится.

2. Для пользователей базы MySQL добавлена пара очень полезных нововведений:
 — Теперь можно указывать длину индекса:
add_index(:accounts, :name, :name => 'by_name', :length => 10)
# => CREATE INDEX by_name ON accounts(name(10))
 
add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length =>
 {:name => 10, :surname => 15})
# => CREATE INDEX by_name_surname ON accounts(name(10), surname(15))

 — Методы add_ и change_column теперь поддерживают параметр :first => true, и :after => :column. Раньше оно добавляло новые колонки в конец таблицы, а теперь можно явно указать куда их ставить:
add_column :comments, :post_id, :integer, :first => true

3. Если вы используете тестирование с помощью стандартных тестов рельсов, то вам могут помочь новые методы assert_blank and assert_present. Первый сравнивает объект с пустой строкой, а второй проверяет его наличие.

4. Добавлена функция объекта Object#presence, которая возвращает сам объект, если он существует и не является пустой строкой, в ином случае возвращает nil. И это очень удобно для выбора одного из различных параметров (любители синтаксического сахара ликуют):
region = params[:state].presence || params[:country].presence || 'US'

5. К типу Enumerable добавлен метод exclude?, который является противополножностью метода include?, и возвращает true, если указанный объект не содержит элемента 
array = ['foo', 'bar']
array.exclude?('boo')  # => true
array.exclude?('bar')  # => false

Также не стоит упускать из внимания несколько интересных гемов.

Sentient User [link]


Назначение его довольно простое — он позволяет использовать current_user в моделях. Кто-то скажет, что в моделях не стоит мешать методы контроллеров, но лично мне этот гем был очень полезен, когда я прописывал различные валидации. Использовать его очень просто — достаточно прописать «include SentientController» в вашем ApplicationController'e. Не стоит забывать, что если ваш модуль авторизации не поддерживает метод current_user, то вам стоит создать его самостоятельно. На таких популярных механизмах как Restful Authentication и Authlogic проблем быть не должно.

Request Rate Limiter [link]


Если ваше приложение уже не такое маленькое как раньше, и количество пользователей постоянно растет — вы должны подумать о безопасности. Этот плагин позволит защититься от слишком частых запросов, которые могут сделать не очень доброжелательные юзеры (или возможные конкуренты). Использовать не сложнее, чем предыдущий:
# config/application.rb
require 'rack/throttle'

class Application < Rails::Application
  config.middleware.use Rack::Throttle::Interval
end

Затем прописываем ограничения на запросы:
use Rack::Throttle::Daily,    :max => 1000  # количество запросов за день
use Rack::Throttle::Hourly,   :max => 100   # количество запросов за час
use Rack::Throttle::Interval, :min => 3.0   # минимальный интервал между запросами

Также можно указать, какое хранилище использовать — поддерживаются GDBM, Memcached и Redis.
Однако доступен этот плагин лишь в Rails 3.0+, так что актуален он будет лишь в будущем, или если вы разрабатываете приложение на бета-версии рельсов.

CanCan [link]


Это замечательный способ ограничения прав юзеров на вашем сайте.
Первое, что нам надо сделать, это создать класс ability, который мы сохраним в models/ability.rb
  class Ability
    include CanCan::Ability

    def initialize(user)
      if user.admin?
        can :manage, :all
      else
        can :read, :all
      end
    end
  end

Именно там будут храниться все разрешения.
Затем, можно использовать это во view:
  <% if can? :update, @article %>
    <%= link_to "Edit", edit_article_path(@article) %>
  <% end %>

В контроллере можно использовать метод authorize!, который вызовет исключение, если у юзера не будет прав на действие:
  def show
    @article = Article.find(params[:id])
    authorize! :read, @article
  end

Исключение можно перехватить и обработать, в нашем случае мы уведомляем юзера об отсутствии доступа с помощью флеша и редиректа:
  class ApplicationController < ActionController::Base
    rescue_from CanCan::AccessDenied do |exception|
      # примечание - лучше использовать redirect_to(root_url, :alert => exception.message)
      flash[:error] = exception.message
      redirect_to root_url
    end
  end

Как вы видите — никаких сложностей тут не возникает, и не стоит изобретать велосипед, когда всё хорошее уже придумано за вас.

Использованы материалы с документации описанных плагинов, а сами плагины были найдены на сайте Riding Rails
+48
7 июня 2010, 17:09
27

комментарии (26)

+2
rwz #
плохой пример про presence.

region = params[:state].presence || params[:country].presence || 'US'

ничем не отличается от

region = params[:state] || params[:country] || 'US'
+1
edelweard #
Меня это тоже насторожило. Попытался найти пояснение, зачем нужен presence.
Оказывается, отличие в том, что он проверяет не только равенство nil, но и то, не является ли объект пустым в том или ином смысле.

См. api.rubyonrails.org/classes/Object.html#M000013, комментарий к следующему за ним методу present?(), который вызывается методом presence(), и в особенности api.rubyonrails.org/classes/Object.html#M000011 — rdoc для blank?(), где и описывается логика: «An object is blank if it‘s false, empty, or a whitespace string. For example, "", " ", nil, [], and {} are blank.»
0
Vizakenjack #
сейчас дополню, спасибо)
0
edelweard #
Автор, добавьте пояснение по этому поводу в статью. Иначе любой Рубист сделает стойку на это ;)
+2
cypok #
Кажется все после этого идут в rdoc и внимательно читают, зачем нужен этот presence…
0
ognevsky #
Я так понял, что это и есть решение избыточности кода а-ля: a = b if b.present?
То есть теперь можно писать a = b.presence или я опять что-то путаю?
0
ed_tsech #
видимо путаешь) потому что если b не будет, то он вернет nil и a будет равно nil.
+1
ognevsky #
Блин, меня достало уже писать так (как выше), особенно если конструкции сложные (и слова длинные). Приходится одно и то же дважды писать: сначала при присвоении, а потом — при проверке. Отстой ;/
0
ed_tsech #
ДА ДА ДА. Например @theme.logo = params[:theme][:logo] if params[:theme][:logo]
0
Vizakenjack #
ну так presence для этого и сделан, разве нет?

@theme.logo = params[:theme][:logo].presence

если есть то поставит лого, если нет то будет nil
+2
ed_tsech #
А вот и нет. Допустим у вас была какая то запись в базе. Пришли данные из формы в которых поле лого было пустым. Вы сделали @theme.logo = params[:theme][:logo].presence И получилось что у вас затерлось поле лого в базе, которое по идее обновляться не должно было.
–1
benone #
Наверное, если оно пришло пустое, то так и надо:)
0
edelweard #
Может быть, поможет конструкция «a ||= b»?
0
ed_tsech #
Вы что сударь.
0
edelweard #
А, ну да, здесь присвоение выполнится, если a не существует, а не b. Извиняюсь.
0
ed_tsech #
Если бы вы писали на чем нибудь другом, а не на Ruby, то не отличалось бы! Смотря конечно что вы ожидали бы получить.

В Ruby все объект, как мы знаем. Пустая строка(массив, хэш) такой же объект как и не пустая поэтому и та и другая преобразовываются в true, во многих других языках не так.
Поэтому нам порой приходится проверять объекты на их «пустоту». Метод blank?

present? = !blank?
0
rwz #
{}[:something] == nil — вот что я имел ввиду

(nil || nil || 'US') == 'US'
+1
ed_tsech #
Да, я прекрасно понял.

Но вы ведь поняли что
region = params[:state].presence || params[:country].presence || 'US'
отличается от
region = params[:state] || params[:country] || 'US'

Потому что во втором случае params[:x] может быть только nil или false, а в первом может быть и пустым объектом.

–2
rwz #
Не бывает блин никаких пустых объектов. presence служит для дропанья пустых строк.

("" || "" || «US») == ""

("".presence || "".presence || «US») == «US»

Вот такой пример более синтетический, но дает более правильное представление о presence, чем пример с params.
+2
ed_tsech #
Сударь вы ошибаетесь. Пройдите еще раз по ссылочке и посмотрите что такое presence, blank? и present? и их сорцы.

[] — пустой массив
{} — пустой хэш

[].presence #=> nil
{}.presence #=> nil
false.presence #=> nil
''.presence #=> nil
0
qweewq #
юху! :)
+1
DarwinTenk #
CanCan тоже очень понравился
0
grossu #
Тут еще много по теме вкусного! ruby-toolbox.com/categories/rails_authorization.html
0
heel #
Ребята, ну мы ведь договаривались не писать эти штуки: «Текст подготовлен в редакторе...». Ну что ж вы так?

А за статью спасибо :)
0
Vizakenjack #
не заметил, оно автоматом как-то добавилось — я даже внимания не обратил)
0
edelweard #
После исправления у вас появились номера строк кода. Или они были, просто я их не заметил при первом прочтении? Мне кажется, что их лучше убрать — когда строк всего-то две-три (или даже одна), то они только запутывают.

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