Using the same "Voting" controller action for two different objects - ruby-on-rails

I am following schneems's great intro to Rails tutorial on creating a Reddit clone, and want to expand the "voting" structure to work not only for questions, but for comments as well, and was having difficulty figuring out how to pass into the controller both question_id and comment_id so it could vote up or down accordingly, rather than restricting the usage to only question_id.
Currently, there is only a create function in my VotesController, defined as the following:
def create
#vote = Vote.where(:question_id => params[:vote][:question_id], :user_id => current_user.id).first #the question_id is baked right in..
if #vote
#vote.up = params[:vote][:up]
#vote.save
else
#vote = current_user.votes.create(params[:vote])
end
redirect_to :back
end
Thanks for your help!

Well, when you try to vote on a comment, that would mean that params[:vote] should contain a :comment_id instead of a :question_id, right?
So your where statement needs to either be
# for a question
where(:question_id => params[:vote][:question_id], :user_id => current_user.id)
# for a comment
where(:comment_id => params[:vote][:comment_id], :user_id => current_user.id)
You approach this in various ways, like by checking if params[:vote].has_key?(:question_id), but an easy option would be to use Hash#slice:
where(params[:vote].slice(:question_id, :comment_id).merge(:user_id => current_user.id))

Related

Rails 4 - Nested Resources

User has_many Tickets.
Ticket belongs_to User (ticket.user_id)
routes.rb
resources :users do
resources :tickets
end
rake routes
user_tickets GET /users/:user_id/tickets(.:format) tickets#index
users/index.html.erb
<%= link_to("View User's Tickets", user_tickets_path(user)) %>
users_controller.rb
private
def set_user
#user = User.find(params[:id])
#tickets = #user.tickets
end
tickets_controller.rb
def index
#search = Ticket.search(params[:q])
#tickets = #search.result.paginate(:page => params[:page], :per_page => 25)
render 'shared/tickets.html.erb'
end
When I hover over link, it shows .../users/[the selected user's id]/tickets
When it goes to the ticket/index page, it shows ALL tickets, not just the tickets with the selected user's id.
I'm pretty sure my route is incorrect, or it may be something else entirely. Any help is appreciated.
EDIT
I think my problem is that I need to call #tickets in the tickets_controller/index method a variety of ways, because I want to use that view for #tickets.all, #user.tickets, #facility.tickets, etc (to keep it DRY).
The same index list of tickets needs to change, based on the link from whence it came (whether it comes from the user list, showing a list of all tickets by that user, or from the facility list, showing a list of all tickets by that facility). I'm just doing something horribly wrong somewhere.
Possible solution I will try:
Maybe I need to create custom routes, like get 'users_tickets' => "users#users_tickets", then put the #tickets conditions in that method and call the shared/tickets.html.erb that way.
Sounds like you need to step through the association. Did you use
tickets_controller.rb
def index
#user = User.find(params[:user_id])
#tickets = #user.tickets
end
in the controller? If this doesn't help, can you post the controller code?
Aren't you trying to whittle down #tickets rather than do another query?
Right now you're redefining #tickets when they hit the tickets index, and it doesn't care that you defined #tickets as just belonging to that user on the users_controller. It just ignores that because you're using direct assignment in your tickets_controller index action. You probably want something like:
tickets_controller.rb
def index
#search = Ticket.search(params[q])
#tickets = #search.result.where(user: #user)
#tickets = #tickets.paginate(:page => params[:page], :per_page => 25)
end
Not tested, but I think that's more what you're wanting.
Ok - I think I need to review nested resources again, because I thought they did what I wanted automatically...or maybe they do, and I just don't get it yet.
In any case, I ended up creating a custom route and custom method in users:
routes.rb
get 'user_tickets' => "users#user_tickets"
users_controller.rb
def user_tickets
#user = User.find(params[:id])
#search = Ticket.where(:user_id => #user.id).search(params[:q])
#tickets = #search.result.paginate(:page => params[:page], :per_page => 25)
render 'shared/tickets.html.erb'
end
then, called:
<%= link_to("View Tickets", user_tickets_path(:id => user.id)) %>
I will do the same for facilities, departments, etc. Not sure this is the best way, but it works.
Thanks everyone for stimulating my brain cells.

Ancestry string not being deifined

essentially I have a category that you can add comments to, this category shows a lists of tasks. When You add comments you have the ability to reply to said comment, when you do so and hover the reply link you’ll see something much like:
http://localhost:3000/categories/2/category_comments/new?parent=6
We then take that id, pass it to the reply forum and then assign it to the ancestry string in the database to "nest" the reply. The problem is, the parent id is not being passed to the form. The form's hidden field is blank. Why? We can walk the path this id should take in the following code.
categories_controller
def show
#category = Category.find(params[:id])
#category_comment = #category.category_comments.build
end
This shows the comment on the category page, and passes the parent_id of the comment your replying to, to the form.
When we click reply, we trigger the category_comments#new and #create methods shown below.
category_comments_controller
def new
#category = Category.find(params[:category_id])
#category_comment = #category.category_comments.build(:parent_id => params[:parent_id])
end
def create
#category = Category.find(params[:category_id])
#category_comment = #category.category_comments.create(params[:category_comment].merge(:user_id => current_user.id))
if #category_comment.save
redirect_to project_category_path(#category.project, #category), :flash => {:success => 'Created comment'}
else
redirect_to :back, :flash => {:error => 'Could not create comment'}
end
end
update:
this is no longer a form issue it is a controller issue, dealing with passing the parent_id to the form.
Try this:
<%= link_to 'Reply', new_category_category_comment_path(#category.id, :parent_id => category_comment.id)%>
Do you have has_ancestry defined in your model? I think not having it there would be a valid explanation for this not working.
Some how this magically fixed its self. I am not sure how or what happened, but it magically works now >.>

I want to destroy data from relationships but I can't

I want user to remove group that user attending groups by removing groupings.
So, I tried write below the code , but when run leave action , it happen error that
Unknown key: group_id.But I don't know how to deal with it . Please some help.
Thanks in advance.
GroupingsController.rb
def leave
#user = current_user
#group = Group.find(params[:id])
#user.remove(#group)
redirect_to :back , notice: "Destroy!"
end
User.rb
has_many :groups, :through => :groupings,:source => :group
def remove(group)
groupings.find_by_group_id(:group_id => group).destroy
end
# attend method is work correctly.
def attend(group)
groupings.create(:group_id => group)
end
You've already specified that you're using group_id (by saying find_by_group_id) - so you don't need to specify it again by passing it as a 'key' (eg :group_id =>)
So your code should just be
def remove(group)
groupings.find_by_group_id(group.id).destroy
end

Username in Profile for Rails

Using RoR 2.3.8. I have the following codes:
user.rb
def to_param
"#{login.downcase.gsub(/[^[:alnum:]]/,'-')}".gsub(/-{2,}/,'-')
end
people_controller.rb
def show
#person = User.find(params[:id])
if current_user == #person
#posts = #person.posts.paginate(:page => params[:page], :order => order)
else
#posts = #person.posts.by_status('published').paginate(:page => params[:page], :order => order)
end
end
I have a column login in Users database where unique username is. People is just a controller to show some posts created by the user.
I will usually link to the index.html.erb under my people controller with the url http://localhost:3000/people/2 with the following code example in User's posts:
<%=h #post.user_name %>
I want the URL to be http://localhost:3000/people/victor where victor is the login for a user. This url should also actually show the profile show.html.erb in people controller.
What else do I need to do? Thanks!
I use the friendly_ID gem for this sort of thing - it's very straightforward - good luck
I would modify routes.rb, something like this:
match 'people/:login' => 'people#show', :as => 'login'
And then modify a people_controller.rb:
def show
#person = User.where(:login => params[:login]).first
end
edited after additional information
corrected error

Rails Polymorphic Associations plus Routes

I have a model, Report, that is polymorphic.
So many itens in my site may have many of it.
And i would like to have a generic controller for posting it.
Its a very simple model, has only a text message and the association.
in my routes, im doing something like
map.resources :users, :has_many => [ :reports ]
map.resources :posts, :has_many => [ :reports ]
but in my reports_controller, i would like to get the relation from with its coming from.
like:
before_filter :get_reportable
def get_reportable
reportable = *reportable_class*.find params[:reportable_id]
end
is this possible?
how could i get the reportable_class and the reportable_id?
I can get the params[:user_id] when it comes from users controller, or params[:post_id] when it comes from posts. I could do a case with all the relations, but it doesnt seem a clean solution at all...
having the polymorphic association would be the best, are there any how?
If you have a single controller that processes requests through two differing paths, then you need to make it aware of the contexts in which it will be called. You often see a lot of code that looks something like this:
before_filter :load_reportable
def load_reportable
if (params[:user_id])
#user = User.find(params[:user_id])
#reportable = #user
elsif (params[:post_id])
#post = Post.find(params[:post_id])
#reportable = #post
end
rescue ActiveRecord::RecordNotFound
render(:partial => 'not_found', :status => :not_found)
return false
end
Since you're using a polymorphic association, you may be able to do something like this instead:
before_filter :load_reportable
def load_reportable
unless (#reportable = #report.reportable)
# No parent record found
render(:partial => 'not_found', :status => :not_found)
return false
end
# Verify that the reportable relationship is expressed properly
# in the path.
if (params[:user_id])
unless (#reportable.to_param == params[:user_id])
render(:partial => 'user_not_found', :status => :not_found)
return false
end
elsif (params[:post_id])
unless (#reportable.to_param == params[:post_id])
render(:partial => 'post_not_found', :status => :not_found)
return false
end
end
end
The trouble with this approach, where you have one controller that serves two entirely different routes, is that generating error messages, such as "user not found" versus "post not found". This can be tricky to get right if you're not inheriting from a Users::BaseController, for instance.
In many cases it's easier to create two independent "reports" controllers, such as users/reports and posts/reports, where any common functionality is imported from a module. These controllers usually inherit from a base controller which performs the loading and error handling. The base controller can also establish layout, page title, etc., without having to re-implement this functionality for each sub-resources controller.
The alternative is to de-couple reports and have it run as its own controller where the relationship to the "reportable" record is mostly irrelevant.
Or try that:
before_filter :get_reportable
def get_reportable
params.each do |name, value|
if name =~ /(.+)_id$/
#reportable = $1.classify.constantize.find(value)
end
end
end
It is going through all the params and tries to find one ending with _id, then grabs that before part and finds relevant record.

Resources