I got this question in a previous interview and couldnt do it , any idea?
What does this return? Where would it be used?
module ApplicationHelper
def show_flash
flash.map{|key, value| content_tag(:div, value, {:class => key})}
end
end
The 'flash' is a ruby-on-rails convention for storing information generated in one request (say, "invalid username" or "session not found" or "thanks for buying from us" or "cart updated") temporarily for being rendered into the next view from the client.
The flash is a hash-like object.
The .map method on hash-like objects will iterate over all items in the hash; in this case, the .map method is being passed a block that accepts two parameters (which it names key and value, because the key could be used to look up the value from the hash). The block uses the content_tag helper to output new <div> elements with the value from the hash and the CSS selector-class key.
So for a flash like this: {:name => "sars", :food => "pizza"}
It would emit HTML roughly like this: <div class="name">sars</div><div class="food">pizza</div>.
This is a clever little helper method that probably saves a fair bit of typing, but it makes some assumptions: order in the view doesn't matter, all the keys are either in the CSS already or the CSS is prepared to handle unknown class elements in a graceful way. This helper might only be used once in a template, but it'd still be helpful to have as a method that could be dropped into other projects later.
module ApplicationHelper
def show_flash
flash.map{|key, value| content_tag(:div, value, {:class => key})}
end
end
Related
I am reading the book Agile web developpment with rails 4.
there is a part where the products' cart is showing only if it is not empty, my question is the function in the view send to the helper only 2 attributes while in the implementation there are 3 parameters.
in the view I have the bellow code, which render to _cart where I have the cart show
<%= hidden_div_if(#cart.line_items.empty?, id: 'cart') do %>
<%= render #cart %>
<% end %>
the helper has:
module ApplicationHelper
def hidden_div_if(condition, attributes = {}, &block)
if condition
attributes["style"] = "display: none"
end
content_tag("div", attributes, &block) end
end
My question is the &block in this case receives id: 'cart' but is it a optional attibute? that why it comes with &. but what about attributes = {}?
I am really not sure how that is happening, could someone explain me a bit?
Thanks!!
The code between and including do and end is the block, and this is the third argument for hidden_div_if, which is simply passed on to content_tag. The & in the definition of hidden_div_if captures the block in your view, whereas the & in the call to content_tag expands it again to pass it along.
The answer here explains this idea nicely with a few examples. I recommend testing everything out yourself in irb to get a feel for it.
UPDATE: Haml, indeed, already does this automatically! However, I had a hash inside of link_to helper, not a Haml tag and didn't even notice it. Silly me! So the question is pretty much invalid.
Haml makes rendering boolean HTML attributes very easy:
%input{checked: #boolean}
renders a simple <input> if #boolean is falsy, or <input checked> otherwise.
Haml also makes it easy to render data attributes:
%a{data: { is_special: false } }
renders: <a data-is-special="false">.
Is there any way to ask Haml to interpret this custom data-is-special attribute as a boolean one? I would like to not have it present if a falsy value is assigned, and for it to be present if anything truthy is assigned.
Code in brackets is normal ruby code, so if only you could perform this task in ruby, you have yourself a solution. I came up with something like that:
def remove_false(hsh)
Hash[hsh.each_pair.select {|key, value| value}]
end
{data: remove_false(is_special: false)} #=> {:data => {}}
This solution does not play well if you have combined keys that you want to treat specially and normally in single hash.
I have a 'widget' that comprises an html/css block of code. It is a type of data layout, which I call the 'stack'.
The stack has bits of .erb (Ruby on Rails) embedded in it, which enters the data for each user.
I need to include this stack in multiple places, where it needs to represent different data from different models.
So, one stack might contain a field called #company.name and the other stack might contain #project.name || "Unidentified Project".
How does one refactor / organize this situation? Options that I can see:
Have two separate stacks, which would introduce redundancy and inconsistency, but would be an obvious answer to the problem without limit to scenario-specific customization.
Include if statements for every data point to test which circumstance the stack is being used for, but this is very code-ugly and unsustainably complicated for more than 2 stacks.
Some unknown unknown.
How would you tackle this?
One simple way would be to write the erb in a generic way so that it works for either a project or a company for example, in your Project model you could put:
def display_name
name || "Unidentified Project"
end
Then in your Company model put:
def display_name
name
end
When you render the ERB, pass in a variable with some generic name like main_object and call its display_name function. The ERB code would not know or care what class main_object is:
<%= main_object.display_name %>
If it bothers you to put display-related functions like display_name in your models, you could use the Presenter pattern. A present is basically a plain-old ruby object that you create from your model(s) and then pass to the view. I saw a good talk on this pattern by Jeff Casimir called "Fat Models Aren't Enough" and the slides are here:
http://en.oreilly.com/rails2011/public/schedule/detail/18514
David's solutions are good. In some situations you may also consider helpers or partials (e.g. if you want to include complex html). For example:
Helper
def display_name(object)
if object.respond_to? :name and object.name
object.name
else
if object.class.respond_to? :human_name
"Unidentified #{object.class.human_name}"
else
"Unidentified #{object.class.name}"
end
end
end
Partial
<%= render :partial => "stack/name/#{object.class.underscore}", :locals => {object.class.underscore => object} %>
With any complex ERB in app/views/stack/name/_project.html.erb:
<label style="<%= "background-color: red;" if project.name.blank? %>">
<%= project.name || "Unidentified Project" %>
</label>
I have a controller which does the following line before rendering the view and outputting an error.
flash[:error]="Flash error"
flash[:info] = "Flash info"
I would like to format this nicely. For that I wrote a helper which looks like this
def show_flash
a=""
[:success, :info, :error, :warning].each do |key|
a += content_tag(:div, flash[key], :id => key, :class => "#{key}") unless flash[key].blank?
end
end
In my view, I call:
<%= show_flash %>
When I try to run this, the web page renders the full text of show_flash, including the div tags, angle brackets and all. When I inspect the element (using Firefox or Chrome), it shows the text surrounded with double quotes.
Then I tried changing one line in the helper as follows:
a = content_tag(:div, flash[key], :id=>key, :class=>"#{key]") unless flash[key].blank?
i.e. I would only capture the last content tag (error) instead of both of them.
In the second case, the web browser rendered the div tag formatted properly with my CSS rules for the "error" class. I didn't see any div tags printed out in the browser.
Why did concatenating two content_tag elements cause me this grief?
I appreciate any help you can give me.
Because "" wasn't marked as html_safe. This is part of Rails' XSS protection that is enabled by default in Rails 3.
You may find this Railscast on XSS protection informative.
It turns out that when going from Rails 2 to Rails 3, html escaping is enabled by default, and you must explicitly disable it before concatenating content_tag strings. The code looks like:
def show_flash
a=content_tag(:span, "",:escape=>false)
[:success, :info, :error, :warning].each do |key|
a = a+content_tag(:div, flash[key], :id => key, :class => "#{key}", :escape=>false) unless flash[key].blank?
end
a
end
That option, :escape=>false is what it took to make it work.
Andrew Marshall pointed me in the right direction, and after some searching, I stumbled on the words of wisdom from Yehuda. That's where the :escape clause became obvious.
I'd like to be able to generate the following markup:
<label for="field">Something <span class="hint">Field hint</span></label>
from the following code:
form_for ... do |f|
f.label :field, :hint => "Field hint"
end
So far I've created an initializer to store the custom functionality which re-opens ActionView::Helpers::FormBuilder and changes the label method, however I'm not sure what the best way to actually get the span into the text for the label. If I try to put the text in directly then rails, rightly so, escapes the content.
I'd quite like to use the existing label infrastructure as it has all the validation error support. This rules out using content_tag and generating it all myself (which would work, but doesn't seem... right).
Instead of changing the default builder, you should create a custom builder and pass it to the form with the :builder parameter.
class HintFormBuilder < ActionView::Helpers::FormBuilder
end
form_for #resource, :builder => HintFormBuilder do |f|
# ...
end
The Hint builder inherits all FormBuilder features, including validation, error messages and so on. Now, you should change what you need to change in order to customize the behavior.
This is a really raw draft.
class HintFormBuilder < ActionView::Helpers::FormBuilder
(%w(label)).each do |selector|
src = <<-end_src
def #{selector}(method, options = {})
hint = options.delete(:hint)
returning(super) do |element|
# replace here the value of element with hint
# if hint != nil
# remember to use gsub! and not gsub
end
end
end_src
class_eval src, __FILE__, __LINE__
end
end
EDIT based on the first comment:
It's always a good idea to not hack the Rails internals because you might need to use, now or in the future, plugins or features that rely on the original behavior. If you don't want to manually append the builder in your forms, you can create an helper.
def search_form_for(record_or_name_or_array, *args, &proc)
options = { :builder => HintFormBuilder }
form_for(record_or_name_or_array,
*(args << options),
&proc)
end
If you want to reopen the original class instead, I would suggest to create a new method. This solution also applies to the custom helper and has the benefit you can customize it without the need to gsub! the response. Yes, gsub! is the common way to do so because when extending the original methods you only have access to the method/options and the result, no the value (that is injected by the #object variable).
class ActionView::Helpers::FormBuilder
def label_with_hint(method, text = nil, options = {})
hint = options.delete(:hint)
# do your own customizations...
#template.label(#object_name, method, text, objectify_options(options))
end
end
EDIT: I was mistaken, you can pass a custom text as a parameter so you don't need to gsub! the returned string. I got confused by the text_field tag.
At this point, you can use either the first (subclassing with/without custom method), second (hacking internals) or third option (hacking internals with custom method) and intercept the text value before it is sent to #template.label.
Also note that text can be nil. If nil, the value is automatically generated from method. You should be aware of this.
Here's what I would have done.
# config/initializers/[anything].rb
ActionView::Base.default_form_builder = CustomFormBuilder
# lib/custom_form_builder.rb
class CustomFormBuilder < ActionView::Helpers::FormBuilder
def label(field, text, options = {})
if options[:hint]
hint = #template.content_tag(:span, options[:hint], :class => "hint")
super(field, "#{field.to_s.humanize} #{hint}", options)
else
super
end
end
end