Ik ben de laatste tijd erg druk geweest met school en werk. Ik ben hiernaast begonnen met een boek over Ruby on Rails 3.0, waardoor ik heel weinig tijd heb gehad om iets aan mijn blog te doen. Een collega van mij gaf me gisteren een goede tip om toch bezig te zijn met het boek en tegelijkertijd met mijn blog. Ik ga elke week een handige Ruby on Rails helper plaatsen op mijn blog om zo mijn boek alvast een beetje te promoten en toch bezig te zijn met mijn blog. Vandaag begin ik met de eerste Ruby on Rails helper. Ik zal proberen elke week minstens 1 helper te plaatsen en zo af en toe tijd te nemen om over andere dingen te schrijven.

Laat ik maar beginnen met mijn helper. Soms wil je vanuit de views bepalen wat er allemaal in de layout wordt getoond. Je wilt een dynamische layout. Je kunt hiervoor ‘content_for’ gebruiken die een string stuurt naar de layout die je kunt aanspreken met ‘yield’. Dit werkt als volgt.

# View
<% content_for(:title, "Dit is de title!") %>

# Layout
<head>
    <title><%= yield(:title) %></title>
</head>

Nu wordt er vanuit de view bepaald wat de titel van de pagina wordt. Erg handig natuurlijk. Het nadeel van content_for is dat er alleen een String kan worden meegegeven. Als je een Array meegeeft, dan wordt hier een String van gemaakt. Hier heb je natuurlijk weinig aan.

Wat je wel kan doen om een Array of Hash mee te sturen is een instance variabele aanmaken in de view en deze aanspreken in de layout. De view die bij de corresponderende actie hoort wordt namelijk eerst gerendert. De layout wordt pas erna gemaakt, waardoor je een instance variabele die in de view is gemaakt kunt aanspreken in de layout. Een voorbeeld.

# View
<% @menu = { :home => true, :products => true } %>

# Layout
<%= link_to "Home", "/" if @menu[:home] %>
<%= link_to "Products", "/products" if @menu[:products] %>

Dit is gelijk een stuk flexibeler. We kunnen dit echter een stuk handiger maken en ervoor zorgen dat je vanuit je view precies kan bepalen welke blokken er in de layout worden getoond. We gaan een helper maken genaamd ‘layout_render’. Hier willen we woorden aan kunnen meegeven die aangeven wat er mag worden getoond in de layout.

# ApplicationHelper
def layout_render *args
    @layout_render = args
end

We kunnen nu meerdere argumenten naar de helper sturen en de helper stopt al deze argumenten in een Array. Je zou nu het volgende in de view kunnen doen.

# De view
<% layout_render :intro, :header, :menu %>

Nu geef je dus aan in de view dat je de intro, header en menu in de layout wilt laten zien. We hebben nog een helper nodig die checkt of iets mag worden getoond in de layout.

# ApplicationHelper
def render?(option)
    @layout_render ||= []
    @layout_render.include?(option)
end

Je kunt nu in de layout het volgende doen om te kijken of de header mag worden getoond.

# Layout
<% if render?(:header) %>
    <h1>De header van de pagina</h1>
<% end %>

Als je nu dus uit de layout_render functie in de view de :header weghaalt, dan wordt de header niet getoond. Handig toch?

Ik zou mezelf kunnen voorstellen dat je ook iets als :all wilt kunnen meesturen aan de layout_render functie. Dus als je :all meegeeft, dat alle onderdelen in de layout worden getoond en dat je dus niet steeds alles moet intypen. Om dit voor elkaar te krijgen doe je een kleine aanpassing in de ‘render?’ helper.

# ApplicationHelper
def render?(option)
    @layout_render ||= []
    @layout_render.include?(option) || @layout_render.include?(:all)
end

Dus als je nu :all meegeeft, wordt gewoon alles getoond. Als je nou ook wilt dat als je layout_render in de view niet aanroept toch alles wordt getoond in de layout, dan voeg je het volgende toe aan de ‘render?’ helper.

# ApplicationHelper
def render?(option)
    @layout_render ||= [:all]
    @layout_render.include?(option) || @layout_render.include?(:all)
end

Nu wordt dus i.p.v. een lege Array een Array gemaakt met :all er al in.

Ik hoop dat jullie wat aan deze helpers hebben. Ik heb er zeker wat aan gehad bij een project voor 45north. Volgende keer dus een nieuwe helper. Tot de volgende keer!