Related
I'm building a most viewed post feature for a simple blog. Each post has a view count that is increased when the Show action is called for that particular post. Then on the Dashboard , I'm trying to list the top 5 posts. So far my code works and returns an array of posts with the post with the highest number of view count being the first index and the last index in the array being the post with the lowest view count. The only thing is when I try to iterate through the array in the view , the view returns:
ERROR
undefined method `title' for nil:NilClass
WHY??? Does it have to do with the "#" infront of the object?
Heres my code.
Dashboard View
<h3> Post </h3>
<% #top_posts.each do |post| %>
<%= post.title %>
<% end %>
Controller Methods
def get_top
#top_posts = []
counts = []
#posts = Post.all
#posts.each do |post|
counts << post.view_count
end
#posts.each do |post|
if counts.max(5).include?(post.view_count)
counts.max(5).each do |n|
if n == post.view_count
#top_posts[counts.max(5).index(n)] = post
end
end
end
end
end
def dashboard
#posts = Post.all
get_top
end
The Top Podcast Array of objects
[#<Post id: 6, title: "Post 6", desc: "", tags: "", view_count: 8, s_desc: "", c_photo: nil, photos: nil, created_at: "2017-06-14 06:02:25", updated_at: "2017-06-15 01:38:40", featured: nil>, #<Post id: 3, title: "post 3", desc: "", tags: "", view_count: 5, s_desc: "", c_photo: nil, photos: nil, created_at: "2017-06-14 05:35:32", updated_at: "2017-06-14 05:35:53", featured: nil>, #<Post id: 5, title: "Post 5", desc: "", tags: "", view_count: 4, s_desc: "", c_photo: nil, photos: nil, created_at: "2017-06-14 06:02:20", updated_at: "2017-06-15 01:38:31", featured: nil>, nil, #<Post id: 4, title: "Post 4", desc: "", tags: "", view_count: 3, s_desc: "", c_photo: nil, photos: nil, created_at: "2017-06-14 05:49:29", updated_at: "2017-06-15 01:38:50", featured: nil>]
The other answer would probably solve your error, but just want to make an attempt to optimize your code.
That array, loop, etc. is unnecessary, ask your db to do that stuff and get the top posts. Fetching all posts and looping over it multiple times...nay, try the following, hopefully this is what you are looking for..
#top_posts = Post.order('view_count desc').limit(5)
That's it, your view needs no change and will work as expected.
Try:
#top_posts << post
instead of:
#top_posts[counts.max(5).index(n)] = post
You don't need to set the array index.
Ok, trying to learn rails commands on associations relationships in console
So if I have User that has_many posts, and Posts that belongs_to user ...
That means all this should work, correct? And these are all the the methods added automagically for this particular relationship, correct?
1. user.posts
2. user.posts=(posts)
3. user.posts << post
4. user.posts.delete(post)
5. user.posts.empty?
6. user.posts.size
7. user.post_ids
8. user.posts.clear
9. user.posts.find
10. user.posts.build(attributes={})
11. user.posts.create(attributes={})
user is: user = User.create(:name => "Michael")
result is: #<User id: 3, name: "Michael", created_at: "2016-06-21 16:52:22", updated_at: "2016-06-21 16:52:22">
&
post is: post = Post.create(:body => "body of txt")
result is: #<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">
1. For user.posts, I get this:
#<ActiveRecord::Associations::CollectionProxy [#<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">, #<Post id: nil, user_id: 3, body: "txt of body2", created_at: nil, updated_at: nil>]>
2. For user.posts=(posts), I get this:
NameError: undefined local variable or method posts' for main:Object and a whole bunch more of rbenv stuff
3. For user.posts << post, I get this:
#<ActiveRecord::Associations::CollectionProxy [#<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">, #<Post id: nil, user_id: 3, body: "txt of body2", created_at: nil, updated_at: nil>, #<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">]>
5. For user.posts.empty?, I get this:
false
6. For user.posts.size, I get this:
3
7. For user.post_ids, I get this:
[3, nil, 3]
9. For user.posts.find, I get this:
ActiveRecord::RecordNotFound: Couldn't find Post without an ID
10. For user.posts.build(attributes={:body => "random body of txt"}), I get this:
#<Post id: nil, user_id: 3, body: "random body of txt", created_at: nil, updated_at: nil>
Interesting thing about the one above is that when I do Post.all, that doesn't show up! But if I do user.posts, it does show up in that ...
11. For user.posts.create(attributes={}), I get this:
=> #<Post id: 4, user_id: 3, body: "body of text via create method", created_at: "2016-06-21 17:49:50", updated_at: "2016-06-21 17:49:50">
4. For user.posts.delete(post), I get this:
(0.3ms) begin transaction
SQL (0.6ms) DELETE FROM "posts" WHERE "posts"."id" = ? [["id", 3]]
(1.8ms) commit transaction
=> [#<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">]
8. For user.posts.clear, I get this:
DELETE FROM "posts" WHERE "posts"."user_id" = ? [["user_id", 3]]
Which means now, when I run this: user.posts, I get this:
#<ActiveRecord::Associations::CollectionProxy []>
So, as you can see from above #2 doesn't work ... as well #9. Why not?
Thanks in advance for your help!
EDIT in response to answer (easier for clarification)
ok, I did posts = user.posts which resulted:
#<ActiveRecord::Associations::CollectionProxy [#<Post id: 5, user_id: 3, body: "body of txt", created_at: "2016-06-21 18:00:57", updated_at: "2016-06-21 18:01:11">, #<Post id: 6, user_id: 3, body: "body of txt2", created_at: "2016-06-21 18:22:02", updated_at: "2016-06-21 18:22:36">]>
then I did user.posts=(posts) which resulted the same answer as above.
But when I ran posts = Post.where(user: user), I got this:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: posts.user: SELECT "posts".* FROM "posts" WHERE "posts"."user" = 3
Also, when I ran user.posts = posts, I got this:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: posts.user: SELECT "posts".* FROM "posts" WHERE "posts"."user" = 3
In regards to user.posts.find(Post.last.id), it worked if I did this:
user.posts.find(Post.last) but if I did something like this:
user.posts.find(Post.last.6), I got this:
SyntaxError: (irb):121: no .<digit> floating literal anymore; put 0 before dotuser.posts.find(Post.last.6)
For #2
You need to define what "posts" is.
If you are trying to "set" posts
posts = user.posts
If you want to do a comparison on the two you would do:
posts = Post.where(user: user)
user.posts == posts #should be true
For #9
You need to pass something into your find method:
user.posts.find(Post.last) #this returns an object if "user" created Post.last
Hope this helps!
Edit:
You can't do user.posts=(posts) because you set data with an array, hash, string, integer, etc.
An equals comparison operator should have 2 ='s. That was my mistake and I have corrected the post.
I use the gem ancestry to create comments.
Now, I can list all comments.
But I want to push serial number to each comment.
For example, if there were 3 comments, the first comment is annotated by 1, the next annotated by 2,..
I have no idea how to do it?
show.html.haml
- if notice
%p.alert.alert-success= notice
= nested_comments(#comment.subtree.arrange(:order => :created_at))
helper
def nested_comments(comments)
if comments.respond_to? :map
comments.map do |comment, sub_comments|
render(comment) + content_tag(:div, nested_comments(sub_comments), :class => "nested_comments")
end.join.html_safe
end
end
each_with_index won't work on recursive
if I have 4 comments, I want to show 0,1,2,3 for each comment
But each_with_index can not make it because it's a recursive call.
comments.each_with_index.map do |(comment, sub_comments), i|
comments
=> {#<Comment id: 2, user_id: 1, ip: nil, content: "I'm id2 the second floor VIVOTEK Releases New Vers...", commentable_id: nil, commentable_type: nil, created_at: "2014-11-07 03:59:38", updated_at: "2014-11-07 06:56:12", ancestry: nil>=>
{#<Comment id: 4, user_id: 1, ip: nil, content: "lala", commentable_id: nil, commentable_type: nil, created_at: "2014-11-07 05:22:41", updated_at: "2014-11-07 05:22:41", ancestry: "2">=>
{#<Comment id: 5, user_id: 1, ip: nil, content: "son of 4", commentable_id: nil, commentable_type: nil, created_at: "2014-11-07 06:38:04", updated_at: "2014-11-07 06:38:04", ancestry: "2/4">=>
{},
#<Comment id: 6, user_id: 1, ip: nil, content: "dild last 252", commentable_id: nil, commentable_type: nil, created_at: "2014-11-07 06:52:15", updated_at: "2014-11-07 06:52:15", ancestry: "2/4">=>
{}}}}
You can use with_index with map
comments.map.with_index do |comment, sub_comments, index|
Every enumerable instance in ruby has a method each_with_index, providing an_enumerator. So in your case I would suggest to use:
- comments.map do |comment, sub_comments|
+ comments.each_with_index.map do |idx, comment, sub_comments|
Hope it helps.
I don't know of an elegant solution. But you could pass a counter into your nested_comments function, and deal with the problem manually -- which might well mean without map at all. Ugly, I know.
To take a simpler example, should you need one:
def nested_foo(result, string, index)
index += 1
result << "\n#{index}: #{string}"
if index >= 10
return result
else
return nested_foo(result, string, index)
end
end
Now in my application.html.erb in views folder, I wrote this.
<p>List of all post IDs: <%= Post.all.each {|i| print i.id } %></p>
I would like it to output just the post.id of each post. But instead it shows this
List of all post IDs: [#<Post id: 1, title: "Our First Post", content: "Content for our first post", created_at: "2012-11-24 11:22:02", updated_at: "2012-11-26 17:40:54", user_id: 1>, #<Post id: 3, title: "Our Second Post", content: "Content for our second post", created_at: "2012-11-24 11:51:32", updated_at: "2012-11-26 17:41:33", user_id: 2>, #<Post id: 8, title: "Our Second Post", content: "Content of Our mandatory Second Post", created_at: "2012-11-24 19:42:02", updated_at: "2012-11-27 20:46:57", user_id: 1>, #<Post id: 10, title: "C Post", content: "Hi I'm Cee nice to meet you", created_at: "2012-11-26 17:51:20", updated_at: "2012-11-26 17:51:20", user_id: 3>, #<Post id: 20, title: "11", content: "11", created_at: "2012-11-27 19:58:48", updated_at: "2012-11-27 19:58:48", user_id: 4>, #<Post id: 21, title: "22", content: "22", created_at: "2012-11-27 19:58:53", updated_at: "2012-11-27 19:58:53", user_id: 4>, #<Post id: 25, title: "I'm Super Singha!", content: "Yessar!!!", created_at: "2012-11-27 20:45:07", updated_at: "2012-11-27 20:45:07", user_id: 6>, #<Post id: 26, title: "Should this be a blog or a forums or a whatever-wha...", content: ";asljdfi;asfi;asdf;lasbfurbofioboboeifhosdsdbvisbvw...", created_at: "2012-11-27 20:46:28", updated_at: "2012-12-02 14:17:14", user_id: 1>, #<Post id: 27, title: "Hullow", content: "Yoyoyo", created_at: "2012-11-30 07:35:38", updated_at: "2012-11-30 07:35:54", user_id: 6>, #<Post id: 649, title: "um", content: "hey", created_at: "2012-11-30 12:20:58", updated_at: "2012-11-30 12:20:58", user_id: 2>, #<Post id: 82692, title: "LALALALAL", content: "hiopsdahfiosadhfioahfio", created_at: "2012-12-02 13:59:04", updated_at: "2012-12-02 14:22:41", user_id: 2>, #<Post id: 82693, title: "ggg", content: "fff", created_at: "2012-12-02 14:29:42", updated_at: "2012-12-02 14:29:42", user_id: 2>, #<Post id: 82694, title: "sick", content: "sick", created_at: "2012-12-02 14:41:32", updated_at: "2012-12-02 14:41:32", user_id: 5>]
I have tried, puts instead of print, that doesn't work either.
Further: I would also like to make a link_to each post show page from the intended result, how can I achieve that?
Here's my repo: https://github.com/nixor/cpblog , here's heroku site: http://still-plains-5469.herokuapp.com/
Thanks.
The problem is with how you are using the ERB tags here:
<%= Post.all.each {|i| print i.id } %>
Whenever you use <%=, the result of the block will be rendered. In your case, Post.all.each {} returns an array object, which is exactly what you are seeing in your rendered HTML.
In order to print out each item, you will need to loop through the items using <% and then print out what you want using <%=.
<p>List of all post IDs:
<% Post.all.each |post| do %>
<%= link_to post.id, post_path(post) %>
<% end %>
</p>
<p>List of all post IDs:
</p>
<%= Post.all.each do |e| %>
<p>
<%= e.Id %>
<p>
<%= link_to "Show", e %>
<% end %>
I've been stuck trying to figure out why a counter cache on my (parent) BlogPosts table won't update from the (child) Comments table. At first I thought the answer provided in my earlier question might be the solution but something happened after I went to bed last night because when I woke up this morning and restarted my Rails console, my BlogPosts (actually just one Post - id# 1) aren't able to find their associated child Comments. I checked the Comments table and the five comments I create are all there, attached to post_id = 1. The output from my Rails console in the earlier question indicates that the post could find the comments last night. Perhaps this explains why the counter cache was not updating but I'm still not sure why the parent would not be able to find its children. Any hints?
Loading development environment (Rails 2.3.2)
>> p = Post.find(1)
p = Post.find(1)
=> #<Post id: 1, title: "test", content: "test", author_id: 1, status: "ok", created_at: "2009-05-21 19:27:14", updated_at: "2009-05-24 07:21:24", comments_count: 0>
>> p.comments.size
p.comments.size
=> 0
>> p.comments
p.comments
=> []
UPDATE: This is strange - I restarted the Rails console again but this time I called p.comments BEFORE I called "p.comments.size" - AND IT FOUND THE COMMENTS!! What's going on here?
Loading development environment (Rails 2.3.2)
>> p = Post.find 1
p = Post.find 1
=> #<Post id: 1, title: "test", content: "test", author_id: 1, status: "ok", created_at: "2009-05-21 19:27:14", updated_at: "2009-05-24 07:21:24", comments_count: 0>
>> p.comments
p.comments
=> [#<Comment id: 5, post_id: 1, author_id: 1, content: "Fifth Comment", status: "ok", created_at: "2009-05-24 07:08:56", updated_at: "2009-05-24 07:08:56">, #<Comment id: 4, post_id: 1, author_id: 1, content: "Fourth Comment", status: "ok", created_at: "2009-05-24 07:05:32", updated_at: "2009-05-24 07:05:32">, #<Comment id: 3, post_id: 1, author_id: 1, content: "Third Comment", status: "ok", created_at: "2009-05-24 06:34:59", updated_at: "2009-05-24 06:34:59">, #<Comment id: 2, post_id: 1, author_id: 1, content: "Second Comment", status: "ok", created_at: "2009-05-24 05:20:43", updated_at: "2009-05-24 05:20:43">, #<Comment id: 1, post_id: 1, author_id: 1, content: "First Comment", status: "ok", created_at: "2009-05-21 19:27:14", updated_at: "2009-05-21 19:27:14">]
>> p.comments.size
p.comments.size
=> 5
UPDATE 2: Following srboisvert's advice I created a new Comment and added it to the Post. This worked and the comments_counter updated to 1.:
Loading development environment (Rails 2.3.2)
>> p = Post.find 1
p = Post.find 1
=> #<Post id: 1, title: "test", content: "test", author_id: 1, status: "ok", created_at: "2009-05-21 19:27:14", updated_at: "2009-05-24 07:21:24", comments_count: 0>
>> com = Comment.new(:post_id => 1, :author_id => 1, :content => 'Sixth Comment', :status => 'ok')
com = Comment.new(:post_id => 1, :author_id => 1, :content => 'Sixth Comment', :status => 'ok')
=> #<Comment id: nil, post_id: 1, author_id: 1, content: "Sixth Comment", status: "ok", created_at: nil, updated_at: nil>
>> p.comments << com
p.comments << com
=> [#<Comment id: 6, post_id: 1, author_id: 1, content: "Sixth Comment", status: "ok", created_at: "2009-05-24 17:59:45", updated_at: "2009-05-24 17:59:45">, #<Comment id: 5, post_id: 1, author_id: 1, content: "Fifth Comment", status: "ok", created_at: "2009-05-24 07:08:56", updated_at: "2009-05-24 07:08:56">, #<Comment id: 4, post_id: 1, author_id: 1, content: "Fourth Comment", status: "ok", created_at: "2009-05-24 07:05:32", updated_at: "2009-05-24 07:05:32">, #<Comment id: 3, post_id: 1, author_id: 1, content: "Third Comment", status: "ok", created_at: "2009-05-24 06:34:59", updated_at: "2009-05-24 06:34:59">, #<Comment id: 2, post_id: 1, author_id: 1, content: "Second Comment", status: "ok", created_at: "2009-05-24 05:20:43", updated_at: "2009-05-24 05:20:43">, #<Comment id: 1, post_id: 1, author_id: 1, content: "First Comment", status: "ok", created_at: "2009-05-21 19:27:14", updated_at: "2009-05-21 19:27:14">]
Can you create a comment in the console, add it to a post and then display it in 3 separate steps?
You are doing lots of non-default fk name specification (though your names don't seem to be that different from what rails would expect so you might want to just use the conventions)so my guess is that somehow your has_many belongs_to are messed up.