Rails helper for Haml - ruby-on-rails-3.1

I have a bit of HAML code that gets repeated in something like 10 views and would like to put it into a helper of some kind. Doing a search here produced some interesting results but ultimately no answers, so:
In application_helper.rb I have this:
def pagination_helper(object)
haml_tag :div, :class => 'apple_pagination page_info' do
page_entries_info #book_formats
paginate #book_formats
end
end
In the view template I have this:
- pagination_helper(#book_formats)
If I try calling it with = to output something I get an error.
The above will not give me an error but it won't call the methods either. I get empty divs.
Ultimately the code I want to repeat is this:
.apple_pagination.page_info
= page_entries_info #book_formats
= paginate #book_formats
The code would be the same except the object would change and I send that from the view template. For example #book_formats would change to #dvds, etc.

The block passed to haml_tag doesn’t automatically get added to the output. You need to use haml_concat:
def pagination_helper(object)
haml_tag :div, :class => 'apple_pagination page_info' do
haml_concat(page_entries_info #book_formats)
haml_concat(paginate #book_formats)
end
end

Related

How to call helper in Haml file

How do I call a method in a helper from a Haml file?
In sample.haml, I need to call the show_message method depending on some condition. Then I moved the method to the helper, but the returned value from the method is treated as just a string, not a Haml element.
This is sample.haml:
- flash.each do |msg|
- if msg.is_a?(Array)
- msg.each do |m|
= show_message(m)
- if msg.is_a?(String)
= show_message(msg)
This is helper.rb:
def show_message(msg)
haml = <<-HAML
%div{class: some_class}
= content_tag :div, #{msg}, id: "id"
HAML
end
If I write the same HTML element in show_message in sample.html directly, it works properly. How can I solve this?
Your helper method needs to construct the full HTML via methods. Since it's not actually part of the HAML, you cannot rely on syntax like %div{class: some_class}. Something like this:
def show_message(msg)
content_tag(:div, class: 'some_class') do
content_tag(:div, msg, id: "id")
end
end
See the content_tag documentation for more usage examples.

Ruby ERB - Create a content_for method

I'm currently working on an ERB View class for a gem. With this class I would like to have some helper methods for ERB templates.
It's okay about basic helpers like h(string). I found erbh gem who help me to understand more how context works.
But now I'm trying to create a content_for method like there is in Rails or Sinatra.
On first time I was using simple Proc to capture the view block and then just calling call method to print it. It was working enough at the beginning.
But after having completed views I saw wired thinks, some content are printed multiple times.
So I take a look on the Sinatra ContentFor helper to understand how they did it and I copy some methods of this helper. I have no errors, but the block return are always empty... and I don't really know why.
My knowledge about ERB are not good enough to know how ERB buffering works.
Code
Here a gist who explain the status of my code. I extracted the code from my library and simplified it a bit.
https://gist.github.com/nicolas-brousse/ac7f5454a1a45bae30c52dae826d587f/66cf76c97c35a02fc6bf4a3bc13d8d3b587356de
What I would like?
I just would like to have content_for methods works like they do with Rails and Sinatra.
Thanks!
After reading this blog article I finally found why it wasn't working. I don't know if I did it in the best way and cleaner way but it works.
So the bug was mainly from the ERB initilization. By using a property instead a local variable as eoutvar it now works.
erb = ERB.new(str, nil, "<>", "#_erbout")
I also change a bit the capture method who is used by content_for helper.
It looks like this now (gist)
def content_for(key, content = nil, &block)
block ||= proc { |*| content }
content_blocks[key.to_sym] << capture_later(&block)
end
def content_for?(key)
content_blocks[key.to_sym].any?
end
def yield_content(key, default = nil)
return default if content_blocks[key.to_sym].empty?
content_blocks[key.to_sym].map { |b| capture(&b) }.join
end
def capture(&block)
#capture = nil
#_erbout, _buf_was = '', #_erbout
result = yield
#_erbout = _buf_was
result.strip.empty? && #capture ? #capture : result
end
def capture_later(&block)
proc { |*| #capture = capture(&block) }
end

Ruby/Sinatra: Passing a URL variable to an .erb template

I'm using Padrino, and I want to take parameters out of URL and use them in an .erb template.
In my app setup I have:
get '/testpage/:id' do
userID = params[:id]
render 'test/index'
end
In my test/ folder I have index.html.erb which is successfully rendered, for a url like http://localhost:9000/testpage/hello123.
However, I've tried printing the params[:userID] on the page with:
<%= #userID %>
The rest of the page renders fine but hello123 isn't anywhere to be found. When I try <%= userID %> I get undefined local variable or method `userID' for #<stuff>
What am I missing here?
Just a guess, because I've never used Padrino, but if it works like Rails this may help you:
get '/testpage/:id' do
#userID = params[:id]
render 'test/index'
end
In sinatra, it's just like this (see "Views/Templates" section):
get '/testpage/:id' do |id|
erb :index, :locals => {:id => id}
end
The template is located in views/index.erb by default. It could be change.

How to implement form_tag helpers without actionview?

I'm working on a Sinatra app and want to write my own form helpers. In my erb file I want to use the rails 2.3 style syntax and pass a block to a form_helper method:
<% form_helper 'action' do |f| %>
<%= f.label 'name' %>
<%= f.field 'name' %>
<%= f.button 'name' %>
<% end %>
Then in my simplified form helper I can create a FormBuilder class and yield the methods to the erb block like so:
module ViewHelpers
class FormBuilder
def label(name)
name
end
def field(name)
name
end
def button(name)
name
end
end
def form_helper(action)
form = FormBuilder.new
yield(form)
end
end
What I don't understand is how to output the surrounding <form></form> tags. Is there a way to append text on only the first and last <%= f.___ %> tags?
Rails has had to use some tricks in order to get block helpers to work as wanted, and they changed moving from Rails 2 to Rails 3 (see the blogposts Simplifying Rails Block Helpers and Block Helpers in Rails 3 for more info).
The form_for helper in Rails 2.3 works by directly writing to the output buffer from the method, using the Rails concat method. In order to do something similar in Sinatra, you’ll need to find a way of writing to the output from your helper in the same way.
Erb works by creating Ruby code that builds up the output in a variable. It also allows you to set the name of this variable, by default it is _erbout (or _buf in Erubis). If you change this to be an instance variable rather than a local variable (i.e. provide a variable name that starts with #) you can access it from helpers. (Rails uses the name #output_buffer).
Sinatra uses Tilt for rendering templates, and Tilt provides an :outvar option for setting the variable name in Erb or Erubis templates.
Here’s an example of how this would work:
# set the name of the output variable
set :erb, :outvar => '#output_buffer'
helpers do
def form_helper
# use the new name to write directly to the output buffer
#output_buffer << "<form>\n"
# yield to the block (this is a simplified example, you'll want
# to yield your FormBuilder object here)
yield
# after the block has returned, write any closing text
#output_buffer << "</form>\n"
end
end
With this (fairly simple) example, an Erb template like this:
<% form_helper do %>
... call other methods here
<% end %>
results in the generated HTML:
<form>
... call other methods here
</form>

How invoke redirect method in Model

I wanna to write code like this
require 'sinatra'
class MyModel
def edit(request)
# ...
updateOK = true
redirect '/article_view' if updateOK
:article_edit
end
end
get '/article_view' do erb :article_view end
get '/article_edit' do erb :article_edit end
post '/article_edit' do
model = MyModel.new
erb model.edit(request)
end
but it dosn't work, it tips that: undefined method `redirect' for #<MyModel:0x24e3910>
Is there any way to invoke redirect method in the my custom model?
Haha, I know how to make the code works, despite it write in wrong way.
require 'sinatra'
class MyModel
def edit(context)
# ...
updateOK = true
context.redirect '/article_view' if updateOK
:article_edit
end
end
get '/' do erb :index end
get '/article_view' do erb :article_view end
get '/article_edit' do erb :article_edit end
post '/article_edit' do
model = MyModel.new
erb model.edit(self)
end
Don't. The model is not responsible for routing or redirecting.
Also, your post route looks borked. You are sending POST data to it which it then passes to the model. The model is created and saved. You can split up these two and if the model.save method returns true you redirect.
post '/model/new' do
model = Model.new params
redirect to("/model/#{model.id}") if model.save
end
Not everybody likes to forfard params to the model, so be careful about that too.
For edits you'd normally use the PUT method because you know the models address. So be careful to not mix them up (unless you know what you're doing) It will save you a lot of thinking.

Resources