Update Cancan in rails - ruby-on-rails

im doing an authorisation application where:
-have admin role, who can manage everything.
-have guest role, who can create posts and edit the posts which he has created.
im facing problem with the guest role. I have done associations where:
-posts belongs_to user(In post model am having user_id attribute also in migration i have referenced posts to users)
-user has_many posts.
when im tryin to create a new post, the user_id is nil. i dunno how to set user_id attribute in Post object.
class ProductsController < ApplicationController
before_filter :self_load, :only=>[:show,:edit,:update,:destroy]
before_filter :authenticate_user, :only=>[:edit,:update,:destroy]
def index
#products=Product.find(:all)
end
def new
#product=Product.new(:user_id=>current_user.id)
end
def create
#product=Product.new(params[:product])
if #product.save
redirect_to root_url, :notice=>'New Product has been added'
else
render :action=>'new'
end
end
def show
end
def edit
end
def update
if #product.update_attributes(params[:product])
redirect_to root_url, :notice=>'Product has been updated.'
else
render :action => 'edit'
end
end
def destroy
#product.destroy
redirect_to root_url
end
def self_load
#product = Product.find(params[:id])
end
def authenticate_user
if current_user
else
redirect_to root_url, :notice=>'You are not authorised to access'
end
end
end
view:
Add Product
<%= form_for(#product) do |f| %>
<% if #product.errors.any? %>
<ul>
<% #product.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<table>
<tr><td><%= f.label 'Title:' %></td>
<td><%= f.text_field :title %></td>
<tr><td><%= f.label 'Description:' %></td>
<td><%= f.text_area :description,:rows=>10 %></td></tr>
<tr><td><%= f.label 'Price:' %></td>
<td><%= f.text_field :price %></td></tr>
<tr><td><%= f.submit 'Save' %></td></tr>
</table>
<% end %>
<%= link_to 'Back', root_url %>
Model
class Product < ActiveRecord::Base
belongs_to :user
attr_accessible :title, :description, :price, :user_id
validates_presence_of :title, :description, :price
validates_uniqueness_of :title
validates_length_of :title, :in=>4..10
validates_length_of :description, :minimum=>10
validates_numericality_of :price
end
Plz help me with this.... if u need any further info u can ask...

if only signed in user can create products, try this
class ProductsController < ApplicationController
def create
#product = current_user.products.build params[:product]
if #product.save
# Stuff is product save succesfully
else
# Stuff is product does not saved
end
end
end

Related

How to implement presence validation for nested model in Rails?

Full source code is here https://github.com/tenzan/postfile
Creating a post working fine.
I have a parent element "Conversation" and its child/nested element "Post".
When I click on "Create Post" with nothing entered, it should throw an error "Body can't be blank".
Instead, it giving another error:
conversation.rb:
class Conversation < ApplicationRecord
belongs_to :contact
has_many :posts
end
post.rb:
class Post < ApplicationRecord
belongs_to :conversation
belongs_to :author, polymorphic: true
has_rich_text :body
validates :body, presence: :true
end
posts_controller.rb:
class PostsController < ApplicationController
before_action :authenticate_user!
before_action :set_conversation
def create
#post = #conversation.posts.new(post_params)
#post.author = current_user
respond_to do |format|
if #post.save
format.html { redirect_to #conversation }
end
end
end
private
def set_conversation
#conversation = Conversation.find(params[:conversation_id])
end
def post_params
params.require(:post).permit(:body)
end
end
I show all posts within from conversation's show.html.erb:
<p id="notice"><%= notice %></p>
<p>
<strong>Subject:</strong>
<%= #conversation.subject %>
</p>
<p>
<strong>Contact:</strong>
<%= link_to #conversation.contact.name, #conversation.contact %>
</p>
<%= link_to 'Edit', edit_conversation_path(#conversation) %> |
<%= link_to 'Back', conversations_path %>
<div id="posts">
<%= render #posts %>
</div>
<%= render partial: "posts/form", locals: { conversation: #conversation, post: Post.new } %>
Posts's partial _form.html.erb:
<%= form_with model: [conversation, post], id: "form" do |form| %>
<div>
<% form.object.errors.full_messages.each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<br>
<%= form.rich_text_area :body %>
<%= form.submit %>
<% end %>
Full source code is here https://github.com/tenzan/postfile
Thanks in advance.
You have this block in your posts_controller, which is where your error is arising:
respond_to do |format|
if #post.save
format.html { redirect_to #conversation }
end
end
Inside a respond_to block, you should have blocks identified by the format type, but you've added an if statement at that top level of the block where Rails is expecting a format.xxx. Move the if outside your respond_to block and you should be fine:
if #post.save
respond_to do |format|
format.html { redirect_to #conversation }
end
else
DO SOMETHING WITH THE ERROR
end
(Also NB that you should handle the error if the post doesn't save, even if it's just to say "Sorry, please try again".)

Form which creates a quantity of another model with form field

I have two models, Hotel and Room. I want to create a form that will allow me to add a new hotel and rooms, which indicates the name of the hotel and the quantity of rooms.
I know I should use "nested form", but it is hard for me to implement it properly.
Here is my code:
HotelsController
class HotelsController < ApplicationController
def index
#hotels = Hotel.all
end
def new
#hotel = Hotel.new
end
def create
#hotel = Hotel.new(hotel_params)
if #hotel.save
redirect_to #hotel
else
render 'new'
end
end
def show
#hotel = Hotel.find(params[:id])
end
def destroy
#hotel = Hotel.find(params[:id])
#hotel.destroy
redirect_to hotels_url, notice: 'Hotel was successfully destroyed.'
end
private
def hotel_params
params.require(:hotel).permit(:name, :rooms_count)
end
end
Hotel model
class Hotel < ApplicationRecord
has_many :rooms, dependent: :destroy
accepts_nested_attributes_for :rooms
end
Room model
class Room < ApplicationRecord
belongs_to :hotel, optional: true # avoiding rails 5.2 belongs_to error
end
form
<%= form_with scope: :hotel, url: hotels_path, local: true do |form| %>
<p>
<%= form.label :name %><br>
<%= form.text_field :name %>
</p>
<p>
<%= form.label :rooms_count %><br>
<%= form.number_field :rooms_count %>
</p>
<p>
<% form.fields_for :rooms do |f|%>
<p>
**THE CODE**
</p>
<% end %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
You forgot the "ERB echo sign" (=) on the fields_for helper.
Correct form:
<%= form.fields_for :rooms do |f|%>
I have found a solution to my question.
def create
#hotel = Hotel.new(hotel_params)
#hotel.rooms_count.times do
#hotel.rooms.build
end
if #hotel.save
redirect_to #hotel
else
render 'new'
end
end
It implement a #hotel.rooms.build as many times as #hotel.rooms_count number entered in Rooms Count field in form.

Ruby on rails - display error message for an associated object

I have the below form written for a movie review. Review is associated with movies. I am not able to display error message for this form. I have written similar code to display error message for validation on movies and that works well.
Code for the 'add new review' form -
<%= form_for([#movie, #movie.reviews.build]) do |f| %>
<% if #reviews.errors.any? %>
<div id="error_explanation">
<h3>
<%= pluralize(#reviews.errors.count, "error") %> prohibited
this review from being saved:
</h3>
<ul>
<% #reviews.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :reviewer %><br>
<%= f.text_field :reviewer %>
</p>
<p>
<%= f.label :comment %><br>
<%= f.text_area :comment %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Models -
MOVIE -
class Movies < ActiveRecord::Base
has_many :reviews, dependent: :destroy
validates_associated :reviews
validates :title, presence: true
validates :rating, presence: true, numericality: { greater_than: 0, less_than_or_equal_to: 10 }
end
REVIEW -
class Review < ActiveRecord::Base
belongs_to :movies
validates :comment, presence: true
end
Adding code for reviews_controller.erb
class ReviewsController < ApplicationController
def create
#movie = Movies.find(params[:movie_id])
#review = #movie.reviews.create(review_params)
redirect_to movies_path(#movie)
end
def destroy
#movie = Movies.find(params[:movie_id])
#review = #movie.reviews.find(params[:id])
#review.destroy
redirect_to movies_path(#movie)
end
private
def review_params
params.require(:review).permit(:reviewer, :comment)
end
end
Here is my code for movies_controller.erb
class MoviesController < ApplicationController
def new
#movie = Movies.new
end
def index
#movies = Movies.all
end
def show
#movie = Movies.find(params[:id])
end
def edit
#movie = Movies.find(params[:id])
end
def create
#movie = Movies.new(movie_params)
if #movie.save
redirect_to #movie
else
render 'new'
end
end
def update
#movie = Movies.find(params[:id])
if #movie.update(movie_params)
redirect_to #movie
else
render 'edit'
end
end
def destroy
#movie = Movies.find(params[:id])
#movie.destroy
redirect_to movies_path
end
private
def movie_params
params.require(:movie).permit(:title,:year,:rating,:description)
end
end
Please help me identify what is going wrong and how to be able to get the validation message working.
Thanks in advance.
You are trying to display error of #review with <% if #reviews.errors.any? %>
But You do not have #reviews in <%= form_for([#movie, #movie.reviews.build) do |f| %>
Better solution, through controller assigning #review = Review.new. In this case it should show you errors, if comment field is empty.
def new
#review = Review.new
end
def create
#movie = Movie.find(params[:movie_id])
#review = #movie.reviews.build(review_params)
if #review.save
else
render 'new'
end
end
In Form
<%= form_for ([#movie, #review) do |f| %>

Rails unable to send id to update controller

I am building a blog application in which I am trying to have two links in that two links I am sending the post_id to my update controller but it is giving me error .And I want to collect that post_id in my update controller and want to check the "status"(Status is basically the column name in my posts table and by default my status column has a status of pending ) .So when admin click on approve the status of column should change to approve and when admin click on decline the status of column should change to decline also user post should be deleted .Admin can access all the users posts whereas user can access his only post
posts_controller
class PostsController < ApplicationController
before_action :authenticate_user!
def index
#posts = Post.user_post(current_user).order('created_at DESC').paginate(:page => params[:page], :per_page => 5)
end
def new
#post = Post.new
end
def show
#post=find_params
end
def create
#post = Post.new(post_params)
#post.user = current_user
if #post.save
Post.upload(params[:post][:files],#post.id)
redirect_to #post
else
render 'new'
end
end
def edit
#post = find_params
puts "cccccccccc#{params[:commit]}"
Post.up(#post.id,params[:commit])
end
def update
#post = find_params
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = find_params
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body)
end
def find_params
Post.find(params[:id])
end
end
posts/_form.html.erb
<%= form_for #post,html: { multipart: true } do |f| %>
<% if #post.errors.any? %>
<div id="errors">
<h2><%= pluralize(#post.errors.count, "error") %> prevented this post from saving:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :title %><br>
<%= f.text_field :title %><br>
<br>
<%= f.label :body %><br>
<%= f.text_field :body %><br>
<br>
<!-- if you want to upload multiple files at a time -->
<%= f.label :files %><br>
<%= f.file_field :files,:multiple => true %><br>
<br>
<%= f.submit %>
<br>
<% end %>
posts/edit.html.erb
<div id="page_wrapper">
<h1>Edit Post</h1>
<%= render 'form' %>
<br>
</div>
migration
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.text :body
t.integer :user_id
t.string :status
t.timestamps
end
end
end
posts/show.html.erb
<div id="post_content">
<h1 class="title"><%= #post.title %></h1>
<p class="date">
Submitted <%= time_ago_in_words(#post.created_at) %> Ago
<% if user_signed_in? %>
<%= link_to 'Edit', edit_post_path(#post) %> |
<%= link_to 'Delete', post_path(#post), method: :delete, data: { confirm: 'Are you sure?' } %>
<%= link_to "approve",[:edit,#post] %>
<%= link_to "decline",[:edit,#post] %>
<% end %>
</p>
<p class="body"><%= #post.body %></p>
<div id="comments">
<h2><%= #post.comments.count %> Comments</h2>
<%= render #post.comments %>
<h3>Add a comment:</h3>
<%= render "comments/form" %>
</div>
</div>
post.rb
class Post < ActiveRecord::Base
has_many :documents
has_many :comments, dependent: :destroy
belongs_to :user
validates :title, presence: true, length: {in: 5..15}
validates :body, presence: true,length: {in: 5..200}
def self.up(id,params)
puts "aaaaaaaa#{id}"
puts "bbbbbbbbbbbbb#{params}"
end
def self.user_post(id)
role = User.find_role(id)
if role == 'admin'
Post.all
elsif role == 'user'
Post.where(user_id: id)
elsif role == 'developer'
Post.where(user_id: id)
end
end
def self.icon(extension)
case extension
when 'pdf'
EXTENSION[1]['pdf']
when 'png' || 'jpg' || 'jpeg'
EXTENSION[0]['png']
when 'doc' || 'odt'
EXTENSION[2]['doc']
end
end
####limit to upload files not more than ######
def self.upload(files,post_id)
files.each do |file|
#file_extension=file.content_type.split('/')[1]
doc = Document.new(document: file,post_id: post_id )
#save is a method which will save the content in the database
doc.save!
end
end
end
routes.rb
Rails.application.routes.draw do
root "posts#index"
devise_for :users
resources :posts do
resources :comments
end
resources :uploads
end
There are a bunch of problems with your code.. I'll not try to address them all but the specific problem you are facing..
first you need to generate path for both approve and decline action..
resources :posts do
patch '/approve' => 'posts#approve', as: :approve #posts_approve_path(post) is the route helper for this
patch '/decline' => 'posts#decline', as: :decline #posts_decline_path(post) is the route helper for this
resources :comments
end
this will generate the required routes.
now inside your view wherever you want to approve and decline.
<%= link_to "approve",posts_approve_path(#post.id), method: :patch %>
<%= link_to "decline",posts_decline_path(#post.id), method: :patch %>
In your controller you need to modify find_params as the generated routes will pass :posts_id as the :id param.
def find_params
id = params[:id] || params[:posts_id]
Post.find(id)
end
Now in your controller add 2 new methods to implement the functionality.
def approve
#post = find_params
#post.update_attribute(:status, 'approved') if #post.present?
redirect_to post_path(#post), notice: 'Post approved'
end
def decline
#post = find_params
#post.destroy if #post.present?
redirect_to post_path(#post), notice: 'Post deleted'
end

Add records using HABTM association

I still new with ruby on rails and I am really blocked.
I am trying to add linked books and authors to database, it add the record in the two tables but not in tne join one. This is my code:
books_controller.rb
class BooksController < ApplicationController
def index
#books= Book.all
end
def new
#book= Book.new
end
def create
#book= Book.new(book_params)
if #book.save
redirect_to books_path
else
redirect_to root_url
end
end
private
def book_params
params.require(:book).permit(:title, {:author_ids =>[]})
end
end
book.rb
class Book < ActiveRecord::Base
has_and_belongs_to_many :authors
before_destroy { authors.clear }
end
author.rb
class Author < ActiveRecord::Base
has_and_belongs_to_many :books
end
books/new.html.erb
<h1>Add new book</h1>
<%= form_for #book do |f| %>
<%= f.label :title %>
<%=f.text_field :title %>
<% for author in Author.all %>
<%= check_box_tag 'book(author_ids[])',author.id, #book.authors.include?(author) %>
<%= author.f_name %>
<% end%>
<%= f.submit %>
<%end%>
Change <%= check_box_tag 'book(author_ids[])',author.id, #book.authors.include?(author) %> into <%= check_box_tag 'book[author_ids][]',author.id, #book.authors.include?(author) %>

Resources