De komende tijd geen nieuwe berichten

Hallo allemaal,

Ik zal de komende tijd geen nieuwe berichten plaatsen op Robin on Rails. Ik wil mezelf focussen op mijn boek, nieuwe portfolio website en mijn werk, waardoor er niet tot nauwelijks tijd is voor mijn blog. Ik zal wel op Twitter wat actiever zijn en meer over Ruby on Rails en andere zaken tweeten. Als mijn boek klaar is zal ik wel weer verder gaan met dit blog. Wie weet maak ik er dan ook echt iets voor in Ruby on Rails. Tot die tijd wens ik jullie allemaal een fijne vakantie en veel plezier met Ruby on Rails!

Als je nog vragen hebt over Ruby on Rails of andere dingen kun je me altijd mailen (check mijn portfolio website http://www.robinbrouwer.nl/) of een berichtje sturen op twitter.

Groeten,
Robin

Rails Helpers: Simpele breadcrumbs

Deze keer zal ik een helper laten zien waarmee je lekker simpel breadcrumbs kan toevoegen aan je website. Je geeft aan waar de breadcrumbs naartoe moeten gaan, wat voor ’seperator’ er moet worden gebruikt en of het einde van de breadcrumbs is bereikt. Eerst de breadcrumbs helper die een blok om alle kruimels plaatst.

def breadcrumbs(html_tag, *args, &block)
	  options = args.extract_options!
	  options[:class] ||= "breadcrumbs"
	  content_for(:breadcrumbs) do
		  content_tag(html_tag, options, &block)
	  end
end

Je stopt dus in yield(:breadcrumbs) een html tag die je meegeeft en geeft een block mee. Hierin stop je dan alle individuele kruimels. Hier is ook een helper voor: crumb.

def crumb(label, options={})
    options[:seperator] ||= "»"
    options[:seperator] = "" if options[:end]
    options[:url].blank? ? "#{label} #{options[:seperator]}" : "#{link_to(label, options[:url], :title => label)} #{options[:seperator]}"
end

Je kunt hier naast een label een :url, :seperator en :end meegeven. Er is een standaard :seperator, maar als je hebt aangegeven dat dit het einde is van de breadcrumb zal deze niet worden getoond. Hier een voorbeeld hoe je de helper kunt gebruiken.

<% breadcrumbs :p do %>
    <%= crumb "posts", :url => "/posts" %>
    <%= crumb "archive", :url => "/posts/archive" %>
    <%= crumb "hello world!", :end => true %>
<% end %>

Er wordt een p-tag aangemaakt met hierin twee links, twee seperators en één gewone tekst. Het resultaat ziet er als volgt uit.

<p class="breadcrumbs">
    <a href="/posts" title="posts">posts</a> »
    <a href="/posts/archive" title="archive">archive</a> »
    hello world
</p>

Als je zou willen kun je de helper geheel naar je eigen wensen aanpassen. Veel plezier ermee!

Rails Helpers: Dottify en Core extensions

Nadat ik de dutch_date_select helper voor form_for had gepost moest ik gewoon uitzoeken hoe je extra methods aan bijvoorbeeld de String class kunt toevoegen. Ik dacht meteen aan een helper die ik een keer had gemaakt om dit uit te testen: Dottify. Deze helper zorgt ervoor dat je een String kunt afknippen en puntjes erachter kunt zetten als het nodig is. Deze zag er zo uit:

def dottify(text, len)
    "#{text[0..len-1]}#{"..." if text.length > len}"
end

Je geeft dus de string en de lengte mee om er wel of niet puntjes achter te zetten. Het leek mij een stuk handiger als je dit direct op een String kunt aanroepen. Ik ben gaan zoeken hoe dit moet en het bleek behoorlijk simpel te zijn.

Het eerste wat je moet doen is een bestand aanmaken in de /lib map. Noem deze ‘core_extensions.rb’. Hierin stop je het volgende:

class String
  def dottify(len)
    "#{self[0..len-1]}#{"..." if self.length > len}"
  end
end

Je doet dus gewoon ‘class String’ en kunt hierin allerlei methods toevoegen. Je spreekt de String zelf steeds aan met ’self’. Je kunt dit dus ook met Integer e.d. doen, maar ook met Rails classes zoals die ik gebruikte in dutch_date_select. Je moet echter nog wel iets doen om dit te laten werken. Maak een bestand aan in /config/initializers en noem deze ‘load_extensions.rb’. Stop hier het volgende in:

require 'core_extensions'

Nu wordt elke keer als de server wordt opgestart ‘core_extensions.rb’ ingeladen. Let dus op: elke keer als je een aanpassing doet moet je de server opnieuw opstarten. Je kunt in ‘core_extensions.rb’ alle aanpassingen aan de verschillende classes doen. Je kunt er ook voor kiezen om dit op te delen in meerdere bestanden. Vergeet deze dan niet toe te voegen aan ‘load_extensions.rb’, zodat ze daadwerkelijk worden ingeladen.

Vrij simpel toch? Om nu Dottify te gebruiken doe je het volgende:

"Dit is een String".dottify(6)   # => "Dit is..."

Hopelijk hebben jullie er wat aan. :)

Rails Helpers: Griddify

Ik had weer zin om een Rails helper te posten. Dit keer kom ik met ‘Griddify’. Dit is een helper waarmee je gemakkelijk een aantal div’jes als grid kunt aanmaken. Er worden een aantal classes en id’s gekoppeld aan de aangemaakte div’jes, zodat je het gemakkelijk mooi kunt maken met CSS. Dit is dan de helper.

def griddify(array, cols, options = {})
	array = array.in_groups_of(cols, options[:fill_with])
	options[:rows] ||= array.length
	options[:row_id] ||= "gridRow"
	options[:row_class] ||= "gridRow"
	options[:column_id] ||= "gridColumn"
	options[:column_class] ||= "gridColumn"

	if options[:rows] > array.length && options[:fill_with] != false
		rows_array = []
 		((options[:rows] - array.length) * cols).times { rows_array.push(options[:fill_with]) }
    	array.concat(rows_array.in_groups_of(cols))
	elsif options[:rows] < array.length
   	(array.length - options[:rows]).times { array.slice!(-1) }
	end

	array.each_with_index do |row, row_index|
		concat "<div class=\"#{options[:row_class]}\" id=\"#{options[:row_id]}_#{row_index}\">"
		row.each_with_index do |column, column_index|
			concat "<div class=\"#{options[:column_class]}\" id=\"#{options[:column_id]}_#{row_index * cols + column_index}\">"
			yield column if block_given? && !column.blank?
			concat "</div>"
		end
		concat "</div>"
	end
end

Behoorlijk uitgebreid dus. Je hoeft niet per se te weten hoe het werkt, maar wel hoe je het kunt gebruiken. Gelukkig is dat behoorlijk gemakkelijk. Als we alle blogposts in de variabele @posts hebben gestopt en we willen alles laten zien in rijen van twee kolommen, dan doe je het volgende.

<% griddify(@posts, 2) do |post| %>
	<%= post.title %>
<% end %>

Er worden div'jes aangemaakt die je zo kunt stijlen dat het er als een grid uitziet. Wil je ook een limiet op het aantal rijen, dan geef je simpelweg een extra parameter mee.

<% griddify(@posts, 2, :rows => 4) do |post| %>
	<%= post.title %>
<% end %>

Nu komen er dus maximaal acht blogposts in het grid. Als er minder dan acht blogposts in de database zitten worden de div'jes toch aangemaakt, zodat het grid er blijft. Naast de :rows kun je ook andere parameters meesturen. Zo kun je elke rij een andere :row_id of :row_class meegeven en elke kolom een andere :column_id of :column_class. Deze staan standaard op 'gridRow' en 'gridColumn'.

Om te illustreren wat er gebeurt als je griddify uitvoert laat ik hier een voorbeeld zien.

<% griddify(@posts, 2, :rows => 2) do |post| %>
	<%= post.title %>
<% end %>

Dit geeft:

<div id="gridRow_1" class="gridRow">
	<div class="gridColumn" id="gridColumn_0">
		Hello world!
	</div>
	<div class="gridColumn" id="gridColumn_1">
		Once upon a time
	</div>
</div>
<div id="gridRow_2" class="gridRow">
	<div class="gridColumn" id="gridColumn_2">
		Pretty neat
	</div>
	<div class="gridColumn" id="gridColumn_3">
		Griddify is awesome!
	</div>
</div>

Veel plezier met deze helper. Het had mij een hoop tijd gekost als ik dit had gemaakt voor de website voor het Nederlands Dans Theater. Hopelijk voorkomt dit voor jullie een hoop overbodig werk.

Rails Helpers: f.dutch_date_select

Gisteren heb ik laten zien hoe je een dutch_date_select helper kunt maken. Het nadeel is echter dat je het niet met form_for kunt gebruiken. Ik heb de helper daarom aangepast zodat deze werkt in form_for. Stop het volgende in de ApplicationHelper.

module ApplicationHelper
  class DutchBuilder < ActionView::Helpers::FormBuilder
    def dutch_date_select(method, *args)
      options = args.extract_options!
      options[:order] = [:day, :month, :year]
      options[:use_month_names] = ["januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december"]
      @template.date_select(@object_name, method, options)
    end
  end
end

We maken een eigen FormBuilder class aan die alles van ActionView::Helpers::FormBuilder erft. We stoppen @template ervoor en geven @object_name als object naam mee. Dit is de manier hoe al deze form_for velden worden gemaakt. Nu kunnen we het bijna in een form_for gebruiken. Je moet eerst nog wat toevoegen aan je form_for.

<% form_for @user, :builder => ApplicationHelper::DutchBuilder do |f| %>
...
<% end %>

We geven nu dus aan dat we de DutchBuilder willen gebruiken. Je kunt er ook voor zorgen dat de helper direct in ActionView::Helpers::FormBuilder wordt gestopt, zodat je dit niet hoeft te doen. Hoe dit moet kun je het beste zelf gaan uitzoeken. We kunnen nu de helper in de form_for aanroepen.

<% form_for @user, :builder => ApplicationHelper::DutchBuilder do |f| %>
    <%= f.dutch_date_select :birthday, :include_blank => true %>
<% end %>

Dat was dan de f.dutch_date_select helper. Veel plezier ermee.

EDIT (18 mei 2010 om 13:27)

Hier een verbeterde versie van de helper. Hij werkte namelijk niet goed met accepts_nested_attributes_for.

module ApplicationHelper
    class ActionView::Helpers::FormBuilder
	def dutch_date_select(method, options = {}, html_options = {})
            options[:order] = [:day, :month, :year]
	    options[:prompt] = {:day => 'dag', :month => 'maand', :year => 'jaar'}
            options[:use_month_names] = ["januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december"]
            @template.date_select(@object_name, method, options.merge(:object => @object), html_options)
        end
    end
end

Rails Helpers: dutch_date_select

Ruby on Rails kent de handige helper ‘date_select’ die automatisch een aantal select tags aanmaakt om een datum te kiezen. Het probleem hiermee is dat het in het Engels is en dat de volgorde niet volgens de Nederlandse standaard is (DD-MM-YYYY). Ik heb daarom even snel een ‘dutch_date_select’ helper geschreven. Let op: gebruik alleen deze helper als je zeker weet dat je alleen Nederlandse gebruikers zult krijgen. Als de website in meerdere talen te bekijken is kun je veel beter de lokalisatie bestanden aanpassen.

def dutch_date_select(object_name, method, *args)
    options = args.extract_options!
    options[:order] = [:day, :month, :year]
    options[:use_month_names] = ["januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december"]
    date_select(object_name, method, options)
end

Behoorlijk gemakkelijke helper dus. Als je een User model hebt met een kolom genaamd ‘birthday’, dan doe je het volgende.

<%= dutch_date_select :user, :birthday %>

Je kunt overigens alle andere parameters meesturen die je bij date_select kunt gebruiken. Bijvoorbeeld :include_blank.

<%= dutch_date_select :user, :birthday, :include_blank => true %>

Dat was dan de dutch_date_select helper. Ik hoop dat je er wat aan hebt. Voor mij is het erg handig geweest bij een project voor 45north.

Rails Helpers: Menu items aanmaken

Ik vond dat het weer tijd was voor een nieuwe Rails helper. Dit keer een helper om een menu item te maken die automatisch een class ‘current’ meegeeft als de huidige URL overeenkomt met de meegestuurde URL. Het is niet de perfecte helper en zal niet voor alle situaties helpen, maar is zeker een goed beginpunt om te customizen. Ik heb de helper ‘menu_li’ genoemd.

def menu_li(label, url="/", *args)
   options = args.extract_options!
   current_page = request.request_uri
   current_check = (url == "/" ? current_page == "/" : current_page.match(url))
   options[:class] = current_check ? "current" : nil
   content_tag(:li, link_to(label, url, :title => label), options)
end

Je kunt nu dus het volgende doen om een menu item aan te maken.

<ul>
   <%= menu_li "Home" %>
   <%= menu_li "Posts", "/posts" %>
</ul>

Als je op de root van de website bent (“/”) dan krijg je de volgende HTML.

<ul>
   <li class="current"><a href="/" title="Home">Home</a></li>
   <li><a href="/posts" title="Posts">Posts</a></li>
</ul>

Als je dus in de index van de PostsController bent krijgt de tweede <li> een class ‘current’. Je kunt ook extra argumenten meegeven, waaronder een extra class.

<ul>
   <%= menu_li "Home" %>
   <%= menu_li "Posts", "/posts", :class => "andere_class" %>
</ul>

Als je in /posts zit dan wordt de class echter niet toegevoegd aan de <li>. De ‘current’ class herschrijft dan de opgegeven class. Als je dit niet wil kun je deze aangepaste helper gebruiken.

def menu_li(label, url="/", *args)
   options = args.extract_options!
   current_page = request.request_uri
   current_check = (url == "/" ? current_page == "/" : current_page.match(url))
   options[:class] = options[:class].blank? ? (current_check ? "current" : nil) : (current_check ? "#{options[:class]} current" : options[:class])
   content_tag(:li, link_to(label, url, :title => label), options)
end

Veel plezier met deze helper. Volgende keer weer een andere helper, dus blijf me volgen.

Rails Helpers: Dynamische layout

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!

Let it Rain!

Voor het vak ‘Gameplay’ heb ik dit weekend een spelletje gemaakt in Flash. Het is ‘Let it Rain’ geworden en is een spel waarbij je boerderijen moet beheren en dorpelingen tevreden moet houden door het weer te beïnvloeden. Je voegt wolken toe aan het spel om het te laten regenen, zodat de oogst op de boerderijen niet uitdroogt en om de bewoners van het dorp tevreden te stellen als er een droge periode is geweest. Hiernaast kun je de zon laten schijnen om wolken te verwijderen en in regenachtige tijden de oogst wat meer zon te geven. Je kunt geld verdienen door je boerderijen te oogsten en de bewoners tevreden te houden. Met het geld kun je extra boerderijen en huizen kopen, om uiteindelijk een heel blij en gelukkig dorp te krijgen. De tevredenheid van de bewoners is erg belangrijk en als ze niet meer tevreden zijn heb je verloren en krijg je de score te zien die je hebt behaald. Het is uiteindelijk een leuk en best wel verslavend spelletje geworden en als je het wilt spelen kun je op het plaatje hieronder klikken.

Laat een bericht achter met jouw hoogste score! Mijn hoogste score tot nu toe is 20260. Ik ben benieuwd wat jullie kunnen halen. :)

Eerste screenshots Enchanted Hands

Hier zijn de eerste drie screenshots van de game die ik aan het maken ben: Enchanted Hands. Hoofdpersoon moet nog gemaakt worden en het interface wordt nog aan gewerkt. Genoeg te doen dus. Maargoed, hier dus de eerste beelden. Enjoy! :)