Searching within an associated array in ruby - rails 3 - ruby-on-rails

So lets say we have a Post model, a User model and a View model.
When a user views a post, a new record is created in the Views table. The table links the user, the post, and the current time. Later if that user goes back to view the post again, the record is updated with a new time. Pretty basic stuff.
Posts has_many views, and Users has_many views
Views belongs to Posts and Users
In the index view of the Posts, I want to call the specific view for each post i.e.
<% #Posts.each do |post| %>
<%= post.name %><br/>
<%= post.views %> # This connects all of the views related to this post.
# How do I get the only one connected to this post and the current_user.id?
<% end %>
I feel like theres some simple way of accomplishing this that I'm totally forgetting

You could do something like
current_user.views.where(:post_id => post.id)
# this may work, not sure
current_user.views.where(:post => post)
or
post.views.where(:user_id => current_user.id)
# this may work, not sure
post.views.where(:user => current_user)

Related

rails association with user and post model?

I am going to do association with user and post model.
I added user_id to post model like this
rails generate migration add_user_id_to_posts
In model
user.rb
has_many :posts
post.rb
belongs_to :user
In post views / it work perfect
<%#posts.each do |post|%>
<%=post.post_name%>
<%=post.user.name.first(20).capitalize, post_path(post.user.id)
<%end%>
In user views /I am getting error
undefined method `post_name' for
#Post::ActiveRecord_Associations_CollectionProxy:0x00007fc9c432bde8
<%#users.each do|user|%>
<%=user.posts.post_name%>
<%=user.name.first(20).capitalize,post_path(user.id)%>
<%end%>
If I want to display like this way what I want to do. Pls need help I am new to rails
I want to add post_id to user model.
The reason you are getting the error is because you're trying to call post_name on a collection of posts that belong to a user. A collection is like an array, so you'll have to iterate through that collection and call post_name on each post.
<% #users.each do |user|%>
<% user.posts.each do |post| %>
<%= post.post_name %>
<% end %>
<%= user.name.first(20).capitalize, post_path(user.id) %>
<% end %>
The reason you're getting a collection of posts is because of your associations. A user has_many posts, which means you are going to get back all the posts that belong to a user. That may mean 0 posts or 10 posts depending on how many times the user has posted.
Because of this, you may want to check if the user has any posts before you iterate through them.
NoMethodError means that method does not exist on that posts. But how and why?
In your first example, post is an object of Post — it must respond_to the method you're calling on it.
To see what methods are available on an object, you can call #methods (which is also available on the class-level):
puts post.methods
That will give you an array of the methods you can call on post.
There is also a method called #class we can call to see what class an object is:
puts user.posts.class
Which will return ActiveRecord::Relation which acts like an array under the hood. Because a User has many Post, we have an array of Post, thus calling name on an array of Post objects will not work.
If you want the first name of the posts that a User has:
user.posts.first.name
But beware, because a user may not have any posts.

Using Thumbs_up to display users who voted on a post

I'm using Thumbs_Up gem to let users vote on a post(called Topic). I've done the voting part but now I want to display the users who voted on that particular post. I'm very new to Ruby On Rails, and I'm stuck at the view part.
Here's my controller: app/controllers/topics_controller.rb
def vote_who
#topic = Topic.find(params[:id])
#vote_list=#topic.voters_who_voted_for
end
In my index.html.erb, I want to display the names of there users who voted on that post. This should be right next to the vote button. Kind of like this,
But how do I send back information from the controller?
Or is this approach completely wrong?
IMHO, it's more preferable to show all voters in the 'show' view of the each particular Topic (imagine you have 100 voters for one topic, what this table in index view will look like?).
This way you don't need any distinct action (due to Rails conventions 1 action == 1 view (in general)). Just put in your show action:
def show
#topic = Topic.find(params [:id])
#vote_list = #topic.voters_who_voted_for
end
Then in your view (show.html.erb) you need to do something like this:
<% #vote_list.each do |voter| %>
<%= voter.email %> #or login, or name, or any user's attribute you want to display in your list
<% end %>
Or even try to simplify this: leave show action in your TopicsController as it is (don't create any #vote_list variable, only find right Topic), and call voters_who_voted_for method inside your show view, e.g.:
<%= #topic.title %>
<%= #topic.body %>
<%= #topic.voters_who_voted_for %>

ruby on rails get table row by two column conditions

For instance, I have two tables in database, Users and Microposts. The Users table stores all the users and has two columns, id and name; the Microposts table stores the posts made by the Users and has three columns: id, post_content and user_id (These two tables, of course, have the timestamp as each entry is created). So basically what I want is have a view page that displays the information stored in Users (id and name) plus the last post created by the corresponding user.
One way I'm thinking of doing is to have it being processed right at the user view page (located in, for example, app/views/Users/index.html.erb). Since I'm probably going to loop through the Users table like this
<% #Users.each do |user| %>
id = user.id
<!-- Do such and such -->
<% end %>
and while looping through the Users table, use the user.id to get the latest post made by the user.
Second way is to basically implement such that the Users table has another column that store the latest post information and updates each time when a transaction is made to the database. So then when implementing the latest post can just be accessed as an attribute.
However, I don't really know which way is better nor how to implement either way...
Any suggestion?
Thanks in advance!
Edit:
Sorry, there is a typo. It's "two tables and one database"
Similar to the other answers but I wanted to add an important little piece that I feel is commonly overlooked. Including the association on the first call to the database.
# not sure the scale of your project but I would paginate #users
# by using includes you prevent the N+1 issues
<% #users.includes(:microposts).each do |user| %>
id = user.id
user.microposts.last
<% end %>
For some documentation on this:
http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
class User
has_many :microposts
end
class Micropost
belong_to :user
end
# to get user's post
# for single user
#user.microposts
# for many users
#users.each do |user|
user.microposts.each do |post|
post.post_content
end
end
Your user has many microposts. So do the following on users view page i.e. app/views/Users/index.html.erb
<% #Users.each do |user| %>
id = user.id
last_post = user.microposts.last <!-- This will give you last post created by the user -->
<% end %>

ActiveRecord - how to do includes after the query was executed?

In a scenario with 1->N->N assocations. For example: Post->Comments->Votes (votes will be list of names of people who voted on the comment). To display a page the query with includes might look like:
#post = Post.where(:id => 100).includes({:comments => :votes}).first
I am starting to add caching support. Which means if the comments partial is already cached I will not need to run include the comments/votes all the time. So I wonder if there is a way to make the code appear like:
# controller
#post = Post.find(100)
# view
<% cache('comments', #post.last_comment_time do %>
<% #post.includes({:comments => :votes}).comments.each do |comment| # ???? %>
<% end %>
Running the "post-query" includes, will "fill in" the associations. So #post.comments will be populated and each comment will include all the votes. Is there a way to achieve this?
P.S. I am aware the view is not the best place to run the query, this is just an example.
in latest releases of rails, all the finder-methods return a proxy object, that will only trigger a database-call once you send it some iterator-method like all or first in your case. this is why you can chain all the calls like Post.where.order.sort.bla.
it's not possible though to load the post model and use an includes call later. includes works by using a join call on the relations that get loaded with the model instance, so that you have just one database-call instead of one for each relation.
executing active_record code in your view is also a bad practice. the data-retrieval is the responsibility of the controller, not the view.
This is a fairly old question but this can be done now like this
# controller
#post = Post.find(100)
# view
<% cache('comments', #post.last_comment_time do %>
<% ActiveRecord::Associations::Preloader.new.preload #post, comments: :votes # this will trigger one query %>
<% #post.comments.each do |comment| # this will not trigger any additional queries %>
<% end %>
Not the cleanest way but it does the job

Complex form with Rails

I have a form where I'd like to create a parent record and a child record at the same time. For a simple example let's say its a Company with the first Employee.
in my controller I do something like:
def new
#company = Company.new
#company.employees.new
end
and in my view this:
<%= form_for(#company) do |form| %>
<div>
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<%= form.fields_for :employees do |employee_form| %>
<div>
<%= employee_form.label :name %>
<%= employee_form.text_field :name %>
</div>
<% end %>
<% end %>
and back in my controller again:
def create
#company = Company.new(params[:company])
#company.employees << Employee.new(params[:company][:employees_attributes]["0"])
# save stuff
end
Question 1:
I couldn't get the employee collection on the company to be populated with the single employee created in the form. When I looked at the params I found the [:employees_attributes]["0"] stuff.
What I have works, but is there a cleaner way to do this?
Question 2:
If the validation doesn't pass for the employee I get a generic "Employees is invalid" instead of the Name required validator message. I get I am calling save on the collection and rails is doing its best to bubble a validation error up, but is there a cleaner way to do this so I can get the errors specific to the employee?
In Short
How can I clean this up so the related models are created automatically from the params, and so that I get the validation messages for a single employee.
Thanks for looking.
1) fields_for arranges for the child objects attributes to be nested inside the parent objects attributes in the params hash that gets sent back to the controller action. To get Rails to automatically update the child objects tell the parent model to accept nested attributes using the accepts_nested_attributes_for declaration.
2) There is an errors object for every ActiveRecord object. Loop through the errors list and display the messages.
Best way to achieve this is to create a partial and a view helper method that will take render the errors for you. then replace the generated errors messages in the forms with a call to your render_error_messages method. You have all the code to do this already in the generated forms. You just need to refactor that code into a partial, create the helper - which should accept an array of model names as a parameter then do what you want with the info. Wither render a partial for each model or render a partial that will deal with child objects as well as the parent object. Totally your call.
3) Change your new action to build rather that create a new child object so instead of
def new
#company = Company.new
#company.employees.new
end
do this
def new
#company = Company.new
#company.employees.build
end
4) Watch those Railscasts to see how accepts_nested_attributes works
http://railscasts.com/episodes/196-nested-model-form-part-1
and
http://railscasts.com/episodes/197-nested-model-form-part-2
Update
So how does the above information leave you in relation to your questions.
1) What I have works, but is there a cleaner way to do this?
You've fixed the new action as per point 3 above right? Now your create action can look like this
def create
#company = Company.new(params[:company])
# save stuff
end
Which is much cleaner as it has reverted to the original generated create action.
You may not think that's much of an update and therefore not that much cleaner. Well in isolation you'd be right. But consider that you could add as many relationships as you like ad add as many fields_for declarations as you like nd you could turn the user -> employee relationship into a has_many (I know that you wouldn't). You could do all that and your create and update actions stay EXACTLY the same and that's why it's cleaner.
2) is there a cleaner way to do this so I can get the errors specific to the employee?
Given my response in point 2 above you know that there is an errors object on the employee object as well as on the user object right? You also know now that you can loop through that errors object to get the messages right?
So you could do this
<% if #user.employee.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.employee.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.employee.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
At the risk of repeating myself I'll just say that you should refactor your error messages view code into a partial that will take any object as a parameter then you can call it from any view thus enabling you to change the styling and the functionality for all your forms.
Hope that's clearer

Resources