I installed the gem 'thumbs_up', '~> 0.6.7' ran the necessary migrations per instructions and now having some issues getting this to function properly. Thanks for bearing with me as I am new with RoR. I'm running rails 4.0.0. and building a simple app where users upload images (pins) and I want the images to be voted on by other users. Problem I am having is that a logged in user is only able to vote on their own images, and not others. Also, once the vote_up or vote_against is clicked on in the view it does nothing, however the vote is being tallied, but the page and the flash notice are both not being rendered.
This is my models/users.rb:
class User < ActiveRecord::Base
# some devise stuff here...
acts_as_voter
end
models/pins.rb:
class Pin < ActiveRecord::Base
acts_as_voteable
end
My pins_controller:
def vote_up
#pin = Pin.find(params[:id])
current_user.vote_for(#pin)
pin.update_vote_count
render root_path
flash[:success] = " You voted Up."
respond_to do |format|
format.js
end
end
def vote_down
#pin = Pin.find(params[:id])
current_user.vote_against(#pin)
#pin.update_vote_count
respond_to do |format|
format.js
redirect_to pins_url, notice: 'You voted Down'
end
end
This is my view:
<strong><%= pin.plusminus %></strong><br/>
<%= link_to "Up Vote", vote_up_pin_path(pin), :method => :post, :remote => true, :class=>'up-vote' %> |
<%= link_to "Down Vote", vote_down_pin_path(pin), :method => :post, :remote => true, :class=>'down-vote' %>
And here is my routes.rb:
resources :pins do
member do
post :vote_up
post :vote_down
post :unvote
end
end
Any help would be very much appreciated, thanks in advance!
Resolved my issue. Was a rookie mistake in that for any user to see the vote option I put the end tag after the option to vote in my view.
Related
I am trying to follow the following guide for starting Ruby on Rails. Everything went well until I try to destroy (delete comments).
I receive the following error:
The action 'show' could not be found for CommentsController
I will post my code below.
http://guides.rubyonrails.org/getting_started.html
comments_controller.rb
class CommentsController < ApplicationController
http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.create(comment_params)
redirect_to article_path(#article)
end
def destroy
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
#comment.destroy
redirect_to article_path(#article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
articles_controller
class ArticlesController < ApplicationController
http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]
def index
#articles = Article.all
end
def show
#article = Article.find(params[:id])
end
def new
#article = Article.new
end
def edit
#article = Article.find(params[:id])
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
routes.rb
Rails.application.routes.draw do
resources :articles do
resources :comments
end
root 'welcome#index'
end
article.rb
class Article < ActiveRecord::Base
has_many :comments, dependent: :destroy
validates :title, presence: true,
length: { minimum: 5}
end
comment.rb
class Comment < ActiveRecord::Base
belongs_to :article
end
articles/index.html.erb
<h1>Listing Articles</h1>
<%= link_to 'New article', new_article_path %>
<table>
<tr>
<th>Title</th>
<th>Text</th>
<th colspan="3"></th>
</tr>
<% #articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Destroy', article_path(article),
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
Just not sure what it can be. Please advise. I will supply more code if needed.
The answer will be in your routes.rb and in the URL you are trying to call.
The URL is probably /comment/123, and the routes.rb likely has something like get 'comment/:id' => 'comment#show' in it (or a resource statement for comments).
So rails tries to show the page for a comment and you did not implement the function. The error message is saying exactly what it needs to say (you do indeed not have a .show method in your CommentsController).
If my guesses were not correct, then please post your routes.rb and the URL you are requesting.
I've been working through the same tutorial, and just today ran into the exact same issue as the original poster.
I didn't notice exactly when it started, but it seemed to have started somewhere in section 6, 7 or 8 (adding comments to blog, refactoring, or adding deletion of comments).
Furthermore, clicking links within the app (i.e. the 'Back' link) would spawn a new tab. Likewise, when clicking the 'Destroy' link, the browser's confirmation dialog would appear over the current tab (correct behavior), but at the same time a new browser tab would appear (incorrect) - I think this new tab behavior (a type of 'show'ing of the comment that the user is in process of deciding whether to go ahead and destroy) is what was causing the error.
After finishing the tutorial (one more section, basic authentication), I quit & re-launched Chrome, and then also quit & restarted the server (ctrl-c, then bin/rails server).
After doing this, all is well. Both the spawning extra tab behavior, as well as the spurious show action being triggered (thus causing the error) - when the destroy confirmation sheet appears - have stopped.
So it seems that this problem was simply some spurious noise - perhaps caused by something that's changed (in the rails environment?) between the time the tutorial was originally written, and when the original poster ran into this behavior (and still persisting today, July '18, with rails 5.2).
I know this is old, but the tutorial "Getting Started with Rails" is still the top result on Google for those looking to start building web applications using Rails.
I encountered the same issue today and it kind of makes sense. If you want to delete a comment, you can't simply navigate to it with a GET request, you need to use a DELETE method.
I got around this by replacing link_to with button_to hoping that Rails would sort out the "magic" and it did.
Code used:
<%= button_to 'Destroy Comment', [comment.article, comment],
method: :delete,
data: { confirm: "Are you sure?" } %>
Instead of
<%= link_to 'Destroy Comment', [comment.article, comment],
method: :delete,
data: { confirm: "Are you sure?" } %>
You can add
def show
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
#comment.destroy
redirect_to article_path(#article)
end
to your comments_controller.rb
Actually these codes are identical to those in the def destroy
add in comment model belongs_to :user if you authenticate with user and other too whatever you belongs_to...
I have had some similar issues, for example:
The same message when trying to delete a comment:
The action 'show' could not be found for CommentsController
When trying to delete an article, it was not being deleted but rendering its page instead.
The confirmation dialog was not showing when deleting neither an article nor a comment.
I have tried to restart the server as well as one of the solutions above that says to create a show method identical to the destroy method. Well, it worked fine for deleting comments but it didn't seem right to me considering the RoR DRY pattern.
I ended up realizing that the browser's extension responsible for blocking JavaScript (NoScript 11.2.9) was active and causing those weird behaviors.
In this special case, it was not related with the Rails itself but external factors.
I have the same problem and I tried to add this to comments_controller.rb and it works
def show
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
#comment.destroy
redirect_to article_path(#article)
end
I ran into same problem but adding
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
worked for me rails 6 and webpack you should use this
Update: As I mentioned earlier you dont have show method in your CommentsController so you wont be able to visit the show page for any comment.
As you said you are trying to visit this url: localhost:3000/articles/2/comments/1 which is the show page for comment having id: 1 and the routes(resources :comments) is trying to take it to show action. Hence the error rightly thrown show could not be found for CommentsController.
Please remove the url from your browser and try visiting again the article show page. It will work.
So I am in the process of setting up a forum and everything is setup/working well except for my replies are not appearing on the thread "show" page. After checking the rails console, I see they are saving but the user_id and discussion_id are no. The user_id is always nil and the discussion_id is always 0. The discussion threads were easier to setup but with having these replies, I obviously seem to be having an issue. Here are my snippets of code:
class PostsController
# ...
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
def create
#post = #discussion.post.new(create_params) do |post|
post.user = current_user
end
if #post.save
redirect_to #discussion, notice: "It has been posted!"
else
render :new
end
end
def destroy
#post = #discussion.posts.find(params[:id])
#post.destroy
flash.notice = "Deleted"
redirect_to discussion_path(#discussion)
end
private
def create_params
params.require(:post).permit(:reply)
end
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end
class DiscussionsController
def show
#discussion = Discussion.friendly.find(params[:id])
#post = Post.new
render :layout => 'discussion'
end
end
Partial rendered to reply:
<h2>Reply</h2>
<%= form_for [ #discussion, #post ] do |f| %>
<p>
<%= f.label :reply, "Reply" %><br/>
<%= f.text_field :reply %>
</p>
<p>
<%= f.submit 'Submit' %>
</p>
<% end %>
Partial rendered to show replies in on discussion page:
<h3><%= post.user.first_name %></h3>
<%= post.reply %>
Posted: <%= post.created_at.strftime("%b. %d %Y") %></p>
<p><%= link_to "Delete Comment", [post.discussion, post], data: {confirm: "Are you sure you wish to delete?"}, method: :delete, :class => "post_choices" %></p>
Just want to mention that I also have the correct associations between the three models (User, Discussion, Post). If there is more code needed, please let me know. I appreciate it very much for any information that may be helpful =)
Joe
EDIT
class User < ActiveRecord::Base
has_many :articles
has_many :discussions
has_many :posts
# ...
end
class Discussion
belongs_to :user
has_many :posts
extend FriendlyId
friendly_id :subject, use: :slugged
end
class Post
belongs_to :discussion
belongs_to :user
end
I could post the entire user model if needed but its all validations/devise aspects =P The other two I listed all of the contents in the models.
Edit 2
Thanks to Max, the user_id returns correctly in the console but still not the discussions. Going go dig around a bit more with the recent changes to see what else =)
There are a few issue you need to deal with.
First you should ensure that Devise is actually authorizing your controller action.
class PostsController < ApplicationController
before_filter :authenticate_user!
end
Otherwise current_user will return nil if there is no signed in user. And I'm
guessing that you do not want un-authenticated users to be able to create posts.
Also if you have a nested route you most likely want to check that the discussion actually
exists before trying to add posts.
class PostsController
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
private
# Will raise an ActiveRecord::NotFoundError
# if the Discussion does not exist
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end
When you are creating resources be careful not to query the database needlessly.
This especially applies to CREATE and UPDATE queries which are expensive.
def create
#post = Post.create(post_params) # INSERT INTO 'users'
#post.discussion_id = params[:discussion_id]
#post.user = current_user
#post.save # UPDATE 'users'
flash.notice = "It has been posted!"
redirect_to discussions_path(#post.discussion)
end
Also you are not even checking if the record was created successfully.
So lets put it all together:
class PostsController
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
def new
#post = #discussion.post.new
end
def create
# new does not insert the record into the database
#post = #discussion.post.new(create_params) do |post|
post.user = current_user
end
if #post.save
redirect_to #discussion, notice: "It has been posted!"
else
render :new # or redirect back
end
end
def destroy
#post = #discussion.posts.find(params[:id])
#post.destroy
flash.notice = "Deleted"
redirect_to discussion_path(#discussion)
end
private
def create_params
# Only permit the params which the user should actually send!
params.require(:post).permit(:reply)
end
# Will raise an ActiveRecord::NotFoundError
# if the Discussion does not exist
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end
So I am making a site where users can only submit a post once, and then the "new post" button goes away forever.
I would also like to put a limit on the overall amount of posts. So, only the first 100 or so people can actually post.
I used rails generate scaffold to build the posting system.
I don't know where to start.
Thanks!
You can either create a constant if all user will have the same limit, or add a field in your user record if you plan for each user to have different limits.
Then you create a validator which check the number of existing posts and forbid creation of new posts if the limit is reached
More info in rails guide: http://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations
An alternative approach is using a policy object. Here's how I would approach this using Pundit.
Updated:
app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
def self.limit_exceeded?(max = 100)
count >= max
end
end
app/models/user.rb
class User < ActiveRecord::Base
has_one :post
end
app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
def create?
!user_has_post? && space_to_post?
end
private
def user_has_post?
user.post.present?
end
def space_to_post?
!Post.limit_exceeded?
end
end
app/controllers/posts_controller.rb
class PostsController < ApplicationController
def show
#post = Post.find(params[:id])
end
def new
#post = Post.new
end
def create
authorize(:post)
#post = current_user.build_post(post_params)
if #post.save
redirect_to #post, notice: "Your post was created!"
else
render :new
end
end
private
def post_params
params.require(:post).permit(:message)
end
end
app/view/posts/new.html.erb
<% if policy(:post).create? %>
<%= form_for(#post) do |form| %>
<%= form.text_area :message %>
<%= form.submit "Post" %>
<% end %>
<% else %>
You cannot post.
<% end %>
This code assumes the user is authenticated. If you haven't incorporated authentication, you'll need to use a gem for that, or roll your own implementation. I'd recommend Devise or Clearance.
Good luck!
I can't seem to get Amistad friendships to work correctly. I am getting the following error:
ActiveRecord::RecordNotFound in FriendshipsController#update
Couldn't find Friendship with id=29
I am also using devise and cancan. I followed the gem setup on the wiki pages and created my controller as described in this related post.
class FriendshipsController < ApplicationController
before_filter :authenticate_user!
def index
#friends = current_user.friends
#pending_invited_by = current_user.pending_invited_by
#pending_invited = current_user.pending_invited
end
def create
#friend = User.find(params[:user_id])
#friendship_created = current_user.invite(#friend)
if #friendship_created
redirect_to users_path, :notice => "Your friend request is pending"
end
end
def update
#friend = User.find(params[:user_id])
#friends = current_user.friends
#pending_invited_by = current_user.pending_invited_by
redirect_to users_path, :notice => "You are now friends!"
end
def destroy
#friend = User.find(params[:user_id])
#friendship = current_user.send(:find_any_friendship_with, #friend)
if #friendship
#friendship.delete
#removed = true
redirect_to users_path, :notice => "You are no longer friends!"
end
end
def createblock
#friend = User.find(params[:user_id])
current_user.block #friend
redirect_to users_path, :notice => "You have blocked #{#friend.first_name}"
end
end
I loop though my users in the following manner checking the current status of the user and offering appropriate actions.
<% if current_user.friend_with? user %>
<%= link_to "Unfriend", friend_path(user), :method => "delete", :class => 'btn btn-mini' %>
<% elsif current_user.invited? user %>
<span class="btn btn-mini disabled">Pending</span>
<% elsif user.invited? current_user %>
<%= link_to "Accept", friend_path(user), :method => "put", :class => 'request-approve btn btn-mini' %>
<%= link_to "Decline", friend_path(user), :method => "delete", :class => 'request-decline btn btn-mini' %>
<% else %>
<%= link_to "Add friend", friends_path(:user_id => user), :method => "post", :class => 'btn btn-mini' %>
<% end %>
Figured it would be useful to see what the friendships table looks like in my schema:
create_table "friendships", :force => true do |t|
t.integer "friendable_id"
t.integer "friend_id"
t.integer "blocker_id"
t.boolean "pending", :default => true
end
add_index "friendships", ["friendable_id", "friend_id"], :name => "index_friendships_on_friendable_id_and_friend_id", :unique => true
I understand the error just cannot figure out how this should change. I think my issue is that I am passing in a friend id and it is expecting a friendship id. My only problem with this solution is that every example or post I can find suggests passing user_id, like this post above where the answerer states the gem developer supplied the code he answers with.
What I feel like I need in my update method is to replace:
#friend = User.find(params[:id])
With this:
#friendship = Friendship.find_by_friend_id(params[:id])
EDIT
I can successfully request a friend, I just cannot accept or decline a friend. I a listing of users, clicking the "Add Friend" link creates the record in the friendships db correctly. If I log ins as that recently requested user and attempt to accept the request is when I get the above error. This also occurs if I attempt to decline the request.
The friends method you asked to see come with the amistad gem, here is the code for that method. As for my Ruby logs the section that displays the error was very long, so I have included it in this gist.
Given my current reputation, I can only post an answer instead of a comment to your question but as far as I can see from the controller sources you posted, you are not calling current_user.approve #friend in your update action.
I used this gem in one of my projects recently without running into any problems. The controller actions look like this:
def update
#friend = User.find_by_id(params[:id])
if current_user.approve #friend
redirect_to friendships_path, notice: t('.confirmation_successful')
else
redirect_to friendships_path, alert: t('.confirmation_unsuccessful')
end
end
def destroy
#friend = User.find_by_id(params[:id])
if current_user.remove_friendship #friend
redirect_to friendships_path, notice: t('.deletion_successful')
else
redirect_to friendships_path, alert: t('.deletion_unsuccessful')
end
end
I hope this helps.
The lookup problem is because you're passing ids inconsistently. In 3 of the links, you're passing the User object directly, which should automatically store the id in params[:id], which you can use in your action as User.find(params[:id]). But in your actions, you're extracting it from params[:user_id], which is empty. Not sure how you're getting an ID of 29 in your error message (or 32 or whatever), but...
If you change all your actions to expect params[:id] and switch the "Add friend" path link to pass in a User object the way the others already are, you should be passing the right data in the right parameter, and the lookup should straighten itself out.
Of course, as Wonky Business points out, you're not actually calling approve in your update method, so nothing will actually link, but at least you should be finding all your model objects.
As an aside, it appears from your paths you're remapping the friendship named routes to friend instead. That's muddling the issue because none of the RESTful routes are actually doing what their noun/verb combination implies: if you call friends_path(user) with a POST, there should be a new Friend object when you're done, but this controller is creating and destroying Friendship objects and leaving the Friend objects alone.
If you delete that alias and switch to friendship_path and so forth, then REST will actually be doing what it says it is: managing friendship objects.
Hope that helps!
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...