19 необщеизвестных приёмов

Автор оригинала: Питер Купер (Peter Cooper)
  • Перевод
Заметка не новая, но я уверен, что и сейчас не все знают всё нижеперечисленное (Здесь и далее курсив переводчика).

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

Замер скорости в контроллерах — это очень просто. Используйте метод benchmark в любой модели.

User.benchmark("adding and deleting 1000 users") do
	1000.times do
		User.create(:name => 'something')
		x = User.find_by_name('something')
		x.destroy
	end
end

Ваш код, конечно, будет получше ;-) Обычные SQL логи не будут писаться внутри метода benchmark, только результаты.

acts_as_nested_set — почти все (хо-хо) хорошо знакомы с acts_as_tree, но незаметно в рельсах появилось acts_as_nested_set. Это похоже на acts_as_tree, но с удобствами: вы можете выбрать всех детей (и их наследников) одного узла одним запростом. Имеется список методов.
image

Простые коллекции через to_proc — надоело писать post.collect { |p| p.title } или post.select { |p| p.activated? }.collect{ |p| p.title}? С помощью небольшого хака переводим символы в процедуры. Можно писать post.collect(&:title) или post.select(&:activated?).collect(&:title)! Здесь об этом куда больше.

Превращение массивов в предложения — если вам в показе (view) надо вывести список имён, вы берёте массив типа ['Peter', 'Fred', 'Chris'], соединяете запятыми и добавляете «and» перед последним, что могло бы поднадоесть. Метод to_sentence спешит на помощь: names.to_sentence вернёт "Peter, Fred, and Chris". (Да, это не портабельно и англоцентрично. В примечаниях к оригинальной статье об этом больше.)

Отправка файла пользователю — обычно статические файлы передаются по прямому УРЛу в обход рельсового приложения. Тем не менее в ряде ситуаций может быть полезно спрятать расположение файла, особенно если вы посылаете что-либо ценное, наподобии е-книги. Может также требоваться ограничить посылку файлов только залогиненным пользователям. Проблему решает send_file. Файлы передаются по 4096 байтов, так что даже большие файлы систему не затормозят.

Выборка элементов страницы через RJS — поменять элемент в RJS нетрудно, но что если мы не знаем, какой именно элемент надо менять, и хотели бы адресоваться через CSS-запрос? Это возможно с методом select. Например, page.select('#items li').each { |item| item.hide }. Мощная штука!

Проверка существования — при выполнении Model.find(id) мы получим исключение, если элемента «id» не нашлось. Чтобы это избежать, сперва выполняем Model.exists?(id), чтобы узнать, есть ли такой.

Числовые хелперы для частых задач — они нечасто используются, но тем не менее весьма полезны:
number_to_currency(1234567.948) # => $1,234,567.95 или human_size(1234567890) # => 1.1GB или number_with_delimiter(999999999) # => 999,999,999. Есть и другие. (То же замечание относительно локализации.)

Простое тестирование роутинга — test_routing — хелпер, который подменяет умолчательный «routes» в routes.rb для опытов. Например:

with_routing do |set|
  set.draw { set.connect ':controller/:id/:action' }
  assert_equal(
     ['/content/10/show', {}],
     set.generate(:controller => 'content', :id => 10, :action => 'show')
  )
end

Здесь подробнее.

Много интересного о запросеrequest.post? и request.xhr? — популярные способы посмотреть на POST- и аякс-запросы, но есть и другие, не столь известные. Например, request.subdomains вернёт массив поддоменов, который можно использовать при авторизации, request.request_uri даст полный локальный URI, request.host это полное имя хоста, request.method вернёт метод в нижнем регистре, и request.ssl? определит, SSL ли это.

Дальнейшее повышение производительности — по умолчанию рельсы записывают сессии на локальную файловую систему. Многие меняют это на ActiveRecordStore для записи сессий в базу. Ещё более быстрая альтернатива будет Memcached, но его не так лекго установить (и не получится, если сервера чужие, и т.д.). И тем не менее можно сделать быстрее, чем ActiveRecordStore, через использование SQLSessionStore Стефана Кейса (Stefan Kaes). Плагин обходит недостатки ActiveRecordStore через свою SQL технику сохранения сессий.

Кеширование статического контента при старте — если у вас есть данные, которые не меняются от рестарта к рестарту, закешируйте их. Например, это мог бы быть YAML или XML файл в /config-e с настройками приложения, и его можно загружать в константу в environment.rb, ускоряя и упрощая доступ.

Проверка валидности (X)HTML-а — не для всех, но если ваш вывод валиден, то есть шансы, что ваши показы (view) рендерятся правильно. Скот Реймонд (Scott Raymond) разработал хелпер assert_valid_markup, который можно использовать для функционального тестирования.

Более опрятное тестирование HTML-вывода — объединим парсер Hpricot от вая (why) со специальным тестовым расширение, и получим мощные тесты наподобие assert_equal "My title", tag('title') или assert element('body').should_contain('something'). Это идеально подходит для тестирования пользовательских шаблонов. Так или иначе, уж лучше, чем assert_tag!

Запуск длинных процессов отдельно в фоне — есть небольшой фреймворк BackgrounDRbот Эзры Зигмунтовича (Ezra Zygmuntovich), который запускается в виде демона и принимает задачи от рельсового приложения, и выполняет их независимо. Мощная вещь, помогает в рассылке писем, получении УРЛов, и прочего, что может затормозить время выполнения запроса основного приложения. Демо-таск увеличивает переменную на 1, после чего делает sleep на 1 секунду. Далее делаем рельсовый метод, который опрашивает эту переменную, и чувствуем разницу. Здесь больше.

Красивые иды в УРЛах — заменим to_param в вашей модели и вернём что-нибудь вроде "#{id}-#{title.gsub(/[^a-z0-9]+/i, '-')}", для УРЛа вида yoursite.com/posts/show/123-post-title-goes-here.... Куда приятнее для пользователя, и не надо ничего менять в Post.find(params[:id]), так как нецифровые символы будут выкинуты автомагически. Полное объяснение тут. (ссылка битая, похоже)

Выделение кусков функциональности в энжины — все слышали про плагины, но прискорбно мало кто используюет энжины (engines)! Энжины это плагины на стероидах (или на барбитуратах). У них могут быть свои модели, контроллеры и показы, и они могут интегрироваться с любым приложением. Это позволяет выделять общие куски функциональности (логины, управление пользователями, контент-менеджмент и т.п.) в отдельные «движки», которые можно включать в разные проекты за считанные минуты. Скажем нет писанию дурацких логинов! Энжины это круто, но должны быть ещё гораздо круче.

Вычисления — желаете вычислить максимум, минимум, среднее, сумму для данных в таблице? Это возможно, с Calcuations из ActiveRecord. Person.average('age'), Person.maximum(:age, :group => 'last_name'), Order.sum('total'): всё это теперь воплощается. Большинство из этого можно настроить дополнительными опциями, почитайте, если этого ещё нет у вас в коде.

Вывод данных в XML или YAML — не только, чтобы создать .rxml шаблон Builder-a для вывода в XML. У ActiveRecord есть метод to_xml, выводящий результат в XML. Работает как с простыми объектами, так и с целыми таблицами: User.find(:all).to_xml. Инклуды также поддерживаются: Post.find(:all, :include => [:comments]).to_xml. То же самое в YAML, с помощью to_yaml.

Поделиться публикацией

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

    0
    Отправка файла пользователю — обычно статические файлы передаются по прямому УРЛу в обход рельсового приложения. Тем не менее в ряде ситуаций может быть полезно спрятать расположение файла, особенно если вы посылаете что-либо ценное, наподобии е-книги. Может также требоваться ограничить посылку файлов только залогиненным пользователям. Проблему решает send_file. Файлы передаются по 4096 байтов, так что даже большие файлы систему не затормозят.


    К сожалению, это неправда. Когда метод send_file используется в приложении Rails, которое работает под Mongrel (под mod_rails не проверял еще), весь файл считывается-таки в память! Поэтому использование send_file для отправки больших файлов приведет только к головной боли.

    Запуск длинных процессов отдельно в фоне — есть небольшой фреймворк BackgrounDRbот Эзры Зигмунтовича (Ezra Zygmuntovich), который запускается в виде демона и принимает задачи от рельсового приложения, и выполняет их независимо. Мощная вещь, помогает в рассылке писем, получении УРЛов, и прочего, что может затормозить время выполнения запроса основного приложения. Демо-таск увеличивает переменную на 1, после чего делает sleep на 1 секунду. Далее делаем рельсовый метод, который опрашивает эту переменную, и чувствуем разницу.


    Не спорю, вещь хорошая. Но это хорошо, если память сервера позволяет крутится еще рубишному демону. Иногда cron задача и rake task бывает достаточно.

    Выборка элементов страницы через RJS — поменять элемент в RJS нетрудно, но что если мы не знаем, какой именно элемент надо менять, и хотели бы адресоваться через CSS-запрос? Это возможно с методом select. Например, page.select('#items li').each { |item| item.hide }. Мощная штука!


    От RJS начинают уже давно отказываться. Он не гибок.

    Проверка существования — при выполнении Model.find(id) мы получим исключение, если элемента «id» не нашлось. Чтобы это избежать, сперва выполняем Model.exists?(id), чтобы узнать, есть ли такой.


    Исключение будет только на dev-среде. В prod-среде вернет nil.
      +1
      Исключение будет только на dev-среде. В prod-среде вернет nil.

      Неправда.
        0
        Кто от RJS отказался, вы? Чем оне не гибок? Просветите.
        Рык создаёт новый инстанс рельсов, за его использование в таких ситуациях надо руки отрывать. DJ — наше всё, а лучше AMQP
        Model.find(id) всегда бросается исключениями.
          0
          RJS вполне гибок, просто «немодный». Сейчас модно делать «ненавязчивый» Javascript, поэтому, в частности, jQuery-гикам RJS кажется некрасивым.
            0
            Про ненавязчивый яваскрипт я понимаю, но мы же не о нём говорим, а об RJS, а это несколько другое.
            Я так понимаю, что ненавязчивый яваскрипты — это когда для всех похожих аякс запросов создаётся функция, а хелпер — link_to_function вместо link_to_remote, или вообще без хелперов, сам главный яваскрипт обработчики вешает.
            Но без RJS в любом случае неудобно, для сложных систем можно написать спецклассы, которые бы кушали json, к примеру, но в любом случае, это либо эвал приходящего кода с вызовом функции (тот же rjs), либо навешивание кучи уродливых коллбэков на каждый аякс чих.
            RJS гибче и красивее, имхо.
            0
            Не вижу ничего плохого в исключениях, их для того и придумали) И руки нужно отрывать за их неиспользование:)
            0
            ну если rake задача из рельсового окружения то она сожрет куда больше памяти чем демон.
              0
              можно искать так — Model.find_by_id(id) тогда вернет nil
              +1
              Спасибо за перевод, до этого использовал только тестирование роутов.
                +7
                bubuq спасибо конечно за перевод, но обрати пожалуйста на дату написания статьи — «July 8th, 2006» множество советов уже устарели, и могут только нанести вред новичкам
                  –4
                  Ну уж и множество. Большинство актуальны.
                    +5
                    BackgroundDrb не актуален.
                  0
                  Мои варианты:
                  >acts_as_nested_set

                  awesome_nested_set

                  >Превращение массивов в предложения

                  script/plugin install git://github.com/yaroslav/russian.git
                  «Peter, Fred и Chris» # запятая перед «и» или «and» не ставится в обоих случаях!
                  Тоже самое относится и к «Числовые хелперы для частых задач» — GB -> ГБ

                  >Запуск длинных процессов отдельно в фоне

                  Приведённый способ крайне нерационален. Я использую aasm и spawn. Куда менее требовательно к ресурсам, чем sleep + переменная.
                    +1
                    Запятая перед «and» в перечислении из трёх и более элементов, конечно же, ставится.
                      0
                      Да, в английском ставится. Пардон.

                      >> %w{ниф-ниф наф-наф нуф-нуф серый_волк}.to_sentence
                      => «ниф-ниф, наф-наф, нуф-нуф и серый_волк»

                      >> %w{ниф-ниф наф-наф нуф-нуф серый_волк}.to_sentence
                      => «ниф-ниф, наф-наф, нуф-нуф, and серый_волк»
                        0
                        В русском тоже ставится, если есть локализация либо gem russian и Rails 2.3.x.
                    0
                    Про to_param можно почитать на apidock.com

                    P.S.
                    ИМХО А битую ссылку лучше убрать.
                    0
                    Дальнейшее повышение производительности — по умолчанию рельсы записывают сессии на локальную файловую систему.


                    Не верно! в CookieStore.
                      +1
                      Это перевод очень старых советов :)
                        0
                        Зато смотрите, как это побудило народ надавать новых )
                          +2
                          А вы — это тот Ярослав, который russian? Наверно, мне лучше было помолчать )
                          • НЛО прилетело и опубликовало эту надпись здесь
                          0
                          Устаревшее:

                          Локальный бенчмаркинг — Rack::Bug.
                          github.com/brynary/rack-bug

                          Фоновое выполнение — Delayed Job или bj.
                          github.com/collectiveidea/delayed_job
                          github.com/github/bj/
                          • НЛО прилетело и опубликовало эту надпись здесь
                            +1
                            Еще, на грани с фактической ошибкой: «общий» to_yaml не является рельсовым методом (методом ActiveSupport), а значит, на него не действуют соглашения методов to_xml и to_json (:only, :include) и прочие.

                            Вообще, to_yaml нужен в основном для сериализации, и «наружу» его отдавать смысла очень мало.

                            Проверку валидности XHTML можно делать, например, если тестировать приложение с помощью Cucumber — парсер nokogiri, который будет использоваться, будет страшно ругаться на незакрытые теги и прочие вещи (если вы еще используете ERB, а не перешли на HAML)
                              0
                              И совсем забыл, раз уж это тред про «советы» для новчиков — всегда используйте :only при отдаче to_xml и to_json. Ну и не забывайте про attr_protected в моделях.
                              • НЛО прилетело и опубликовало эту надпись здесь
                              0
                              По поводу send_file — лучше проанализировать, на каком сервере будет работать production и отправлять заголовок либо X-Sendfile для Apache2, либо X-Accel-Recirect для nginx. Ну в development будет send_file, да. Был какой-то плагин даже для этого.

                              На одном проекте выставление заголовка для nginx позволило сменить состояние сервера с «пипец какой загруженнй» до «сервак то вообщем простаивает».
                              • НЛО прилетело и опубликовало эту надпись здесь

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

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