Ildar Shaynurov's blog

Web and iOS Developer

Rails: Точки и другие спец символы в маршрутах.

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

Создадим такой маршрут:

1
get 'user/:user_id/files/:file_name' => 'user_files#file', :as => :user_file

И попробуем им воспользоваться:

1
link_to 'Download File', user_file_path('some_cool_file.jpg')

При выполнении это кода произойдет примено такая ошибка:

1
2
ActionView::Template::Error (No route matches {:action=>"file", :file_name=>"some_cool_file.jpg",
:user_id=>#<User id: 27>, :controller=>"user_files"}):

Это происходит потому что .jpg интерпертируется как формат, а формата .jpg в этом action не предусмотрено. Для исправления нужно указать что это расширение часть параметра :file_name. Сделать это можно путем указания ограничения по регулярному выражение для этого параметра включающего все символы /.+/:

1
2
get 'user/:user_id/files/:file_name' => 'user_files#file', :as => :user_file,
  :constraints => {:file => /.+/ }

Учтите что это регулярное выражение включает все символы, и в маршруте вида /user/1/files/pictures/coolest/some_cool_picture.jpg в параметр :file будет переданно значение coolest/some_cool_picture.jpg. Если же вы не хотите интерпретировать / как часть параметра то этот символ надо исключить из регулярного выражения:

1
2
get 'user/:user_id/files/:file_name' => 'user_files#file', :as => :user_file,
  :constraints => {:file => /[^\/]+/ }

В этом случае попытка построения маршурта вида:

1
link_to 'Download File', user_file_path('pictures/coolest/some_cool_picture.jpg')

будет вызывать ошибку.

Ruby: Переменные в регулярных выражениях

Бывает что нужно вставить одно регулярное выражение в другое. Это можно сделать так же как со строкой:

1
2
3
 > REGEXP1 = /[A-Za-z0-9\_][A-Za-z0-9\-\_\.]+/
 > /^#{REGEXP1}$/
 => /^(?-mix:[A-Za-z0-9\_][A-Za-z0-9\-\_\.]+)$/

Но при такой вставке могут быть проблемы при дальнейшем преобразовании, например в строку. Сравните:

1
2
> /^#{REGEXP1}$/.to_s
=> "(?-mix:^(?-mix:[A-Za-z0-9\\_][A-Za-z0-9\\-\\_\\.]+)$)"

и

1
2
> /^[A-Za-z0-9\_][A-Za-z0-9\-\_\.]+$/.to_s
=> "(?-mix:^[A-Za-z0-9\\_][A-Za-z0-9\\-\\_\\.]+$)"

Как видно, в результате выражения различаются. Для того чтоб такого не присходило можно воспользоваться методом #source:

1
2
> /^#{REGEXP1.source}$/.to_s
=> "(?-mix:^[A-Za-z0-9\\_][A-Za-z0-9\\-\\_\\.]+$)"

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

Vim: удаление Trailing Spaces

Для удаления trailing spaces можно воспользоваться такой командой:

1
:%s/\s*$//g

Так же удобно повесить ее на горячу клавишу:

1
map <silent> <Leader>ts :%s/\s*$//g<CR>

Rails: события before и after для remote ссылок.

У нас есть ссылка которая делает ajax запрос:

1
link_to 'Cool work', cool_work_path, :remote => true

Мы хотим сделать какую-то обработку перед отправление запрос и после его выполнения. Для этого в пакете jquery-rails предусмотрены следующие события:

  • ajax:before - Вызывается в самом начале обработки запроса.
  • ajax:beforeSend - Вызывается непосредственно перед отправкой запроса. Если возвращается false то выполнение запроса будет остановлено.
  • ajax:success - Вызывается непосредственно после выполнения запроса если он был выполнен успешно.
  • ajax:complete - Вызывается после выполнения запроса независимо от статуса.
  • ajax:error - Вызывается если произошла ошибка.

Вернемся к нашей ссылке. Добавим к ней аттрибут id чтоб было легче на нее ссылаться.

1
link_to 'Cool work', cool_work_path, :remote => true, :id => 'cool_link'

И в application.js пишем обработчик для событий.

1
2
3
4
5
6
7
8
9
10
11
$(function () {
  $("#cool_link").live("ajax:before", function () {
    $(this).html("Processing...")
  };
});

$(function () {
  $("#cool_link").live("ajax:complete", function () {
    $(this).html("Cool work")
  };
});

Ruby: Log Return Value

Если вам нужно вывести в лог возврщаемое из функции значение то можно воспользоваться методом tap():

1
2
3
def some_function
  complex_calculation.tap{ |v| Rails.logger.debug "returnd value = #{v}" }
end