Can someone explain to me what this code is doing? - ruby-on-rails

I am currently building an online course and I was having trouble accessing a lesson that is associated with a certain course.
A developer friend of mine solved the problem for me but I'm not really sure why this code works and if there are different ways, more of a Rails conventional way to write this code.
<% #courses.each do |course| %>
<tr>
<td><%= link_to course.title, "courses/#{course.id}" %></td>
</tr>
<% end %>
I am not sure what this part "courses/#{course.id}" is doing. Is there a way to write this using a more conventional seeming names route helper?

It should be the same as course_path(course)
This call just figure out the path for you. The expression in your code simply build this path putting together "courses/" and the id of the course (but using interpolation, not concatenation).

As Ursus answer explains, courses/#{course.id} creates URL directing to specific course path by using string interpolation. For example, if #courses variable is an array with Course objects with ids: [1, 2, 3], then you will receive links directing to "course/1", "course/2", course/3".
To replace that interpolation, you can simply write
<%= link_to course.title, course %>
It will create the same output as "courses/#{course.id}"
To learn more about string intepolation, you can start here: http://ruby-for-beginners.rubymonstas.org/bonus/string_interpolation.html

For some reason your friend did this:
<td><%= link_to course.title, "courses/#{course.id}" %></td>
...instead of this:
<td><%= link_to course.title, course %></td>
...and I have no idea why. The second example is how you use links in Rails. The first example doesn't safeguard you against possible future URL changes.

Related

How to present JSON information in a html.erb file (Ruby)?

I am new to Ruby/Rails and I've been given a task to make a Ruby site (.html.erb) look a bit nicer. One of the things requested was to present information on the site, which is currently shown as JSON, as nice-looking html. The line of html.erb is as follows:
<%= #buyer.generate_profile.inspect %><br>
and will display the information it receives on the site as JSON. What can I do to parse through the JSON and make it so that the site will display the information as proper html?
You might want to check out JSON.pretty_generate. You could display the result in a code block like:
<%# If your data is a JSON string, convert it to a hash %>
<% hash = JSON.parse(#buyer.generate_profile) %>
<pre>
<code>
<%= JSON.pretty_generate(hash) %>
</code>
</pre>
So the generate_profile method returns a Hash object and calling inspect on it will create string output of the Hash syntax in Ruby, so the result looks like your comment.
If you wanted it to look like JSON, the simplest way would be to use the to_json method like this
<%= #buyer.generate_profile.to_json %>
but honestly, it's not much better. And even if you were to look into methods that can present it with proper line breaks, etc. that wouldn't improve it much either imo.
I would recommend you learn how to iterate over the keys and values in a Hash, because that would allow you to create a custom HTML layout that could look however you wanted it to.
As an example, I'll show you how you could create a simple table based on the data from your comment
<table>
<thead>
<tr>
<th>Key</th>
<th>XX</th>
<th>YY</th>
<th>ZZ</th>
</tr>
</thead>
<tbody>
<% #buyer.generate_profile.each_pair do |key, sub_hash| %>
<tr>
<td><%= key %></td>
<td><%= sub_hash[:xx] %></td>
<td><%= sub_hash[:yy] %></td>
<td><%= sub_hash[:zz] %></td>
</tr>
<% end %>
</tbody>
</table>
The main thing here is the use of each_pair which is a method that iterates over each key and value of the hash. In your case it sounds like the values of the first hash are sub-hashes, hence my use of sub_hash as the block argument.
You could technically use each_pair on the sub-hashes as well, but I'm guessing they all have the same keys, that's why I manually added columns and cells for XX, YY, ZZ in combination with sub_hash[:xx], etc.
Once you get a hang of how iterating over Hashes and arrays work, then I would recommend scrapping the table design and instead look into more modern web design approaches like Flex and CSS-Grid, but one thing at a time :)

How to avoid hitting database in the view

I get that one should not ping the database in the view... but wondering about the right solution. In one of my views, I need to pull info on an #order, it's child items, and also Amount, another model, based on each child item. Something like this:
<% #order.items.each do |item| %>
<td><%= item.name %></td>
<td><%= Refund.where(item_id:item.id).first.amount %></td>
<td><%= Amount.where(item_id: item.id).first.amount %></td>
<% end %>
For the sake of avoiding the db hits in the view, the only solution I've thought of is to create a huge hash of all the relevant data in the controller, which is then accessed from the view. So it would be something like this:
# controller (writing quickly, code may not be totally right, hopefully you get gist
data = Hash.new
data["items"] = []
#order.items.each do |item|
item_hash = {
"name" => item.name,
"amount" => Amount.where(item_id: item.id).first.amount,
"refund" => Refund.where(item_id:item.id).first.amount
}
data["items"] << item_hash
end
# view code
<% data["items"].each do |item| %>
<td><%= item["name"] %></td>
<td><%= item["refund"] %></td>
<td><%= item["amount"] %></td>
<% end %>
And I know SO hates this type of question... but I really need to know... is that the best solution? Or are there are best practices? The reason I ask is because it seems very clean in the view, but very bulky in the controller, and also it gets quite unwieldy when you have a much more complex set of nested tables, which is what I actually have (i.e., the data hash would be quite funky to put together)
First of I would use associations between item and the 2 other classes, so that you can do
item.refund
item.amount
Instead of Refund.where(...). You could further define methods such as
def refund_amount
refund.amount
end
And similarly for the other one (and hopefully come up with a better name than amount_amount.
This keeps both your view and controller clean but it won't be any faster. So far all of the approaches involve running 2 database queries per item which is the real issue as far as I'm concerned - whether those excess queries happen in the view or the controller is of lesser concern.
However you can avoid this with Active Record's include mechanism:
Item.include(:amount,:refund).where("your conditions here")
Will load the named associations in bulk rather than loaded them one at a time as each item is accessed.

Generating proper paths in namespaced Rails scaffolds

When you use rails generate scaffold admin/user --model-name=User or rails generate scaffold_controller --model-name=User it generates almost everything in a namespaced fashion. You get app/controllers/admin/users_controller.rb with your controller and app/views/admin/users/ filled with your views.
The one thing it doesn't get right is your paths. You have to manually go and replace references to user_path with admin_user_path and the like. This is pretty tedious.
Is there a way to tell Rails to generate the paths to point to your new namespace, rather than the namespace that the model is in?
Using Rails 4.
With rails build-in generators you can't.
See the generator source code to understand why:
<td><%%= link_to 'Show', <%= singular_table_name %> %></td>
<td><%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %></td>
<td><%%= link_to 'Destroy', <%= singular_table_name %>, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
As you can see, it goes with edit_<%= singular_table_name %>_path to generate the edit path, without considering name-spacing. (And haml-rails does the same)
The best thing to do, if you have time and patience for it, would be to fix this on the codebase and propose a PR. That's the main point of open-source after all.
If you go this direction, have a look first at open issues, I haven't dive deep into but it seems that different conversations are going on about that matter. Like https://github.com/rails/rails/pull/13927 or https://github.com/rails/rails/issues/21652
Or you can use existing gems like Beautiful-Scaffold that seem to be supporting namepacing

Possible to use Rails with no HTML? (Just Ruby) [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I'm a Rails beginner messing around with a super simple app.
What I'm wondering is whether you can use Rails strictly with the Ruby language and forget HTML all together.
Why I'm asking:
In my /testapp/app/views/welcome/index.html.erb file, (which is my homepage), I tested it with the code:
puts "Hello World!"
When I navigate to the page using the rails server the text includes the puts. I even changed the file name to /testapp/app/views/welcome/index.rb (omitting the html), it didn't work.
Can I use Ruby like this? Or do I need to modify something within Rails to use Ruby like this? I don't get it.
Thanks!
If you are going to be learning Rails you will need to do it in conjunction with HTML. When I learned I started with ERB and then moved to Haml.
If you want the page to just show static text you can put Hello World! right in the index.html.erb file.
Mostly though, you'll use these views to dynamically create web pages for you. That way if your website is storing books and users continue to add more books to the website, your index page will automatically include all the books everyone entered. In your index.html.erb that code would look something like:
<tbody>
<% #books.each do |book| %>
<tr>
<td><%= book.title %></td>
<td><%= book.body %></td>
<td><%= link_to 'Show', book %></td>
<td><%= link_to 'Edit', (edit_book_path(book)+"/hello?") %></td>
<td><%= link_to 'Destroy', book, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
I highly suggest running in your console:
rails generate scaffold books title:string author:string
Run the migration:
rake db:migrate
Fire up your server and go to localhost:3000/books
This should give you a visual example of what I'm talking about and then you can walk through the code to help you understand what Rails is doing.

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