Construct strings in ruby - ruby-on-rails

I have a model called Book and another one called Magazine.
They share the same index view, where pictures of the covers are shown.They are also displayed according to their appearance date, so instances of those two models are mixed in the view...
Each cover has a clickable tite, and leads the user to a description page for this particular book or magazine...
Now in my view,i want to be able to do something like :
<%= link_to document.title, "#{document.class.name.underscore}"_path(document) %>
So in the case of book, i want this line to be replaced by the path from book_path(document) when document is a book,and by the path generated by magazine_path(document) when the document is a magazine.
À la bash script syntax...
How would i realize this.
Thank you very much!

Try:
<%= link_to document.title, polymorphic_path(document) %>
Polymorphic path, when executed with a model, checks the class of passed model, brings it do underscored notation and executes model_name_path. Seems to be exactly what you need.

You can always do this with eval.
<%= link_to "Title", eval("#{document.class.name.underscore}_path(document)") %>
There is also send, which is cleaner, but also metaprogramming:
<%= link_to "Title", send("#{document.class.name.underscore}_path", document) %>

Related

How to interpolate ruby inside of an interpolated bit of ruby in ERB

I'm trying to create a situation where one user makes message templates and another one can plug in values. I'm using the best_in_place gem, which will allow a user to edit the message on the show page.
The problem is this. When I call the message, with the required erb to make the gem work, it treats all of this as a regular string, not as ruby.
This is unclear, I'm sorry.
Here's the code.
#announcement.content = "The <%= best_in_place #announcement, :train %> is arriving in five minutes."
/show.html.erb
<%= #announcement.content %>
I want it to put "The click to set train is arriving in five minutes." and if the user clicks where it says "click to set train," a text field will open for them to edit (this is something the best-in-place gem does).
Instead, it puts "The <%= best_in_place #announcement, :train %> is arriving in five minutes."
I understand why it is doing this, but I don't know how to make it instead interpret the ruby I'm trying to pass in.
Ideas?
Use regular old string interpolation:
#announcement.content = "The #{best_in_place #announcement, :train} is arriving in five minutes."
You can use ERB to render any ERB template string. In this case something like:
<%= ERB.new(#announcement.content).result %>
Although you likely won't have access to all your Rails helpers, etc.
The Rails way to do this:
#announcement.content_type = :arriving
Later:
<%= render(partial: #announcement.content_type)
In _arriving.erb:
The <%= best_in_place #announcement, :train %> is arriving in five minutes.
TL;DR: ERB is not Ruby, and Rails uses both at different times.
You want simple Ruby string interpolation here:
#announcement.content = "The #{best_in_place #announcement, :train} is arriving in five minutes."
This is unclear, I'm sorry.
Not to worry, the Rails framework throws so many different new concepts at you it can be frustrating for newcomers.
Start from this: the Ruby framework builds the answer to the user's browser from a collection of resources Each file is evaluated by an interpreter for its own language. The trick is: look at the extension.
Files ending in .coffee will be compiled into javascript, files ending in .scss will become CSS, and in the same way files ending in .erb will yield HTML.
ERB is a language composed of mostly HTML already, plus a tag that allows you to interpolate Ruby. ERB stands for Embedded Ruby.
What about files ending in .rb, like the file in which you (surely) are evaluating #announcement.content = "The <%= best_in_place[...]" (a controller, I guess)?
Well, that's just pure Ruby :) that's why the ERB interpolation syntax <%= ... > is not recognized.
What you want to do in the controller, is (as you're trying to do) preparing the data for the view. The ruby in the <%= ... > tag in ERB will have access to the controller's instance variables, i.e. the variables with an # in front defined in the controller. But to define those, inside the controller, you should rely on Ruby alone.
Take-home message:
Be aware of which language you are writing in at each moment. For example:
# show.html.erb
<p>Here is ERB, which will be interpreted straight into HTML</p>
<% "Inside the '<% ...' tag is Ruby, but results won't show up in the HTML because there's no '<%='."%>
<% which_language = "Ruby" # Even variable assignments, and comments, do work %>
<%= "Inside the '<%=' tag, you're writing and interpolating #{which_language} :)" %>
I think the fact that I wasn't clear made it hard to answer this question.
What I'm doing is transforming user-inputted text (using a method in the model, called by the controller) to replace certain keywords with erb tags that call the best_in_place plugin. In my view, when presenting this content to another user, I wanted to call this content, which is saved as an attribute in the database, in such a way that it would render correctly for the other user to have the best_in_place functionality active.
Here's what I ended up doing. It is working, but if you have better ideas, please let me know.
In the announcements#create view, the user creates an announcement with certain pre-defined blocks of bracketed text as well as free-input text. For example, they might write "[train] is leaving from [platform] in [time] minutes."
When they hit save, the controller's create action calls the construct_message method from the model. It looks like this:
def construct_message(msg)
msg.gsub! '[train]', '<%= best_in_place #announcement, :train_id, :as => :select, collection: Train::list_trains, place_holder: "Click here to set train." %>' #note: list_trains and list_platforms are methods on the model, not really important...
msg.gsub! '[platform]', '<%= best_in_place #announcement, :platform_id, :as => select, collection: Platform::list_platforms, placeholder: "Click here to set platform." %>'
msg.gsub! '[time]', '<%= best_in_place #announcement, :number_of_minutes, placeholder: "Click here to set." %>'
end
Then, when I want to show that attribute in my view, I'm using render :inline, like this.
on announcements/:id
<p id="notice"><%= notice %></p>
<p>
<strong>Content:</strong>
<% announcement = #announcement %>
<%= render :inline => announcement.content, locals: { :announcement => announcement } %>
</p>
This allows the erb call that I wrote into the attribute to be functional.
Also note that I'm choosing to use a local rather than instance variable here; this is because in announcements#index, I also render this text and the table there uses local variables.

Rails Display Link in Database Using link_to

In Rails, I have a "notifications" class, one field of which is "link". The links contained within this class are formatted like: exchange_path(6), where that is the path to the show action in the exchange controller.
I'm now trying to output this link as such:
<%= link_to "View Exchange", notification.link %>
This line is in a loop which begins as such:
<% #notifications.each do |notification| %>
When I click this link, it takes me to localhost:3000/users/exchange_path(6) instead of localhost:3000/exchanges/6 like I would expect. (The loop generating the faulty link is on localhost:3000/users/2)
this could be scary...
<%= link_to "View Exchange", eval(notification.link) %>
should evaluate and use the path helpers. but you need to be 100% sure that nothing bad gets put in the link field..
You could do this:
<%= link_to("View Exchange", "/#{notification.link.gsub('(', '/').gsub(')', '').gsub('_path', 's')}") %>
or set up a method in your model that formats it for you:
def format_link
link.gsub('(', '/').gsub(')', '').gsub('_path', 's')
end
and just call that in your link_to:
link_to("View Exchanges", notification.format_link)
This will only work if all the links are formatted exactly as the example in the question

Ruby on Rails: Create action seems to fail

I've been stuck on this problem for days. First off, I now know this code is horribly wrong. I've been trying to fix it, but it's way more important in the short term that this link is created. In my view (I'm so sorry), I call the create method like this, if a certain condition is met:
index.html.erb (controller: subjects_controller)
<%= Baseline.create(subject_id: sub.subject_id) %>
I do this several times on the page, from several controllers (i.e., FollowUp3Week.create(subject_id: sub.subject_id) works). All of the other controllers work. I've checked, and double checked, every controller action and compared them to each other, and they appear the same.
So instead of creating the record, it leaves something like this instead:
#<Baseline:0x007f944c4f7f80>
I'm at a bit of a trouble shooting loss. Once again, I know how wrong it is to have these database actions in the view. But I didn't know that when I made the page, and I really need this to function before I can take the time to learn how to rearrange everything through the MVC.
Any advice would be greatly appreciated. Let me know what other code you might want to look at.
EDIT 1.
link Creation:
<% if Baseline.where(subject_id: sub.subject_id).first != nil %>
<%= link_to "edit", baseline_path(Baseline.where(subject_id: sub.subject_id).first) %>
<% else %>
<%= Baseline.create(subject_id: sub.subject_id) %>
<% end %>
First of all, making DB calls in views is a big NO! NO!
Secondly, to answer why you see the output as
#<Baseline:0x007f944c4f7f80>
for
<%= Baseline.create(subject_id: sub.subject_id) %>
You are trying to render an instance of Baseline model. Its just how the instance would be displayed. If you want to display a particular attribute's value in view then just do
<%= Baseline.create(subject_id: sub.subject_id).subject_id %>
Also, this code will not create a link. To create a link you would have to call link_to helper in your view.
What you need to do is, move the Baseline.create call in the controller. Set an instance variable in the action which renders this particular view as below:
def action_name
#baseline = Baseline.create(subject_id: sub.subject_id)
end
After this in you view you can easily access all the attributes of #baseline instance.
For example:
To access subject_id
<%= #baseline.subject_id %>
To create a link for show page of #baseline, provided you have a RESTful route to show action for baselines
<%= link_to "Some Link", #baseline %>

how to get the actual username to display instead of #<User:0x5424a68> (ruby on rails 3)

In my view, I'm using this to display the user
Made a comment on <%= link_to activity.trackable.micropost.user, activity.trackable.micropost.user %>
When I do this, it works, but the link shows up as something like #<User:0x5424a68>
I tried using activity.trackable.micropost.user.username, activity.trackable.micropost.user.name, and other variations but they didn't work.
What do I need to add after .user?
The activity.trackable is from the PublicActivity gem.
Open rails console and type:
User.instance_methods.grep(/name/)
It will give you a list of methods on User that contain the string 'name'. Chances are, that you will find the method you are looking for in the list (if there is any).
Try
<%= activity.trackable.micropost.user.inspect %>
or
<%= activity.trackable.micropost.user.to_s %>
This should give you a decent idea of what you need to add..

Using link_to in Rails 3 on the end of a string

There is probably a really simple answer to this but, as I'm a Rails newbie, I'm having great difficulty identifying the appropriate syntax.
Basically, I want to display a string with a link on the end, in which "Jimmy" here represents both the individual record and the link to that record:
"This video was posted by Jimmy"
I'd like to create a local variable to store the string, so my initial thought was to create the variable as follows:
my_string = "This video was posted by " + (link_to user.name, user)
However, this doesn't appear to work. Instead, it simply displays the generated HTML in the browser, i.e.:
This video was posted by Jimmy
This isn't what I want - I obviously want it to display:
This video was posted by Jimmy
in which Jimmy is the link.
Any ideas? I've tried adding .html_safe to the end of the string, but that doesn't work.
Thanks!
A much easier way to do this would be:
<td>Video created by <%= link_to user.name, user %></td>
No need to use string concatenation or use <%= "Video created by" %>, there's no need to run that through the Ruby parser, just use the plain text version :)
Check out raw
<%= raw my_string %>
Though, assuming this is in your view, I don't know why you'd be storing this to some my_string variable.
<span>This video was posted by <%= link_to user.name, user %></span>
Thanks for your help! I managed to fix the issue without needing to declare a variable (I was trying to be too clever).
The final (elided) code, in case anyone is interested, is as follows (in a table cell):
<td><%= "Video created by " %><%= link_to user.name, user %></td>
As always, it turns out the code is much easier to apply once you know how :-)
Thanks again!

Resources