How do I yield results from multiple statements in a Ruby block? - ruby-on-rails

I have a pair of Rails helpers, one of which is meant to accept a block and the other of which just renders a button. Here are simplified versions the helper definitions:
def nav_wrapper(nav_at, id, css_class, &block)
"<ul class="complicated">\n #{yield} \n</ul>".html_safe
end
def nav_btn(nav_at, caption, id = caption.downcase.dasherize)
"Nav button codes goes here".html_safe
end
I'm trying to set things up such that I can do something like this:
<%= nav_wrapper(#nav_at, "Top Nav", "class") do %>
<%= nav_btn(#nav_at, "Foo", "id") %>
<%= nav_btn(#nav_at, "Bar", "id") %>
<%= nav_wrapper(#nav_at, "Sub Nav", "class") do %>
<%= nav_btn(#nav_at, "SubFoo", "id") %>
<%= nav_btn(#nav_at, "SubBar", "id") %>
<% end %>
<% end %>
But, the yield in the nav_wrapper method only picks up the last statement of each block. So in this example, I get the Top Nav wrapper, Foo and Bar are skipped, I get the Sub Nav wrapper (being the last statement in the outer nav_wrapper block), SubFoo is skipped, and I get SubBar (being the last statement in the inner nav_wrapper block).
I know that the reason for this behavior is that the block of code is implicitly returning the last evaluated value, but I know there are lots of template helpers that render all the interstitial lines (form_for, for example). Can someone help me figure out what the magic trick is here?

When a ERB template is compiled, it is converted to code which adds strings to a buffer. (Look at the source code for ERB to see what I mean.) Methods from Action View like form_for execute the block, and then retrieve the text in the ERB buffer.
Open up the lib/ruby/1.9.1/gems/1.9.1 folder, and look for actionpack. Open up whatever version of Action Pack you have, and go to lib/action_view/helpers/capture_helper.rb. There is a method in there called capture, which is used by form_for to execute a block and retrieve the text generated by ERB.
If you are writing a Rails helper, then presumably capture will be available to your code. If not, try include ActionView::Helpers::CaptureHelper.

Related

Rails output polymorphic associations

I want to implement a search functionality in my Rails app by using the pg_search gem. I've set up everything like it says in the documentation. Then I've set up a search controller with a show action:
def show
#pg_search_documents = PgSearch.multisearch(search_params)
end
The search itself works but I have a really annoying problem in my view. Whatever I do, it always outputs an array of PgSearch::Document objects. Even when I only write this in my view:
<%= #pg_search_documents.each do |document| %>
<% end %>
I get this (I've shortened it):
[#<PgSearch::Document id: 2, content: "…", searchable_id: 28, searchable_type: "Vessel">, #<PgSearch::Document id: 3, content: "…", searchable_id: 27, searchable_type: "Vessel">]
I know that pg_search sets up a polymorphic association which I've never dealt with before — could that be the problem?
Thanks in advance
<%= #pg_search_documents.each do |document| %>
<% end %>
This is a classic error, one I remember being puzzled over when I first started learning Rails. The mistake is using <%= %> with each. The return value of each is the array that you're iterating over (in this case, #pg_search_documents), and by using <%=, you're telling Rails to create a string from that array and insert it into your view. That generally isn't what you want: you want the view to be generated by the code inside the block you're passing to each.
Use <% #pg_search_documents.each do |document| %> instead (omitting the =) and you'll avoid the dump of the array's content.
You may also need to use .searchable as #blelump suggests, but I wanted to answer the other half of your question, as it's a common pitfall.
To get back to the original source model, searchable call is needed on these search result records, e.g:
<% #pg_search_documents.each do |document| %>
<%= document.searchable %>
<% end %>
You can also switch back to the source model within your controller, e.g:
#pg_search_documents = PgSearch.multisearch(search_params).collect(&:searchable)
Then, the #pg_search_documents will contain Vessel elements.

Difference between render 'posts' and render posts (without quotes)

I was going through a video and where he was rendering something like below example:
<div>
<%= render posts %> # no quotes to posts
</div>
Though he has even created a partial with _posts.html.erb, he is calling with quotes to posts.
Though he has mentioned something about it like it calls, active record model, then class and then something...i could not understand it properly. Can anyone explain clearly this with simple example.
Render with quotes
<%=render 'post'%>
Rails is going to look in the current folder for a partial file which starts with _
Render without quotes
Is still going to use the same partial, but post in this case is a variable. I think this is translating to this:
<%= render partial: "post", locals: {any_string: your_variable(in this case is post)} %>
Again I haven't checked that.
The _post.html.erb is the partial, which can look like this:
<b><%=any_string%></b>
If your_variable which was assigned to any_string will contain the string 'My name is'
Your partial will print 'My name is' in bold.
Anyway partial are more complex, and they are used for DRY-ing (Don't repeat yourself) the code.
You can see more examples here.
With quotes then you are explicitly rendering a partial of that name. Without quotes something quite interesting is happening. posts (without quotes) is a variable that will be an activemodel list of records.
Now what the call to render does is it will look at the type of each of the models and then find the correct partial for the model (which will be the name of the model camel_cased) and render each one in turn.
EDIT:
If you have a model called Post and you assign some of those records to a variable (he uses posts I assume but I'll use foo to disambiguate) like so:
foo = Post.all
then by calling render foo the render function will see that you have an activerecord collection of records, it will then check the model associated with these records (Post in our example) and will loop through all of them rendering them to a partial called _post.html.erb with a local variable for each record assigning the record to post.
<%= render foo %>
is equivalent to:
<% foo.each do |my_post| %>
<%= render partial: "post", locals: {post: my_post} %>
<% end %>

Ruby on Rails puts in erb

I'm working on a web application that has a view where data is fetched and parsed from a text file (the textfile is only available at the backend, not to the user). I've written a function that takes in the text file and converts it to an array of strings, it's called txt_to_arr. Then I have another function line_fetcher which just calls txt_to_arr and outputs a random string from the array.
In my view, I call the controller's function as so: <% line_fetcher %>.
I've put both txt_to_arr and line_fetcher into the view controller's helper rb file, and when I run rails s, the random string is not rendered at all. I've also tried <% puts line_fetcher %>
I've checked in Bash that the function does output random strings from the text file, so the function does work correctly. Also, the text file being parsed is in the public folder. Does anyone have an idea why this might be?
Thanks a lot!
Try placing the code in the controller and assigning the output to a variable using
a=`line_fetcher` (note the backtics) as detailed at
http://rubyquicktips.com/post/5862861056/execute-shell-commands
and then <%= a %> in your view.
and place the file in the root of your rails app
Simple erb like <%= line_fetcher %> would work good for simple variables.
But if you want output of any model/database instance then do:
<%= ModelName.first.inspect %>
Note the inspect word.
And in case of using HAML do:
=ModelName.first.inspect
In ERB: The <% %> signify that there is Ruby code here to be interpreted. The <%= %> says interpreted and output the ruby code, ie display/print the result.
So it seems you need to use the extra = sign if you want to output in a standard ERB file.
<%= line_fetcher %>
Use <%= %> to output something in your view, so:
<%= line_fetcher %>

Document partial parameters in Rails

Is there any standard or emerging standard to document the parameters that can be passed into a Rails partial ?
When _my_partial.html.erb expects a title and an elements local var passed with render 'my_partial', title: t, elements: e, there must be a common way to document their names, expected types and roles, without reading the whole partial code. Something like RDoc or Tomdoc for methods and classes. Isn't there ?
Edit: I've found a post whose author advocates initializing parameters with <% var ||= 'default_val' %> in the first lines of the partial, which is indeed a safe practice and a kind of in-code doc. Is there really no comment/parameter-declaration solution for this ?
At the beginning of your partial, simply call all the variables that are referenced.
# _my_partial.html.erb
<% title %> <--- first line of file
<% elements[0] %>
<h3><%= title %></h3>
<% elements.each do |element| %>
<p> etc ... </p>
Reasons why this is good for your project:
it does not rely on comments or non-code files
any developer on the project can quickly find out which variables are needed by looking at the top of the file in question
by calling the variables, you ensure that a missing variable will result in an exception.
elements is called with square brackets because we also want it to blow up if it's not an enumerable, right?
The practice of using <% var ||= 'default_val' %> is actually unsafe because it allows bugs to hide. You want your code to immediately blow up the moment something isn't done right. And if these variables should be passed, then you want the code to blow up when they're not there.

Rendering a variable with erb

I've got the following problem: I have rhtml (html minced together with ruby inside <% %> and <%= %> tags) stored in a database which I want to render. The information is acquired through a query. I need to be able to evaluate the information I get from the database as though as it was normal content inside the .erb-file. What I currently have:
<% #mymods.each do |mod| %>
<%= render_text(mod["html"])%>
<% end %>
Where mod["html"] is the variable containing the rhtml-code and #mymods an array of objects from the query. I have currently no idea what function I should use (render_text does, of course, not work).
Help is greatly appreciated.
/TZer0
You can use the ERB object to render text without the text being in a file.
Just pass the text with the <%= %> tags. You could put something like the following as an application_helper function.
def render_erb_text(text, args={})
b = binding
template = ERB.new(text, 0, "%<>")
template.result(b)
end
And then in your template
<%= render_erb_text("<%= %w(hi how are you).join(' - ') %>")%>
You might also consider rendering the text in your controller as you can handle any render errors better there than during view evaluation.
Take a look at the ERB documentation for more information regarding variable binding etc.
I'm not familiar with the details of how this works under the covers, but there could be some serious risk in running this code on bad or malicious database data. Evaluating ruby code from user input or any un-vetted source should be done very carefully, if at all.

Resources