ruby '2.7.3'
rails (6.1.4.1)
Looks strange:
When I query some (some specific) rows in DB using activerecord and try to assign it to a variable, it raises "nil can't be coerced into Integer"
But when I don't try to assign it to a variable, it works:
Recipient.find(45638)
=> #<Recipient id: 45638, company_id: 7, callout_id: 18, phone: "***", created_at: "2022-02-14 02:14:04.816032000 +0000", updated_at: "2022-02-15 06:50:37.828979000 +0000", payload: {"first_name"=>"", "last_name"=>"", "lead_id"=>"388014"}, started_at: nil, finished_at: 1644907333994, call_duration: 95, call_result: nil, report_data: {"Recording"=>"...", "UF_CRM_1601886091616"=>"76"}, job_status: "longCallWithResult", job_id: "2105242339", aasm_state: "finished", attempts: 1, tag: "...", tag_payload: {}, code: "37cca71006817c4f9e2e172d4e0afe80", schedule_at: "2022-02-11 09:07:47.000000000 +0000", synced_to_external_crm: false>
2.7.3 :035 > recipient = Recipient.find(45638)
Traceback (most recent call last):
TypeError (nil can't be coerced into Integer)
It happens only with specific rows in DB. Not only with model object, but with ActivRecord::Relation objects also:
Works:
recipients = Recipient.where(callout_id: 18).where('schedule_at IS NULL OR schedule_at <= ?', DateTime.current).limit(1).offset(5)
=> #<ActiveRecord::Relation [#<Recipient id: 45640, company_id: 7, callout_id: 18, phone: "***", created_at: "2022-02-14 04:59:10.175701000 +0000", updated_at: "2022-02-15 06:50:38.224724000 +0000", payload: {"firs...
Doesn't work:
2.7.3 :045 > recipients = Recipient.where(callout_id: 18).where('schedule_at IS NULL OR schedule_at <= ?', DateTime.current).limit(1).offset(6)
Traceback (most recent call last):
TypeError (nil can't be coerced into Integer)
Same issue happens with different models:
Model 'Callout':
2.7.3 :001 > callout = Callout.find 18
=> #<Callout id: 18, company_id: 7, name: "***", token: [FILTERED], created_at: "2021-12-19 13:50:51.383907000 +0000", updated_at: "2022-02-08 09:19:20.153542000 +0000", auto_queue: true>
2.7.3 :002 > callout = Callout.find 17
Traceback (most recent call last):
TypeError (nil can't be coerced into Integer)
2.7.3 :003 > Callout.find 17
=> #<Callout id: 17, company_id: 1, name: "***", token: [FILTERED], created_at: "2021-11-27 11:07:30.927895000 +0000", updated_at: "2022-02-14 07:59:24.154910000 +0000", auto_queue: false>
2.7.3 :004 > Callout.find 18
=> #<Callout id: 18, company_id: 7, name: "***", token: [FILTERED], created_at: "2021-12-19 13:50:51.383907000 +0000", updated_at: "2022-02-08 09:19:20.153542000 +0000", auto_queue: true>
Model 'Company':
2.7.3 :005 > Company.find 7
=> #<Company id: 7, name: "***", created_at: "2021-12-19 13:39:12.045709000 +0000", updated_at: "2021-12-19 13:39:12.045709000 +0000", balance: 0, api_token: [FILTERED]>
2.7.3 :006 > c = Company.find 7
=> #<Company id: 7, name: "***", created_at: "2021-12-19 13:39:12.045709000 +0000", updated_at: "2021-12-19 13:39:12.045709000 +0000", balance: 0, api_token: [FILTERED]>
2.7.3 :008 > Company.find 1
=> #<Company id: 1, name: "***", created_at: "2021-10-22 12:23:19.831733000 +0000", updated_at: "2021-12-01 20:15:04.464389000 +0000", balance: 0, api_token: [FILTERED]>
2.7.3 :007 > c = Company.find 1
Traceback (most recent call last):
TypeError (nil can't be coerced into Integer)
Models source code:
class Recipient < ApplicationRecord
validates :phone, presence: true, uniqueness: { scope: :callout_id }
validates :code, presence: true, uniqueness: true
belongs_to :company
belongs_to :callout
before_validation :generate_code
private
# Generates unique code for every recipient
# because phone column can't be unique
def generate_code
if self.code.nil?
self.code = Recipient.make_code self.phone, self.callout_id, self.payload
end
end
end
class Callout < ApplicationRecord
belongs_to :company
has_many :recipients, dependent: :destroy
validates :company_id, presence: true
validates :name, presence: true
scope :auto_queue, -> { where(auto_queue: true ) }
end
class Company < ApplicationRecord
before_validation :generate_token!
has_many :callouts, dependent: :destroy
has_many :recipients, dependent: :destroy
has_many :user_companies, dependent: :destroy
has_many :users, through: :user_companies
has_many :invoices, dependent: :destroy
has_many :balance_logs, dependent: :destroy
validates :name, presence: true
def generate_token!
if api_token.nil?
update! api_token: SecureRandom.hex
end
end
end
That's related to some unexpected issue related to the use of --nomultiline or IRB.conf[:USE_MULTILINE] = false inside .irbrc file.
Similar issue with the hack
To avoid that issue, you can just skip using --nomultiline option, when launching your rails console.
bundle exec rails c -e production
A quick one-off solution if you mostly want --nomultiline behavior and/or just don't want to mess with configs:
x = string_with_weird_characters; nil
Related
I'm having an issue with my scheduled text messages. I run a rake task that checks to see if a text message should be put into a Sidekiq queue. The record is processed (the text is sent) but a new empty record is generated and the sentstatus is not updated to "true".
send_scheduled_text.rake
require_relative '../../app/workers/send_text_worker'
namespace :send_scheduled_text do
task:texts => :environment do
TextMessage.all.each do |text_message|
if text_message.sentstatus == false
if (Date.today == text_message.scheduled_date) && (Time.now.hour >= text_message.scheduled_time.hour)
# Sidekiq code:
SendTextWorker.perform_async(text_message.id)
end
end
end
end
end
send_text_worker.rb
class SendTextWorker
include Sidekiq::Worker
def perform(text_message_id)
text = TextMessage.find(text_message_id)
text.send_text_message(text.content, text.phone)
end
end
text_message.rb
require 'twilio-ruby'
require 'date'
class TextMessage < ActiveRecord::Base
belongs_to :client, dependent: :destroy
belongs_to :step, dependent: :destroy
has_many :coach_emails
before_save :grab_phone
def grab_phone
self.phone = phone
end
def send_text_message(message, phone)
twilio_sid = ENV["TWILIO_ACCT_SID"]
twilio_token = ENV["TWILIO_AUTH_TOKEN"]
twilio_phone_number = ENV["TWILIO_PHONE_NUMBER"]
begin
#twilio_client = Twilio::REST::Client.new(twilio_sid, twilio_token)
#twilio_client.account.sms.messages.create(
:from => "+1#{twilio_phone_number}",
:to => phone,
:body => message)
rescue Twilio::REST::RequestError => e
puts e.message
end
if e != "400" || e != "500"
self.sentstatus = true
end
self.save!
send
send
Rails console: before rake task is called
(sentstatus is false)
irb(main):001:0> TextMessage.all
TextMessage Load (0.5ms) SELECT "text_messages".* FROM "text_messages"
=> #<ActiveRecord::Relation [#<TextMessage id: 164, client_id: nil, content: "Testing Sidekiq processing", incoming_message: false, created_at: "2015-02-02 04:43:29", updated_at: "2015-02-02 04:43:29", scheduled_date: "2015-02-01", sentstatus: false, step_id: 4, phone: "+14127364161", scheduled_time: "2000-01-01 14:00:00">]>
Rails console: After rake task is called
(sentstatus is false, should be true. I also have this new bizarre empty record)
irb(main):001:0> TextMessage.all
TextMessage Load (0.5ms) SELECT "text_messages".* FROM "text_messages"
=> #<ActiveRecord::Relation [#<TextMessage id: 164, client_id: nil, content: "Testing Sidekiq processing", incoming_message: false, created_at: "2015-02-02 04:43:29", updated_at: "2015-02-02 04:43:29", scheduled_date: "2015-02-01", sentstatus: false, step_id: 4, phone: "+14127364161", scheduled_time: "2000-01-01 14:00:00">,
#<TextMessage id: 165, client_id: nil, content: nil, incoming_message: nil, created_at: "2015-02-02 04:45:24", updated_at: "2015-02-02 04:45:24", scheduled_date: nil, sentstatus: true, step_id: nil, phone: nil, scheduled_time: nil>]>
I have a feeling this is a Sidekiq nuance that I'm missing. Thanks for any thoughts!
I ended up moving my model logic to my worker. Presto -- timing works and I'm not generating any extra nil records.
I have 2 models, Car and Person. Each person can have many cars. I have loaded some data through from seed.rb but when I try Car.find_by(owner_id: 1) in the console, I see just one car. When I am expecting 2 cars because I assigned 2 cars to one particular Person. Why am I just seeing one car?
car.rb (model)
class Car < ActiveRecord::Base
belongs_to :owner, class_name: "Person", foreign_key: "owner_id"
has_many :place_rents
validates :owner, presence: true
validates :registration_number, presence: true
validates :model, presence: true
end
person.rb
class Person < ActiveRecord::Base
has_many :cars, foreign_key: "owner_id"
has_many :parkings, foreign_key: "owner_id"
validates :first_name, presence: true
end
seed.rb
people = Person.create([{first_name: "Emmanuel", last_name: "Hayford"}, {first_name: "Steve", last_name: "Jobs"},
{first_name: "James", last_name: "Bond"}, {first_name: "Michael", last_name: "Jordan"}])
addresses = Address.create([{street: "Limanowskiego", city: "Kraków", zip_code: "98-734"}, {street: "Zeromskiego",
city: "Wrocław", zip_code: "45-622"}, {street: "Chopin", city: "Gdańsk", zip_code: "98-734"},
{street: "Putin", city: "Opole", zip_code: "90-938"}])
cars = Car.create([{owner: Person.first, model: "BMW", registration_number: "UJ8483"}, {owner: Person.second, model: "Lambo", registration_number: "JH4857"},
{owner: Person.second, model: "Jaguar", registration_number: "DW3455"}, { owner: Person.second, model: "Ferrari", registration_number: "KP8734"},
{owner: Person.first,model: "Bugatti Veyron", registration_number: "ZK9837"}])
parkings = Parking.create([{kind: "indoor", hour_price: 45.50, day_price: 200, places: 5, owner: Person.first, address_id: Address.first},
{kind: "outdoor", hour_price: 20.50, day_price: 150.00, places: 10, owner: Person.second, address_id: Address.second},
{kind: "street", hour_price: 270.50, day_price: 89.45, places: 7, owner: Person.second, address_id: Address.second},
{kind: "private", hour_price: 40.50, day_price: 30.45, places: 2, owner: Person.find(2), address_id: Address.find(2)},])
place_rents = PlaceRent.create([{parking: Parking.first, car: Car.first, start_date: Time.now, end_date: Time.now + 3.days, price: 30}])
2.1.4 :023 > Car.all
Car Load (0.5ms) SELECT "cars".* FROM "cars"
=> #<ActiveRecord::Relation [#<Car id: 1, registration_number: "UJ8483", model: "BMW", owner_id: 1, created_at: "2014-11-10 00:10:26", updated_at: "2014-11-10 00:10:26">, #<Car id: 2, registration_number: "JH4857", model: "Lambo", owner_id: 2, created_at: "2014-11-10 00:10:26", updated_at: "2014-11-10 00:10:26">, #<Car id: 3, registration_number: "DW3455", model: "Jaguar", owner_id: 2, created_at: "2014-11-10 00:10:26", updated_at: "2014-11-10 00:10:26">, #<Car id: 4, registration_number: "KP8734", model: "Ferrari", owner_id: 2, created_at: "2014-11-10 00:10:26", updated_at: "2014-11-10 00:10:26">, #<Car id: 5, registration_number: "ZK9837", model: "Bugatti Veyron", owner_id: 1, created_at: "2014-11-10 00:10:26", updated_at: "2014-11-10 00:10:26">]>
2.1.4 :024 > Car.find_by(owner_id: 1)
Car Load (0.6ms) SELECT "cars".* FROM "cars" WHERE "cars"."owner_id" = 1 LIMIT 1
=> #<Car id: 1, registration_number: "UJ8483", model: "BMW", owner_id: 1, created_at: "2014-11-10 00:10:26", updated_at: "2014-11-10 00:10:26">
2.1.4 :025 >
You misunderstand how find_by works:
find_by(*args)
Finds the first record matching the specified conditions.
find_by only gets one record from the database, hence the LIMIT 1 in the SQL it generates. If you want to find several things, then use where:
Car.where(owner_id: 1)
or, since you have the associations set up, use Person#cars:
person = Person.find(1)
cars = person.cars
Car.find_by_owner_id(1) is the same as Car.where(owner_id: 1).first so basically it returns one record rather than an array of record. Car.where(owner_id: 1) will get you your two records.
I'm using Validates_Overlap Gem which can be found here: https://github.com/robinbortlik/validates_overlap
The essence is I have two rooms that can be booked. I want the validation to step in when the same room already has a CONFIRMED booking in the SAME room. It shouldn't throw me an error when the other room is booked, or if the same room is booked but hasn't been confirmed.
My code so far is as follows
validates :start_time, :end_time,
:overlap => {
:exclude_edges => ["starts_at", "ends_at"],
:scope => { "bookings.studio_id" => proc {|booking| booking.studio_id}} && { "bookings.is_confirmed" => proc {|booking| booking.is_confirmed == true}}
}, on: :update
This returns the following from my server:
Booking Exists (0.4ms) SELECT 1 AS one FROM "bookings" WHERE ((bookings.end_time IS NULL OR bookings.end_time >= '2014-10-23 20:00:00.000000') AND (bookings.start_time IS NULL OR bookings.start_time <= '2014-10-24 03:00:00.000000') AND bookings.id != 9 AND bookings.is_confirmed = 't') LIMIT 1
There are two other bookings (with this studio_id) and none of them are confirmed. What gives?
Here are all the bookings with :studio_id => 2
[#<Booking id: 1, studio_id: 2, engineer_id: 5, is_confirmed: false, title: "", allDay: false, created_at: "2014-10-23 19:59:01", updated_at: "2014-10-23 19:59:01", start_time: "2014-10-23 19:00:00", end_time: "2014-10-23 21:00:00", user_id: nil, booker: "Client", client_id: 3>,
#<Booking id: 8, studio_id: 2, engineer_id: 1, is_confirmed: false, title: "", allDay: false, created_at: "2014-10-24 03:07:34", updated_at: "2014-10-24 03:07:34", start_time: "2014-10-23 19:00:00", end_time: "2014-10-23 22:00:00", user_id: nil, booker: "Pat Sullivan", client_id: nil>,
#<Booking id: 9, studio_id: 2, engineer_id: 2, is_confirmed: false, title: "", allDay: false, created_at: "2014-10-24 03:26:17", updated_at: "2014-10-24 03:26:17", start_time: "2014-10-23 20:00:00", end_time: "2014-10-24 03:00:00", user_id: nil, booker: "Client", client_id: 4>]
Update I noticed that the studio_id isn't being noticed with the && in the scope line. How can I have the scope register both? Can I do it within the scope line or should I create a method?
I've also tried a simpler
validates :start_time, :end_time,
:overlap => {
:exclude_edges => ["starts_at", "ends_at"],
:scope => "is_confirmed" && "studio_id"
}, on: :update
This does the same thing -- only uses the later "studio_id"
I know, that the names of options are confusing and I'm sorry for that.
I suggest you to implement your named scope called :confirmed and pass it as :query_option parameter.
I think, it should look like this:
class Booking < ActiveRecord::Base
scope :confirmed_scope, -> {confirmed: true}
validates :start_time, :end_time, :overlap => {
:exclude_edges => ["starts_at", "ends_at"],
:scope => "studio_id",
:query_options => {:confirmed_scope => nil}
}, on: :update
end
BTW... be careful if you are using Rails 4.1, there is a change https://github.com/robinbortlik/validates_overlap#rails-41-update
Short explanation: what you pass as a :scope option, this behave like attribute. But you can extend it by :query_options. What is inside query options will be called in the query chain. So internally it will be called like this:
Booking.confirmed_scope.where("starts_at > 18-02-2014 AND ends_at < 20-02-2014 AND studio_id = 1")
Is it more clear now?
Am I missing something here?
Discourse model:
class Discourse < ActiveRecord::Base
#<Discourse id:, user_id: , sub_discourse_id: , title: , body: , deleted: , delete_date: , created_at:, updated_at: >
has_many :discourse_replies
has_many :replies, through: :discourse_replies
end
DiscourseReply model:
class DiscourseReply < ActiveRecord::Base
belongs_to :discourse
belongs_to :reply, class_name: 'Discourse'
end
console:
Loading development environment (Rails 4.0.2)
2.0.0p247 :001 > fd = Discourse.create(title: 'first', body: 'first')
=> #<Discourse id: 5, user_id: nil, sub_discourse_id: nil, title: "first", body: "first", deleted: nil, delete_date: nil, created_at: "2014-04-04 23:32:13", updated_at: "2014-04-04 23:32:13">
2.0.0p247 :002 > fd.discourse_replies
=> #<ActiveRecord::Associations::CollectionProxy []>
2.0.0p247 :004 > fd.create_discourse_reply
NoMethodError: undefined method 'create_discourse_reply' for #<Discourse:0x00000003396450>
2.0.0p247 :004 > fd.discourse_replies.build(title: "reply to first", body: "reply to first")
ActiveRecord::UnknownAttributeError: unknown attribute: title
In a nutshell, why is create_discourse_reply an undefined method?
You've defined has_many association for discourse_replies so to create a associated object you need to do
fd.discourse_replies.create
fd.create_discourse_reply would have been created the object if you'd have associated it with has_one or belongs_to association.
I have...
/app/models/input.rb:
class Input < ActiveRecord::Base
has_many :questions, :dependent => :destroy
after_commit :create_matching_questions
def create_matching_questions
#element_id = Element.all.select{|e| e.meta == true}.first.id
#standard_id = Standard.all.select{|s| s.meta == true}.first.id
#description = ["Does the site stock ", self.name, "?"].join
Product.all.each do |product|
#question = product.questions.find_or_create_by_element_id_and_standard_id_and_description!(#element_id, #standard_id, #description)
self.questions << #question
#question.fields.find_or_create_by_name("The site sells this product and it is in stock")
#question.fields.find_or_create_by_name("The site sells this product but it is not in stock")
#question.fields.find_or_create_by_name("The site does not sell this product")
#question.update_attributes :active => true
end
return true
end
end
/app/models/question.rb:
class Question < ActiveRecord::Base
belongs_to :input
after_commit :create_matching_surveys
def create_matching_surveys
if self.active == true
self.reload.product.reviews.each do |review|
review.competitors.each do |competitor|
(1..self.iterations).each do |iteration|
survey = competitor.surveys.find_or_create_by_question_id_and_iteration!(self.id, iteration)
survey.save
end
end
end
return true
else
self.destroy_matching_surveys
end
end
def destroy_matching_surveys
self.surveys.each do |survey|
survey.destroy if survey.question_id == self.id
end
return true
end
end
Why, then, do I get...
> #finance = Good.create! :name => "Finance"
=> #<Good id: 6, name: "Finance", created_at: "2013-06-13 02:56:20", updated_at: "2013-06-13 02:56:20">
> #super = Input.create! :name => "Superannuation"
=> #<Input id: 11, name: "Superannuation", mispelling: nil, typo: nil, created_at: "2013-06-13 02:56:28", updated_at: "2013-06-13 02:56:28">
> #first = #super.questions.first
=> #<Question id: 48, standard_id: 1, description: "Does the site stock Superannuation?", element_id: 2, condition_id: nil, blueprint_name: nil, blueprint_url: nil, additive: false, instructions: nil, created_at: "2013-06-13 02:56:41", updated_at: "2013-06-13 02:56:41", active: false, postscript: "<p>If you have any comments about this question or ...", iterations: 1, product_id: 1, precondition_id: nil, input_id: 11>
> #last = #super.questions.last
=> #<Question id: 60, standard_id: 1, description: "Does the site stock Superannuation?", element_id: 2, condition_id: nil, blueprint_name: nil, blueprint_url: nil, additive: false, instructions: nil, created_at: "2013-06-13 02:56:43", updated_at: "2013-06-13 02:56:43", active: false, postscript: "<p>If you have any comments about this question or ...", iterations: 1, product_id: 23, precondition_id: nil, input_id: 11>
> #super.destroy
=> #<Input id: 11, name: "Superannuation", mispelling: nil, typo: nil, created_at: "2013-06-13 02:56:28", updated_at: "2013-06-13 02:56:28">
> #super.destroyed?
=> true
> #first.destroyed?
=> false
> #last.destroyed?
=> false
Surely #first and #last should be destroyed automatically?
I had the same problem, solved it by :dependent => :delete_all instead of :dependent => :destroy.
:delete_all doesn't call the destroy method from your controller and delete data directly from your database.