На Follow me нужно было сделать возможность кастомного оформления страниц: фон, стили, баннеры, чтоб, например, забрендировать какую-нибудь передачу. Задача чуть менее, чем полностью банальная, но получилось вроде забавно.
В стандартной модели Branding задаётся урл, для которого брендирование, период активности, автомат для состояний, ну и все описанные выше свойства кастомные.
Логика выбора брендирования — в классе BrandingSelector, для каждого свойства выбирается первый брендинг с ним:
class BrandingSelector
attr_reader :controller, :urls
def initialize(controller)
@controller = controller
@urls = [controller.request.fullpath, "*"]
if controller.action_name == 'show' && controller.respond_to?(:resource) && controller.send(:resource).respond_to?(:parent_url)
@urls.insert(-2, controller.send(:resource).parent_url)
end
@urls = @urls.flatten
end
%w{css top_banner sidebar_banner image}.each do |method|
define_method method do
(branding = first_branding_with(method)) && branding.send(method)
end
end
def first_branding_with(method)
brandings.find{|b| b.send("#{method}?")}
end
private
def brandings
@brandings ||= Branding.current.scoped_by_url(urls).sort_by{|b| urls.index(b.url)}
end
end
Если бы я не поленился сделать has_many-роуты для подкастов (для отдельной передачи я использую /podcasts/miks-noizar вместо гипотетического /flows/mixes/miks-noizar, например), то можно было бы всю систему завязать полностью на иерархии урлов, иначе пришлось лепить костыль в виде метода parent_url, который для подкаста возвращает /flows/#{flow.permalink}, в принципе это даёт какое-то пространство для игры: например постам в блоге можно было бы поставить /blog.
Ну и как-то выводим это:
<% if css = @branding_selector.try(:css) %>
<style media="screen" type="text/css">
<%= css.html_safe %>
</style>
<% end %>