How does recalling a variable work in a method? - ruby-on-rails

I have the following method:
vendor_orders = VendorOrder.where(id: params[:vendor_order_ids])
orders = Order.find(vendor_orders.pluck(:order_id))
products = Product.joins(:vendor_product).where(vendor_products:{vendor_id: current_user.id }).ids #get all vendor_products that match current_user.vendor
line_items = LineItem.joins(:shop_product).where(cart_id: orders.pluck(:cart_id), fulfillment_status: "processing", shop_products: {product_id: products}).where.not(fulfillment_status: "canceled")
messages = []
n = 0
puts "line items: #{line_items.count}" #puts out 1
line_items.map do |li|
if li.update_attribute(:fulfillment_status, params[:mass][:fulfillment_status])
n+=1
else
messages << "#{vendor_order.vendor_order_token}"
end
end
puts "line items2: #{line_items.count}" #puts out 0
if n == line_items.count
flash.keep[:notice] = "Update for #{vendor_orders.count} order(s) and #{n} product(s) successful"
else
flash.keep[:notice] = "Failed update for Order: #{messages.join if messages.any?}"
end
puts "line item3 #{line_items.count}" #puts out 0
respond_to do |format|
format.html { redirect_to vendor_orders_path }
end
The question i have is about the puts
When calling line_items.count after I update the line_items to then not match the variable, does it recall line_items from above?
Is this true? I always assumed once something was defined and passed, it would stay at the rate, unless redefined.

Marek solved the issue by letting me know .count is a call to the database which will then call what you defined previously.
To solve this, I just use line_items_count = line_items.count and use that above any alterations to check against it.

Related

undefined method `<<' for #<Answer::ActiveRecord_Relation:0x007fada31c7430>

Hi I create a controller Game to display a Q/A game
And I am blocked with <<, here is the code
def play
lvlup(lvl)
if lvl == 1
set_questions
else
get_questions
end
#answers = Answer.where.not(id: question.answer_id).limit(2).order("RANDOM()")
#answer ||= []
#answers << question.answer
#answers = #answers.shuffle
render 'play'
end
I create an array and I put the related answer in the global answers I want to display 4 Max.
Why does the undefined is here?
Here is the total code
class GamesController < ApplicationController
attr_accessor :lvl
def welcome
end
def congrat
end
def play
lvlup(lvl)
if lvl == 1
set_questions
else
get_questions
end
#answers = Answer.where.not(id: question.answer_id).limit(2).order("RANDOM()")
#answer ||= []
#answers << question.answer
#answers = #answers.shuffle
render 'play'
end
def loose
#question = Question.find(params[:question])
flash.now[:alert] = "Miss..."
render 'loose'
end
def check
#lvl = params[:lvl].to_i
answer_id = params[:id].to_i
question = Question.find(params[:question])
if #lvl == lvlmax
render action: 'congrat' and return
elsif answer_id == question.answer_id
flash.now[:notice] = "Well done !"
play
else answer_id != question.answer_id
loose
end
end
private
def lvlup(value)
#lvl = 1 + value.to_i
end
def lvlmax
#lvlmax = Question.all.count
end
def set_questions
#questionsids = []
Question.all.shuffle.each do |d|
#questionsids << d.id
end
cookies[:questions] = #questionsids
end
def get_questions
#questions = cookies[:questions].split('&')
end
def questions
#questions = cookies[:questions]
end
def question
#question = Question.find(questions[lvl])
end
end
Thank you for your help.
You are trying to append to the #answers result - this is an ActiveRecord relation, you cannot append data to that array.
Add .to_a in the end of your line where you set #answers to allow you to append to the array.
#answers = Answer.where.not(id: question.answer_id).limit(2).order("RANDOM()").to_a
mtrolle's answer might be correct, but I have my doubts as to why ActiveRecord::Relation was not returned as Array by default. (Also as mentioned by BroiStatse in his comment.)
I too noticed the same problem with my local setup however it was attributed to another issue all together. I am sharing this here in case you too happen to use MySQL.
Answer.where.not(id: question.answer_id).limit(2).order("RANDOM()")
returns an ActiveRecord::Relation object. And it translates to following SQL:
SELECT `answers`.* FROM `answers` WHERE (id != ID) ORDER BY RANDOM() LIMIT 2
When I try running the same in MySQL, I get:
ERROR 1305 (42000): FUNCTION database.RANDOM does not exist
Apparently MySQL does not have RANDOM() function, instead it uses RAND().
Converting ActiveRecord query accordingly returned correct Array to me:
Answer.where.not(id: question.answer_id).limit(2).order("RAND()")

Ruby each block next when exception or error is raised

I have the following method in my rake task.
def call
orders = Spree::Order.complete.where('completed_at >= :last_day', last_day: Time.now - 30.days)
orders.each do |order|
order_tracking = order.shipments.first.tracking
next if order_tracking.nil?
shipment = order.shipments.first
results = fedex.track(tracking_number: order_tracking)
tracking_info = results.first
status = tracking_info.status.to_s
delivery_date = tracking_info.delivery_at
shipment.is_delivered = delivered?(status)
shipment.date_delivered = delivery_date
shipment.save
puts "-> Shipping status was updated for #{order.number}"
end
end
If there is an order with no tracking number I skipping it with next on line 5.
My question: How would I do next if a tracking number is invalid and the following error is raised:
Fedex::RateError: Invalid tracking number.
Ideally I would like to change line 5 to:
next if order_tracking.nil? || order_tracking.raised(Fedex::RateError) # something like that
Thank you in advance.
Also RateError is raised here:
def process_request
api_response = self.class.post(api_url, :body => build_xml)
puts api_response if #debug == true
response = parse_response(api_response)
if success?(response)
options = response[:track_reply][:track_details]
if response[:track_reply][:duplicate_waybill].downcase == 'true'
shipments = []
[options].flatten.map do |details|
options = {:tracking_number => #package_id, :uuid => details[:tracking_number_unique_identifier]}
shipments << Request::TrackingInformation.new(#credentials, options).process_request
end
shipments.flatten
else
[options].flatten.map do |details|
Fedex::TrackingInformation.new(details)
end
end
else
error_message = if response[:track_reply]
response[:track_reply][:notifications][:message]
else
"#{api_response["Fault"]["detail"]["fault"]["reason"]}\n--#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
end rescue $1
raise RateError, error_message
end
end
added:
private
def fedex_track(tracking)
fedex.track(tracking_number: tracking)
end
And changed results on line 7 to:
results = fedex_track(order_tracking) rescue next

Cant found model with out an ID in rails 3.2.12

i ve this method. I m not at all able to understand the error which is
Couldn't find Company without an ID
in ActiveRecord::RecordNotFound in CustomersController#bulk_create
This method is written to create customers for a company in bulk by taking their name and numbers in format name:number.
The method is as follows:
def bulk_create
res = ""
comp_id = params[:customer][:selected_companies].delete_if{|a| a.blank?}.first
comp = Company.find(comp_id)
s = SentSmsMessage.new
s.set_defaults
s.data = tmpl("command_signup_ok", customer, comp) unless params[:customer][:email].length > 0
s.data = params[:customer][:email] if params[:customer][:email].length > 0
s.company = comp if !comp.nil?
s.save
unless comp_id.blank?
params[:customer][:name].lines.each do |line|
(name, phone) = line.split(/\t/) unless line.include?(":")
(name, phone) = line.split(":") if line.include?(":")
phone = phone.gsub("\"", "")
phone = phone.strip if phone.strip.to_i > 0
name = name.gsub("\"", "")
name = name.gsub("+", "")
phone = "47#{phone}" if params[:customer][:active].to_i == 1
customer = Customer.first(:conditions => ["phone_number = ?", phone])
if customer.nil?
customer = Customer.new
customer.name = name
# customer.email
# customer.login
# customer.password
customer.accepted_agreement = DateTime.now
customer.phone_number = phone
customer.active = true
customer.accepted_agreement = DateTime.now
customer.max_msg_week = params[:customer][:max_msg_week]
customer.max_msg_day = params[:customer][:max_msg_day]
customer.selected_companies = params[:customer][:selected_companies].delete_if{|a| a.blank?}
res += "#{name} - #{phone}: Create OK<br />" if customer.save
res += "#{name} - #{phone}: Create failed<br />" unless customer.save
else
params[:customer][:selected_companies].each do |cid|
new_company = Company.find(cid) unless cid.blank?
if !new_company.nil?
if !customer.companies.include?(new_company)
customer.companies << new_company
if customer.save
res += "#{name} - #{phone}: Customer exists and the customer was added to the firm #{new_company.name}<br />"
else
res += "#{name} - #{phone}: Customer exist, but something went wrong during storage. Check if the client is in the firm.<br />"
end
else
res += "#{name} - #{phone}: Customer exists and is already on firm #{new_company.name}<br />"
end
end
end
end
s.sms_recipients.create(:phone_number => customer.phone_number)
end
s.save
s.send_as_sms
#result = res
respond_to do |format|
format.html { render "bulk_create"}
end
else
#result = "You have not selected any firm to add these users. Press the back button and try again."
respond_to do |format|
format.html { render "bulk_create"}
end
end
end
I want to update one situation here. That when i submit the form blank then it gives this error. Also if i filled the form with the values then its show the situation which the method is returning in case of fail.
res += "#{name} - #{phone}: Create failed <br />"
The tmpl method
private
def tmpl(setting_name, customer, company = nil)
text = ""
if customer.companies.count > 0
sn = "#{setting_name}_#{#customer.companies.first.company_category.suffix}".downcase rescue setting_name
text = Setting.value_by(sn) rescue ""
end
textlenth = text.length rescue 0
if textlenth < 3
text = Setting.value_by(setting_name) rescue Setting.value_by("command_error")
end
return fill_template(text, customer, company)
end
From the model customer.rb
def selected_companies=(cmps)
cmps.delete("")
# Check the old ones. Make a note if they are not in the list. If the existing ones are not in the new list, just remove them
self.companies.each do |c|
self.offer_subscriptions.find(:first, ["customer_id = ?", c]).destroy unless cmps.include? c.id.to_s
cmps.delete c.id.to_s if cmps.include? c.id.to_s
end
# Then create the new ones
cmps.each do |c2|
cmp = Company.find(:first, ["id = ?", c2])
if cmp && !c2.blank?
offerSubs = offer_subscriptions.new
offerSubs.company_id = c2
offerSubs.save
end
end
end
def selected_companies
return self.companies.collect{|c| c.id}
end
The association of customer is as follows:
has_many :offer_subscriptions
has_many :companies, :through => :offer_subscriptions
This code is written by the some one else. I m trying to understand this method but so far not being able to understand this code.
Please help.
Thanks in advance.
You are getting 'Couldn't find Company without an ID' error because your Company table doesn't contain record with id = comp_id
Change comp = Company.find(comp_id) to comp = Company.find_by_id(comp_id).
This will return nil instead of an error.
Add comp is not nil condition is already handled in your code.
Your comp_id line is returning nil.
comp_id = params[:customer][:selected_companies].delete_if{|a| a.blank?}.first
Post the params that get passed to this function and we could hopefully find out why. In the meantime you could enclose the block in a begin - rescue block to catch these errors:
begin
<all your code>
rescue ActiveRecord::RecordNotFound
return 'Unable to find a matching record'
end
try this:
comp = ""
comp = Company.find(comp_id) unless comp_id.nil?
instead of comp = Company.find(comp_id)
further nil checking present in your code.
Reason being
params[:customer][:selected_companies].delete_if{|a| a.blank?} = []
so [].first = nil
therefor, params[:customer][:selected_companies].delete_if{|a| a.blank?}.first = nil
and comp_id is nil
So check the log file and check what is coming in the parameter "selected_companies"
when you will find the parameter, everything will be understood well....

Rspec instance variables and controller testing

I'm writing a bowling score calculator, and I'm trying to set up RSpec tests, but for some reason I can't get my tests to work correctly.
players_controller_spec.rb:
require 'spec_helper'
describe PlayersController do
let(:player_names) { ["player1",
"player2",
"player3",
"player4"] }
describe "POST bowl" do
before(:each) do
#game = Game.create!
player_names.each do |name|
Player.create!(:name => name)
end
#game.players = Player.all
Player.all.each do |player|
(0..9).each do |number|
player.frames << Frame.create(:number => number)
end
end
end
describe "for the player's third bowl" do
before(:each) do
#game.players[#game.current_player].frames[9].update_attributes({:number => 9, :first_bowl => "X", :second_bowl => "X", :score => 20})
#game.update_attributes({:current_player => 0, :current_frame => 9})
end
describe "if the bowl is a number score" do
before(:each) do
post :bowl, {:score => "5", :id => #game.id}
end
it "should update the player's score" do
#game.players[#game.current_player].frames[#game.current_frame].score.should == 25
end
end
end
end
end
players_controller.rb
def bowl
#game = Game.find(params[:id])
#score = params[:score]
#current_player = #game.current_player
#current_frame = #game.current_frame
#player = #game.players[#current_player]
#frame = #player.frames[#current_frame]
if #frame.first_bowl.nil?
#frame.first_bowl = #score
if #score == "/"
raise "Error"
end
if #score == "X" && #frame.number == 9
#frame.bonus = 2
end
#frame.score = (/\A[0-9]\z/ === #score ? #score.to_i : 10)
elsif #frame.second_bowl.nil?
#frame.second_bowl = #score
if #frame.score + #score.to_i > 10
raise "Error"
end
if #score == "X"
if #frame.number != 9 || (#frame.number == 9 && #frame.first_bowl != "X") # can't be a spare has to be number or strike
raise "Error"
end
end
if #score == "/" && #frame.number == 9
#frame.bonus = 1
end
if /\A[0-9]\z/ === #score
#frame.score += #score.to_i
elsif #score == "/"
#frame.score = 10
elsif #score == "X"
#frame.score = 20
end
elsif #frame.third_bowl.nil?
#frame.third_bowl = #score
if #frame.number != 9
raise "Error"
end
#frame.bonus = nil
#frame.update_attributes({:score => (/\A[0-9]\z/ === #score ? #frame.score + #score.to_i : #frame.score + 10)})
else
raise "Error"
end
#frame.save
if #game.current_frame > 0
#prev_frame = #player.frames[#frame.number-1]
if #prev_frame.nil?
#prev_frame = Frame.create(:number => #game.current_frame-1)
#player.frames << #prev_frame
#player.frames = #player.frames.sort_by { |f| f.number }
end
update_scores
end
The spec in question is players_controller_spec.rb and at the start of the tests I'm creating a new game with 4 players and each player with 10 frames. Before each test, I'm setting a certain frame's values to be fit what I'm trying to test. The test above is an example where I want to make sure that bowling a score of 5 on the third bowl on the last frame correctly updates the score. But, even though in the debugger I see that the score is updated in the frame (when I debug in the controller method), once I return to the Rspec test, it doesn't work. It expects 25 but gets nil. Is there something I'm missing about how instance variables are transferred between specs and controllers?
So first off there is no 'transferring'. The controller and the example are 2 completely independent objects, each with their own instance variables (You can use the assigns spec helper to retrieve the value of a controller instance variable though).
That's not the root cause of your issue. You do, even before the controller executes, have an #game instance variable that is the game you are interested in. However with activerecord, every time you do Game.find you'll receive separate ruby objects (corresponding to the same database row). Once the row has been loaded from the database it doesn't notice changes made to the database behind its back.
You can reload the object with #game.reload
As a side note this sort of stuff is easier to work with if most of that logic was pushed down into one of your models rather than sitting in the controller.

Rails - Fetch results on the basis of number of params in query string

I am working on an events application where i want to filter events depending on the 3 parameters location or starts_at or ends_at in the query string. There can be any one, two or all the parameters in the query string. In i use if-else statement i need to make 6 cases which will make my code clumsy. Rather i am thinking to implement something this way:
class EventsController < ApplicationController
def index
unless params.empty?
unless params[:location].nil?
#events = Event.where("location = ?", params[:location])
end
unless params[:starts_at].nil?
unless #events.empty?
#events = #events.where("start_date = ?", params[:start_date])
else
#events = Event.where("Date(starts_at) = Date(?)", params[:starts_at])
end
end
unless params[:ends_at].nil?
unless #events.empty?
#events = #events.where("end_date = ?", params[:end_date])
else
#events = Event.where("Date(ends_at) = Date(?)", params[:ends_at])
end
end
end
end
end
But this code doesnt work since where query doen not work on an array. Can someone suggest me some solution for this..
You should be able to pass your params hash directly to where, and it will form the correct SQL based on the keys and values of that hash:
Event.where(params)
An example in the console:
1.9.3p194 :001 > puts Example.where(:location => 'here', :started_at => '2012-08-13').to_sql
SELECT "examples".* FROM "examples" WHERE "examples"."location" = 'here' AND "examples"."started_at" = '2012-08-13'
Try Following
def index
unless params.empty?
where_array, arr = [], []
if params[:location]
where_array << "location = ?"
arr << params[:location]
end
if params[:starts_at]
where_array << "start_date = ?"
arr << params[:starts_at]
end
if params[:ends_at]
where_array << "end_date = ?"
arr << params[:ends_at]
end
#events = arr.blank? ? [] : Event.where([where_array.join(" AND "), *arr])
end
end

Resources