Rails - Best way / how to create Date-based Nav - ruby-on-rails

I'm new to rails and am working on extending the functionality of my basic blog app. What I would like to do is create date-based navigation links. For example, I would like to have a list of links with the names of the months (as the links) and when you click on the month it shows you all the articles published in that month.
I'm struggling with how to best accomplish this.
Should I create a new Model / View / Controller for something like an ArticleArchive? Or is the solution more simple based on my needs?
I've searched the other posts in the community and none seemed to answer this. Any help with how to structure this and possibly implement is appreciated. Thanks!

Here's an example on approaching this, although in this I wanted to sort it by day. This is for your controller action:
def index
#article_days = Article.all.group_by{ |r| r.published_at }
end
To modify this to months, you'd want to do something like r.published_at.beginning_of_month in the example above and essentially group_by the name of the month.
In the view template:
<% #article_days.sort.each do |pub, articles| %>
<h3><%= pub.strftime('%e %A, %B %Y') %></h3>
<% for article in articles %>
<%= article.title %><br/>
<%= article.summary %>
<% end %>
<% end %>
There's a screencast on this as well.
UPDATE
OK - so you only want the names of the month appearing. Keep the instance variable we setup in your index action along with your other code (you probably have setup #articles = Article.all). Then where you want the links listed do:
<% #article_months.sort.each do |pub, articles| %>
<h3><%= pub.strftime('%B') %></h3>
<% for article in articles %>
<%= link_to "#{article.title}", article_path(article) %>
<% end %>
<% end %>

Related

How much should I avoid computations in my views?

I am building an application where n users can talk to each other (like a messaging application) in public. Because you might want to have a different bio for each talk you do (for example a discussion about me regarding Ruby on Rails would need a different bio than one about Psychology) I have a Spkr model which has a User and a Tlk. The below code successfully means that on the users profile page, for each instance of them being a Spkr, the Tlk, and it's participants is visible with each Spkr's image (so if a Tlk has three participants, then all three images will be visible).
The setup is such where the default image is the User's image, but the Spkr can also customise their image by uploading one as a Spkr. I am worried that I am loading the front end with too much computation. Right now everything works... so is it ok? Or should I be limiting the computation happening when building views?
Thank you
<% #user.spkrs.each do |spkr| %>
<%= link_to show_tlk_path(spkr.tlk) do %>
<h4><%= spkr.tlk.title %></h4>
<% spkr.tlk.spkrs.each do |speaker| %>
<div class="tlk-tlking-image spkr-image image-spkr-<%= spkr.id %>"
<% if speaker.image.present? %>
style="background-image: url(<%= rails_blob_url(speaker.image) %>)"
<% elsif speaker.user.image.present? %>
style="background-image: url(<%= rails_blob_url(speaker.user.image) %>)"
<% end %>
>
</div>
<p><%= speaker.name %></p>
<% end %>
<% end %>
<% end %>
It tends to be considered good practice to keep the view as free of 'back end' calculations as possible. These files are often worked on by front end developers who may not even know how to code ruby, so the less of it that is in the view the better. It's also just not where it belongs in rail's Model Controller View framework.
First of all the code you've put can be simplified to:
<% #user.spkrs.each do |spkr| %>
<%= link_to show_tlk_path(spkr.tlk) do %>
<h4><%= spkr.tlk.title %></h4>
<% spkr.tlk.spkrs.each do |speaker| %>
<div class="tlk-tlking-image spkr-image image-spkr-<%= spkr.id %>"
style="background-image: url(<%= rails_blob_url((speaker.image || speaker.user.image) %>)"
>
</div>
<p><%= speaker.name %></p>
<% end %>
<% end %>
<% end %>
But as you say, if you want to handle this in a more appropriate place, I'd add a method to the Speaker class:
# app/models/speaker.rb
class Speaker << ApplicationBase
def image_for_view
image || user.image
end
end
This will let you call speaker.image_for_view which I think reads nicely in the view file itself.
Along with the great answer let me just add something that might help you to make views more clear. Might not be relevant to your question directly but might help you to get some idea how you can make views beautiful.
The first thing to make views look good are helpers. Though rails provide helpers for every controller, helpers are global meaning it can be used anywhere in any views. So, global formatings should be done with helpers. Like if you want a date formatter that needs to be used in a lot of view files, you can create a helper called date_helper.rb in app/helpers and put you desired date formatting -
module DateHelper
def formatted_date(date)
date.strftime([%m/%d/%Y')
end
end
Next is what rails people like to call a Presenter pattern. This is helpful when you don't want some logic to be shared across all views. Some logic that doesn't feel like belongs in controller or model are put there to make views readable. Suppose you have a view like below which is a bit messy -
<p>
Post title: <%= post.title.gsub("forbidden word", "") %>
<%= link_to "Read post", post, class: "w-75 p-3 text-#{post.draft? ? "orange" : "green"} border-#{post.draft? ? "orange" : "green"}" %>
</p>
To make this more beautiful you can create a presenter class named post_presenter.rb which should reside in app/presenters and write some code like -
class PostPresenter
def initialize(post)
#post = post
end
def title_without_forbidden_words
#post.title.gsub("forbidden word", "")
end
def css_color
#post.draft? ? "orange" : "green"
end
end
and in the view -
<% presenter = PostPresenter.new(post) %>
<p>
Post title: <%= presenter.title_without_forbidden_words %>
<%= link_to "Read post", post, class: "w-75 p-3 text-#{presenter.css_color} border-#{presenter.css_color}" %>
</p>
Such way a view might be more clear and also it can be lifesaver for frontend developers. This are the best two methods I found till now that makes a rails view beautiful which I always try to use.
Examples are taken from rubyguides website. Thanks to them,

rails if current id do

I'm developing an online course site using Ruby on Rails and bootstrap. I have a model for Courses and a model for Lessons. In the lesson controller show view I am not only showing the lesson page with video, discussion and notes but also a list of all the lessons belonging to the course. This is done by:
<%= #lessons.each do |lesson| %>
<%= link_to [lesson.course, lesson] do %>
<%= lesson.title %>
<% end %>
<% end %>
The user can, from the individual lesson page, pick the next lesson to watch.
Question:
The URL generated is: localhost:3000/courses/1/lessons/2 (I'll change this with Friendly Id gem later). I would like to show in the list of all lessons on the individual lesson page which lesson the user is watching right now. So basically maybe have something say "You are currently watching : Lesson with id 2" and have it in a different background color with some custom html. How can I have different HTML and CSS for the currently watching lesson in the all lessons list?
Thanks a lot in advance!
/Jacob
A simple way would be to add a conditional, in the case you have the current lesson available in a local variable or method current_lesson. Then it can look like this:
<%= #lessons.each do |lesson| %>
<% if lesson.id == current_lesson.id %>
... other html ...
<% else %>
<%= link_to [lesson.course, lesson] do %>
<%= lesson.title %>
<% end %>
<% end %>
<% end %>

Stuck trying to list associated model

I think I am deeply misunderstanding how to write instances.
Miniatures have_many Manufacturers through the Productions table.
On the miniatures show page I am trying to list all the manufacturers for the current miniature and have them link the Manufacturer show page. Like so:
<% #miniature.manufacturers.each do |manufacturer| %>
<%= link_to #miniature.manufacturer.name, manufacturer_path %>
<% end %>
Needless to say it does not work. It gives "undefined method `manufacturer'".
I have tried A LOT of different combinations to no avail. The following version puts all the manufacturers, rolled into one link, once for each manufacturer a miniature has, and links to /manufacturers. A big mess.
<% #miniature.manufacturers.each do |manufacturer| %>
<%= link_to #miniature.manufacturers.map(&:name).join(', '), manufacturer_path %>
<% end %>
I have been working on other things and hoping I would get the hang of this but I'm pretty sure it's something pretty fundamental about how I set up the instance.
If it's more likely something I need to add to the controller then I can add my controller code here. Any help much appreciated.
Does this work:
<% #miniature.manufacturers.each do |manufacturer| %>
<%= link_to manufacturer.name, manufacturer_path(manufacturer) %>
<% end %>

Creating Links on Rails

I've just started using rails yesterday, so this is a kinda noob question
for example, a user is at www.example.com/name
and I want to make several links to www.example.com/name/:id
So I tried something like this:
<% #items.each do |item| %>
<%= link_to item.name, '/name' :id %>
<% end %>
I know, it was a complete guess on how I should write the code, but the restful code sends to a completely wrong link. How should I write this three lines?
Use the route helper:
<% #items.each do |item| %>
<%= link_to item.name, item_path(item) %>
<% end %>
ps: when you have a simple question like this one, take a look at this guide, you'll often find the answer.
Try
<%= link_to item.name, item_path(item) %>
item_path is a URL helper method which spits out the link to show a name.
URL helpers have the general form:
{action}_{class}_path({object or object_id})
If {action}_ is omitted, then the default action is assumed (normally show).

Ruby on Rails: grouping blog posts by month

Hy guys. I've created a simple blog app with the usual CRUD actions. I've also added a new action in the PostController called "archive" and an associated view. In this view I want to bring back all blog posts and group them by month, displaying them in this kind of format:
March
<ul>
<li>Hello World</li>
<li>Blah blah</li>
<li>Nothing to see here</li>
<li>Test post...</li>
</ul>
Febuary
<ul>
<li>My hangover sucks</li>
... etc ...
I can't for the life of me figure out the best way to do this. Assuming the Post model has the usual title, content, created_at etc fields, can someone help me out with the logic/code? I'm very new to RoR so please bear with me :)
group_by is a great method:
controller:
def archive
#this will return a hash in which the month names are the keys,
#and the values are arrays of the posts belonging to such months
#something like:
#{ "February" => [#<Post 0xb5c836a0>,#<Post 0xb5443a0>],
# 'March' => [#<Post 0x43443a0>] }
#posts_by_month = Posts.find(:all).group_by { |post| post.created_at.strftime("%B") }
end
view template:
<% #posts_by_month.each do |monthname, posts| %>
<%= monthname %>
<ul>
<% posts.each do |post| %>
<li><%= post.title %></li>
<% end %>
</ul>
<% end %>
#Maximiliano Guzman
Good answer! Thanks for adding value to the Rails community. I'm including my original source on How to Create a Blog Archive with Rails, just in case I butcher the author's reasoning. Based on the blog post, for new developers to Rails, I'd add a couple suggestions.
First, use Active Records Posts.all method to return the Post result set for increased speed and interoperability. The Posts.find(:all) method is known to have unforeseen issues.
Finally, along the same vein, use beginning_of_month method from the ActiveRecord core extensions. I find beginning_of_month much more readable than strftime("%B"). Of course, the choice is yours.
Below is an example of these suggestions. Please see the original blog post for further detail:
controllers/archives_controller.rb
def index
#posts = Post.all(:select => "title, id, posted_at", :order => "posted_at DESC")
#post_months = #posts.group_by { |t| t.posted_at.beginning_of_month }
end
views/archives/indext.html.erb
<div class="archives">
<h2>Blog Archive</h2>
<% #post_months.sort.reverse.each do |month, posts| %>
<h3><%=h month.strftime("%B %Y") %></h3>
<ul>
<% for post in posts %>
<li><%=h link_to post.title, post_path(post) %></li>
<% end %>
</ul>
<% end %>
</div>
Good luck and welcome to Rails!

Resources