Rails - embedded if statement, unexpected result - ruby-on-rails

I'm getting an unexpected result from an if statement in my Article controller. My intention is to allow the article author or an admin user (it's a boolean column in the users table) to update an article. I want to check if the current user was the original author, and if so, update the author name, just in case the person has changed their username after the article was posted.
The problem is that when an admin updates the article, its not only changing the author name to admin, its also setting the user_id to admin! I don't understand why. The admin.id does not match the article.user_id, so why is this happening?
Here's the bit from the article controller:
def update
#article = Article.find(params[:id])
if #article.user_id == current_user.id || current_user.admin?
#update author in case of user name change
if #article.user_id = current_user.id
#article.author = current_user.name
end
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
elsif
redirect_to articles_path, :notice => "You do not have permission to edit this resource."
end
end

if #article.user_id = current_user.id
this is an assignment (and always true)
it needs to be if #article.user_id == current_user.id

Related

Offer options if record already exists in Ruby on Rails

I'm creating a database of products and want to check when a new product is created, if that product already exists.
99% of the time, the product should have a unique product code, but sometimes this will not be the case when a revised version comes out with the same product code.
I would like to have it so that the database checks if the product code exists already. If it does, then the user can either go ahead and create the product anyway, go to the product that already exists, or cancel.
I am trying to achieve this in the controller but cannot seem to get the syntax of the exists? method correct. Can someone point out where I am going wrong, please?
def create
#product = Product.new(product_params)
if Product.product_code.exists?(#product.product_code)
render 'new'
flash[:error] = "This product already exists."
elsif #product.save
redirect_to #product
else
render 'new'
end
end
You should note that what you are doing in the controller or adding a standard uniqueness validation to the model will not allow duplicate Products to be created at all.
This will just keep sending the user back to the form:
def create
#product = Product.new(product_params)
if Product.exists?(product_code: #product.product_code)
render 'new'
flash[:error] = "This product already exists."
elsif #product.save
redirect_to #product
else
render 'new'
end
end
If you want to simply warn the user once you can attach a virtual attribute on the model and use it as condition for the validation:
class Product < ApplicationRecord
attribute :dup_warning, :boolean, default: true
validate :lax_product_code_uniquenes
def lax_product_code_uniqueness
if new_record? && !dup_warning && Product.exists(product_code: self.product_code)
errors.add(:product_code, 'is not unique - are you sure?')
self.dup_warning = true
end
end
end
Then add the virtual attribute to the form:
<%= form_with(model: #product) do |f| %>
...
<%= f.hidden_input(:dup_warning) %>
...
<% end %>
And you don't need to really do anything in the controller besides add dup_warning to the params whitelist.
def create
#product = Product.new(product_params)
if #product.save
redirect_to #product
else
render 'new'
end
end
def product_params
params.require(:product)
.permit(:foo, :bar, :product_code, :dup_warning)
end
Instead if Product.product_code.exists?(#product.product_code) possibly you should use if #product.product_code.present?
UPD: thanks to all, commented below.
Correct use of exists? is Product.where(product_code: #product.product_code).exists?

Rails Create article with other user not current_user

I would like to create an article with other user not current_user and for that I'm saving in a session the id to the other user and I recover this id with a collection in the view to this point everything work fine but when I'm trying to use my helper :selected_user into my articles controller with a if sentence doesn't work here is my code:
def new
if selected_user.present?
#article = selected_user.articles.build state: :step1
render_wizard
else
#article = current_user.articles.build state: :step1
render_wizard
end
end
so, I'm asking if the selected_user.present? I would like to create the article with this user_id but else I would like to create it with the current_user
my create method is:
def create
if selected_user.present?
step = :step1
#article = selected_user.articles.build article_params_step1
#article.state = step.to_s
if #article.save
redirect_to wizard_path(next_step, article_id: #article)
else
render_wizard
end
else
step = :step1
#article = current_user.articles.build article_params_step1
#article.state = step.to_s
if #article.save
redirect_to wizard_path(next_step, article_id: #article)
else
render_wizard
end
end
end
so, yeah when I run my view the controller jump to the else section.
just for clarify my selected_user not return nil but here is the implementation:
selections_controller.rb:
class SelectionsController < ApplicationController
before_action :authenticate_user!
def create
session[:selected_user_id] = params[:user][ :user_id]
redirect_to root_path
end
end
and in my application_controller.rb:
helper_method :selected_user
def selected_user
#selected_user ||= User.find(session[:selected_user_id])
end
and in the view:
<%= form_tag( { :controller => "selections", :action => "create" } , :class => "navbar-form navbar-left") do %>
<%= collection_select(:user, :user_id, User.all, :id, :name, prompt: "Escoge cliente")%>
<%= submit_tag 'Enviar' %>
<% end %>
if I try create an article without select an user from my collection appear this error:
Couldn't find User with 'id'=
but when I select my user from the collection everything works fine. so just I want when I don't select nothing create with the current_user.
Thanks for your time !
Regards !
The reason why you were seeing the error
Couldn't find User with 'id'=
when you haven't selected a user was that the session[:selected_user_id] was nil and your old selected_user with following code was throwing the error.
def selected_user
#selected_user ||= User.find(session[:selected_user_id])
end
User.find method expects either a single id or an array of ids. If you give a single id and if it finds the relevant record in the database then it will returns that instance. If you give an array of ids and if it finds those relevant records in the database, then it will return array of those instances. But if you pass nil to it, then it will through the error Couldn't find User with 'id'= as it won't find a relevant record.
But your updated selected_user implementation:
def selected_user
#selected_user ||= session[:selected_user_id] && User.find_by_id(session[:selected_user_id])
end
is working because, first you are checking for the existence of session[:selected_user_id] value and second you are using User.find_by_id instead of User.find.
User.find_by_id either returns a single instance of the record if it finds it in the database or will return nil if it doesn't find the record. It will never through an error.
Refer to ActiveRecord#find and ActiveRecord#find_by for more info.
I'm not sure why is working and what is the different but my solution for the problem it was to add this to my selected_user method:
def selected_user
#selected_user ||= session[:selected_user_id] && User.find_by_id(session[:selected_user_id])
end
and with that I don't have the nil error and entry to the if statement without errors.

how to only edit and destroy my own content only?

Have a basic blog (it's actually edgeguide's blog: http://edgeguides.rubyonrails.org/getting_started.html)
Then I integrated Devise into it. So, user can only log in and see their own information.
Now trying to change it somewhat.
I'd like the users to see all content, but only edit and destroy their own only.
Trying to use before_action filter like this:
`before_action :authorize, :only => [:edit, :destroy]`
And this is the authorize method that I wrote:
def authorize
#article = Article.find(params[:id])
if !#article.user_id = current_user.id then
flash[:notice] = "You are not the creator of this article, therefore you're not permitted to edit or destroy this article"
end
end
But it doesn't work. Everything acts as normal, and I can delete mine and everyone's else content.
How do I get it that I can destroy ONLY my own content, and not everyone's else?
Not using CanCan, nor do I want to.
Not sure if this is worth including or not, but originally when I had everyone see their own content, that was via create action:
def create
#article = Article.new(article_params)
#article.user_id = current_user.id if current_user
if #article.save
redirect_to #article
else
render 'new'
end
end
You're having several problems
first, look at that :
if !#article.user_id = current_user.id then
You're only using one = instead of == so you are doing an assignation that will evaluate to current_user.id
Also, in your condition, you're only setting a flash message but not doing anything to really prevent the user.
Here's a corrected version :
def authorize
#article = Article.find(params[:id])
unless #article.user_id == current_user.id
flash[:notice] = "You are not the creator of this article, therefore you're not permitted to edit or destroy this article"
redirect_to root_path # or anything you prefer
return false # Important to let rails know that the controller should not be executed
end
end

Checking current_user is a post's author

I'm trying to limit editing capability to an article's author in my rails app.
In my controller I'm adding the user id as the author which works fine:
def create
#article = Article.new(article_params)
#article.author = current_user.id
...
end
then checking it on the edit action:
def edit
#article = Article.find(params[:id])
redirect_to root_path, notice: 'Whoa there amigo!' unless current_user.id == #article.author
end
Editing results in redirect every time.
(I'm not using Devise, CanCan or anything else at this point as this is the only functionality I need right now.)
Update
Based on comments, I updated my create method to:
def create
#article = current_user.articles.build(article_params)
and my edit method to:
def edit
#article = current_user.articles.find(params[:id])
redirect_to root_path, notice: 'Whoa..' unless current_user.id.to_s == #article.user_id
This works but "current_user.id.to_s" seems a bit hacky for something that must be incredibly common.
In create action:
def create
#article = Article.new(article_params)
#article.author = current_user
...
end
And in edit change the condition to:
unless current_user == #article.author
This way you compare user objects and not user objects with ids.

Wants to create a new instance of books instead of update

What I want is to create a profile pages, where i can view the previous book field and if the customer change the text then it would create a new books.
I have the following models with has_many relationship
Customer -- ID, First, Last, Email
Book -- ID, Description
Book_Managers -- ID, Customer_id, Book_id, Visible
Right now what i have is a customer edit which allow me to see multiple form by rendering from many more models like books, phones, etc...
Here my customer Controller
def edit
#customer = Customer.find(params[:id])
if #customer.books.any?
#book = #customer.books.order("created_at DESC").first
else
#book = #customer.books.build
end
end
What i would like to see is if i created a new instance when going to book form i should see the last and able to modify "The JavaScript Bible" to something "The Java Bible" and it would not update it but just create a new version. Right now when going to the form book i see nothing. And if i do for some odd reason it was only allowing me to update.
class BooksController < ApplicationController
def create
#book = current_customer.books.build(params[:book])
if #book.save
flash[:success] = "Book Created"
redirect_to root_url
else
render 'customer/edit'
end
end
def index
#books = Book.all
end
def destroy
#book.destroy
redirect_to root_url
end
end
ADDED THIS
def update
#book = current_customer.books.build(params[:book])
if #book.save
flash[:success] = "Book Updated"
redirect_to root_url
else
render 'customer/edit'
end
end
To my book controller, the only problem right now is my association, i can't seem to find any book with the current customer. is there somethign wrong with my query?
There is some gems for versioning. This that : https://www.ruby-toolbox.com/categories/Active_Record_Versioning
You can do something like this :
def update
params = params[:book].merge(:previous_version => params[:id])
#book = current_customer.books.create(params[:book])
end
It will create a new book on each update. The last version will be the book without "previous_version".

Resources