-
-
Save lenary/119874 to your computer and use it in GitHub Desktop.
# stolen from http://github.com/cschneid/irclogger/blob/master/lib/partials.rb | |
# and made a lot more robust by me | |
# this implementation uses erb by default. if you want to use any other template mechanism | |
# then replace `erb` on line 13 and line 17 with `haml` or whatever | |
module Sinatra::Partials | |
def partial(template, *args) | |
template_array = template.to_s.split('/') | |
template = template_array[0..-2].join('/') + "/_#{template_array[-1]}" | |
options = args.last.is_a?(Hash) ? args.pop : {} | |
options.merge!(:layout => false) | |
locals = options[:locals] || {} | |
if collection = options.delete(:collection) then | |
collection.inject([]) do |buffer, member| | |
buffer << erb(:"#{template}", options.merge(:layout => | |
false, :locals => {template_array[-1].to_sym => member}.merge(locals))) | |
end.join("\n") | |
else | |
erb(:"#{template}", options) | |
end | |
end | |
end |
hey chadoh,
To use this, in your application, have the following line:
helpers Sinatra::Partials
this adds the module to the helpers, which can be used in both the controllers and the views. then in erb call it with <%= partial :foo %>
Usage examples here: http://www.sinatrarb.com/faq.html#partials
Just for completeness, I am also new to Ruby and Sinatra so I'm posting how I got it working. And a big thank you for the code -- I'm still trying to grok what's going on in those few lines, and know that it saved me a ton of work :-)
-
saved the file to 'partials.rb' in my application root directory.
-
Added require 'partials' in my main app file ('social.rb' in this case)
-
Added helpers Sinatra::Partials in the same app file.
-
Created a parent.erb and child _some_partial.erb
-
Wanted to pass the variable inside an each iterator so that I can render a different partial based on some decision for each loop:
<% @info_pane.stories.each do |s| %>
-
<%if s.type == 'photo'%> <%= partial(:ip_photo, :locals => {:item => s}) %> <%else%> ...etc
Here's my code if it helps: http://socks.codeplex.com/SourceControl/changeset/view/37669983e5ee#views%2ffb_me.erb
@dvhthomas what exactly are you asking?
technically what you showed me should work, but at the same time, i haven't played with sinatra in far too long, so am not 100% sure if it will.
I'm not asking anything, I'm sharing what worked for me because for a complete neophyte, the previous 'how-to' was not obvious.
ah, ok, thanks
You don't have to pass in :layout => false
. It defaults to false
for nested templates.
undefined method `extract_options!' for []:Array
I've got this error when I'm following the steps described above
Any ideas?
Thanks for this super useful snippet of code!
I ran into one issue. If you have local variables assigned with a collection, they are not passed on during iteration. This is fixed in the sample code below:
def partial(template, *args)
template_array = template.to_s.split('/')
template = template_array[0..-2].join('/') + "/_#{template_array[-1]}"
options = args.last.is_a?(Hash) ? args.pop : {}
options.merge!(:layout => false)
locals = options[:locals].nil? ? {} : options[:locals] # SAVE LOCALS
if collection = options.delete(:collection) then
collection.inject([]) do |buffer, member|
buffer << slim(:"#{template}", options.merge(:layout =>
false, :locals => {template_array[-1].to_sym => member}.merge(locals))) # MERGE THEM BACK TO EACH
end.join("\n")
else
slim(:"#{template}", options)
end
end
Why the partial include using only .erb tempaltes with _prefix - I\m currently using HAML templates - is it possible to render haml partials - for example my footer is including with options for the language - can I use it in haml render engine ?
You don't need the partial method for this, at all, simply call != haml :footer
..and Im getting this way just "footer" as a string, parsed in my footer
Really? You're sure you didn't do != haml "footer"
by accident?
ahh - sorry it's included, but how can I pass arguments like for lang and use it in the footer.haml
!= haml :footer, :locals => { :lang => :en }
instance variables set in a route/before filter/view will also be available
Many thanks guys - I think it's working fine.
You can also abstract the calls to the template engines by passing the engine name and then looking up the method on self:
self.method(engine).call
Then you would call the method like this:
=partial(:item, :haml, :collection => @items)
Here is the updated method:
module Sinatra::Partials
def partial(template, engine, *args)
template_array = template.to_s.split('/')
template = template_array[0..-2].join('/') + "/_#{template_array[-1]}"
options = args.last.is_a?(Hash) ? args.pop : {}
options.merge!(:layout => false)
locals = options[:locals] || {}
if collection = options.delete(:collection) then
collection.inject([]) do |buffer, member|
buffer << self.method(engine).call(:"#{template}", options.merge(:layout =>
false, :locals => {template_array[-1].to_sym => member}.merge(locals)))
end.join("\n")
else
self.method(engine).call(:"#{template}", options)
end
end
end
So I'm not sure about including this amendment, but people can use it if they want to.
I have various issues with it, first of all being the additional complexity it adds to the code, and second being the approach of .method(:name).call(*args)
above .send(:name, *args)
(yes, not sure why i prefer the second, does anyone have any well-founded opinions?)
Iain Barnett (@yb66) made a gem of this code - https://github.com/yb66/Sinatra-Partial - with some improvements . I might submit a pull request later this week with some of my ideas about how we can get over the selection of template renderers.
You could also call Sinatra's render
method directly, some things like inline markaby or auto-detecting content-types (irrelevant for partials) just won't work.
@rkh that's actually what i was planning on doing, though I didn't know a render
method existed, so I was going to write my own. Time to dive into the sinatra source
I spent some time studying base.rb before making my implementation, and I considered using render directly, but it I personally did not like the idea of bypassing the built-in template rendering methods.
Excellent! I will be switching my current project over to using the gem. Thanks for all of the great work!
I hate being the n00b, but I don't understand how to use this. I know how I'd like to use it, but it's not working for me.
I save this file to my app's home directory, with app.rb, and in app.rb I 'require "partials" '. No complaints from Ruby, so I'm fairly certain it's being included. But when I navigate to the page that uses a partial, I get the error "NoMethodError at /thoughts; undefined method `partial' ".
I'm sure this is painfully obvious and that's why I'm having such a hard time finding anyone saying how to do it, but I can't figure it out. I probably just don't understand how to use modules. Any help would be greatly appreciated.
Thanks!