So i have rails app where all posts are displayed on /posts, which is where I want them. There, I have 10 posts per page. But, along with this page - i would like to take the last three posts and display them in a div on the root page.
Not sure where to start.
Thanks
Try this:
%div
-Post.last(3).each do |p|
%h1= p.title
%p= p.author
%p= p.content
Post.last(3) returns the last 3 posts that you're looking for. Hope this helps.
p.s. you may want to refactor this by moving the Post.last(3) into a variable in your controller like #latest_posts = Post.last(3)and iterate over that.
The last finder method will return the results in ascending order. If you want to return the results ordered by created_at in descending order, here's how I would approach it (unit test included).
app/models/post.rb
class Post < ActiveRecord::Base
def self.recent(max = 3)
limit(max).order(created_at: :desc)
end
end
spec/models/post_spec.rb
RSpec.describe Post, type: :model do
describe ".recent" do
it "returns the most recent" do
first_post = Post.create(created_at: 3.days.ago)
second_post = Post.create(created_at: 2.days.ago)
third_post = Post.create(created_at: 1.day.ago)
result = Post.recent(2)
expect(result).to eq([third_post, second_post])
end
end
end
In your controller(s):
#recent_posts = Post.recent
In your view:
<div id="recent-posts">
<ul>
<% #recent_posts.each do |post| %>
<li><%= post.title %></li>
<% end %>
</ul>
</div>
If you want to reuse the view code, put it into a partial and render that in your view(s).
Related
I don't even know how to phrase the question. I have a blog with a feed. When a person clicks through to the show page, I would like to have a link with the image to the next article in a right sidebar. When it gets to the first article in the database or the newest one I either don't want a link with a picture or maybe one to the oldest story in the databse to loop back around.
I have code working where it gets the next article and displays its cover photo with a link to it. If somone could help me write the conidtion for the first article in the databse so I don't get errors that'd be great. Here's the code I have:
the show page:
<div id="next-story-sidebar">
<%= link_to "next story", #next_home_blog, style: "font-size:20px;" %>
<%= image_tag #next_home_blog.image.to_s, style: "width:60px;height:60px;" %>
</div>
home_blog.rb
def next
self.class.where("id > ?", id).first
end
def previous
self.class.where("id < ?", id).last
end
def last
self.class.where("id = ?", id).last
end
home_blogs_controller.rb
def show
#home_blog = HomeBlog.find(params[:id])
#next_home_blog = #home_blog.next
end
error when I click the next story link which takes me to the first article in the database: undefined method `image' for nil:NilClass
It is because you need a base case for your queries.
self.class.where("id > ?", id).first
The issue is that if you have id of 1,2,3 and you are on number 3. This will return a 0 length collection and first on an empty collection it is nil.
To fix this you can either do nil checking everywhere in your app
<% if #next_home_blog %>
<div id="next-story-sidebar">
<%= link_to "next story", #next_home_blog, style: "font-size:20px;" %>
<%= image_tag #next_home_blog.image.to_s, style: "width:60px;height:60px;" %>
</div>
<% end %>
Or do something where you return a NullBlog to represent that concept and handle it more OO style. Here is a link to the NullObject pattern to get you starting if you want to investigate that. https://robots.thoughtbot.com/rails-refactoring-example-introduce-null-object
I have blog post that I want to put on 2 different show views. I want the 4 most recent post to show but all of my post are 1 of 2 different :post_types (manager or user). So i want 1 view to show the 4 most recent manager post and the other view to show the 4 most recent user post. Where should I put this logic, the controller or somewhere in the model and how do I make a method to get the 4 most recent post of each type?
Currently i have this in the controller
def index
#blog1 = Post.order(:date => :desc).first
#blog2 = Post.order(:date => :desc).offset(1).first
#blog3 = Post.order(:date => :desc).offset(2).first
#blog4 = Post.order(:date => :desc).offset(3).first
end
But it doesn't separate the post by type
This will do the job:
def index
#manager_posts = Post.where(post_type: 'Manager').order('date DESC').limit(4)
#user_posts = Post.where(post_type: 'User').order('date DESC').limit(4)
end
Then in your view
<% #manager_posts.each do |manager_post| %>
<%= manager_post.content %>
<% end %>
<% #user_posts.each do |user_post| %>
<%= user_post.content %>
<% end %>
This seemed to get it
#blog1 = Post.where(:post_type == 'Manager' ).order(:date => :desc).first
I have two tables which are one to many (1 challenge to many entry)
I want to get the last entry for all challenges but I also want to get the title of the challenge for that last entry.
So far I have:
def index
#discovers = Challenge.all.map{|c| c.entries.last}
end
How to I also add the fact I want the Challenge.title?
def index
#challenges = Challenge.all
end
Then inside your view
<% #challenges.each do |challenge| %>
<%= challenge.title %> # will give you challenge title
<%= challenge.entries.last %> # will give you last entry for the challnge
<% end %>
The RoR App has posts and categories, the main view is a row set of categories/posts. It shows posts (newer than 3 days) per category. Empty categories are not shown.
I want to sort the index view to show first the categories with more recent posts. So when a new post is created for a category, the category goes up to the first "row" of the app. Inside a category, posts should be also sorted from newer to older.
Therefore I need to implement two levels of sorting, categories with newest post go first and posts are sorted by creation date inside a category. I am really stuck, how would you implement this?
My controller:
def index
#categories = Category.includes(:posts).select{|c| c.posts.where(['created_at > ?', 3.days.ago]).count > 0}
end
My view:
<% #categories.each do |category| %>
# show category name
<% category.posts.where(['created_at > ?', 3.days.ago]).each do |post| %>
# show post title and date
<% end %>
<% end %>
UPDATE: to show what I've tried so far:
I tried to sort the #categories first, based on the posts' creation_date (as explained, Category has_many:posts). In my controller:
#categories = Category.includes(:posts).order('posts.created_at').select{|c| c.posts.where(['created_at > ?', 3.days.ago]).count > 0}
Then in my view, sort the posts as they are loaded:
<% #categories.each do |category| %>
# show category name
<% category.posts.order('created_at').where(['created_at > ?', 3.days.ago]).each do |post| %>
# show post title and date
<% end %>
<% end %>
Regarding the counter_cache sugestion; I understand it'd be only for optimization of database calls, although not strictly necessary.
SOLUTION: it can be done as stated in the previous UPDATE; but with the change of order('posts.created_at') per order('posts.created_at DESC') in the controller; and the change of order('created_at') per order('created_at DESC') in the view.
Can you try the following? (sorry for the joins but it will be faster)
Category.joins(:posts)
.select('categories.*')
.group('categories.id')
.where('posts.created_at > ?', 30.days.ago)
.having('COUNT(posts.*) > 0')
.order('posts.created_at DESC')
See SOLUTION in the original post.
Thanks a lot to all the open-handed contributors.
Come across a little stumbling block when linking to a post within my app. I am truncating the text of a post and providing a 'Read More' link. There are 2 areas where posts are viewed, one for everyone ( public) and also one for the admin user to edit/delete posts.
So in my public view I am doing this
<% #toppost.each do |t| %>
<div class="post-item">
<h2><%= t.title %></h2>
<ul id="newsHome">
<li><%= date_output(t.published_on) %></li>
<li><%= t.department.name %></li>
<li>by Admin</li>
</ul>
<% if t.comments.length > 350 %>
<p class="post-description"><%= truncate(t.comments, length: 350).html_safe %>
<%= link_to '...Read more', t %></p>
<% else %>
<p class="post-description"><%= t.comments.html_safe %></p>
<% end %>
</div>
<% end %>
However when clicking read more it takes me to the url
/posts/:id
which is actually the place where an admin user views posts, so at the moment a user will get redirected back to the home page as the posts controller has
before_filter :authenticate_user!
The place where all posts are viewed publicly are on specific news pages , for example
localhost:3000/tynewyddnews
localhost:3000/woodside
localhost:3000/sandpiper
localhost:3000/outreach
My question is how to link to that post at its location in the public part of the site.
Index action where top_posts method used(see below for method)
def index
#title = 'Home'
#toppost = Post.top_posts
end
top_posts method
def self.top_posts
#Array with each of the 4 departments - first record
top_posts = [
self.tynewydd_posts.first,
self.woodside_posts.first,
self.sandpiper_posts.first,
self.outreach_posts.first
]
#remove entry if nil
top_posts.delete_if {|x| x==nil}
return top_posts
end
Controller
def tynewyddnews
#title = 'Ty Newydd News'
tynewyddpost = Post.tynewydd_posts.reverse
tynewyddpost.pop
#tynewyddpost = tynewyddpost
#tynewyddpostlatest = Post.tynewydd_posts.first
end
Model
scope :tynewydd_posts, :include => :department, :conditions => {"departments.name" => "Ty Newydd"}, :order => "posts.published_on DESC"
There are another 3 scopes for the other departments, all the same looking for the condition department name
Hopefully ive added enough info, anything else needed please ask
EDIT
Been thinking this through and not sure if im on right track, but for each post i need it to link to its appropriate news page in the public pages controller.
tynewyddnews
sandpipernews
outreachnews
Woodsidenews
So in my link_to i need to pass a route to the appropriate action depending upon the type of post.. so how to give each post a type and then link to that?
Thanks
Wow, ok, where do I begin.
You need a route that points to that controller action, for example
`get '/tynewyddnews' => 'news#tynewyddnews', :as => 'public_news' # gives you the route public_news_path
in your view
= link_to 'Read More', public_news_path
So ya, that should do it. BTW you can also use the .truncate() method. You pass in the length as an argument, and it adds the ... for you