how to use this class method within my Rails 3 code? - ruby-on-rails

I am really hoping to get this resolved tonight so any help would be great.
I added this class method into my post.rb model
def self.without_review
where(review: false)
end
What I am trying to do is ONLY show all posts on the site where review=false. If review=true, I want to manually approve them before they're displayed. Right now, all posts are getting displayed whether the review is true or false.
Here's my post controller
class PostsController < ApplicationController
before_filter :signed_in_user
before_filter :load_post, only: :destroy
def create
#post = current_user.posts.build(params[:post])
if #post.save
flash[:success] = "Shared!"
redirect_to root_path
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
#post.destroy
redirect_to root_path
end
private
def correct_user
#post = current_user.posts.find_by_id(params[:id])
redirect_to root_path if #post.nil?
end
def load_post
#post = current_user.admin? ? Post.find(params[:id]) : current_user.posts.find(params[:id])
end
end
and here's the full post.rb model
class Post < ActiveRecord::Base
attr_accessible :content, :review
belongs_to :user
validates :user_id, presence: true
validates :content, presence: true
default_scope order: 'posts.created_at DESC'
def self.without_review
where(review: false)
end
end
The schema of the posts table to show how "review" is set up (last row)
create_table "posts", :force => true do |t|
t.text "content"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.boolean "review", :default => false
The Static Pages Controller
class StaticPagesController < ApplicationController
def home
if signed_in?
#post = current_user.posts.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
def post1
if signed_in?
#post = current_user.posts.build
end
end
end
UsersController (def show)
def show
#user = User.find(params[:id])
#posts = #user.posts.paginate page: params[:page], :per_page => 15
end

You didn't show the controller/action where the actual listing of posts is generated, but I guess you have to replace Post.all with Post.without_review there.

Related

association between two model in rails , controller

I can not find the issue with my association, but continuously getting error related to the association. I added has_many to Schools and belongs_to to members.
class CreateMembers < ActiveRecord::Migration[5.0]
def change
create_table :members do |t|
t.string :name
t.string :email
t.timestamps
end
end
end
class CreateSchools < ActiveRecord::Migration[5.0]
def change
create_table :schools do |t|
t.string :name
t.timestamps
end
end
end
class AddSchoolRefToMembers < ActiveRecord::Migration[5.0]
def change
add_reference :members, :school, foreign_key: true
end
end
Controller:
class MembersController < ActionController::Base
before_action :set_school
def index
#members = Member.all
end
def new
#member = Member.new
end
def create
#member = Member.new(member_params)
#member.school = #school
#member.save
redirect_to members_path
end
private
def set_school
#school = School.find(params[:school])
end
def member_params
params.require(:member).permit(:name, :email,:school)
end
end
Instead of assigning the #school itself you should assign the id of that school:
def create
#member = Member.new(member_params)
#member.school = #school.id # here it is #school.id
#member.save
redirect_to members_path
end
The associations work with IDs not Arrays.
#school return the school record completely you just need the id to create the association.

Sort a list of Items in Rails based on Likes done by Users

I'm new to ruby on rails so please forgive the question. I tried following this example Rails sort tags by most used (tag.posts.count) but kept getting an error "undefined method `order' for Items:Module". I am trying to sort a list of items based on an item's likes. So an item with 5 likes should be placed above an item with only 3 likes. I have listed below all my relevant code down below. Thank you so much guys!!
Like.rb
class Like < ApplicationRecord
belongs_to :item, :counter_cache => true
belongs_to :user
end
Likes_controller.rb
class Items::LikesController < ApplicationController
before_action :authenticate_user!
before_action :set_book
def create
#item.likes.where(user_id: current_user.id).first_or_create
respond_to do |format|
format.html {redirect_to #item}
format.js
end
end
def destroy
#item.likes.where(user_id: current_user.id).destroy_all
respond_to do |format|
format.html {redirect_to #item}
format.js
end
end
private
def set_book
#item = Item.find(params[:item_id])
end
end
Item.rb
class Item < ApplicationRecord
has_many :likes, :counter_cache => true
users_controller.rb
class UsersController < ApplicationController
before_action :authenticate_user!
before_action :set_user, only: [:show, :edit, :update, :destroy]
def index
#items = Item.all
Items.order('likes_count')
end
def show
#items = Item.find(params[:id])
end
private
def set_user
#item = Item.find(params[:id])
end
end
index.html.erb
<% #items.each do |item| %>
<%= item.product %>
<div><%= image_tag(item.avatar.url(:thumb)) %></div>
<% end %>
Migrations Relevant
class AddLikecountsToItem < ActiveRecord::Migration[5.0]
def change
add_column :items, :likes_count, :integer, :null => false, :default => 0
end
end
class CreateLikes < ActiveRecord::Migration[5.0]
def change
create_table :likes do |t|
t.integer :user_id
t.integer :item_id
t.timestamps
end
end
end
in users_controller.rb
def index
#items = Item.order('likes_count')
end

Rails authorization with cancan

I am following this tutorial
I am trying to authorize user only If user is admin he should be able to see all post and comments otherwise the normal user can see its own post only .I have read github page but was quite confusing
[post_controller.rb]
class PostsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
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
[comments_controller]
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:name, :body))
#comment.user = current_user
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
[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' # or whatever role you assigned to a normal logged in user
can :manage, Post, user_id: user.id
can :manage, Comment, user_id: user.id
end
end
[comment.rb]
class Comment < ActiveRecord::Base
belongs_to :post
end
[post.rb]
class Post < ActiveRecord::Base
has_many :comments, dependent: :destroy
validates :title, presence: true, length: {minimum: 5}
validates :body, presence: true
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
end
[migration]
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, default: 0, null: false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
t.timestamps
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
end
end
[migration]
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :name
t.text :body
t.references :post, index: true
t.timestamps
end
end
end
[migration]
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.text :body
t.timestamps
end
end
end
It seems you do not yet have a user relationship to post and comment in which you need in order to identify if the user owns/created the comment/post
Run:
rails generate migration AddUserToPost user:belongs_to
rails generate migration AddUserToComment user:belongs_to
bundle exec rake db:migrate
Then add the association relationships:
post.rb
class Post < ActiveRecord::Base
belongs_to :user
# ..
end
comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
# ..
end
user.rb
class User < ActiveRecord::Base
has_many :posts
has_many :comments
# ..
end
Now you can identify who owns the post/comment, and what posts/comments a user owned/created with something like the following pseudo-code:
# rails console
post = Post.find(1)
post_owner = post.user
comment = Comment.find(1)
comment_owner = comment.user
user = User.find(1)
user_comments = user.comments
user_posts = user.posts
Now, the next step is to auto-associate the logged-in user to newly created posts/comments. This is done through the controllers:
posts_controller.rb
class PostsController < ApplicationController
authorize_resource
# ..
def create
#post = Post.new(post_params)
#post.user = current_user # I assume you have a variable current_user, or if you are using Devise current_user is already accessible
if #post.save
redirect_to #post
else
render :new
end
end
end
comments_controller.rb
class CommentsController < Application
authorize_resource
# ..
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:comment].permit(:name, :body))
#puts "hhhhhhhhhh#{#comment}"
#comment.user = current_user # I assume you have a variable current_user, or if you are using Devise current_user is already accessible
#comment.save
redirect_to post_path(#post)
end
end
Now, at this point. Whenever a post/comment gets created, the logged-in user is automatically associated to it (as the owner).
Finally, we could just update the Ability class to only authorize users to :edit, :update, :show, and :destroy actions, if the user_id: current_user (logged-in user).
ability.rb
class Ability
include CanCan::Ability
def initialize(user)
# if not logged in (Guest)
unless user
# cant do anything unless you add more `can` here
# else if logged in
else
case user.role
when 'admin'
can :manage, Post
can :manage, Comment
when 'normal' # or whatever role you assigned to a normal logged in user
can :manage, Post, user_id: user.id
can :manage, Comment, user_id: user.id
# If you don't have a role name for a normal user, then use the else condition like Rich Peck's answer. Uncomment the following instead, and then comment the `when 'normal' block of code just above
# else
# can :manage, Post, user_id: user.id
# can :manage, Comment, user_id: user.id
end
end
end
end
Just a final helpful information to the Ability above:
can :manage, Post, user_id: user.id
This is just a shorthand equal to:
can [:show, :edit, :update, :destroy], Post, user_id: user.id
can [:index, :new, :create], Post
You will notice that user_id: user.id is not taken into consideration for :index, :new, and :create because these are :collection methods, and not :member methods. More info here
If you want readability and customizability, you may opt to use the longer one above instead of the shorthand :manage.
#app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
case user.role
when "admin"
can :manage, :all
else
can :read, Post #-> cannot read comments
end
end
end
The above is how the ability class should look. You can replace the switch/case with if/else.
--
You're missing the evaluation of your objects, specifically with the can? & authorize methods:
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
#post = Post.find params[:post_id]
#comment = #post.comments.new comment_params
#comment.save if authorize! :create, #comment
redirect_to #post
end
def destroy
#post = Post.find params[:post_id]
#comment = #post.comments.find params[:id]
#comment.destroy if authorize! :destroy, #comment
redirect_to #post
end
private
def comment_params
params.require(:comment).permit(:name, :body)
end
end
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def show
#post = Post.find params[:id]
end
end
#app/views/posts/show.html.erb
<%= #post.title %>
<%= render #post.comments if can? :read, #post.comments %>
1) Change this line in PostsController, delete this condition: except [index, show]. Or user could see pages without authorization.
before_action :authenticate_user!
2) Change index action and other with this style. Use - current_user.
def index
if current_user.has_role? :admin
#posts = Post.all.order('created_at DESC')
else
#posts = current_user.posts.order('created_at DESC')
end
end
You can write you abilities in this way
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
case user.role
when "admin"
can :manage, :all
else
can :read, Post, :user_id => user.id
end
end
end
And just load resources of post using ability resource so that it only load post of current user if other than admin
class CommentsController < Application
load_and_authorize_resource
def index
#posts = #posts
end
end

Rails: Struggling with Associations (nested comments)

First off, I apologize for the amount of code and for what I'm asking. But I need help desperately. I can't wrap my head around this concept whatsoever.
I have a basic CRUD (projects) and I'm trying to nest another CRUD (discussions) to projects so that there can be a discussion for each project. Now, I have been trying to do this for five days straight. I can't figure it out for the life of me. I've read and researched everything there is to be read and researched. I can't figure it out on my own.
So, I've started fresh. I've set up a new project and got the basics, but I have no clue where to go from here. I would be so incredibly appreciative if someone could take the time to write me step by step instructions. Or, if you're able to do it quick, perhaps even finish my code for me? Because I'm going to have to do about 5 more of these, so if I were to have 1 fully completed one I could reference that would be so amazing.
projects_controller.rb
class ProjectsController < ApplicationController
def index
#projects = Project.all
end
def show
end
def new
#projects = Project.new
end
def create #no view
#projects = Project.new(project_params)
if #projects.save
redirect_to projects_path, :notice => "Your project was sent!"
else
render "new"
end
end
def edit
#projects = Project.find(params[:id])
end
def update #no view
#projects = Project.find(params[:id])
if #projects.update_attributes(project_params)
redirect_to projects_path, :notice => "Your project has been updated."
else
render "edit"
end
end
def destroy #no view
#projects = Project.find(params[:id])
#projects.destroy
redirect_to projects_path, :notice => "Your project has been deleted."
end
private
def project_params
params.require(:project).permit(:title, :description)
end
end
discussions_controller.rb
class DiscussionsController < ApplicationController
def index
#discussion = Discussion.all
end
def show
#discussions = Discussion.find(params[:id])
#projects = #discussions.Project.all
end
def new
#discussions = Discussion.new
end
def create #no view
#discussions = Discussion.new(discussion_params)
if #discussions.save
redirect_to discussions_path, :notice => "Your discussion was submitted."
else
render "new"
end
end
def edit
#discussions = Discussion.find(params[:id])
end
def update #no view
#discussions = Discussion.find(params[:id])
if #discussions.update_attributes(discussion_params)
redirect_to discussions_path, :notice => "Your discussion has been updated."
else
render "edit"
end
end
def destroy #no view
#discussions = Discussion.find(params[:id])
#discussions.destroy
redirect_to discussions_path, :notice => "Your discussions has been deleted."
end
private
def discussion_params
params.require(:discussion).permit(:title, :description)
end
end
routes.rb
Rails.application.routes.draw do
resources :homes
resources :projects
resources :discussions
root "homes#index"
Models:
discussion.rb
class Discussion < ActiveRecord::Base
belongs_to :project
end
project.rb
class Project < ActiveRecord::Base
has_many :discussions
end
Migrates:
_create_projects.rb
class CreateProjects < ActiveRecord::Migration
def change
create_table :projects do |t|
t.string :title
t.text :description
t.date :due_date
t.timestamps null: false
end
end
end
_create_discussions.rb
class CreateDiscussions < ActiveRecord::Migration
def change
create_table :discussions do |t|
t.string :title
t.text :description
t.timestamps null: false
end
end
end
_create_nested_discussions
class NestedDiscussion < ActiveRecord::Migration
def change
add_column :discussions, :project_id, :integer
end
end
One thing I noticed is that you don't permit project_id attribute in your strong parameters. So add :project_id in your params in discussions_controller.rb:
private
def discussion_params
params.require(:discussion).permit(:title, :description, :project_id)
end
You probably want your routes to be as such:
resources :projects do
resources :discussions
end

rails:Sqlite:how to make date only with two foreign key?

One of my database table(feedback) have 2 key
class CreateFeedbacks < ActiveRecord::Migration
def change
create_table :feedbacks do |t|
t.string :strengths
t.string :weaknesses
t.string :recommendations
t.string :rating
t.integer :user_id
t.integer :subject_id
t.timestamps
end
end
end
I want one user to only have one feedback for one subject.How to add code
def create
#feedback = Feedback.new(params[:feedback])
#feedback.user_id=current_user.id
if #feedback.save
flash[:success] = "Welcome #{current_user.name}!"
redirect_to #feedback
else
render 'new'
end
end
class FeedbacksController < ApplicationController
def new
#feedback = Feedback.new
##user = Subject.find_all_by_teacher_id(current_user.id)
user_subject
end
def create
#feedback = Feedback.new(params[:feedback])
#feedback.user_id=current_user.id
if #feedback.save
flash[:success] = "Welcome #{current_user.name}!"
redirect_to #feedback
else
render 'new'
end
end
def show
#feedback_user_all= Feedback.find_all_by_user_id(current_user.id)
end
def user_subject
#course = Course.find(current_user.course_id)
#subject = #course.subjects
end
end
Use this in your Feedback class:
class Feedback < AR::Base
validates :user_id, uniqueness: {scope: [:subject_id]}
end
This will throw an error if you try to try to save two different Feedbacks with the same user_id and subject_id.
To let the user fix their submission, you should load the #subject and #course before rendering the edit form again:
def create
#feedback = Feedback.new(params[:feedback])
#feedback.user_id=current_user.id
if #feedback.save
flash[:success] = "Welcome #{current_user.name}!"
redirect_to #feedback
else
# Calling this method again should load the
# necessary instance variables for the form
# to work properly.
user_subject
render 'new'
end
end

Resources