I am using the gem file acts_as_votable. Currently, when I click the "Like" button it seems that it is working, and the flash notice says "You have liked it!" However, I would like to keep track of how many likes I have for each review. So, I added this line of code in my index.html.erb file (below) but I am getting an error stating: "Could not find table 'votes' "
<td><%= link_to 'Like', like_review_path(review), method: :post%>
(<%=review.get_upvotes.size%>)</td>
This is what I have in my reviews_controller.rb file:
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy, :upvote]
def upvote
#review = Review.find(params[:id])
#review.upvote_by #current_user
flash[:notice] = 'You liked the review!'
redirect_to :back
end
This is for my routes.rb file:
resources :reviews do
member do
post "/like", to: "reviews#upvote"
end
end
Finally, this is what I have in my review.rb file:
class Review < ApplicationRecord
acts_as_votable
belongs_to :user
end
Everything seems to be correct on your code.
try to run the migrations, this seems to be the problem. when you run this
rails generate acts_as_votable:migration
you need to run the migrations too
rake db:migrate
check that the migrations are in the last version and if this doesn't work, restart the rails server, but if it says that could not find the table, then you just didn't runned the migrations, or they returned an error, or deleted the table manually after the migration was done. please see those cases so we can discard this options
Related
I have an anchor tag, which when clicked I want it to open this route http://localhost:3000/shops/1 where 1 is the id of the seller_profile, however I am getting this error
No route matches {:action=>"show", :controller=>"seller_profiles", :id=>nil}, missing required keys: [:id]
<span class="icon"><%= image_tag "user.svg"%></span><span>My Profile</span>
class SellerProfile < ApplicationRecord
belongs_to :seller
end
class ApplicationController < ActionController::Base
helper_method :profile_of_current_seller
def profile_of_current_seller
#profile_of_current_seller ||= Shop.find(seller_id:session[:seller_id]) if session[:seller_id]
end
end
resources :sellers do
resources :seller_profiles, shallow: true
end
class SellerProfilesController < ApplicationController
before_action :set_seller_profile, only: [:show, :edit, :update, :destroy, :index]
# GET /seller_profiles/1
# GET /seller_profiles/1.json
def show
end
private
# Use callbacks to share common setup or constraints between actions.
def set_seller
#seller = current_seller
end
def set_seller_profile
#seller_profile = SellerProfile.find(params[:id])
end
end
You're getting this error because #profile_of_current_seller is nil. In the above scenario seller_profile_path(#profile_of_current_seller) is the same as seller_profile_path(nil). You would want to pass seller_profile_path an instance of seller_profile.
Assuming you have access to #seller_profile, amend your anchor tag to the following:
<span class="icon"><%= image_tag "user.svg"%></span><span>My Profile</span>
#profile_of_current_seller ||= Shop.find(seller_id:session[:seller_id]) if session[:seller_id]
This code would cause a different error than what you are receiving if session[:seller_id] was present. Something like "Couldn't find Shop with id=". You should use find_by instead of find. Find only allows you to look up records by id.
#profile_of_current_seller ||= Shop.find_by(seller_id:session[:seller_id]) if session[:seller_id]
So, because you are not receiving the "Couldn't find Shop with id=" error, the issue with your link is that session[:seller_id] (and therefore, #profile_of_current_seller) is nil.
Also, in your controller you are using params[:id] to set the seller profile, but are passing the id of #profile_of_current_seller, which is an instance of Shop...so Jeremy's Answer may help you out with that.
Issue: I have installed the friendly_id gem, it is working how it supposed to for my Users table, but for my Listings model, the friendly URL to use a :name instead of :id isn't working.
Here's my Listings controller in a nutshell:
def show
#order = #listing.orders.new()
#as you can see, I have no need for a find because i noticed i didn't need it for the show page to show up correctly - idk
end
private
def set_listing
#listing = Listing.find(params[:id])
end
What's confusing to me is that I don't even need something like #listing=Listing.find(params[:id]
I tried using this in the show: #listing=Listing.friendly.find(params[:id]
but it didn't make a difference. I also tried changing the "set_listing" method to include the friendly snippet but that didn't help either - just a shot even though it shouldn't work.
I included this in the model - just like i did for my Users table:
extend FriendlyId
friendly_id :name, use: :slugged
For example, I have my listing, id: 1, name: Listing Test 1, slug: listing-test-1
I go to domain.com/listings/listing-test-1 and I get
Couldn't find Listing with 'id'=listing-test-1
I did everything the same as I did with the Users table but it doesn't seem to work.
Question: Is there something can be interfering with this issue?
FriendlyId does not override the default .find method on your model. Well at least not since version 5 was released way back in 2013.
To lookup a record based on the id and slug column you need to explicitly call .friendly.find:
class ListingsController < ApplicationController
before_action :set_listing, only: [:show, :edit, :update, :destroy]
# ...
def show
#order = #listing.orders.new()
end
# ...
private
def set_listing
#listing = Listing.friendly.find(params[:id])
end
# ...
end
The reason you are currently getting an error is most likely that before_action :set_listing calls the set_listing method before the show action is called.
class ListingsController
before_action :set_listing, only: [:show, :edit, :update, :destroy]
def show
# this line is never reached - and should not really be needed anyways
#listing = Listing.friendly.find(params[:id])
#order = #listing.orders.new()
end
private
def set_listing
# will raise ActiveRecord::NotFoundError
#listing = Listing.find(params[:id])
end
end
Since Listing.find(params[:id]) will raise an ActiveRecord::NotFoundError you never reach the show action. The solution is just to fix the callback method in the first place.
I had this same challenge when working on a Rails 6 application.
Here's how I solved it:
By default Friendly_id gem does not generate slugs for existing records in a Rails application. So if you have existing records for a model, slugs won't be generated for them. They will only be generated for newer records.
To generate slugs for existing records, do the following. Say our model name is Listing and the column we want to use for the slug is name, just do the following:
Start your rails console using:
rails console
and run the command below to generate slugs for existing records:
Listing.find_each(&:save)
Restart your rails server, and check again. You should see slugs now for all the records.
That's all.
I hope this helps
I want to check if a current object's user-id is the same as the id of the current user, so I can allow some operations only to logged-in users. I am using the Devise gem to help me with authentication.
That said, I want to ask a question with a broader scope. I have build associations, at least I think so, but when I open the corresponding pages in the browser I get the error:
undefined method 'user' for nil:NilClass
I know that this error often happens when a particular object in the database is not instantiated or has no entries, but I am using the console and a PostgreSQL GUI tool, to check if the data is present.
This is a screenshot https://www.evernote.com/shard/s233/sh/305c5194-87e0-4019-9eba-9a7f5d7a2839/7c89b4842cc6efc1/res/b7879832-7829-4fe3-b81a-386b6f81cc11/skitch.png?resizeSmall&width=832
First to clarify that I understand right, here's what some things do:
If you define a method (def x) within a controller's "private" section this means, that the data is only available within your controller?
With a callback (before_action) you populate your app's REST methods with the data of the private method, it might want to use?
Now I have an image model with:
class Image < ActiveRecord::Base
mount_uploader :image, ImageUploader
belongs_to :user
belongs_to :game, inverse_of: :images
end
The user model reads like this:
class User < ActiveRecord::Base
...
has_many :images
has_many :games
validates :first_name, :last_name, presence: true
end
In the corresponding image controller I use:
class ImagesController < ApplicationController
before_action :set_image, only: [:show, :edit, :update, :destroy]
before_action :set_game
before_action :authenticate_user!
before_action :check_user
...
private
def set_image
#image = Image.find(params[:id])
end
def set_game
#game = Game.all
end
def check_user
unless (#image.user == current_user) || (current_user.admin?)
redirect_to root_url, alert: "Sorry but you are not allowed to visit this page."
end
end
def image_params
params.require(:image).permit(:title, :alt, :desc, :image, :category)
end
end
With #image.user in the check_user method I try to get the user's id. If I only use the current_user.admin? it works but not as intended, obviously.
As you can see in the screenshot above, the user_id field is populated, so I have no idea why I get this error. Maybe I forgot something?
Based on you error message, the problem is on #image.user in check_user method. Here, #image is nil. You should check if #image.nil? there.
Probably change it to:
#image = Image.find(params[:id])
unless !#image.nil? && ((#image.user == current_user) || (current_user.admin?))
BTW, you should only check user in :show, :edit, :update, :destroy like:
before_action :check_user, only: [:show, :edit, :update, :destroy]
What you're asking is something called authorization.
Authentication - does user exist?
Authorization - does user have permission?
Devise provides authentication, whilst authorization has no "standard" process for Rails.
What you're asking is the base line requirement for authorization in a Rails based application. The way to fix this is to use one of the authorization gems, namely CanCanCan or Pundit to ensure the user can change the required objects.
I'd personally set up authorization as follows:
#Gemfile
gem 'cancancan'
#app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :read, Image, user_id: user.id
end
end
This will allow you to simply call can? :read, #image to validate the authorization for the user.
Fix
The real problem you have is that you're trying to call .user on a non-existent variable.
for nil:NilClass
When you see the above error, it means that you're calling a method on an undeclared variable.
Unlike other programming languages, Ruby doesn't so much treat the variable as undeclared, but as nil - confusing many developers. In short, the error means you're trying to call .user on a variable which does not have the method present; the solution being to ensure #image is declared.
-
The error seems to be caused by this:
#image.user #-> #image does not exist
Therefore, you have to check why #image has not been declared.
I would hazard a guess that the error is caused by your routes. You need to make sure you're calling the images controller properly:
#config/routes.rb
resources :images
#app/controllers/images_controller.rb
class ImagesController < ApplicationController
def show
#image = Image.find params[:id]
authorize! :read, #image
end
end
This should enable only users who own the image to view it. You'll not have to worry about authentication because that will be handled by Devise.
You can use respond_to? method to check if an object can respond to a particular method before calling it. Just like this.
object.respond_to?(:method_name)
Trying to learn rails, building very simple app.
by going to
<%= link_to 'Show Orders', orders_path %>)
I get list of orders for one customer. There are some attributes stored for customer, including first name and second name.
I try to create header with text "Listing orders for John":
<h1>Listing Orders for <%= #customer.try(:name) %></h1>
Result is:
Listing Orders for
What do i do wrong?
UPD:
Well, customer and order actually "nicknames" for Feature and Assets (relations are the same), for easier understanding. Sorry for confusing.
So, Assets controller show method:
class AssetsController < ApplicationController
before_action :set_asset, only: [:show, :edit, :update, :destroy]
def show
end
private
def set_asset
#asset = Asset.find(params[:id])
end
Feature model:
class Feature < ActiveRecord::Base
has_many :assets, dependent: :destroy
end
Asset model:
class Asset < ActiveRecord::Base
belongs_to :feature
end
try returns nil instead of raising exception.So that customer.name is nil.I am sure it wont be coming for all customers.remove try and code will raise exception as name is nil
Ok, finally managed it. A bit different approach - i now show assets on features pages.
class FeaturesController < ApplicationControll
def show
#assets = Asset.where(feature_id: #feature.id)
end
And in features/show.html.erb
<h2>Assets</h2>
<%= render #feature.assets %>
Seems like total magic to me, but still works just like needed.
This is closed now.
I wonder what is a proper way to resolve the following problem.
I have two models :
Publisher and Issue. Publisher has_many issues.
I want to manage issues both from Publishers list and from Issues list.
For example :
on publishers list user can click link "Issues" placed near each publisher. then he goes to Issues list but filtered only for proper publisher. He can click "create new issue" and goes to form for add new issue. On this form i don't need to show him a select list to choose publisher
on Issues list user can click "create new issue" and go to form but this time he should choose publisher from select, which will be related to created issue.
Simply speaking i need crud action for issue alone and for publisher issue.
First I try to make a :
resources :issues
resources :publishers do
resources :issues
end
and in issue controller :
before_filter :find_issue
def find_issue
#publisher = Publisher.find(params[:publisher_id]) if params[:publisher_id]
#issues = #publisher ? #publisher.issues : Issue
end
but i have to make many if condition in my views and controller.
For example if issue is created from publisher , on success i want to redirect to publisher_issues_path instead of issue_path a vice versa. Same problem with all link like "back to list" and similar. So code is in my opinion not very transparent.
Now i wonder to use namespaces.
namespace :publishers, do
resources :issues
end
and make
# app/controllers/publishers/issues_controller.rb
module Publishers
class IssuesController < ApplicationController
# actions that expect a :publisher_id param
end
end
# app/controllers/issues_controller.rb
class IssuesController < ApplicationController
# your typical actions without any publisher handling
end
and make separate view for both controller actions.
Is there a better or cleaner way to resolve this kind of problem? I want to make my code dry as possible.
Many thanks for reply.
Routes:
resources :issues
resources :publishes do
resources :issues
end
Controller:
class IssuesController < ApplicationController
before_filter :build_issue, only: [:new, :create]
before_filter :load_issue, only: [:show, :edit, :update, :destroy]
def index
#issues = issues.page(params[:page])
end
... all other actions will have access to #issue
private
def issues
if params[:publisher_id].present?
Publisher.find(params[:publisher_id]).issues
else
Issue
end
rescue ActiveRecord::RecordNotFound
redirect_to issues_path(alert: 'Publisher not found')
end
def build_issue
#issue = issues.new(issue_params)
end
def load_issue
#issue = issues.find(params[:id])
rescue ActiveRecord::RecordNotFound
redirect_to issues_path(alert: 'Issue not found')
end
def issue_params
# whitelisted attributes goes here
end
end
To avoid using conditions, use actions instead of full named path, i.e:
redirect_to action: :index
link_to 'Issues', {action: :index}