Testing after_save callback evaluating saved_change_to_attribute? - ruby-on-rails

I have recently upgraded my application to rails 5.1 causing me to start using saved_change_to_attribute? in my after_save callbacks. This has caused a test to break.
The code that I am trying to test is the following which gets called through another function in an after_save:
MODEL.RB:
def items_update_needed?
return (online_only? || saved_change_to_online_only?) &&
(saved_change_to_published? && !complete? ||
saved_change_to_online_only? ||
saved_change_to_listing? ||
saved_change_to_third_party_bidding_url? ||
saved_change_to_starts_at? ||
saved_change_to_scheduled_end_time? ||
saved_change_to_closing_speed_lots? ||
saved_change_to_closing_speed_minutes?)
end
TEST.RB:
test "items_update_needed? NOT for a live auction UNLESS it was previous timed" do
# Timed auction NEEDS items update if starts_at changes
#online_only.starts_at = 5.minutes.from_now
assert #online_only.items_update_needed?
#online_only.reload
end
The assert line fails with an error of: Expected false to be truthy.
If I drop a debugger in items_update_needed?, saved_changes_to_starts_at? returns false.
If I put a save before the assert it still returns false which makes sense because the function would have been called already through the after_save callback.
So I am trying to determine how can you actually test this method and get a return of true?

Related

debug Model validation in Rails

I am new to RoR and I am trying to debug a custom validation in my Model. I've tried the following in my code, as well as puts self.input_values.inspect and Rails.logger.info self.input_values.inspect but no outcome in the logs. Am I address wrong debugging? How can I check the value of self.input_values?
validate :i_is_i
def i_is_i
unless self.cust_record == 'number' && self.input_values.all? do |value|
'#################'
Rails.logger.debug self.input_values.inspect
'#################'
value.match(/^\d*$/)
end
errors.add(:input_values, "only numbers!")
throw(:abort)
end
end
They might not be getting printed because the condition is failing and it doesn't go inside block. Do:
validate :i_is_i
def i_is_i
p "CUST_RECORD >>>>>>>>>>>>>>>>> #{self.cust_record}"
p "INPUT_VALUES >>>>>>>>>>>>>>>>> #{self.input_values.all?}"
unless self.cust_record == 'number' && self.input_values.all? do |value|
# Stuff you wanna do when it goes inside
end
end

Rails set(wait: 2.minutes) method for active job does not works

Creating an background job with the resque_scheduler gem on Redis server.
class Estamps::OrderAssignment < ActiveRecord::Base
after_save :enqueue_check_status
def enqueue_check_status
AutoRejectionJob.set(wait: 2.minutes).perform_later(self.id)
end
end
class AutoRejectionJob < ActiveJob::Base
queue_as :default
def perform(*args)
order_assignment_id = args[0]
order_assignment = Estamps::OrderAssignment.find(order_assignment_id)
if order_assignment.status_id == 1 || order_assignment.status_id == nil
order_assignment.status_id = 3
order_assignment.save!
end
end
end
On creation of OrderAssignment record or when updated after 2 minutes it should run AutoRejection Job. Here the prob is the set(wait: 2.minutes) does not seem to run, i.e.
AutoRejectionJob.perform_later(self.id)
works perfectly fine, but
AutoRejectionJob.set(wait: 2.minutes).perform_later(self.id)
does nothing. Haven't been able to rectify the issue. Newbie to Rails so please help.
I see no problem with your code.
I checked : .set(wait: 2.minutes) works as expected in rails 5.0.2 on top of ruby 2.4.0
So does your call of the job.
The way I see it, you're trying to set a status used elsewhere.
Probably, the error is due to the OrderAssignment being manipulated in an outside treatment (destroyed ?)
Since you said you're new to rails (I suppose that's what "newbie" means) I'm going to make some suggestions. Disregard them if you're past that ...
There also are some great debugging tools out there to help you find what's going on : byebug, better_errors, pry and of course, the rails console.
Do yourself a favor : try them.
When I can't find my way around some behavior that goes beyond my grasp, I use some "puts", and some "try / catch errors" structures (begin rescue ensure in ruby)... :
def perform(*args)
puts "####### JOB TRIGGERED ######"
begin
order_assignment_id = args[0]
order_assignment = Estamps::OrderAssignment.find(order_assignment_id)
puts "#{order_assignment.inspect}"
if order_assignment.status_id == 1 || order_assignment.status_id == nil
order_assignment.status_id = 3
order_assignment.save!
end
puts "####### JOB DONE ######"
rescue StandardError => e
# ... display e.message ...
ensure
#...
end
end
check your rails version.
check your rails logs ( log folder, all the jobs will write message to log files when performed )

facing issues about array in rails

I am implementing a program in rails where there is a form and after submitting the form it will check if there is any record with duplicate value for a specific field in database. My database table is students. So my corresponding model name is Student. I am writing this code (what I have just discussed) in my controller.
But I am facing the following error. I am using some arrays for internal operations. When I wrote that particular function in ruby only(not rails) then it was working fine. Moreover I am also facing error due to the use of "length".
My error is:
NoMethodError in StudentsController#create
undefined method `[]' for nil:NilClass
My controller code is:
class StudentsController < ApplicationController
def new
#student=Student.new
#students=Student.all
end
def create
#student=Student.new(u_params)
ret_val=string_check
if ret_val==1
#student.save
redirect_to new_student_path , :notice => "Inserted!!!"
else
redirect_to new_student_path , :notice => "Match,Not inserted!!!"
end
end
def u_params
params.require(:student).permit(:id ,:firstname, :lastname)
end
def u_params_second
params.require(:student).permit(:firstname)
end
def string_check
count =0;
#temp1=:firstname
temp1=params[:firstname]
supplied_val=temp1
puts "Entered in string_check method"
for i in 46..100
temp2=Student.find_by(id:i)
table_val=temp2.firstname
size1=supplied_val.to_s.length
size2=table_val.to_s.length
arr1=Array.new
arr2=Array.new
# arr1[i] ||= []
# arr2[i] ||= []
for i in 0..size1
arr1.push(supplied_val[i])
end
for i in 0..size2
arr2.push(table_val[i])
end
for i in 0..size1
if arr1[i]=="#" || arr1[i]=="#" || arr1[i]=="{" || arr1[i]=="}" || arr1[i]=="(" || arr1[i]==")" || arr1[i]=="[" || arr1[i]=="]" || arr1[i]=="." || arr1[i]==";" || arr1[i]=="," || arr1[i]=="%" || arr1[i]=="&" || arr1[i]=="*" || arr1[i]=="!" || arr1[i]=="?" || arr1[i]=="$" || arr1[i]=="^" || arr1[i]==":" || arr1[i]=="-" || arr1[i]=="/"
count=count+1
# puts count
arr1[i]=""
end
end
# puts arr1
puts arr1.join
final1=arr1.join
for i in 0..size2
if arr2[i]=="#" || arr2[i]=="#" || arr2[i]=="{" || arr2[i]=="}" || arr2[i]=="(" || arr2[i]==")" || arr2[i]=="[" || arr2[i]=="]" || arr2[i]=="." || arr2[i]==";" || arr2[i]=="," || arr2[i]=="%" || arr2[i]=="&" || arr2[i]=="*" || arr2[i]=="!" || arr2[i]=="?" || arr2[i]=="$" || arr2[i]=="^" || arr2[i]==":" || arr2[i]=="-" || arr2[i]=="/"
count=count+1
# puts count
arr2[i]=""
end
end
# puts arr2
puts arr2.join
final2=arr2.join
if final1==final2
flag=0
else
flag=1
end
return flag
end
end
end
The routes.rb file is:
Rails.application.routes.draw do
resources :students
end
My error is: NoMethodError in StudentsController#create
undefined method `[]' for nil:NilClass
It simply means that you are trying to access something as an array that is actually a nil object, and not an array.
To get rid of this error, you can a technique called short-circuit in Ruby.
Let's say your following piece of code is producing the said error:
arr1[i]
You can use an if condition like this:
if arr1
arr1[i]
end
Or use short-circuit technique like this:
arr1 && arr1[i]
If you sure that the relevant code snippet was working for ruby and it's not for rails, the problem is most likely due to variable i used at inner and outer loops both. In any case, this needs to be fixed first or else it will result in unexpected behaviour only.
Outer Loops:
for i in 46..100
Inner Loops:
for i in 0..size1
for i in 0..size2
...
Keep i for outer loop and change the inner loop iterator to j
Hope it Helps : )
Adding to the answers of #harish and #arslan, there may be a case where, temp2=Student.find_by(id:i) may fail because there may not be a student with that id.
So, temp2 may return nil at that time.
for i in 0..size2
arr2.push(table_val[i]) // this may get failed
end
Then arr2.push will not work because table_val[i] is nil, so there are chances of undefined method [] for nil class.

ActiveRecord - check if value is null, 0 or 1

Rails 3.2.2, Ruby 1.9.2
I'm using MySql and there is column "MyColumn" of TINYINT type. I need to show the status of it on a page. So I created a helper method.
module MyControllerHelper
def result(a)
case a
when false then 'false 0'
when true then 'true 1'
when blank? then 'blank or nil'
end
end
end
The bottom line is that it can also be empty or nil. So it doesn't work as I need. It constantly returns either false 0 or true 1 but never blank or nil even if it should do.
What did I do wrong?
A case uses === for comparison so that's equivalent to:
if false === a
'false 0'
elsif true === a
'true 1'
elsif blank? === a
'blank or nil'
else
nil
end
Rails adds a blank? method to Object that looks like this:
def blank?
respond_to?(:empty?) ? empty? : !self
end
so you can call blank? anywhere, even without a specified receiver: there will always be a self and it will always be an Object. Now you should see that when blank?, while syntactically valid, makes no sense at all: it doesn't call a.blank? and see if a true value came back, it simply checks self.blank? === a for whatever self happens to be.
You're probably better off using an explicit if/else for this:
def result(a)
# false.blank? is true so you don't want a.blank? here.
if(a.nil?)
'nil'
elsif(a)
'true 1'
else
'false 0'
end
end

Rails validate association only when loaded

I have an activity model which has_many participants and I'd like to ensure that a participant always exists when updating an activity and its participants. I have the following method in my activity model which does the trick:
def must_have_participant
if self.participants.size == 0 || self.participants.size == self.participants.to_ary.find_all{ |p| p.marked_for_destruction? }.count
self.errors[:base] << I18n.t(:msg_activity_must_have_participant)
end
end
The problem is that the participants are lazy loaded if I'm simply updating the activity on its own which I'd like to avoid. I've tried the following alternative, however, loaded? returns false when removing all participants using the :_destroy flag.
def must_have_participant
if self.new_record? || self.participants.loaded?
if self.participants.size == 0 || self.participants.size == self.participants.to_ary.find_all{ |p| p.marked_for_destruction? }.count
self.errors[:base] << I18n.t(:msg_activity_must_have_participant)
end
end
end
Is there an alternative to loaded? that I can use to know whether the participants are going to be updated?
I did something like this in a recent validation that I created. I searched for the original record and checked the original value against the new value. No guarantees my code will work for you but here is my code for your application:
orig_rec = self.find(id)
if participant_ids.size != orig_rec.participant_ids.size
Note that I checked the size of participant_ids instead of fetching all the participant records and checking the size of them. That should be more efficient.
I don't know if there is some kind of built in function to do this or not in ruby, I'll be curious to see what someone who is more rails specific may suggest.
For reference I've amended the method like so:
def must_have_participant
if self.new_record? || self.association(:participants).loaded?
if self.participants.size == 0 || self.participants.size == self.participants.select{ |p| p.marked_for_destruction? }.size
self.errors[:base] << I18n.t(:msg_must_have_participant)
end
end
end

Resources