Обзор новинок в 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
    Поделиться публикацией

    Комментарии 26

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

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

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

      region = params[:state] || params[:country] || 'US'
        +1
        Меня это тоже насторожило. Попытался найти пояснение, зачем нужен 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
          сейчас дополню, спасибо)
            0
            Автор, добавьте пояснение по этому поводу в статью. Иначе любой Рубист сделает стойку на это ;)
            +2
            Кажется все после этого идут в rdoc и внимательно читают, зачем нужен этот presence…
              0
              Я так понял, что это и есть решение избыточности кода а-ля: a = b if b.present?
              То есть теперь можно писать a = b.presence или я опять что-то путаю?
                0
                видимо путаешь) потому что если b не будет, то он вернет nil и a будет равно nil.
                  +1
                  Блин, меня достало уже писать так (как выше), особенно если конструкции сложные (и слова длинные). Приходится одно и то же дважды писать: сначала при присвоении, а потом — при проверке. Отстой ;/
                    0
                    ДА ДА ДА. Например @theme.logo = params[:theme][:logo] if params[:theme][:logo]
                      0
                      ну так presence для этого и сделан, разве нет?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                      Самое читаемое