The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Rendering markaby in your helpers

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Matthias Georgi

Posts: 54
Nickname: georgi
Registered: Apr, 2007

Matthias Georgi is a Ruby on Rails freelancer.
Rendering markaby in your helpers Posted: Apr 2, 2007 6:56 AM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by Matthias Georgi.
Original Post: Rendering markaby in your helpers
Feed Title: Matthias Georgi
Feed URL: http://feeds.feedburner.com/matthias-georgi?format=xml
Feed Description: Webdev, Gamedev and Interaction Design.
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Matthias Georgi
Latest Posts From Matthias Georgi

Advertisement
Generating markup in your rails helpers is a general practice in rails and is used throughout all rails helpers. Normally you use content_tag to generate markup. But often you will encounter situations, where nested tags force you to write ugly helper code like the following helper method from the rails library:
def options_for_select(container, selected = nil)
container = container.to_a if Hash === container

options_for_select = container.inject([]) do |options, element|
if !element.is_a?(String) and element.respond_to?(:first) and element.respond_to?(:last)
is_selected = ( (selected.respond_to?(:include?) && !selected.is_a?(String) ? selected.include?(element.last) : element.last == selected) )
if is_selected
options << "<option value=\"#{html_escape(element.last.to_s)}\" selected=\"selected\">#{html_escape(element.first.to_s)}</option>"
else
options << "<option value=\"#{html_escape(element.last.to_s)}\">#{html_escape(element.first.to_s)}</option>"
end
else
is_selected = ( (selected.respond_to?(:include?) && !selected.is_a?(String) ? selected.include?(element) : element == selected) )
options << ((is_selected) ? "<option value=\"#{html_escape(element.to_s)}\" selected=\"selected\">#{html_escape(element.to_s)}</option>" : "<option value=\"#{html_escape(element.to_s)}\">#{html_escape(element.to_s)}</option>")
end
end

options_for_select.join("\n")
end


We will now rewrite this code with inline markaby. We need therefore the following helper method:

def markaby(&proc)
assigns = {}
instance_variables.each do |name|
assigns[ name[1..-1] ] = instance_variable_get(name)
end
Markaby::Builder.new(assigns, self).capture(&amp;proc)
end


We need to collect the instance variables of the current template and pass a hash of instance variable names along with their values to the markaby builder. As second parameter we pass the current template, so that the builder can access other helper methods.

Ok, let's rewrite the options_for_select helper. The method takes an array of values which should be displayed as options. Alternatively you may pass an list of pairs like [['first',1],['second',2] or an Hash, which maps from option labels to their values.

One thing I did was to refactor the is_selected test into a lambda. It is cleaner to separate the test and probably more efficient. Inside the loop we are testing , if we have pairs or simple values and generate markup by sending the option method to the builder, which causes the markaby builder to generate an option tag. Tag attributes are defined with a hash, which we pass to the option method. A tag method takes an optional block, which defines the content of a tag, in our case simply the text of the option.

def options_for_select(container, selected = nil)
container = container.to_a if Hash === container

if selected.respond_to?(:include?) and !selected.is_a?(String)
is_selected = lambda { |e| selected.include? e }
else
is_selected = lambda { |e| selected == e }
end

is_pair = lambda {|e| !e.is_a?(String) and e.respond_to?(:first) and e.respond_to?(:last) }

markaby do
container.each do |element|
if is_pair[element]
if is_selected[element.last]
option(:value => element.last, :selected => 'selected') { h element.first }
else
option(:value => element.last) { h element.first }
end
else
if is_selected[element]
option(:value => element, :selected => 'selected') { h element }
else
option(:value => element) { h element }
end
end
end
end
end


Our defined markaby method is even more useful, we can accept a block for our helper method and use it inside the markaby code:

def tasks(&block)
markaby do
div.tasks {
ul {
markaby(&amp;block)
}
}
end
end


If we have a common pattern like a list of tasks for many templates, we can generate the common code with the tasks method and put the actual tasks in the block:

tasks {
task 'Back to articles'.t, articles_url
task :edit, @article
task :versions, @article
}


So, you can see, there is also a task helper, which is defined as follows:

def task(text, url_or_resource, html_options={})
if text.is_a? Symbol
task "#{text.to_s.humanize}".t, {:action => text, :id => url_or_resource}, html_options
else
markaby { li { link_to text, url_or_resource, html_options } }
end
end


If the link text is a symbol, we are going to infer the url from the action name which is the first parameter and the recource, which is the second parameter in this case. Otherwise we generate a list element and delegate the arguments to the link_to helper.

By using this simple abstraction, we have hidden the details of task links, which is really DRY. Instead of repeating the same pattern over and over again, we have a common place to decide, how the tasks should look like. Markaby makes it really easy to generate nested structures, as it takes advantage of ruby's block syntax.

In a future post I will discuss an advanced form builder, like the one already implemented in Rails, but this time implemented with the help of Markaby. It is even possible to separate form generation and form layouting by using a separate form renderer. So step by, if you want to delve into further Markaby Magic.

Read: Rendering markaby in your helpers

Topic: toggle.el Previous Topic   Next Topic Topic: The Philosophy of Javascript: An Introduction to OO

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use