Check if record exists from controller in Rails - ruby-on-rails

In my app a User can create a Business. When they trigger the index action in my BusinessesController I want to check if a Business is related to the current_user.id:
If yes: display the business.
If no: redirect to the new action.
I was trying to use this:
if Business.where(:user_id => current_user.id) == nil
# no business found
end
But it always returns true even when the business doesn't exist...
How can I test if a record exists in my database?

Why your code does not work?
The where method returns an ActiveRecord::Relation object (acts like an array which contains the results of the where), it can be empty but it will never be nil.
Business.where(id: -1)
#=> returns an empty ActiveRecord::Relation ( similar to an array )
Business.where(id: -1).nil? # ( similar to == nil? )
#=> returns false
Business.where(id: -1).empty? # test if the array is empty ( similar to .blank? )
#=> returns true
How to test if at least one record exists?
Option 1: Using .exists?
if Business.exists?(user_id: current_user.id)
# same as Business.where(user_id: current_user.id).exists?
# ...
else
# ...
end
Option 2: Using .present? (or .blank?, the opposite of .present?)
if Business.where(:user_id => current_user.id).present?
# less efficiant than using .exists? (see generated SQL for .exists? vs .present?)
else
# ...
end
Option 3: Variable assignment in the if statement
if business = Business.where(:user_id => current_user.id).first
business.do_some_stuff
else
# do something else
end
This option can be considered a code smell by some linters (Rubocop for example).
Option 3b: Variable assignment
business = Business.where(user_id: current_user.id).first
if business
# ...
else
# ...
end
You can also use .find_by_user_id(current_user.id) instead of .where(...).first
Best option:
If you don't use the Business object(s): Option 1
If you need to use the Business object(s): Option 3

In this case I like to use the exists? method provided by ActiveRecord:
Business.exists? user_id: current_user.id

with 'exists?':
Business.exists? user_id: current_user.id #=> 1 or nil
with 'any?':
Business.where(:user_id => current_user.id).any? #=> true or false
If you use something with .where, be sure to avoid trouble with scopes and better use
.unscoped
Business.unscoped.where(:user_id => current_user.id).any?

ActiveRecord#where will return an ActiveRecord::Relation object (which will never be nil). Try using .empty? on the relation to test if it will return any records.

When you call Business.where(:user_id => current_user.id) you will get an array. This Array may have no objects or one or many objects in it, but it won't be null. Thus the check == nil will never be true.
You can try the following:
if Business.where(:user_id => current_user.id).count == 0
So you check the number of elements in the array and compare them to zero.
or you can try:
if Business.find_by_user_id(current_user.id).nil?
this will return one or nil.

business = Business.where(:user_id => current_user.id).first
if business.nil?
# no business found
else
# business.ceo = "me"
end

I would do it this way if you needed an instance variable of the object to work with:
if #business = Business.where(:user_id => current_user.id).first
#Do stuff
else
#Do stuff
end

Something new to try (:
Assign a variable or return
return unless #business = Business.where(user_id: current_user.id).first
Method would exit at this point if there are no businesses found with current user's ID, or assigns instance variable #business to the first business object.

Related

Error deleting record in console ruby on rails

I am trying to delete a record using the console. I have a model for "User". I tried several methods in the console:
a = User.where(:id => '18')
a.destroy
a.delete
User.where(:id => '18').destroy
User.where(:id => '18').delete
Using all of these methods, I got the same error: "Wrong number of arguments (0 for 1)"
Does anyone know what I am doing wrong?
Thx!
Try:
a = User.find(18)
a.destroy
When we use where, result will be ActiveRecord::Relation, means multiple records, on which you can't call destroy directly. You will need to call destroy by iterating over the result.
users = User.where(:id => 18)
users.each do |user|
user.destroy
end
I can add something here, The issue with your code that you are passing string while it expects an integer 'Number'
Your code should be as the following:
a = User.where(:id => 18).first
a.destroy
Without using first array of object will be returned and you can't use destroy method directly on it, in case you don't want to add first then your code should be like:
a = User.where(:id => 18)
a.each do |obj|
obj.destroy
end

Rails - 'can't dump hash with default proc' during custom validation

I have 2 models. User and Want. A User has_many: Wants.
The Want model has a single property besides user_id, that's name.
I have written a custom validation in the Want model so that a user cannot submit to create 2 wants with the same name:
validate :existing_want
private
def existing_want
return unless errors.blank?
errors.add(:existing_want, "you already want that") if user.already_wants? name
end
The already_wants? method is in the User model:
def already_wants? want_name
does_want_already = false
self.wants.each { |w| does_want_already = true if w.name == want_name }
does_want_already
end
The validation specs pass in my model tests, but my feature tests fail when i try and submit a duplicate to the create action in the WantsController:
def create
#want = current_user.wants.build(params[:want])
if #want.save
flash[:success] = "success!"
redirect_to user_account_path current_user.username
else
flash[:validation] = #want.errors
redirect_to user_account_path current_user.username
end
end
The error I get: can't dump hash with default proc
No stack trace that leads to my code.
I have narrowed the issue down to this line:
self.wants.each { |w| does_want_already = true if w.name == want_name }
if I just return true regardless the error shows in my view as I would like.
I don't understand? What's wrong? and why is it so cryptic?
Thanks.
Without a stack trace (does it lead anywhere, or does it just not appear?) it is difficult to know what exactly is happening, but here's how you can reproduce this error in a clean environment:
# initialize a new hash using a block, so it has a default proc
h = Hash.new {|h,k| h[k] = k }
# attempt to serialize it:
Marshal.dump(h)
#=> TypeError: can't dump hash with default proc
Ruby can't serialize procs, so it wouldn't be able to properly reconstitute that serialized hash, hence the error.
If you're reasonably sure that line is the source of your trouble, try refactoring it to see if that solves the problem.
def already_wants? want_name
wants.any? {|want| want_name == want.name }
end
or
def already_wants? want_name
wants.where(name: want_name).count > 0
end

Is there a more ruby way of doing this

Ok so i have this helper
def current_company_title
(Company.find_by_id(params["company_id"]).name rescue nil) || (#companies.first.name rescue nil) current_user.company.name
end
Basically what I am achieving with this is the following ...
If the param["company_id"] exists then try to get the company and if not then
if #companies exists grab the first company name and if not then get the current users company name
This works but the rescues seem like a hack...any idea on another way to achieve this
Indeed rescue is kind of a hack, id' probably split it up into two methods and then use try to fetch the name if available: http://api.rubyonrails.org/classes/Object.html#method-i-try
def current_company
#current_company ||= Company.find_by_id(params[:company_id]) || #companies.try(:first) || current_user.try(:company)
end
def current_company_name
current_company.try(:name)
end
Company.find_by_id(params["company_id"]).name`
find and its derivates are meant to be used when you're sure-ish you'll have a positive result, and only in some cases (row was deleted, etc) errors. That's why it raises an exception. In your case, you're assuming it's gonna fail, so a regular where, which would return nil if no rows was found, would do better, and remove the first rescue
#companies.first.name rescue nil
could be replaced by
#companies.first.try(:name)
I'll let you check the api for more on the topic of try. It's not regular ruby, it's a Rails addition.
Less "magic", simple code, simple to read:
def current_company_title
company = Company.where(id: params["company_id"]).presence
company ||= #companies.try(:first)
company ||= current_user.company
company.name
end
Ps. Not a big fan of Rails' try method, but it solves the problem.
def current_company_title
if params["company_id"]
return Company.find_by_id(params["company_id"]).name
elsif #companies
return #companies.first.name
else
return current_user.company.name
end
end
The rescues are a hack, and will obscure other errors if they occur.
Try this:
(Company.find_by_id(params["company_id"].name if Company.exists?(params["company_id"]) ||
(#companies.first.name if #companies && #companies.first) ||
current_user.company.name
then you can extract each of the bracketed conditions to their own methods to make it more readable, and easier to tweak the conditions:
company_name_from_id(params["company_id"]) || name_from_first_in_collection(#companies) || current_user_company_name
def company_name_from_id(company_id)
company=Company.find_by_id(company_id)
company.name if company
end
def name_from_first_in_collection(companies)
companies.first.name if companies && companies.first
end
def current_user_company_name
current_user.company.name if current_user.company
end
[Company.find_by_id(params["company_id"]),
#companies.to_a.first,
current_user.company
].compact.first.name

Rails Given an array of objects from a db, is there a way to get an item in the array w/o having to rehit the db?

Given:
#votes (user_id, option_id)
If do: #project.votes I get all the votes for that project.
If I then want to see what the current user voted for I have to do:
Votes.where(:user_id => current_user.id).first
This is a record that's already in the #project votes query. Is there a way I can find the record in that first query w/o having to rehit the db?
Thanks
You can just use the regular ruby Enumerable#select method:
#votes = project.votes.all
# ... other code ...
current_user_votes = #votes.select{ |v| v.user_id == current_user.id }
This will return an array of all the user's votes for the project. If the user is only allowed one vote, and you want a single value, not an array, just use .first like so:
#votes = project.votes.all
# ... other code ...
current_user_vote = #votes.select{ |v| v.user_id == current_user.id }.first

In Rails, what is the best way to update a record or create a new one if it doesn't exist?

I have a create statement for some models, but it’s creating a record within a join table regardless of whether the record already exists.
Here is what my code looks like:
#user = User.find(current_user)
#event = Event.find(params[:id])
for interest in #event.interests
#user.choices.create(:interest => interest, :score => 4)
end
The problem is that it creates records no matter what. I would like it to create a record only if no record already exists; if a record does exist, I would like it to take the attribute of the found record and add or subtract 1.
I’ve been looking around have seen something called find_or_create_by. What does this do when it finds a record? I would like it to take the current :score attribute and add 1.
Is it possible to find or create by id? I’m not sure what attribute I would find by, since the model I’m looking at is a join model which only has id foreign keys and the score attribute.
I tried
#user.choices.find_or_create_by_user(:user => #user.id, :interest => interest, :score => 4)
but got
undefined method find_by_user
What should I do?
my_class = ClassName.find_or_initialize_by_name(name)
my_class.update_attributes({
:street_address => self.street_address,
:city_name => self.city_name,
:zip_code => self.zip_code
})
Assuming that the Choice model has a user_id (to associate with a user) and an interest_id (to associate with an interest), something like this should do the trick:
#user = User.find(current_user)
#event = Event.find(params[:id])
#event.interests.each do |interest|
choice = #user.choices.find_or_initialize_by_interest_id(interest.id) do |c|
c.score = 0 # Or whatever you want the initial value to be - 1
end
choice.score += 1
choice.save!
end
Some notes:
You don't need to include the user_id column in the find_or_*_by_*, as you've already instructed Rails to only fetch choices belonging to #user.
I'm using find_or_initialize_by_*, which is essentially the same as find_or_create_by_*, with the one key difference being that initialize doesn't actually create the record. This would be similar to Model.new as opposed to Model.create.
The block that sets c.score = 0 is only executed if the record does not exist.
choice.score += 1 will update the score value for the record, regardless if it exists or not. Hence, the default score c.score = 0 should be the initial value minus one.
Finally, choice.save! will either update the record (if it already existed) or create the initiated record (if it didn't).
find_or_create_by_user_id sounds better
Also, in Rails 3 you can do:
#user.choices.where(:user => #user.id, :interest => interest, :score => 4).first_or_create
If you're using rails 4 I don't think it creates the finder methods like it used to, so find_or_create_by_user isn't created for you. Instead you'd do it like this:
#user = User.find(current_user)
#event = Event.find(params[:id])
for interest in #event.interests
#user.choices.find_or_create_by(:interest => interest) do |c|
c.score ||= 0
c.score += 1
end
end
In Rails 4
You can use find_or_create_by to get an object(if not exist,it will create), then use update to save or update the record, the update method will persist record if it is not exist, otherwise update record.
For example
#edu = current_user.member_edu_basics.find_or_create_by(params.require(:member).permit(:school))
if #edu.update(params.require(:member).permit(:school, :majoy, :started, :ended))

Resources