I made a app for managing personal library (books and comics) and it was working well but all of the suddent it started to show to all users (new or existing) all the books that were introduced by a given user (it seems to display the entire db); but i have devise and pundit implemented and it was protecting the user information until now.... Need some help solving this issue.
Code follows but if needed some more, please shout.
Thanks in advance.
Book_controller.rb
class BooksController < ApplicationController
before_action :authenticate_user!
def index
unless params[:term].present?
#books = policy_scope(Book)
else
#books = policy_scope(Book)
#books = Book.search_by_full_name(params[:term])
end
#books = Book.order(:title)
respond_to do |format|
format.html
format.csv { send_data #books.to_csv }
format.xls # { send_data #products.to_csv(col_sep: "\t") }
end
#books = Book.paginate(page: params[:page])
end
def show
#book = Book.find(params[:id])
authorize #book
end
def new
#user = User.find(params[:user_id])
#book = Book.new
authorize #book
end
def create
#user = User.find(params[:user_id])
#book = Book.new(book_params)
#book.user = #user
authorize #book
if #book.save
redirect_to user_books_path
flash[:notice] = 'Success. Your book was added to the Library'
else
render "new"
flash[:notice] = 'Book not created. Please try again'
end
end
def edit
#user = User.find(params[:user_id])
#book = Book.find(params[:id])
authorize #book
end
def update
#book = Book.find(params[:id])
#book.update(book_params)
authorize #book
redirect_to user_book_path
end
def destroy
#book = Book.find(params[:id])
#book.destroy
authorize #book
redirect_to user_books_path
end
private
def book_params
params.require(:book).permit(:title, :author, :photo, :comments)
end
end
book_policy.rb
class BookPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.where(user: user)
end
def index?
record.user == user
end
def show?
true
end
def new?
true
end
def create?
true
end
def edit?
true
end
def update?
true
end
def destroy?
record.user == user
end
end
end
application_policy.rb
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def index?
false
end
def show?
true
end
def create?
true
end
def new?
create?
end
def update?
true
end
def edit?
update?
end
def destroy?
true
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope.all
end
end
end
book.rb
class Book < ApplicationRecord
belongs_to :user
has_many :loans
has_one_attached :photo
validates :title, presence: true
validates :author, presence: true
include PgSearch::Model
pg_search_scope :search_by_full_name, against: [:title, :author],
using: {
tsearch: {
prefix: true
}
}
def self.to_csv(options = {})
CSV.generate(options) do |csv|
csv << column_names
all.each do |book|
csv << book.attributes.values_at(*column_names)
end
end
end
self.per_page = 12
end
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
after_create :send_welcome_email
has_many :books
has_many :comics
has_many :wishlists
has_many :loans, through: :books
has_one_attached :photo
private
def send_welcome_email
UserMailer.with(user: self).welcome.deliver_now
end
end
When you reassign the #books instance variable, the previous scope is lost. Try sth like this:
#books = policy_scope(Book).order(:title)
#books = #books.search_by_full_name(params[:term]) if params[:term].present?
respond_to do |format|
format.html { #books = #books.paginate(page: params[:page]) }
format.csv { send_data #books.to_csv }
end
You are overwriting the #books variable many times instead of doing some chaining.
#books = policy_scope(Book)
#books = #books.search_by_full_name(params[:term]) if params[:term].present?
#books = #books.order(:title)
respond_to ...
end
Solved it... I wasn't applying current_user on each book, in views/index... That way, each user had their own books db but when rendering index, it was showing everyone's books, no matter which user created it... It's the simple things, most of the times:P
Related
I'm renewing a small library app and my search filter (pg_search) doesn't work with a model that is referenced with another (in this case, model Book as User references, for each user to have it's own set of books).
But if i delete the references, the search works... In the case that if the books were available to every user but that's not the purpose.
What am i missing?
Thanks in advance for any help.
Book.rb
class Book < ApplicationRecord
belongs_to :user
include PgSearch::Model
pg_search_scope :search_by_full_name, against: [:title, :author],
using: { tsearch: { prefix: true } }
validates :title, presence: true
validates :author, presence: true
end
User.rb
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :books
end
books_controller.rb
class BooksController < ApplicationController
def index
if params[:term].present?
#books = Book.search_by_full_name(params[:term])
else
#books = Book.all
end
end
def show
#book = Book.find(params[:id])
end
def new
#user = User.find(params[:user_id])
#book = Book.new
end
def create
#user = User.find(params[:user_id])
#book = Book.new(book_params)
#book.user = #user
if #book.save
redirect_to user_books_path
flash[:notice] = 'Success. Your book was added to the Library'
else
render "new"
flash[:notice] = 'Book not created. Please try again'
end
end
def edit
#user = User.find(params[:user_id])
#book = Book.find(params[:id])
end
def update
#book = Book.find(params[:id])
#book.update(book_params)
redirect_to user_book_path
end
def destroy
#book = Book.find(params[:id])
#book.destroy
redirect_to user_books_path
end
private
def book_params
params.require(:book).permit(:title, :author, :photo, :comments)
end
end
_search_book.html.erb
<%= form_tag user_books_path(current_user.id, #book), method: :get do %>
<%= text_field_tag 'term', params[:term], placeholder: "Enter title or author" %>
<%= submit_tag 'Search!' %>
<% end %>
It's solved... I was using current_user on the views instead of applying all the method on the controller and on the view leave it with only the model instance.
Thanks everyone
I am creating a todo application via Ruby on Rails. I have created my own model for tags and taggings without the use of act_as_taggable gem. I have used devise for user authentication. One of my feature is to have a page which can show all of the user's tasks which is related by tags. However I have tried to change my show and index method in the tags controller to incorporate current_user but it always throws me this error
(undefined method tag for # Did you mean? tags tap):
Error
I am unable to figure out how I can edit my code to create tags within the current_user and properly process the tag_list for my current_user.
This is are the relevant codes:
Task model
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings
validates :item, presence: true,
length: { minimum: 5 }
belongs_to :user
validate :due_date_cannot_be_in_the_past
def self.tagged_with(name)
Tag.find_by!(name: name).tasks
end
def tag_list
tags.map(&:name).join(" ")
end
def tag_list=(names)
self.tags = names.split(" ").map do |name|
Tag.where(name: name).first_or_create!
end
end
def due_date
due.to_s
end
def due_date=(str)
self.due = Chronic.parse(str).to_date.to_s
rescue
#invalid_date = true
end
def validate
errors.add :due, 'is not a valid date' if #invalid_date
end
def due_date_cannot_be_in_the_past
if due.past?
errors.add(:due_date, "is not a valid date")
end
end
end
Task Controller:
def index
#incomplete_tasks = Task.where(complete: false)
#complete_tasks = Task.where(complete: true)
end
def show
#task = current_user.tasks.find(params[:id])
end
def new
#task = current_user.tasks.build
end
def edit
#task = Task.find(params[:id])
end
def create
#task = current_user.tasks.build(task_params)
if #task.save
redirect_to #task
else
render 'new'
end
end
def update
#task = Task.find(params[:id])
if #task.update(task_params)
redirect_to #task
else
render 'edit'
end
end
def destroy
#task = Task.find(params[:id])
#task.destroy
redirect_to tasks_path
end
def complete
#task = current_user.tasks.find(params[:id])
#task.update_attribute(:complete, true)
flash[:notice] = "Task Marked as Complete"
redirect_to tasks_path
end
private
def task_params
params.require(:task).permit(:item, :description, :tag_list, :due)
end
end
Tags Controller:
def show
#tag = current_user.tag.find(params[:id])
end
def index
#tag = current_user.tag.all
end
Please do let me know if any other information is needed to make this problem clearer.
Don't know why this is happening here.
NoMethodError in PostsController#update
undefined method `user' for nil:NilClass
My user has admin : true and I can't update other users.posts.
I want to let all users see the content, but only registered users can create content. And if the user is admin or the creator of that content he can edit/update/destroy it as well.
post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
validates :title, presence: true, length: { minimum: 5 }
validates :body, presence: true, length: { minimum: 240 }
end
user.rb
class User < ApplicationRecord
include Encryptable
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_attached_file :avatar
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\z/
validates :level, numericality: { less_than_or_equal_to: 100, only_integer: true }, allow_blank: true
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable
before_validation :downcase_email #, :populate_iv_fields #if you need/want iv to change more often
before_create :create_encryption_key
after_create :save_encryption_key
after_create :build_user_consents
attr_encrypted :email, key: :encryption_key
has_many :user_consents, dependent: :destroy
#entry point for exporting user's personal information
def self.export_personal_information(user_id)
return nil unless User.exists?(user_id)
descendants = ApplicationRecord.descendants.reject{|model| !model.has_personal_information?}
result = Hash.new
descendants.each do |descendant|
result[descendant.name] = descendant.export_personal_information_from_model(user_id)
end
return result
end
#simplest example, we just export to json
def self.export_personal_information_from_model(user_id)
return User.find(user_id).to_json
end
#overwrite this to true for methods that you will want to be included in export_personal_information
def self.has_personal_information?
true
end
#helper method if you are creating a user from console and want them to have all consents set
def fill_consents
hash = Hash.new
ConsentCategory.all.map(&:id).each do |id|
hash[id]='on'
end
self.registration_consents=hash
end
#unfortunately not having an email field that you can just "write to" breaks
#Devise. Below some necessary workarounds
def email_changed?
encrypted_email_changed?
end
def downcase_email
self.email = self.email.downcase
end
def registration_consents=(consents)
#consents = consents
end
def registration_consents
#consents
end
validate :validate_consents_completeness
validates_presence_of :email, if: :email_required?
validates_uniqueness_of :username, allow_blank: false, if: :username_changed?
validates_length_of :username, within: 6..20, allow_blank: true
validate :validate_email_uniqueness #validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
validates_format_of :email, with: Devise.email_regexp, allow_blank: true, if: :email_changed?
validates_presence_of :password, if: :password_required?
validates_confirmation_of :password, if: :password_required?
validates_length_of :password, within: Devise.password_length, allow_blank: true
def password_required?
!persisted? || !password.nil? || !password_confirmation.nil?
end
#end original devise
def email_changed?
self.encrypted_email_changed?
end
def email_required?
true
end
def email_unique?
records = Array(self.class.find_by_email(self.email))
records.reject{|u| self.persisted? && u.id == self.id}.empty?
end
#unfortunately, this is an O(n) operation now that has to go through ALL the users to see if an email is unique. Sorry!
#if you need it to ne O(1) then consider adding email_hash field instead
def self.find_by_email(email)
users = User.all
users.each do |user|
return user if user.email.downcase == email.downcase
end
return nil
end
protected
def validate_email_uniqueness
errors.add(:email, :taken) unless email_unique?
end
def validate_consents_completeness
return if self.id #we assume that already created user has all consents
errors.add(:registration_consents, 'Sie müssen allen erforderlichen Bedingungen zustimmen um fortzufahren.') and return unless self.registration_consents
consents = ConsentCategory.where(mandatory: true).map(&:id)
ids = self.registration_consents.keys.map(&:to_i) #we are relying on a fact that checkboxes that are not checked are not sent to Rails back-end at all
consents.each do |consent_type|
errors.add(:registration_consents, 'Sie müssen allen erforderlichen Bedingungen zustimmen um fortzufahren.') and return unless ids.include?(consent_type)
end
end
def build_user_consents
ids = registration_consents.keys
categories = ConsentCategory.where(id: ids)
raise 'Die vom Benutzer eingereichte Zustimmungsliste enthält Objekte, die nicht in der Datenbank vorhanden sind!' if categories.count != ids.count
categories.each do |category|
consent = UserConsent.new
consent.consent_category = category
consent.user = self
consent.requires_revalidation = false
consent.agreed_at = self.created_at
consent.save!
end
end
end
post_policy.rb
class PostPolicy < ApplicationPolicy
attr_reader :user, :post
def initialize(user, post)
#user = user
#post = post
end
def index?
true
end
def create?
user.present?
end
def new?
user.present?
end
def update?
return true if post.user_id == user.id || user == user.admin?
end
def destroy?
return true if post.user_id == user.id || user == user.admin?
end
private
def post
record
end
end
application_policy.rb
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def index?
false
end
def show?
false
end
def create?
false
end
def new?
create?
end
def update?
user.admin?
end
def edit?
user.admin?
end
def destroy?
user.admin?
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope.all
end
end
end
post_controller
class PostsController < ApplicationController
before_action :find_post, only: %i[destroy edit update comment_owner upvote downvote]
after_action :verify_authorized, except: [:index, :show]
layout '_app_nav'
def index
return redirect_to post_path(params[:post_id]) if params[:post_id]
return redirect_to user_path(params[:user_id]) if params[:user_id]
#post = Post.all.order('created_at DESC')
#posts = Post.all.order('created_at DESC')
#user = User.all
#posts = if params[:suche]
else
Post.all.order('created_at DESC')
end
#comments = Comment.all
end
def new
#post = Post.new
end
def create
#post = current_user.posts.build(post_params)
authorize #post
if #post.save!
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
#user = #post.user
#comments = Comment.where(post_id: #post).order('created_at DESC').paginate(:page => params[:page], :per_page => 5)
end
def edit
authorize #post
#post = Post.find(params[:id])
end
def update
#user = User.find(params[:id])
#post = Post.find(params[:id])
authorize #post
#post.update(title: params[:title], body: params[:post_params])
redirect_to post_path(#post)
end
def destroy
#post = Post.find(params[:id])
#post.destroy
authorize #post
redirect_to posts_path
end
=begin
def upvote
#post.upvote_from current_user
redirect_to post_path(#post)
end
def downvote
#post.downvote_from current_user
redirect_to post_path(#post)
end
=end
private
def post_params
params.require(:post).permit(:title, :body, :user_id)
end
def find_post
#post = Post.find(params[:id])
end
end
application_controller
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery with: :exception
before_action :authenticate_user!
before_action :configure_permitted_parameters, if: :devise_controller?
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) do |user_params|
user_params.permit(:username, :email, :password, :password_confirmation, registration_consents: {})
end
end
private
def user_not_authorized
flash[:alert] = 'test'
redirect_to(root_path)
end
end
registrations_controller:
class RegistrationsController < Devise::RegistrationsController
private
def account_update_params
params.require(:user).permit(:email, :username, :avatar, :current_password, :password, :password_confirmation)
end
end
edit:
updating my post_policy.rb with #post such as return true if user.present? && user == #post.user || user == user.admin? resolves in -> Pundit::NotAuthorizedError in PostsController#update
not allowed to update?
In your PostsController, you need to include update method inside array where you specify before which methods should authenticate_user before action run:
before_action :authenticate_user!, only: [:create, :destroy, :new, :edit, :update]
If you're using devise you can use this callback, in your application_controller.rb to validate you have a user logged in.
before_action :authenticate_user!
With that you avoid doing
user.present?
Your post_policy.rb should look like this
class PostPolicy < ApplicationPolicy
attr_reader :user, :post
def initialize(user, post)
#user = user
#post = post
end
def index?
true
end
def create?
user.present?
end
def new?
user.present?
end
def update?
return true if record.user_id == user.id || user == user.admin?
end
def destroy?
return true if record.user_id == user.id || user == user.admin?
end
private
def post
record
end
end
Also, to avoid that users can enter the links in the browser, you can do an extra step on you controller, which is adding the following callback and method
class PostsController < ApplicationControl
before_action :authorization
private
def authorization
authorize(Post)
end
end
EDIT:
Make sure your ApplicationController looks like this one to prevent the error Pundit::NotAuthorizedError.
class ApplicationController < ActionController::Base
include Pundit
before_action :authenticate_user!
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
private
def user_not_authorized
flash[:alert] = 'You are not authorized to perform this action.'
redirect_to(root_path)
end
end
I am trying to modify Michael Hartl sample app from railstutorial.
My static_pages controller looks like that:
class StaticPagesController < ApplicationController
def home
if logged_in?
#micropost = current_user.microposts.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
def help
end
def about
end
def contact
end
end
and a Im geting error mesage like this:
NoMethodError in StaticPagesController#home
undefined method `microposts' for #<User:0x00000004d39d58>
else
match = match_attribute_method?(method.to_s)
match ? attribute_missing(match, *args, &block) : super
end
end
user model:
class User < ActiveRecord::Base
has_many :listings, :class_name => "Micropost", :foreign_key => "seller_id", dependent: :destroy
has_many :bids, foreign_key: "bidder_id"
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: {case_sensitive: false}
has_secure_password
validates :password, length: { minimum: 6 }, allow_blank: true
def to_s
self.name
end
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
# Activates an account.
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
end
# Defines a proto-feed.
# See "Following users" for the full implementation.
# Returns a user's status feed.
def feed
following_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
Micropost.where("seller_id IN (#{following_ids})
OR seller_id = :seller_id", seller_id: id)
end
# Follows a user.
def follow(other_user)
active_relationships.create(followed_id: other_user.id)
end
# Unfollows a user.
def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id).destroy
end
# Returns true if the current user is following the other user.
def following?(other_user)
following.include?(other_user)
end
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
user controller
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
:following, :followers]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
#micropost = #user.microposts.build
#microposts = #user.microposts.paginate(page: params[:page])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# Before filters
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
I not sure is this problem related to controller implementation or views or meybe microposts implementations. Please gyus help for complete rails noob
The first line in your model is:
class User < ActiveRecord::Base
has_many :listings, :class_name => "Micropost", :foreign_key => "seller_id", dependent: :destroy
In your User Model you should have:
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
You have defined:
has_many :listings, :class_name => "Micropost", :foreign_key => "seller_id"
So you must use:
#micropost = current_user.listings.build
Instead of yours:
#micropost = current_user.microposts.build
The reason is Rails takes :listings as the 'official name' of the relation.
When you specify :class_name => "Micropost" - for Rails that means the actual name.
So you can use as many official names as you want for one actual dependence.
I am trying to build a blog where there will be 2 users admin and normal user.Admin can view every post and comment .Whereas normal user can view his only post and comment .I have applied my logic but it is not working .In my code every user can view each other post and comment which I don't want .I have uploaded my code at github
link
[ability.rb]
class Ability
include CanCan::Ability
def initialize(user)
unless user
else
case user.roles
when 'admin'
can :manage, Post
can :manage, Comment
when 'user'
can :manage, Post, user_id: user.id
can :manage, Comment, user_id: user.id
end
end
class PostsController < ApplicationController
before_action :authenticate_user!
authorize_resource
def index
#posts = Post.all.order('created_at DESC')
end
def new
#post = Post.new
end
def show
#post = Post.find(params[:id])
end
def create
#post = Post.new(post_params)
#post.user = current_user
if #post.save
redirect_to #post
else
render 'new'
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(params[:post].permit(:title, :body))
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body)
end
end
[comment_controller]
class CommentsController < ApplicationController
authorize_resource
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:comment].permit(:name, :body))
#comment.user = current_user
#comment.save
redirect_to post_path(#post)
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
end
[user.rb]
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :posts
has_many :comments
end
[post.rb]
class Post < ActiveRecord::Base
has_many :comments, dependent: :destroy
validates :title, presence: true, length: {minimum: 5}
validates :body, presence: true
belongs_to :user
end
[comment.rb]
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
Firstly...
CanCan is no longer maintained; CanCanCan should be added to your Gemfile:
#Gemfile
gem "cancancan"
--
You'll be able to use the following:
#app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
case user.roles
when "admin" #-> use double quotes for evaluating strings
can :manage, [Post, Comment]
when "user"
can :manage, [Post, Comment], user_id: user.id
end
end
end
--
You also need to make sure you're calling authorize!.
Although authorize_resource is good, in your case, you need to make sure you're sticking to convention...
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
authorize_resource :post
authorize_resource :comment
def create
#post = Post.find params[:post_id]
#comment = #post.comments.new comment_params
#comment.user = current_user
#comment.save
redirect_to #post
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find params[:id]
#comment.destroy
redirect_to #post
end
private
def comment_params
params.require(:comment).permit(:name, :body)
end
end
Most likely you just need to add :read instruction along with :manage to disallow user to view other users posts.
Like this:
class Ability
include CanCan::Ability
def initialize(user)
unless user
else
case user.roles
when 'admin'
can :manage, Post
can :manage, Comment
when 'user'
can :manage, Post, user_id: user.id
can :manage, Comment, user_id: user.id
can :read, Post, user_id: user.id
can :read, Comment, user_id: user.id
end
end
end
end
please refer to this page: https://github.com/ryanb/cancan/wiki/defining-abilities
Hope you have maintain the roles for each users as admin or normal.
you can try this
in ability.rb
include CanCan::Ability
def initialize(user)
case user.role
when User::Roles::ADMIN
can :manage, :all
end
when User::Roles::USER
can :create, Comment, :user_id => user.id
end
end
Hope this will help