Duke Nukem

Hoe de plugin werkt kun je lezen op: http://agilewebdevelopment.com/plugins/acts_as_taggable_on_steroids

Nu hebben we op deze site tenminste twee typen content die we willen kunnen taggen, Events en Articles. En daar komen er vast nog meer bij. Denk aan vaactures, projecten en misschien wel werknemers. Een ActiveRecord model “taggable” maken is betrekkelijk eenvoudig:

class Article acts_as_taggable end


Zo, je kunt nu alle artikelen taggen, met methods als article.taglist.add(), article.taglist = “tag1, tag2, tag3” en meer zulks. Tag clouds genereren is ook een eitje. Maar hoe gaan we met de controllers om? Je zou iets kunnen maken als een ArticleTagsController en een EventTagsController.

Op zich prima, maar niet erg DRY. In plaats daarvan heb ik een TaggingsController opgezet die oneindig veel soorten models kan taggen, zolang ze maar acts_as_taggable implementeren.

class TaggingsController < ApplicationController def create # object to be tagged really exist and is taggable # an expecpetion will be raised if we can't find an object @tagged_model = instance_eval("model = #{params[:taggable_type]}.find(#{params[:taggable_id]})") # check if the model can be tagged raise Exception("objects of type #{params[:taggable_type]} cannot be tagged") unless @tagged_model.respond_to?(:tag_list) params[:tags].split(",").each do |tag| @tagged_model.tag_list.add(tag) end @tagged_model.save respond_to do |format| format.js # render create.ejs format.html do flash[:notice] = "tag toegevoegd" # generate dynamic redirect # should work if RESTful routes are defined for the model instance_eval "redirect_to #{params[:taggable_type].downcase}_url(\"#{@tagged_model.to_param}\")" return end end end end


Hoe werkt dit nou?

We krijgen via een POST drie parameters binnen: taggable_type, taggable_id en tags Mbv van de taggable_type bepalen we de class van het object dat we willen gaan taggen. Eerst halen we dat object op met een dynamisch geconstrueerde ActiveRecord find. Ik gebruik hier instance_eval. Hier komt bijvoorbeeld dit uit: Article.find(39) – welke artikel nummer 39 uit de database ophaalt (duh!).

Vervolgens gaan we checken of dit object wel getagged kan worden. Met Ruby doe je dat meestal simpelweg door te checken of een object een bepaalde method heeft : responds_to?

Dan gebruiken we tags_list.add() om alle tags toe te kennen. acts_as_taggable_on_steroids zorgt er achter de schermen voor dat de juiste database kolommen gevuld worden. En dat er geen dubbele tags worden bewaard. Prettig.

Daarna komen we in een responds_to blok, voor de doorgwinterede railzer gesneden koek. Was deze request een gewone ouderwetse request, dan redirecten we terug naar de Show pagina van het zojuist getagde model. Wederom met een instance_eval. Dit genereert een call als deze:

redirect_to article_url(@tagged_model.to_param)

Dit zal in rails2 trouwens niet meer nodig zijn. je kunt dan gewoon dit doen: redirect_to @tagged_model en rails weet precies waar je naar toe wilt.

Maar eigenlijk, en dat bedenk ik nu ter plekke, kan dit gewoon redirect_to :back worden, want je wilt toch altjd weer terug naar de pagina waar je vandaag komt.

Dat alles in 1 controller wordt geregeld is misschien niet zo RESTful, maar in dit geval is het volgens mij wel een goede keuze. Ik vraag me trouwens af of die instance_eval in per se nodig is… Dat mag een meer ervaren Rubyist me nog eens vertellen.