Yield and default case || do not output default case - ruby-on-rails

I have a simple yield use case and for some unknown reason the default case is never shown:
In my super_admin layout I have:
<%= yield :body_id || 'super_admin_main' %>
My controller
class Superadmin::GolfsController < ApplicationController
layout "super_admin"
def show
end
end
My show view
With or without
<% content_for(:body_id) do %>sadmin_golfs<% end %>
With: sadmin_golfs is shown.
without: empty string is shown instead of super_admin_main
Can anyone reproduce the same behavior ?

Try <%= yield(:title).presence || 'My Default Title' %>
Object#presence is equivalent to object.present? ? object : nil (AS 3 rc docs), and essentially allows the traditional syntax with the titles.

Use parentheses:
<%= (yield :body_id) || 'super_admin_main' %>
Or
<%= yield(:body_id) || 'super_admin_main' %>
Without them it is assuming yield (:body_id || 'super_admin_main')
EDIT: Rails 3 uses ActiveSupport::SafeBuffer instead of string/nil (Rails 2), so the output is not nil even if there is no content_for provided. So try:
<%= yield(:body_id).empty? ? 'super_admin_main' : yield(:body_id)%>

Why no test if there are a content_for or not define in view compilation.
In the content_for code we can see :
def content_for(name, content = nil, &block)
ivar = "#content_for_#{name}"
content = capture(&block) if block_given?
instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}".html_safe)
nil
end
So in your case, the #content_for_body_id is define if a content_for is in your view.
You can made :
<%= instance_variable_defined?('#content_for_body_id') ? yield(:body_id) : 'super_admin_main' %>
If you prefere you can generate an helper after
def yield_or(part, result)
instance_variable_defined?("#content_for_#{part}") ? instance_variable_get("#content_for_#{part}") : result
end
and call it in your view by
<%= yield_or(:body_id, 'super_admin_main') %>
It's works only with Rails 2.3.x
In Rails 3 :
there are this method content_for?

In rails 3
raises undefined method `present'

I know this is an old question but I have a solution for Rails 2.3.
I've extended shingara's yield_or helper method above so it can now accept a block:
module ApplicationHelper
def yield_or(name, content = nil, &block)
ivar = "#content_for_#{name}"
if instance_variable_defined?(ivar)
content = instance_variable_get(ivar)
else
content = block_given? ? capture(&block) : content
end
block_given? ? concat(content) : content
end
end
and this can be used in your templates:
<% yield_or :something do %>
<p>something else</p>
<% end %>
or
<%= yield_or :something, 'something else' %>

<div class= <%= (yield :content_with_bunners).present? ? yield(:content_with_bunners) : "col-md-10"%>>

You can use content_for?(:body_id), the code will be like.
<%= content_for?(:body_id) ? yield(:body_id) : 'super_admin_main' %>

Related

Rails print user input and tag at the same time

I would like to print with <%= %>. PS: the following works but looks bad.
<%= "String: #{html_escape #user.input} <br>".html_safe unless #user.input.blank? %>
I know I can do
<% unless #user.input.blank? %>
String: <%= #user.input %><br>
<% end %>
Is it possible to write a method that looks like that:
def print_if_not_blank (string, input)
string.insert(html_escape input).html_safe unless input.blank?
end
printf_if_not_blank "String: #{} <br>", #user.input
I guess I could always have a param like "String: EVIL < br>" and than replace "EVIL" but that's not better than my current solution.
You could write a helper like this:
def print_if_present(template, input)
return unless input.present?
string = template % { input: html_escape(input) }
string.html_safe
end
What is used like this in your view:
<%= print_if_present('String: #{input} <br>', #user.input)

How do i add an active class to a dynamic link?

I have a sidebar that holds a list of posts. I need the corresponding post on my sidebar to have an active class. What i have currently does not work so what is the best way to do this?
def is_active?(path)
current_page?(path) ? "active" : ""
end
<% #posts.each do |post| %>
<%= link_to post.title, post, class: is_active?(posts_path) %>
<% end %>
As I said in my comment, methods ending by ? should return a boolean value. If you decide to go against the convention this will make things harder for us.
I suggest you actually use an active_link_to like it is explained in that question.
However the main problem was that you didn't generate the URL properly for each post :
is_active?(posts_path)
The posts_path is the path to the index and not the individual post resource. You should use something like post_path(post)
You want to do something like this :
First your is_active? method, because it has a ? should return a boolean
def is_active?(path)
current_page?(path)
end
Then you can use it this way (you need to get the URL of the post using the post_path(post) helper)
<% #posts.each do |post| %>
<%= link_to post.title, post, class: ('active' if is_active?(post_path(post))) %>
<% end %>
EDIT : because is_active? does the same thing as current_page? you should simply replace the is_active? code by an alias declaration
alias :is_active? current_page?
I had to develop such a solution some years before. I implemented the following as helper methods to detect an active link. Be aware that this is not the best solution I think. I had to provide a full url. Fell free to edit the code to use a path or a parameter hash instead.
# Returns true or false if the page is displayed that belongs to the given url.
def link_selected?(url)
request_method = request.method.to_sym
begin
url_params = Revolution::Application.routes.recognize_path(
url, {:method=>request_method}
)
rescue Exception => e
{}
end
begin
request_params = Revolution::Application.routes.recognize_path(
request.url, {:method=>request_method}
)
rescue
{}
end
return true if url_params == request_params
return false
end
def is_active?(url)
return link_selected?(url) ? 'active' : nil
end

Adding a class to a helper method call

So I have a helper method that I am trying to apply css to without putting it in a div or any other element. How would I go about applying the css class to this helper in rails?
I tried:
<%= first_letter_content(e.content), :class => "first-letter" %>
and
<%= (first_letter_content(e.content), :class => "first-letter") %>
both resulting in syntax error, unexpected ',', expecting ')'
Helper code:
def first_letter_content(content)
first_letter = content[0]
return first_letter
end
Any suggestions? I have been trying to find the proper syntax, but no luck.
Your helper does not support options (extra args) but you are trying to give a HTML class to the element.
You should wrap the content of first_letter_content inside a div/span (depending on what you want, block or inline) and apply the class on this HTML element:
<div class='first-letter'>
<%= first_letter_content(e.content) %>
</div>
Or you can directly wrap the content[0] inside a div in the helper method:
def first_letter_content(content, options = {})
content_tag(:div, content[0], options)
end
And use it like this:
first_letter_content(content, class: 'first-letter')
first_letter_content(content, class: 'first-letter', id: 'something')
first_letter_content(content)
Also, you can refactor your helper method to this:
def first_letter_content(content)
content[0]
end
It is a minor improvement but in Ruby the "last thing" used in a method will be returned by this method.
Examples:
def something
a = 2
b = 3
a
end
# => returns `2`
def something_else
a = 2
b = 3
end
# => returns `3`
def whatever
a = 12
nil
end
# => returns `nil`
I am trying to apply css to without putting it in a div or any other element
Css classes are for DOM elements, so you should wrap this content into some element/node.
For example:
def first_letter_content(content, css_class)
content_tag(:div, content[0], class: css_class)
end
Call:
<%= first_letter_content(e.content, "first-letter") %>

ERB -- content_for_with_default

I am trying to reduce the repetitive code with the following pattern in an ERB template:
<% if content_for(some_key) %>
<%= yield(some_key) %>
<% else %>
Some default values here
<% end %>
I've tried defining the following method in ApplicationHelper but understandably it's not working as expected;
def content_for_with_default(key, &block)
if content_for?(key)
yield(key)
else
block.call
end
end
Here's how I'm trying to use it:
<%= content_for_with_default(some_key) do %>
Some default values here
<% end %>
How can I write the content_for_with_default helper so that it has the intended effect?
Your helper should be like this:
def content_for_with_default(key, &block)
if content_for?(key)
content_for(key)
else
capture(&block)
end
end
EDIT: difference between capture(&block) and block.call
After the erb file is compiled, the block will be some ruby code like this:
');#output_buffer.append= content_for_with_default('some_key') do #output_buffer.safe_concat('
');
#output_buffer.safe_concat(' Some default values here
'); end
You see, the strings within the block are concatenated to the output_buffer and safe_concate returns the whole output_buffer.
As a result, block.call also returns the whole output_buffer. However, capture(&block) creates a new buffer before calling the block and only returns the content of the block.

optional local variables in rails partial templates: how do I get out of the (defined? foo) mess?

I've been a bad kid and used the following syntax in my partial templates to set default values for local variables if a value wasn't explicitly defined in the :locals hash when rendering the partial --
<% foo = default_value unless (defined? foo) %>
This seemed to work fine until recently, when (for no reason I could discern) non-passed variables started behaving as if they had been defined to nil (rather than undefined).
As has been pointed by various helpful people on SO, http://api.rubyonrails.org/classes/ActionView/Base.html says not to use
defined? foo
and instead to use
local_assigns.has_key? :foo
I'm trying to amend my ways, but that means changing a lot of templates.
Can/should I just charge ahead and make this change in all the templates? Is there any trickiness I need to watch for? How diligently do I need to test each one?
I do this:
<% some_local = default_value if local_assigns[:some_local].nil? %>
Since local_assigns is a hash, you could also use fetch with the optional default_value.
local_assigns.fetch :foo, default_value
This will return default_value if foo wasn't set.
WARNING:
Be careful with local_assigns.fetch :foo, default_value when default_value is a method, as it will be called anyway in order to pass its result to fetch.
If your default_value is a method, you can wrap it in a block: local_assigns.fetch(:foo) { default_value } to prevent its call when it's not needed.
How about
<% foo ||= default_value %>
This says "use foo if it is not nil or true. Otherwise assign default_value to foo"
I think this should be repeated here (from http://api.rubyonrails.org/classes/ActionView/Base.html):
If you need to find out whether a certain local variable has been assigned a value in a particular render call, you need to use the following pattern:
<% if local_assigns.has_key? :headline %>
Headline: <%= headline %>
<% end %>
Testing using defined? headline will not work. This is an implementation restriction.
In my case, I use:
<% variable ||= "" %>
in my partial.
I don't have idea if that is good but for my is OK
I know it's an old thread but here's my small contribution: i would use local_assigns[:foo].presence in a conditional inside the partial.
Then i set foo only when needed in the render call:
<%= render 'path/to/my_partial', always_present_local_var: "bar", foo: "baz" %>
Have a look at te official Rails guide here. Valid from RoR 3.1.0.
This is a derivative of Pablo's answer. This allows me to set a default ('full'), and in the end, 'mode' is set in both local_assigns and an actual local variable.
haml/slim:
- mode ||= local_assigns[:mode] = local_assigns.fetch(:mode, 'full')
erb:
<% mode ||= local_assigns[:mode] = local_assigns.fetch(:mode, 'full') %>
I think a better option that allows for multiple default variables:
<% options = local_assigns.reverse_merge(:include_css => true, :include_js => true) %>
<%= include_stylesheets :national_header_css if options[:include_css] %>
<%= include_javascripts :national_header_js if options[:include_js] %>
Ruby 2.5
Erb
It's possible, but you must to declare your default values in the scope.
VARIABLE the word for replacement.
# index.html.erb
...
<%= render 'some_content', VARIABLE: false %>
...
# _some_content.html.erb
...
<% VARIABLE = true if local_assigns[:VARIABLE].nil? %>
<% if VARIABLE %>
<h1>Do you see me?</h1>
<% end %>
...
More intuitive and compact:
<% some_local = default_value unless local_assigns[:some_local] %>
If you do not want to pass local variable to partial each time you call it you do this:
<% local_param = defined?(local_param) ? local_param : nil %>
This way you avoid undefined variable error. This will allow you to call your partial with/without local variables.
A helper can be created to look like this:
somearg = opt(:somearg) { :defaultvalue }
Implemented like:
module OptHelper
def opt(name, &block)
was_assigned, value = eval(
"[ local_assigns.has_key?(:#{name}), local_assigns[:#{name}] ]",
block.binding)
if was_assigned
value
else
yield
end
end
end
See my blog for details on how and why.
Note that this solution does allow you to pass nil or false as the value without it being overridden.

Resources