Increment/decrement integer in user model by 1 - ruby-on-rails

I have a user model which has these columns - name, email, books_on_loan
I have a booking system where users can check out/in books. When A user checks a book out I want to increase their 'books_on_loan_ integer by one, and vice versa.
So far I have tried it like this and am getting an error "undefined method `+' for nil:NilClass" - it doesnt like the #user.books_on_loan
def check_out
#params = params[:book]
title = #params[:title]
name = #params[:name]
#book = Book.find_by_title(title)
#user = User.find_by_name(name)
if #user == nil
#note = 'This user is not registered.'
elsif #book == nil
#note = 'This book is not in the library.'
elsif #book.onloan == 1
#note = 'This book is on loan.'
elsif #user.books_on_loan == 3
#note = 'This user already has 3 books out.'
else
#book.onloan = 1
#book.save
#books_loaned = BooksOnloan.create(book_id: #book.id, user_id: #user.id)
#books_loaned.save
#user.books_on_loan = #user.books_on_loan + 1
#user.save
#note = 'The book was checked out.'
end
respond_to do |format|
format.html
end
end

Try this:
#user.increment :books_on_loan, 1

You can try with following codes, that will handle nil value for #user.books_on_loan
#user.books_on_loan = #user.books_on_loan.to_i + 1
or
#user.books_on_loan = (#user.books_on_loan || 0) + 1
or
#user.books_on_loan = (#user.books_on_loan.present? ? #user.books_on_loan : 0) + 1

While most answers here are ok, you'd rather change the root of the problem otherwise you'd have to use guard causes in your whole app.
It seems books_on_loan to be an attribute in db, so do :
change_column :users, :books_on_loan, :integer, default: 0
#and change existing bad data
User.update_all({books_on_loan: 0}, {books_on_loan: nil})
Or you could change the getter in your class:
def books_on_loan
super || 0
end
Side note, design wise, its not that a good idea to have an integer in db maintaining the current books on loan: you could loose sync the real ones.
You'd rather count a real association.

Simply do #user.books_on_loan.to_i. The to_i will convert nil into 0 automatically

ost answers are very correct but I'd like to add the following.
To answer your question, incrementing the value of an attribute (even on a variable that returns nil) can be done this way:
#user.books_on_loan =+ 1
decrementing being: #user.books_on_loan =- 1
However, you can leverage the benefit of proper model associations here and have a has_many -> through relationship between User and Book where by adding/removing a book for a user, the counter is adjusted accordingly.

Rails provides practical abstraction that you can use: increment and decrement.
So you could now do something like this:
#user.increment(:books_on_loan, 1)
Increment takes 2 argument. The table column and the number by which you increment. It initializes the attribute to zero if nil and adds the value passed as by (default is 1).
Similarly you can use
#user.decrement(:book_on_loan, 1)
More on this HERE

Related

Rails: How to access session parameter / ActiveRecord::StatementInvalid in Orders#create

I am working on a multistep form for an order placement section which uses a session session[:order_params] to store all form inputs before submit.
I need to be able to access a particular parameter (land) from the session in order to query for another resource (shippingservice) when navigating back in the form.
In my orders_controller.rb I have:
#shippingservices = #cart.available_shipping_services.joins(:lands).where(:lands => {:id => params[:id]})
but would need to specify the land.id from the session[:order_params].
When using session[:order_params] I get ActiveRecord::StatementInvalid in Orders#create:
Mysql::Error: Unknown column 'id.ship_to_last_name' in 'where clause': SELECT `shippingservices`.* FROM `shippingservices`
INNER JOIN `zones` ON `zones`.`id` = `shippingservices`.`zone_id`
INNER JOIN `lands_zones` ON `lands_zones`.`zone_id` = `zones`.`id`
INNER JOIN `lands` ON `lands`.`id` = `lands_zones`.`land_id`
WHERE `id`.`ship_to_last_name` = 'Smith'
AND `id`.`ship_to_address` = 'Somewherestreet'
AND `id`.`ship_to_city` = 'Nowheretown'
AND `id`.`ship_to_postal_code` = '99999'
AND `id`.`phone_number` = 'some number'
AND `id`.`shippingservice_id` = '34'
AND `id`.`email` = 'someone#example.tld'
AND `id`.`land_id` = '85'
AND `id`.`ship_to_first_name` = 'John'
AND (weightmin <= 200 AND weightmax >= 200 AND heightmin <= 12 AND heightmax >= 12 AND shippingservices.shippingcarrier = '1') AND (lengthmax >= 210 AND widthmax >= 149)
Since the correct land_id is present I am wondering how to provide only that value to the query.
Thank you in advance!
As per the description mentioned in the post, you want to access a particular key stored in session at a particular key.
Assuming order_params is a hash, you can get land_id using the below mentioned code:
session[:order_params][:land_id]
This will return the value of land_id and thus you can use it in the query.
To set session variable, you can set some data in a controller action
For eg:
app/controllers/sessions_controller.rb
def create
# ...
session[:current_user_id] = #user.id
# ...
end
And read it in another: app/controllers/users_controller.rb
def index
current_user = User.find_by_id(session[:current_user_id])
# ...
end

How can I iterate through a model then iterate again in my view?

I want to pull data for each of my users. I grab their person_id from my user table, then use each person's ID to figure out how many days each person has available, and show that in my view.
I'm not sure if I am doing this correctly because I am iterating in my controller then again in my view.
def how_many_days_users_have
#my_group = User.all.pluck(:person_id)
#my_group.each do |v|
#indirect_id_v = Empaccrl.where("person_id = ? and is_active = ?", '#{v]', 'Y').pluck(:a_code).first
#v_range = Empaccrl.where("person_id = ? and is_active = ?", '#{v]', 'Y').pluck(:ac).first
#v_range_taken = Empaccrl.where("person_id = ? and is_active = ?", '#{v]', 'Y').pluck(:taken).first
#total_v_hours = #v_range.to_d - #v_range_taken.to_d
#total_v_days = #total_v_hours / 8
end
Then in my view I use this to show me this data:
%tr.trace-table
-#indirect_id_v.each do |idd|
%tr.trace-table
%td.trace-table{:style => 'border: solid black;'}= idd
-#total_v_days.each do |days|
%tr.trace-table
%td.trace-table{:style => 'border: solid black;'}= days
Okay, first things first, move some of that junk to your model, like so:
class Empaccrl < ActiveRecord::Base
def self.all_people
where(person_id: User.all.pluck(:person_id))
end
def self.active_people
all_people.where(is_active: 'Y')
end
def self.active_vacation_data
active_people.select(:person_id, :ac, :taken)
end
def total_v_hours
ac.to_d - taken.to_d
end
def total_v_days
total_v_hours / 8
end
end
Then you can use:
peoples_vacation_information = Empaccrl.active_vacation_data.all
peoples_vacation_information.map do |person|
p "person #{person.person_id} has #{person.total_v_days} vacation days"
end
Honestly, you don't even need all that, but I'm not sure why you are doing what you are doing, so I figured better be safe and add stuff. Whatever you don't need, just ignore.

Rails Fixnum Error

I have a simple query that Rails seems to be interpreting as a fixnum, but I'm not sure why. My code looks like this:
#user_with_points = Point.select("sum(points) as points, user_id").order("points desc").group("user_id")
#user_with_points.each_with_index do |user_with_point, index|
When I add puts #user_with_points, it shows:
#<Point:0x6360138>
#<Point:0x6322f38>
However, I'm receiving this error this error:
NoMethodError: undefined method 'each' for 75:Fixnum
adding Entire Code
def self.update_overall_rank_and_points
#user_with_points = Point.select("sum(points) as points, user_id").order("points desc").group("user_id")
rank = 0
points = 0
#user_with_points.each_with_index do |user_with_point, index|
#user = User.find(user_with_point.user_id)
if user_with_point.points != points
points = user_with_point.points
rank += 1
end
#user.rank = rank
#user.points = user_with_point.points
#user.save
end
end
Your query is returning a scalar value which the sum of points as an integer. The total of your query happens to be 75, hence the error. Therefore you can't do an each against it since it's not an enumeration.
Try:
#user_with_points = Point.sum(:points, :group => :user_id, :order => 'sum(points)')
#user_with_points.each do |user_id, points|
#...
user = User.find(user_id)
if user.points != points
puts "not equal!"
end
end

undefined method `is_current' for #<ActiveRecord::Relation:0x38622a8> in ruby on rails update data

I'm trying to update some data in database but I'm getting this error:
undefined method `is_current' for #<ActiveRecord::Relation:0x38622a8>
My controller:
#user = User.where("user_id = #{user_id} and is_current = 1")
if #user.nil?
puts"xxxxxxxxxxxxxxxxxxxxxxxxxxx"
else
#user.is_current = 0
#user.to = Time.now
#user.save
end
User.where returns an array. You need to say .first afterwards. Additionally do not use a string in where with out escaping your arguments. Your query is not secure and is open to SQL injection.
#user = User.where(user_id: user_id, is_current: 1).first
try to write query in model, it's good practice, so you can do
#user = User.where("user_id = #{user_id} and is_current = 1")
replace with
#user = User.user_is_current(user_id,1) # two argument 1) user_id and 2) is_current value
in your model
def user_is_current(user_id,is_current)
where(user_id: user_id, is_current: is_current)
end
it will give data in array so you can write
#user = User.user_is_current(user_id,1).first

Rails - Find or Create based on TimeStamp? possible?

In my controller I'd like to do something like the following:
#book = Book.find(:all, :conditions = > [" created_at > with in the last 1 minute "]
if #book.nil?
# Just incase we didn't create a book, we'll initialize one
#book = Book.create()
end
#chapters = #book.chapters.build etc.............
* In sum, when the user is uploading a chapter, if they've recently created a book, I want the chapter to automatically go to that book and to make a new book.
Thoughts? thank you all
Hi Your code may be something like
time = Time.now
#book = Book.find(:all, :conditions = > [" created_at >= '#{Time.utc(time.year, time.month, time.day, time.hour, time.min - 1)}'"]) // .first if you're sure that it'll return just one record
if #book.blank? //.blank? not .nil? because the result of find is [] not nil
# Just incase we didn't create a book, we'll initialize one
#book = Array.new() //if you're sure that find'll return just one book you may don't change your code here
#book.first = Book.create()
end
//if you're sure that find'll return just one book you may don't change your code here
#book.each do |book|
#chapters = #book.chapters.build etc.............
end
if you're looking for a book created by some user you must pass user_id to this method and your conditions'll be
:conditions = > [" created_at >= '?' AND author_id = ?", Time.utc(time.year, time.month, time.day, time.hour, time.min - 1), params[:author_id]])

Resources