posts_controller.rb destroy method
def destroy
if !request.xhr?
render_404
return
end
if user_signed_in?
if Post.exists?(:post_id => params[:id])
if Post.post_is_mine(params[:id], current_user.id)
#return = { :error => false, :response => "Post deleted" }
else
#return = { :error => true, :response => 'You are not allowed to perform this action.' }
end
else
#return = { :error => true, :response => 'This post doesn\'t exist.' }
end
else
#return = { :error => true, :response => 'Please login before you delete a post.' }
end
render :json => ActiveSupport::JSON.encode( #return )
end
post.rb
def self.post_is_mine(post_id, user_id)
#where(:user_id => user_id, :post_id => bucket_id)
where("user_id = ? and post_id = ?", user_id, bucket_id)
end
when i check what queries are being run when i destroy a post, i can only see the .exists? to run but not the .post_is_mine which simply goes through as it returns TRUE
i tried several other names as the method since something could cause the problem or even simply trying out the if statement with .post_is_mine but still, the query was not run
Could there be a problem on the model as to how i use the where clause?
Yes. #where returns an ActiveRecord Relation, which is used to generate your query. The relation will not be evaluated in your code, so the query from .post_is_mine will never be executed. if Post.postis mine(params[:id], current_user.id) returns true because the Relation object is not nil.
What you really want is to use exists? in the post_is_mine method.
def self.post_is_mine(post_id, user_id)
exists?(:user_id => user_id, :post_id => bucket_id)
end
EDIT:
I was curious about the difference between my answer and Pavling's answer. For anyone else wondering:
#exists? executes a SQL statement with SELECT 1 FROM ...
#any? executes a SQL statement with SELECT COUNT(*) FROM ...
In practice there is likely not much difference between the two, but some crude benchmarks indicated that #any? is faster (with AR 3.2.6 and Postgresql 9.1 on OSX)
The "where" will return an empty collection, which evaluates as truthy. You would need to add a check to see if there are any records in it to get the right true/false.
def self.post_is_mine(post_id, user_id)
where("user_id = ? and post_id = ?", user_id, bucket_id).any?
end
Related
I sort my "Posts" differently depending on if a user has clicked "trending", "new", or "top".
case params[:sort_type]
when "trending"
#posts = Post.order("trending_score DESC").limit(30)
when "new"
#posts = Post.order("created_at DESC").limit(30)
when "top"
#posts = Post.order("total_votes DESC").limit(30)
end
There is a lot of repeated code, but I'm not sure how to factor out the contents of the order.
One way would be simply to do:
CHOSEN_ATTRIBUTES = {'trending' => 'trending_score', 'new' => 'created_at', 'top' => 'total_votes'} #this could be a constant up in the class
chosen_attr = CHOSEN_ATTRIBUTES[params[:sort_type]]
#posts = Post.order("#{chosen_attr} DESC").limit(30)
Another way yet would be to create a scope with parameter for that. In your model:
CHOSEN_ATTRIBUTES = {'trending' => 'trending_score', 'new' => 'created_at', 'top' => 'total_votes'}
scope :order_query_by, lambda {|attr| order("#{CHOSEN_ATTRIBUTES[attr]} DESC").limit(30)}
Then whenever you want to call depending on the attribute, use:
Post.order_query_by 'trending'
I'm in Rails 3. Here's my code for a method which creates Update records in response to certain attributes being changed on a model called Candidate:
before_save :check_changed, on: [:update]
def check_changed
tracked_attributes = ["period_contributions", "total_contributions",
"period_expenditures", "total_expenditures",
"debts_and_loans", "cash_on_hand",
"major_endorsements",
"rating_afl_cio",
"rating_cal_tax",
"rating_cc",
"rating_eqca",
"rating_lcv",
"rating_now"]
changes.each do |key, value|
if tracked_attributes.include?(key)
Update.create(:candidate_id => self.id, :attribute_name => key,
:new_value => value[1], :old_value => value[0])
end
end
end
The issue is that I have some rake tasks I'm running to do batch updates to the data, which end up triggering this callback unintentionally. I'd like for it only to run when a Candidate is updated from within the admin tool aka CRUD interface. Any advice on the best way to do this?
I will only use callbacks when it is something that always needs to happen, no matter the source. Magically skipping or including them normally leads to pain down the road.
My suggestion is to create a different method on the model that does the check and use that for the crud actions.
class Candidate
#...
def check_changed_and_update(attributes)
check_changed
update(attributes)
end
private
def check_changed
tracked_attributes = ["period_contributions", "total_contributions",
"period_expenditures", "total_expenditures",
"debts_and_loans", "cash_on_hand",
"major_endorsements",
"rating_afl_cio",
"rating_cal_tax",
"rating_cc",
"rating_eqca",
"rating_lcv",
"rating_now"]
changes.each do |key, value|
if tracked_attributes.include?(key)
Update.create(:candidate_id => self.id, :attribute_name => key,
:new_value => value[1], :old_value => value[0])
end
end
end
end
Then in the controller for candidate just change update to check_changed_and_update:
class CanidateController < ApplicationController
def update
#...
respond_to do |format|
if #candidate.check_changed_and_update(canidate_params)
format.html { redirect_to #candidate, notice: 'Candidate was successfully updated.' }
else
format.html { render action: 'edit' }
end
end
end
This has an added bonus of making it obvious what is going to happen when update is called.
Now you can just use the normal active record api in your rake tasks.
I'm reworking a Rails 2 website. Right now, I'm getting an error, and I think it's because a value is being submitted as blank instead of .nil. However, my attempts to keep it nil don't seem to be working. I appreciate any help you have to offer.
From Model, based on Make blank params[] nil
NULL_ATTRS = %w( start_masterlocation_id )
before_save :nil_if_blank
protected
def nil_if_blank
NULL_ATTRS.each { |attr| self[attr] = nil if self[attr].blank? }
end
View
I've got jQuery that adds a value to this hidden field when a start_masterlocation_id exists. I've also got jQuery that removes the field attribute when it does not exist.
<%= hidden_field :newsavedmap, :start_masterlocation_id, :id => "start-masterlocation-id-field" %>
Finally, here is the part of the controller that is throwing the error. This is the controller for the page that holds the form (Maptry), not the controller for Newsavedmap. I think I have to delete the #newsavedmap.id, #newsavedmap.mapname, and #newsavedmap.optimize lines now that I'm going with form handlers, but I don't think that's related to this error.
Controller
def maptry
#itinerary = Itinerary.find(params[:id])
if #itinerary.user_id == current_user.id
respond_to do |format|
#masterlocation = Masterlocation.find(:all)
format.html do
end
format.xml { render :xml => #masterlocation }
format.js do
#masterlocation = Masterlocation.find(:all)
render :json => #masterlocation.to_json(:only => [:id, :nickname, :latitude, :longitude])
end
end
if params[:newsavedmap_id]
#newsavedmap = Newsavedmap.find(:first, :conditions => {:id => params[:newsavedmap_id]})
#waypoint = Waypoint.find(:all, :conditions => {:newsavedmap_id => params[:newsavedmap_id]})
#newsavedmap.id = params[:newsavedmap_id]
#newsavedmap.mapname = Newsavedmap.find(:first, :conditions => {:id => params[:newsavedmap_id]}).mapname
#newsavedmap.optimize = Newsavedmap.find(:first, :conditions => {:id => params[:newsavedmap_id]}).optimize
if !#newsavedmap.start_masterlocation_id.nil?
#start_name = Masterlocation.find(:first, :conditions => {:id => #newsavedmap.start_masterlocation_id}).name
end
if !#newsavedmap.end_masterlocation_id.nil?
#end_name = Masterlocation.find(:first, :conditions => {:id => #newsavedmap.end_masterlocation_id}).name
end
else
#newsavedmap = Newsavedmap.new
end
else
redirect_to '/'
end
end
Error
This does not occur when a start_masterlocation_id is present in the database.
undefined method `name' for nil:NilClass
I solved this by ignoring the nil problem. Instead, I used
!#newsavedmap.start_masterlocation_id.blank?
This evaluates whether the data in the record is blank and then either does or doesn't do something. Obviously, this results in unwanted "blank" data being left inside my database, so it's not ideal, but it helped me move forward with the project. I'd appreciate any responses that deal directly with the nil issue and tell me how to have Rails ignore blank data in a form.
I have class methods like these in a "message" model:
def delete_all_users_messages(user_id, parent_id)
message = Message.find_by_parent_id(parent_id)
message.children.where(:sender_id => user_id ).update_all(:sender_status => 1)
message.children.where(:recipient_id => user_id ).update_all(:recipient_status => 1 )
thread = message.message_thread
thread.update_attribute(:sender_status, 1) if thread.sender_id == user_id
thread.update_attribute(:recipient_status, 1) if thread.recipient_id == user_id
if thread.sender_status == 1 && thread.recipient_status == 1
thread.delete
Message.delete_all(:parent_id => parent_id)
end
end
def delete_all_users_selected_messages(message_ids, user_id, parent_id)
Message.where(:id => message_ids, :sender_id => user_id).update_all(:sender_status => 1)
Message.where(:id => message_ids, :recipient_id => user_id).update_all(:recipient_status => 1)
Message.where('id != ? AND parent_id = ?', parent_id, parent_id).where(:sender_status => 1, :recipient_status => 1).delete_all
thread = MessageThread.find_by_message_id(parent_id)
children_exist = thread.message.children.where('id != ? AND parent_id = ?', parent_id, parent_id).any?
unless children_exist
thread.delete
thread.message.delete
end
I use them in my messages_controller like this:
def destroy_all_messages
Message.delete_all_users_messages(current_user.id, params[:format])
flash[:success] = "Messages deleted"
redirect_to messages_path
end
def destroy_selected_messages
Message.delete_all_users_selected_messages(params[:message_ids], current_user.id, params[:format])
flash[:success] = "Messages deleted"
redirect_to :back
end
I would like to also return an error message if something went wrong.. but because all the logic for deleting messages is in my model it's quite hard to do this.
Can anyone provide a solution with an example of how I could use some kind of condition to show my success message if messages were deleted and an error if they weren't because something went wrong. There must me some kind of markers I can put in the class methods to confirm they have passed a certain point successfully.
Thanks for your time
Kind regards
There are two suggestions I'd make for you here.
The first is have your methods return false if what they were called to do didn't happen. So, have something like this:
if thread.delete && Message.delete_all(:parent_id => parent_id)
return true
else
return false
end
Then in your controller you can check the status of the method:
def destroy_all_messages
if Message.delete_all_users_messages(current_user.id, params[:format])
flash[:success] = "Messages deleted"
redirect_to messages_path
else
flash[:error] = "Messages could not be deleted"
render :action => :show
end
end
This is the method I would recommend; if you need multiple error messages for different steps in the process, I would break apart your delete_all_user_messages method into separate components, each one indicating true or false for whether they succeeded or failed.
Alternatively, you could raise custom error messages and rescuing them in your code. Something like this:
raise ThreadNotDeleted unless thread.delete
And then in your controller you rescue ThreadNotDeleted and deal with it there. While this works I think the other method is preferable.
I check for
validates :group_id, :presence => true
in my model "project". Yet when I create a project without a group_id, I don't get a validation-error. If I try to do
p = Project(:name => "Test")
p.save
the rails console returns false, with save! it tells me the validation for group_id failed. So the validation is somehow performed on the save method, yet it isn't by default in my app. Do I have to do a manual
if #project.save == true
...
end
or something in my controller?
Thank you,
B
You can check #project.valid? before save.
def signup
if request.post?
#user_data = UserData.new(:login => params[:user], :password => params[:password])
if #user_data.valid?
if #user_data.save
session[:cuser] = UserData.authenticate(#user_data.login, #user_data.password).login
redirect_to(:controller=> 'sync', :action=> 'all')
return
end
else
#error_on_signup = true
end
end