I have the following code in my rails application,
<% = link_to variant.name, add_coaching_product_path(:variant => variant) %>
I want the above path add_coaching_product_path to send in a variable, and use in rails view.
I tried the following way, but it didn't work.
<% = link_to variant.name, "#{add_coaching_product_path}(:variant => #{variant})" %>
Note: add_coaching_product_path is kind of method created by Rails routes.
You can do it as below,
<% = link_to variant.name, send("add_coaching_product_path", { variant: variant }) %>
Instead of "add_coaching_product_path", you can pass dynamic path in form of string.
I have a model Post with a text attribute Body, and I want to be able to insert images into the text, using HTML tags, so an example Body value would look like this:
post.body = "this is text <img src='file.png'/> that was an image"
and then post.body.html_safe prints as text and an image. But this doesn't work on production, because the file name gets a unique hash during assets precompilation, so the only way to get the file's path is to use image_url. But when
post.body == "this is text <img src='#{image_url("file.png")}'"
the image's path is just literally "#{image_url("file.png")}" in plain text. I'm really confused about this because when I set the post.body in the view, it suddenly works, even though it's set to the same value. The only difference is where the value is set.
Here's a demonstration of what I mean:
Scenario 1
A post is created with a click and then edited with a form:
create button:
<%= link_to "new post", create_post_path %>
edit view:
<%= form_for(#post, url: update_post_path(#post)) do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body %>
<%= f.submit "save" %>
<% end %>
controller:
class PostsController < ApplicationController
def create
#post = Post.create
redirect_to edit_post_path(#post)
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
#post.update_attributes(post_params)
#post.save
redirect_to root_path
end
private
def post_params
params.require(:post).permit(:title, :body)
end
end
The saved blog view:
<% Post.each do |post| %>
<%= post.body.html_safe %>
<% end %>
The picture is missing and this is the text in the source code:
<img src='#{image_url("file.png")}' />
Scenario 2
Same thing as in Scenario 1
The saved blog view:
" %>
Picture prints normally and in the source code the img src is the full path of the file.
What's the difference between these two scenarios??? I'm so confused. I feel like it has something to do with Rails security measures when the post_params are being used but I have no idea.
Scenario 1
When you do:
Post.each do |post|
post.body.html_safe
end
ERB will not inspect the post.body content to interpret Ruby code. Code into the #{...} syntax in a string is only interpreted when you declare a string in ruby code, not when it is present in a string coming from database from example.
Formulated another way, when ERB sees post.body, it only understands "output the content of the string", but it will not parse the string searching for ruby code inside #{}.
The scenario 2 works because it has nothing to do with scenario 1. Scenario 2 isn't about ERB outputting a string. In scenario 2, the line post.body = "<img src='#{image_url("file.png")}' />" is interpreted by the Ruby parser, which will search for #{} in the string being defined, and will execute the code inside those.
Wrong solution
The first solution that comes to mind would be to parse manually the post.body content so it interprets Ruby code inside #{}, but that would be a huge security vulnerability if some untrusted people edit the posts, as they could write any Ruby code in the post and get it executed.
In the same style, you could use ERB <%= ... %> instead of Ruby's #{ ... }. Example: ERB.new("My name is <%= name %>").result.
Better solution
Don't save Ruby code within the post.body. You could save only the image filename in the src, and before rendering the post, apply some preprocessing to replace the filename with the full path. You will need a Ruby library to parse the post html and find all the img tags. One such library is Nokogiri.
I have been having some problems with variables inside link_to tags, which only get to work when wrapped in a raw.
What does raw actually mean? Is it a good practice to use it to wrap strings and variables inside a tag?
From the official Rails raw documentation:
This method outputs without escaping a string. Since escaping tags is now default, this can be used when you don't want Rails to automatically escape tags. This is not recommended if the data is coming from the user's input.
It's not a good practice to use raw because it bypasses the default Rails input sanitization. Use it only if you know what you are doing.
If you need to use raw HTML inside the link to, you can also pass it as a block.
<%= link_to root_url do %>
<span>My link</span>
<% end %>
Another alternative is to use the Rails helpers which sanitizes the input.
<%= link_to content_tag(:span, "Unsafe input"), root_url %>
raw outputs without escaping the string
raw docs
Why you had problems, link_to helper accepts block as argument, you can insert any content inside link_to helper
For example:
<%= link_to 'link' do %>
<p> First paragraph </p>
<%= 'ruby string' %>
<% end %>
Will produce
<a href='/link'>
<p> First paragraph </p>
ruby string
</a>
In a Rails 3.2 app I can DRY up view code that contains links to the current object by doing something like
#helper
def current_url(new_params)
url_for params.merge(new_params)
end
#shared partial
<%= link_to "JSON", current_url(:format=>:json) %>
But what if I need to link to a nested resource? For example, a polymorphic Comment model that is commentable to both a User and an Object model. This is set up with nested routes, so to link to a comments view I would use
#user view
<%= link_to "User's Comments", user_comments_path(#user) %>
#object view
<%= link_to "Object's Comments", object_comments_path(#object) %>
Is there a way to DRY this up without passing a local to a partial? I want something like
#shared/_comments_link.html.erb
<%= link_to "#{resource.class.name}'s Comments", #{resource}_comments_path(#{resource}) %>
But I can't see how to make the url_helper aware of what of the parent class is.
a helper would look like this
def link_to_comments(resouce)
name = "#{resource.class}'s Comments"
path = send :"#{resource.class.name.underscore}_comments_path", resource
link_to name, path
end
For the following code:
<%= link_to "Some Page", some_path %>
How do I apply a css class current using the current_page? helper method?
Or if some other better way is available?
In app/helpers/application_helper.rb
def cp(path)
"current" if current_page?(path)
end
In your views:
<%= link_to "All Posts", posts_path, class: cp(posts_path) %>
Basically write a simple wrapper around it. Additionally you could extend the method to allow additional classes to be applied by adding arguments. Keeps the views concise/dry. Or, without extending the method, you could just do simple String interpolation like so to add additional classes:
<%= link_to "All Posts", posts_path, class: "#{cp(posts_path)} additional_class" %>
In my case I have a lot of name spaced controllers, that is why I like to show if the current view also is in the Menu Path, I had use the solution of Michael van Rooijen and then I customize for my case.
Helper
def cp(path)
"current" if request.url.include?(path)
end
View
<%= link_to "All Posts", posts_path, class: cp(posts_path) %>
Now if my menu bar is /users and my current page is /users/10/post also the link /users is set with "current" class
I branched off of Michael's answer and tweaked the helper:
def active_class?(*paths)
active = false
paths.each { |path| active ||= current_page?(path) }
active ? 'active' : nil
end
Here's how you'd use it:
<%= link_to "Bookings", bookings_path, class: active_class?(bookings_path) %>
You can pass multiple paths to it in case you have a tab which could be rendered by multiple views:
<%= content_tag :li, class: active_class?(bookings_path, action: 'new') %>
And the great thing about this is if the conditions are false, it will insert nil. Why is this good? Well, if you provide class with nil it won't include the class attribute on the tag at all. Bonus!
In the interest of not having to repeat your self too much by having to check current_page inside the link_to method all the time, here's a custom helper that you can use (put this in app/views/helpers/application_helpers.rb
def link_to_active_class(name, active_class_names, options = {}, html_options = {}, &block)
html_options[:class] = html_options[:class].to_s + active_class_names if current_page?(options.to_s)
link_to name, options, html_options, &block
end
Example usage:
<div> <%= link_to_active_class('Dashboard', 'bright_blue', dashboard_path, class: 'link_decor') </div>
if you are on http://example.com/dashboard, then it should return:
<div> <a href='/dashboard' class='link_decor bright_blue'>Dashboard</a> </div>
Regards.
I'd do it this way :
<%= link_to "Some Page", some_path, :class => current_page? ? "current" : "" %>
I tried to combine a couple of the mentioned techniques with my own needs.
def current_page(path)
'current' if current_page?(path)
end
def create_nav_link(string, path, method)
link_to string, path, data: { hover: string }, method: method
end
def create_nav_item(string, path, method = nil)
content_tag :li, create_nav_link(string, path, method), class: current_page(path)
end
Basically it allows you to use it like this:
create_nav_item("profile", profile_path) which will result in:
<li>Profile</li>,
or <li class="current">Profile</li> if this is the current page.
I didn't use request.url.include?(path) since it will also always highlight the "Home" button, and I couldn't think of a work around by far.
A variant to Eric Boehs solution (the most robust one IMHO), if you are linking directly to an object of the class (i.e. you don't show the index), with an added application helper:
def booking_link
Booking.find(8)
end
You can use the following in the view (the dd is used in the context of zurb foundation)
<%= content_tag :dd, link_to(t('hints.book'), booking_link), class: active_class?(booking_path) %>-
I think if would be good idea if you generate whole link_to from your helper method. Why to repeat the same code ( :-) DRY principle)
def create_link(text, path)
class_name = current_page?(path) ? 'current' : 'any_other_class'
link_to text, path, class: class_name
end
Now you can use like:
<%= create_link 'xyz', any_path %> (in views) which would render as xyz
Hope it helps!