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 :)
Related
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.
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.
I have been building a practice application for rails. It's an simple blog application with only articles model, and tagging system which is done using Postgresql Arrays. Everything is fine except, I couldn't get the pages for specific tags to working.
The Activerecord query to fetch the posts matching the given tags is working properly in rails console, but the same fails when tried with View. I have verified and the params are passing through, please help.. I don't receive any error messages, from what I could understand the rails passes the ActiveRecord::Relation::ActiveRecord_Relation_Article as a result of query to the view, but the view file fails to display it list by list...
Below are the relevant files,
routes.rb
get 'tags/:tag', to: 'articles#tagview', as: :tag
articles_controller.rb
def tagview
#articles = Article.where("'params[:tag]' = ANY (tags)")
end
tagview.html.erb
<% #articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.content %></td>
<td><%= article.tags.join(" ") %></td>
</tr>
<% end %>
The resulting page is just blank. whereas the above query works in Rails console.
The problem
Your original code sends a literal 'params[:tag]' string to the database, and it would have no idea what to do with it. The resulting query looks something like:
SELECT * FROM articles WHERE ('params[:tag]' = ANY (tags));
This only returns articles that has actually been tagged with the literal "params[:tag]" string.
The solution
I guess you intend for params[:tag] to be replaced with the actual tag value coming in from params. To do this, you'd need to do something like:
Article.where(['? = ANY (tags)', params[:tag]])
which is going to result in something like this being sent to the database (fx when params[:tag] is set to "foo"):
SELECT * FROM articles WHERE ('foo' = ANY (tags));
which is much more likely to be what you want.
Further reading
If you want to read more about how to construct queries with Active Record, the section about Conditions in the Active Record Query Interface guide is a good place to start.
You probably won't find anything in the official documentation that talks about using PostgreSQL arrays as that is not a general database feature, though.
Okay so in the index portion of my controller I set
#patients = Patient.all
then in patients_helper.rb
def race_abrev
return self.caucasian
end
where caucasian is an integer datatype column in the patients table
then in the view index.html.erb
<% #patients.each do |p| %>
<td><%= p.gender %></td>
<td><%= p.ethnicity %></td>
<td><%= p.race_abrev %></td>
<% end %>
I get a
undefined method `race_abrev' for #<Patient:0xb4d95cd8>
I've checked the table and I'm expecting patient.caucasian to return the integer 1, what am I missing..any insight to a fundamental misunderstanding I seem to have?
race_abrev is a helper, not a method on Patient:
<%= race_abrev(p) %>
And the helper itself would return p.caucasian, although it'd seem like you'd actually want to do something with the value of caucasian, like a compare or something.
All this said, I'm not sure why you're not defining it (or what "it" actually is) on the model, since so far it doesn't seem to have anything to do with the view, which is what view helpers are for.
If you're storing something in the DB you want to transform it may or may not belong in a view helper; if it's to turn it into something human-readable I'd be more likely to put it in the model.
I'm aware that variants of this question have been asked before, but I do believe my question is different ;-)
I have 2 Ruby on Rails classes (with assoc. controllers, models, etc). These are Devices and Messages.
In the Devices index.html view, I need to make reference to the newest Message record.
<table>
<tr>
<th>Device Name</th>
<th>Checked</th>
</tr>
<% newestMessage = Message.find(:all, :order => "updated_at DESC", :limit => 1) %>
<% #devices.each do |device| %>
<tr>
<td><%= device.name %></td>
<td><%= newestMessage.updated_at %></td>
</tr>
<% end %>
</table>
(I believe I should move the logic to a helper, but for the time being I'm keeping it as simple as possible while I get it working)
When I access this blahblah.com/devices, I get...
undefined method `updated_at' for #<Array:0x103f36c00>
Due to the line,
<td><%= newestMessage.updated_at %></td>
Now, I've seen questions like this that suggest to add the equivalent of the following to messages_controller.rb,
attr_accessor :updated_at
However, I've tried this and it doesn't help. Also, I don't think this should be necessary anyway as 'updated_at' is derived from the database, built with scaffold, etc.
If I just print 'newestMessage' it seems to have a sensible value.
My question is, how should I access fields within newestMessage, which are of class Message from the Device index.html.erb?
Thanks in advance.
Try newestMessage = Message.last then newestMessage.updated_at