I've a simple posts model with title:string and rating:integer and I want to add the ability to rate the posts. So far I have
#Post controller
def increase
#post = Post.find(params[:id])
#post.increment! :rating
flash[:notice] = "Thanks for your rating."
redirect_to #post
end
#Post show
<%= link_to "Rating", increase_post_path %>
#Routes
map.resources :posts, :member => { :increase => :put }
When I click on rating, I get unknown action. I'm able to increase the rating when I add #post.increment! :rating to update, but not when I create my own method. Any suggetions?
If you have a standard routes saying
map.resources :posts
you should replace it with:
map.resources :posts, :member => { :increase => :put }
And this will create the route "increase_post_path" with method "put" for your app.
You need to add the action to your routes.rb file. See this excellent guide for details.
Here's my solution, if anyone is interested. Thanks for your help guys.
#Views
<%= link_to post.rating, increase_post_path(post) %>
#Controller
def increase
#post = Post.find(params[:id]).increment!(:rating)
flash[:notice] = "Thanks for rating"
redirect_to posts_url
end
#Routes
map.resources :posts, :member => { :increase => :put }
This works if you want something just for you, something that won't be abused. Obviously adding rating to your websites where others can vote up unlimited times is just asking for trouble.
I suggest using Votefu, does ratings and even has user karma. The author was nice enough to make an example app.
Related
I have an app where users can ask questions and bookmark certain questions. I'm done with the users, questions, and answers, so I've added a BookmarkController & Bookmarks model. At first, I considered using associations, but my app has a few associations already so I'm (or I've attempted at) using query parameters such as user_id and question_id to fetch bookmarks.
The structure is a bit like StackOverflow. A user navigates to a single question view and bookmarks it on that page. This creates a new bookmark model containing the user_id of current_user and the question_id. The user can go to his profile to view all the questions he bookmarked, fetched using his user_id. (Answers cannot be bookmarked. Only questions.)
I've been getting a 'param is missing or the value is empty: bookmark' error, although I have followed similar steps I did for my QuestionsController. It would be great if someone could help me out in identifying what's wrong/bad about my code!
rake routes (first part omitted)
bookmark_question PUT /questions/:id/bookmark(.:format) questions#bookmark
questions GET /questions(.:format) questions#index
POST /questions(.:format) questions#create
new_question GET /questions/new(.:format) questions#new
edit_question GET /questions/:id/edit(.:format) questions#edit
question GET /questions/:id(.:format) questions#show
PATCH /questions/:id(.:format) questions#update
PUT /questions/:id(.:format) questions#update
DELETE /questions/:id(.:format) questions#destroy
route.rb (excerpt)
# Questions
get '/questions/:id' => 'bookmarks#create'
show.html.erb (questions#show)
<% if current_user %>
<%= link_to "Bookmark", :controller => 'bookmarks', :action => 'create' %>
<% end %>
BookmarksController
class BookmarksController < ApplicationController
def new
#bookmark = Bookmark.new
end
def create
#question = Question.find(params[:id]) # when I delete this line, I get a new error - "undefined local variable 'params'"
#bookmark = Bookmark.new(bookmark_params)
#bookmark.user_id = current_user.id
#bookmark.question_id = #question.id
#bookmark.save
redirect_to #question
end
def destroy
end
private
def bookmark_params
params.require(:bookmark).permit(:user_id, :question_id)
end
end
Bookmark model
class Bookmark < ApplicationRecord
validates :user_id, presence: true
validates :question_id, presence: true
end
QuestionsController
(at the moment, contains no reference to Bookmarks. I thought so because I did the routing, but this might be where I'm going wrong)
class QuestionsController < ApplicationController
def index
#questions = Question.all
end
def show
#question = Question.find(params[:id])
#answers = Answer.all
# Delete only appears when no answers
#deletable = (current_user== User.find(#question.user_id)) && (#question.answers.all.size==0)
end
def new
#question = Question.new
end
def create
if logged_in?
#question = Question.new(question_params)
#question.user_id = current_user.id
#question.save
redirect_to #question
else
redirect_to login_path
end
end
def destroy
#question = Question.find(params[:id])
#question.destroy
redirect_to root_path
end
private
def question_params
params.require(:question).permit(:picture_url, :country, :educational_level, :topic)
end
end
profile index.html.erb (just for ref)
<% if (#bookmarks.count == 0) %>
///
<% else %>
<%= #bookmarks.each do |bookmark| %>
<!-- Show bookmark content here like Question.find(bookmark.question_id) etc -->
<% end %>
<% end %>
I have looked a the previous qns that have the same error as me. But they were all using associations. I hope to not use associations as the bookmark model only needs to keep a record of the user id and qn id.
UPDATE
So, referring to the answers given, I updated my erb to:
<% if logged_in? %>
<%= link_to "Bookmark", :controller => 'bookmarks', :action => 'create', bookmark: {user_id: current_user.id, question_id: #question.id} %>
<% end %>
hence specifying the controller and action (and the params) that need to be directed. But rails sends an error:
No route matches {:action=>"create", :bookmark=>{:user_id=>2, :question_id=>4}, :controller=>"bookmarks", :id=>"4"}
So I assume it was a routing problem. As Pavan suggested, I did consider nesting my resources, but the nesting is already one level deep, as such:
resources :questions do
resources :answers
end
And I reckon doing something like:
resources :questions do
resources :bookmarks # or resources :bookmarks, only: create
resources :answers
end
won't work. (And it didn't :( )
I'm not so sure how to get this routing problem fixed (tried Googling). Thanks.
param is missing or the value is empty: bookmark
The reason for the error is bookmark_params expects a :bookmark key to be present in the params hash, which in your case is missing since you are not passing any.
Change link_to like below:
<% if current_user %>
<%= link_to "Bookmark", :controller => 'bookmarks', :action => 'create', bookmark: {user_id: current_user.id, question_id: #question.id} %>
<% end %>
Also, the route get '/questions/:id' => 'bookmarks#create' isn't right and would conflict with this route question GET /questions/:id(.:format) questions#show. I would instead recommend building nested routes
resources :users do
resources :questions do
resources :bookmarks, only: [:create]
end
end
Update:
Along with the above, you should change #question = Question.find(params[:id]) to #question = Question.find(params[:bookmark][:question_id])
'param is missing or the value is empty: bookmark, this error means that, there is no bookmark key present in your params object, but you defined your bookmark_params to have one:
def bookmark_params
params.require(:bookmark).permit(:user_id, :question_id)
end
That's why it's throwing the above error message.
You should make sure you send the user_id and question_id key/value pairs under the bookmark key. Something like this:
bookmark: { user_id: 1, question_id: 2}.
So, your code should look something like this (adding the bookmark to params):
<%= link_to "Bookmark", :controller => 'bookmarks', :action => 'create', bookmark: {user_id: current_user.id, question_id: #question.id} %>
I've made a comments section for my 'posts' views and I've got remote: true working on the form so when you hit enter and submit the 'new comment' form to the database, it updates in the background fine (the comment is created, page doesn't redirect or change) but I can't get it to load the comments on the page. You have to refresh the page to see them.
I could do redirect_to :back in the comments controller after saving but that takes user to top of the page rather than staying put to see the comment appear.
I've tried render 'posts#show' after saving the comment in the comment controller create action but that tries to send you to /comments/posts/:slug/. If it actually rendered the posts show action I think this would work.
Comments controller:
class CommentsController < ApplicationController
before_action :find_commentable
def show
end
def new
#comment = Comment.new
end
def create
#comment = #commentable.comments.new comment_params
#comment.author = current_user if current_user
#comment.save
end
private
def comment_params
params.require(:comment).permit(:body, :author_id, :post_id)
end
def find_commentable
#commentable = Comment.find(params[:comment_id]) if params[:comment_id]
#commentable = Post.find_by_slug(params[:post_id]) if params[:post_id]
end
end
Comment section on post show view:
%ul#post-comments
= render 'comment_feed'
= form_for [#post, Comment.new], remote: true do |f|
= f.text_field :body, class: 'js-new-comment-field', placeholder: "Write a comment..."
posts/show.js.erb:
$("#post-comments").html("<%= escape_javascript render("comment_feed") %>");
Routes.rb:
resources :posts do
collection do
match 'search' => 'posts#search', via: [:get, :post], as: :search # For ransack search
end
resources :comments
end
resources :comments do
resources :comments # Replies on comments
end
Got it working!
This helped loads:
https://gemfile.wordpress.com/2014/03/06/rails-blog-comments-with-ajax/
I added this second match line to routes.rb and it's refreshing the page with the new comment now:
resources :posts do
collection do
match 'search' => 'posts#search', via: [:get, :post], as: :search
match '/comments/posts/:slug' => 'posts#show', via: [:get, :post], as: :comment
end
resources :comments
end
It's not ajax though: demo
I am implementing a like system in my rails app using David Celis gem called Recommendable. I've gotten everything to work in the console but I can't get the right routes and I'm getting the "No route matches [GET] "/categories/1/posts/1/like" error.
I have the following in my models:
class Category < ActiveRecord::Base
has_many :posts, :dependent => :destroy
extend FriendlyId
friendly_id :name, use: :slugged
end
class Post < ActiveRecord::Base
belongs_to :category
end
In my Post Controller I have:
class PostsController < ApplicationController
before_filter :authenticate_user!
before_filter :get_category
def like
#post = Post.find(params[:id])
respond_to do |format|
if current_user.like #post
else
flash[:error] = "Something went wrong! Please try again."
redirect_to show_post_path(#category, #post)
end
end
end
end
In my routes I have:
resources :categories do
resources :posts do
put :like, :on => :member
end
end
match 'categories/:category_id/posts/:id', :to => 'posts#show', :as => 'show_post'
Can someone please point at my errors? I can get the PUT to work but I dont know where the GET error is coming from as I am trying to redirect back to the post if an error occurs when the user like's a certain post. Thank you in advance.
EDIT:
In my view I have:
- title "#{#post.class}"
%p#notice= notice
%p
%b Title:
= #post.title
%p
%b Description:
= #post.description
%p
%b Likes:
= #post.liked_by.count
= link_to 'Edit', edit_category_post_path(#post)
\|
= link_to 'Back', category_posts_path
\|
= link_to 'Like', like_category_post_path(#post)
Replace:
= link_to 'Like', like_category_post_path(#post)
with:
= link_to 'Like', like_category_post_path(#category, #post), method: :put
Or, as I like it:
= link_to 'Like', [#category, #post], method: :put
I think your like has to be:
def like
#post = Post.find(params[:id])
respond_to do |format|
format.html do
if current_user.like #post
flash[:notice] = "It's ok, you liked it!"
redirect_to :back
else
flash[:error] = "Something went wrong! Please try again."
redirect_to show_post_path(#category, #post)
end
end
end
end
Your route expects a PUT request, while you're issuing a GET request.
You'll need to either access your route via a button_to with :method => :put so that your app is issuing PUT requests (the correct solution), or change your route to use GET requests (the wrong way to make requests which modify state):
get :like, :on => :member
I have a problem with passing params. in show view i have
=link_to "Send message", {:controller => "users", :action =>
"send_message", :user_id => #user.id}
and my users_controller have method
def send_message
#user = User.find(params[:user_id])
#message=Message.new
redirect_to send_message_path
end
and my show method
def show
#user = User.find(params[:id])
#post=Post.new
#posts=#user.posts.reverse.paginate(:page => params[:page],:per_page => 10)
end
but i have and error after clicking on link
Couldn't find User without an ID
in that line, in send_message method
#user = User.find(params[:user_id])
what i am doing wrong? i read a lot examples about this , but it doesnt work(
Thanks in advance!!!
=link_to "Send message", [:send_message, #user]
and in your routes:
resources :users do
post :send_message, :on => :collection
end
What happens if you try to change :user_id to :id, like this
=link_to "Send message", {:controller => "users", :action => "send_message", :id => #user.id}
def send_message
#user = User.find(params[:id])
#message=Message.new
redirect_to send_message_path
end
If that works it's because your route isn't set up to use a :user_id param. If it doesn't it would help to know more about your route.
In your routes you should write:
resources :users do
post :send_message, :on => :member
end
And in your view you write:
= link_to "Send Message", send_message_user_path(#user)
This will use id instead of user_id.
So in your controller you need to use params[:id] as well.
Imho this is cleaner than #fl00r solution, because it is a member method, not a collection.
But I am a bit confused as to why you would have that method inside the controller at all.
Because the only thing it does is redirect to the send_message_path anyway. The instance variables you set are all lost then. So instead in your view write this:
= link_to 'Send message', send_message_path(:user_id => #user.id)
And this will send you to the correct controller straightaway (where you will have to retrieve the user).
Also your show method could use some work: you should define a has_many :posts in your User model.
The README does not show how to handle the controller and view aspects of setting up this plugin. I have been searching for a couple hours and can't find anything that shows how to use this plugin.
After even more searching, I gave up on finding a tutorial and came up with this. If anyone can point out a better / cleaner way to do this, please let me know. Otherwise, here is what I am using now in case this will benefit anyone else.
First, install the plugin with script/plugin install http://github.com/jackdempsey/acts_as_commentable.git -r 2.x
Then, generate the comment model and migration with script/generate comment and migrate the database with rake db:migrate
The tricky bit is nesting comments under other resources in a polymorphic way. Here is what I did:
# In config/routes.rb
map.resources :comments, :path_prefix => '/:commentable_type/:commentable_id'
# In app/controllers/comments_controller.rb
before_filter :load_commentable
def create
#comment = #commentable.comments.build(params[:comment])
#comment.user = current_user
respond_to do |format|
if #comment.save
format.html { redirect_to #commentable }
else
format.html { render :action => 'new' }
end
end
end
protected
def load_commentable
#commentable = params[:commentable_type].camelize.constantize.find(params[:commentable_id])
end
# In app/views/comments/_form.html.erb
<%= form_for(:comment, :url => comments_path(commentable.class.to_s.underscore, commentable.id)) do |f| %>
# In app/views/model_that_allows_comments/show.html.erb
<%= render :partial => 'comments/form', :locals => {:commentable => #model_that_allows_comments} %>
I think that shows the relevant parts clearly enough to understand what is happening. It makes it possible to add acts_as_commentable to any model. You just have to pass in the commentable object in the locals hash when rendering the comments form and the same comments controller / view code should work.
acts_as_commentable merely exposes you a Comment model and takes care of the plumbing between that model and your commentable models. It doesn't give you any controllers or views. You are responsible for deciding how you want to implement this part of your application.
It is pretty straightforward, though. For example...
# in routes.rb
map.resources :posts, :has_many => :comments
# in your comments controller...
class CommentsController < ApplicationController
before_filter :get_post
def get_post
#post = Post.find(params[:post_id])
end
def index
#comments = #post.comments.all # or sorted by date, or paginated, etc.
end
end
# In your haml view...
%h1== Comments for #{#post.title}:
%ul
- comments.each do |comment|
%h3= comment.title
%p= comment.comment
You'll see the comments for a particular post when you go to /posts/1/comments now.
I think the best way to add comments to any model is creating a method called comment in your ApplicationController.rb file like this.
def comment
# Extracts the name of the class
klass = self.class.to_s[/\A(\w+)sController\Z/,1]
# Evaluates the class and gets the current object of that class
#comentable_class = eval "#{klass}.find(params[:id])"
# Creates a comment using the data of the form
comment = Comment.new(params[:comment])
# Adds the comment to that instance of the class
#comentable_class.add_comment(comment)
flash[:notice] = "Your comment has been added!"
redirect_to :action => "show", :id => params[:id]
end
and then just create some partial _comment.rb to use it in any model you want
<%= form_tag :action => "comment", :id => Your_model_goes_here %>
<p><label for="comment_title">Title</label><br/>
<%= text_field 'comment', 'title' %></p>
<%= text_area "comment", "comment", :rows => 5, :cols => 50 %> <br />
<%= submit_tag "Comment!.." %>
</form>
I hope it's useful for someone...