Partials in Sinatra

Sorry for the long delay in posting anything, but work has been crazy busy of the past month.

Anyway, following on from the last post about helpers in Sinatra, I thought I’d post an example that mimics partials in Rails. Partials are small pieces of template code that allow you to break your views up into smaller chunks. The main difference from normal views is that the layout doesn’t get rendered again. A partial can contain local variables, allowing for them to be reused with different values. They can save you a lot of time when you want to use the same piece of view code repeatedly.

Easy Partials

An easy way to create partials is to just use the following helper method:

helpers do

def partial template
  erb template, :layout => false
end

end

This basically uses the same mechanism that is used to render views (The erb method), but setting the layout option to false so that the layout is not rendered again. Example usage might be if you wanted to write some information about shops. You could create a partial called shop.erb that contained the following code:

<h3>Shop Partial</h3>
<h4>The shop is called Ikea</p>

Then inside the main view you would use this method to call that code:

<%= partial :shop %>

This is a very simple and quick method that allows you to break your view code up into smaller chunks that can be displayed within views, but it isn’t very versatile.

Intermediate Partials

I’m going to try and improve the partial helper method by making it more like the Rails implementation. The first thing I want to do is use the Rails convention of placing an underscore before all partial filenames to differentiate them from the standard views. The other thing I want to do is to allow local variables to be inserted into the partial code.

helpers do

def int_partial(template,locals=nil)
  locals = locals.is_a?(Hash) ? locals : {template.to_sym =>         locals}
  template=('_' + template.to_s).to_sym
  erb(template,{:layout => false},locals)      
end

end

To use this partial helper, you would have to create a file called _shop.erb that referred to the name of the shop as a variable:

<h3>Shop Partial</h3>
<h4>The shop is called <%= name %></p>

You would then call the partial helper like so:

<%= partial :shop,{:name => 'Ikea'} %>

Rails has convention that if the local variable name is the same as the name of the partial, then it doesn’t need to be explicity set. So if the _shop.erb' file is changed so that the variable is called 'shop' rather than 'name':

<h3>Shop Partial</h3>
<h4>The shop is called <%= shop %></p>

Now it can be called with the shorter code like below:

<%= partial :shop,'Ikea' %>

This is a big improvement on the easy partial code and makes the partials much more versatile by adding the use of local variables.

Advanced Partials

The intermediate partials helper is pretty good, and works well in most situations, but now, we’re I want to take it up another notch and allow arrays and even whole objects to be passed to it as arguments. The code is a bit longer this time:

helpers do

def partial(template,locals=nil)
  if template.is_a?(String) || template.is_a?(Symbol)
    template=('_' + template.to_s).to_sym
  else
    locals=template
    template=template.is_a?(Array) ? ('_' + template.first.class.to_s.downcase).to_sym : ('_' + template.class.to_s.downcase).to_sym
  end
  if locals.is_a?(Hash)
    erb(template,{:layout => false},locals)      
  elsif locals
    locals=[locals] unless locals.respond_to?(:inject)
    locals.inject([]) do |output,element|
      output <<     erb(template,{:layout=>false},{template.to_s.delete("_").to_sym => element})
    end.join("\n")
  else 
    erb(template,{:layout => false})
  end
end

end

To use this partial, you would use the same _shop.erb template as before. Now you can pass an array of shop names to it like so:

<%= partial :shop,["Ikea","Tesco","Footlocker"] %>

This will render the _shop.erb template 3 times, but changing the name each time.

The other thing that is useful for partials is to be able to pass them a whole object (probably one that has just been extracted from a database) and then to render a partial that has access to that object. For example, say you have a person object, with ‘name’ and ‘age’ attributes. You could create a _person.erb file that contained information about that person.

My name is <%= person.name %> and I am <%= person.age %> years old.

Then create a new person (note that you’d need to have some sort of Person class set up, possible with a database to store them in too):

@person = Person.new(:name=>'DAZ',:age=>32)

The following code would render the person partial and set the local person variable to be @person.

<%= partial @person %>

This would output the following view code:

My name is DAZ and I am 32 years old.

You can also pass an array of objects. For example, say you have an array of Person objects called @satc that you have just pulled from your database, then you could call this code:

<%= partial @satc %>

And you’d get the following output:

My name is Carrie and I am 45 years old.
My name is Charlotte and I am 45 years old.
My name is Miranda and I am 45 years old.
My name is Samantha and I am 52 years old.

Cool. It doesn’t quite do all the same things that Rails partials do, but it does do most of the stuff that you’d want to do with partials in a relatively small amount of code. There is a fair amount going on in the code and some fairly (for me anyway) complex Ruby in places.

Feel free to use any of these helpers and if you can find any problems with them or come up with any improvements or want to know how any of it works then let me know in the comments.

I’ve put a working example with all the code on github.

blog comments powered by Disqus