Подарки
Сенека в письмах Луцилию в каждое письмо старался вставить цитату древних мыслителей, он называл это подарком. К сожалению цитаты древних программистов выглядят так, как на рисунке в начале статьи и я не могу воспользоваться этим же способом. Вместо этого в каждую запись я стараюсь включить код или случай из своей практики.
Сегодня обилие информации в открытом доступе потенциально позволяет любому специалисту создать проект любого уровня, тайных знаний практически нет. Самым дефицитным ресурсом является личное время. И если вы потратите целый день на поиск какой-нибудь ошибки, то выпуск проекта мечты также отложится на этот самый день. Но чем больше случаев из практики вы знаете, тем больше вероятность, что в повседневной деятельности вы распознаете ошибку или особенность и сэкономите драгоценное время. Сегодняшний пост я хочу посвятить различным мелочам, которые у меня накопились и может быть что-то вам тоже пригодиться.
Упасть на первом сломанном спеке
Когда я мечтал пропатчить test/unit
, чтобы он падал на первом сломанном тесте. Так как скучно смотреть
на множество ошибок, которые часто вызваны одной причиной. В rSpec
такая возможность есть в
стандартной поставке, ничего патчить не нужно:
rspec --fail-fast
rspec --fail-fast -b # -b(--backtrace) - еще и вывести полный трейс ошибки
Блоки текста с красивыми отступами
class A
def do_some
cmd = <<-CMD
rake db:migrate
rake db:test:prepare
CMD
system cmd
end
end
Раньше я делал примерно так, когда мне нужно было вставить большие куски текста. Коллега подсказал метод strip_heredoc из рейлс, который позволяет вернуть коду необходимую красоту:
class A
def do_some
cmd = <<-CMD.strip_heredoc
rake db:migrate
rake db:test:prepare
CMD
system cmd
end
end
Фильтры в контроллерах запускаются в области видимости контроллера
Мне кажется, что я где-то прочитал, что блочные фильтры в контроллере нужно писать так:
class HomeController < ApplicationController
before_filter do |controller|
raise "Access Denied" if controller.request.port == 3000
end
end
Но оказывается параметр controller
не нужен. Из-за особенностей реализации фильтров через AS-колбеки activesupport/lib/active_support/callbacks.rb#L337 код вызывается как обычный
метод, поэтому можно написать так:
class HomeController < ApplicationController
before_filter do
raise "Access Denied" if request.port == 3000
end
end
Параметр size в image_tag
image_tag('image.jpg', size: '70x25')
# => <img alt="Image" height="25" src="/assets/image.jpg" width="70" />
Это штришок тоже почему-то ускользнул от меня, я долгое время использовал явные :width
и :height
(кстати вы всегда
'ширина' и 'высота' пишете правильно с первой попытки? :-))
Использование хэлпер методов вне контроллера
Не всегда удается развести всю логику строго по MVC, поэтому всегда можно подхачить, например вот так:
class User < ActiveRecord::Base
attr_accessible :first, :second
delegate :content_tag, :safe_join, to: 'ApplicationController.helpers'
def full_name
# Конечно заносить элементы оформления в модель не очень хорошо,
# я бы даже сказал совсем не хорошо. Но для этого нам и нужны
# интуиция, опыт и лень, чтобы отличить добро от зла.
safe_join [content_tag(:b, first), second], ' — '.html_safe
end
end
%p= @user.full_name # => <p><b>Alexey</b> — Vakhov</p>
Вообще я поддерживаю разумные хаки. Бизнес-логику обязательно нужно хачить. Я всегда сначала во вьюхе буду создавать длинную
колбаску @products.rejected(&:disabled?).select{|p| p.price > 1000}.reverse
. И только когда она станет совсем неприличной,
я вынесу ее с длинным, стремным именем в ApplicationHelper
и начну мусорить там. Возможно это и нарушает академические
стандарты, но зато все скажут спасибо при поддержке. Так как, чтобы поправить вьюху, нужно будет заглянуть всего в один файл.
А если вы увидите что-нибудь типа @products.render_me(self)
, в контроллере окажется, что @products
это декоратор, в декораторе
render_me
реализован через родительский класс и пачку миксинов. Вообщем после 4-го уровня абстракции кэш вашей
памяти переполнится окончательно. К тому же руби все равно не переплюнуть C++ в плане абстракции. Простой код должен
выглядеть просто, сложный - сложно.
Обход двух и более массивов одного размера
Глубинный эстетизм кода мне тоже не чужд, поэтому когда мне пришлось написать пару раз что-то похожее на:
array_a = [1, 2, 3]
array_b = [5, 6, 7]
if array_a.size == array_b.size
array_a.each_with_index do |a, ind|
b = array_b[ind]
# do something with a & b
end
end
несимметричность данного решения меня раздражала. И в рейлс я случайно заметил кусочек, который почему-то не пришел в голову раньше:
if array_a.size == array_b.size
array_a.size.times do |ind|
a = array_a[ind]
b = array_b[ind]
# do something with a & b
end
end
Второй вариант мне нравится сильно больше. Не удивлюсь, если в руби есть еще более симметричный способ обойти несколько массивов одновременно, ведь API руби - безгранично.
Tweet