Rspec instance variables and controller testing - ruby-on-rails

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.

Related

How does recalling a variable work in a method?

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.

Rails devise after_sign_in first time, check attribute and update another user one time

After a user's first sign in, I want to see if another user referred them by the referral_code_used column, and increase that user's referral_count + 1. Currently, a user is logged in and is taken to home_path
I have tried writing an after_sign_in_path_for(resource) method. This gets the job done, with respect to the referral count. The issue is this takes the user to users/:id path instead of home.
def after_sign_in_path_for(resource)
if resource.sign_in_count > 1
puts "sign in count is greater than 1"
elsif resource.sign_in_count === 1
puts "sign in count is 1"
if resource.referral_code_used != nil
referral_code = resource.referral_code.strip
length = referral_code.length
referral_code = referral_code[5..length]
#user_referral_code = referral_code.to_i
if User.exists?(id: #user_referral_code)
referral_user = User.find_by_id(#user_referral_code)
referral_user.referral_count = referral_user.referral_count + 1
referral_user.save
end
end
puts "signin count = 1"
end
#redirect_to home_path
end
When I put in a redirect_to home_path, it says Render and/or redirect were called multiple times in this action. I'm guessing this is in the new sessions controller, which is handled by devise, but am unsure about that.
When I put it in the application controller, like below, there are no routing issues, but it calls it multiple times and adds more than 1 to referral_count:
before_action :check_referral_code
def check_referral_code
if user_signed_in?
if current_user.sign_in_count > 1
puts "sign in count is greater than 1"
elsif current_user.sign_in_count === 1
puts "sign in count is 1"
if current_user.referral_code_used != nil
referral_code = current_user.referral_code_used.strip
length = referral_code.length
referral_code = referral_code[5..length]
#user_referral_code = referral_code.to_i
if User.exists?(id: #user_referral_code)
referral_user = User.find_by_id(#user_referral_code)
referral_user.referral_count = referral_user.referral_count + 1
referral_user.save
end
end
puts "signin count = 1"
end
end
end
relevant routes:
devise_for :users, controllers: { registrations: "users/registrations" }
resources :users
get 'profile/home', to: 'profile#home', as: 'home'
What is the correct way to check the referral_code_used on first sign in, and credit the other user one time? Thanks!
try replacing redirect_to home_path with home_path
def after_sign_in_path_for(resource)
if resource.sign_in_count > 1
puts "sign in count is greater than 1"
elsif resource.sign_in_count === 1
puts "sign in count is 1"
if resource.referral_code_used != nil
referral_code = resource.referral_code.strip
length = referral_code.length
referral_code = referral_code[5..length]
#user_referral_code = referral_code.to_i
if User.exists?(id: #user_referral_code)
referral_user = User.find_by_id(#user_referral_code)
referral_user.referral_count = referral_user.referral_count + 1
referral_user.save
end
end
puts "signin count = 1"
end
home_path
end

Storing associations in variables

Hey I am trying to make a method polymorphic so that it can access different models associations, but treat them the same
module Unitable
# Adds new units without duplication
def add_units(type, units = {})
if type.downcase == "army"
relationships = self.members
elsif type.downcase == "character"
relationships = self.owns
end
units.each do |name, amount|
unit = Unit.find_by(name: name)
if relationships.where(unit_id: unit.id).first.nil?
relationships.create(unit_id: unit.id, amount: amount) #<--- This is where the error occurs
else
relationship = relationships.find_by(unit_id: unit.id)
original_amount = relationship.amount
new_amount = amount + original_amount
relationship.update_attribute(:amount, new_amount)
end
end
end
end
This throws
ERROR: current transaction is aborted, commands ignored until end of transaction block
This is the root method
def gen_starting_units
credit = 9
#transaction do
units = Unit.tagged_with(["#{race}", "1"]).sample(2)
while credit > 0
unit = units.sample
if unit.cost <= credit
self.army.add_units("army", unit.name => 1)
credit -= unit.cost
end
end
#end
end

Increase speed of Insert/Update/Delete using ActiveRecord / Rails

I'm iterating over a collection of updates (which include additions, updates and deletions). I'm currently looping out over these updates, checking to see whether it exists. If it does, updating the information. If it doesn't, creating a record.
This seems to be taking an unpleasurable amount of time. I'd say 1 every 2 seconds, maybe slightly faster. But I've got 60,000 left to process and it's going to take 109 hours (give or take) to complete. Which isn't acceptable.
Is there anyway to perform this more efficiently?
Update.all.each do |u|
product = Product.find(:all, :conditions => ["product_codes = ?", u.product_codes])
if u.update_type == "Delete"
daap = DeletedProduct.new
daap.product_codes = u.product_codes
daap.date_deleted = timestamp
daap.save
if product.count == 1
product.first.destroy
puts "Product #{ u.product_codes } deleted"
u.destroy
#delete = #delete + 1
elsif product.count > 1
product.each do |pro|
pro.destroy
puts "Product #{ u.product_codes } deleted (UPDATE #{ u.id})"
#delete = #delete + 1
end
u.destroy
else
#notfound.push(u.product_codes)
u.destroy
#delete = #delete + 1
end
elsif u.update_type == "AddOrUpdate"
if product.count == 1
p = Product.first
p.data = u.data
p.last_updated = timestamp
p.save
puts "Product Updated #{ p.product_codes }"
u.destroy
#update = #update + 1
else
p = Product.new
p.product_codes = u.product_codes
p.data = u.data
p.last_updated = timestamp
p.save
puts "Product Added #{ p.product_codes }"
u.destroy
#delete = #delete + 1
end
end
end
EDIT: It's the product lookup that takes so long. Is there anyway to speed up this part of the process? 'product = Product.find(...'

Ruby on rails rake task not returning up-to-date model info

While building a billing system I encountered the following problem:
In scheduler.rake :
#users.each do |user|
begin
puts "CALCULATING COSTS =========="
costs = user.total_billable_price_per_month(Time.now)
lead_count = user.total_billable_leads_count(Time.now)
click_count = user.total_billable_clicks_count(Time.now)
puts "CREATING NEW INVOICES ======="
#invoice = user.invoices.new
# if user.free_credits
# invoice.total_price_in_cents = 0
# else
# invoice.total_price_in_cents = costs.cents
# end
#invoice.total_leads = lead_count
#invoice.total_clicks = click_count
#invoice.total_price_in_cents = costs.cents
# Als de vorige factuur onder de 10 euro was, dan geld bij huidige factuur optellen
if user.invoices.last && user.invoices.last.total_price_in_cents < 1000
puts "Bedrag onder 10 euro"
puts "Last invoice = #{user.invoices.last.total_price_in_cents}"
#invoice.total_price_in_cents += user.invoices.last.total_price_in_cents
end
puts "SAVING INVOICE FOR #{user.name} with ID = #{user.id}"
# Factuur saven
#invoice.save
#Als de factuur hoger of gelijk is als 10euro, dan factuur aanmaken
if #invoice.total_price_in_cents >= 1000
#Moneybird factuur versturen
puts "COSTS ARE HIGHER THAN 10 EURO, CREATING MONEYBIRD INVOICE"
moneybird_invoice = MoneybirdInvoice.new
sleep(15)
moneybird_invoice.contact_id = MoneybirdContact.find_by_customer_id(user.id).id
sleep(15)
moneybird_invoice.details_attributes = [
{ :description => "Aantal leads", :amount => lead_count, :price => user.lead_price.cents.to_f/100},
{ :description => "Aantal clicks", :amount => click_count, :price => user.click_price.cents.to_f/100}
]
puts "TRYING TO SAVE MONEYBIRD INVOICE"
if moneybird_invoice.save && moneybird_invoice.put(:send_invoice)
puts "SUCCESFULLY SAVED INVOICE"
#GET UPDATED PAY URL
#sent_mb_invoice = MoneybirdInvoice.get(moneybird_invoice.id)
sleep(15)
#invoice.update_attributes(:moneybird_invoice_id => #sent_mb_invoice['invoice_id'], :moneybird_pay_url => #sent_mb_invoice['pay_url'])
else
puts "MONEYBIRD INVOICE FAILED"
puts moneybird_invoice.errors.inspect
end
else
# GEEN MONEYBIRD FACTUUR AANMAKEN
end
rescue
puts "ER IS IETS FOUT GEGAAN MET FACTUREREN USER ID = #{user.id} & NAME = #{user.name}"
puts #invoice.errors.inspect
end
end
This piece of code should constantly increment each time the rake task is run, except when the total amount reaches > 1000.
if user.invoices.last && user.invoices.last.total_price_in_cents < 1000
puts "Bedrag onder 10 euro"
puts "Last invoice = #{user.invoices.last.total_price_in_cents}"
#invoice.total_price_in_cents += user.invoices.last.total_price_in_cents
end
The code above always puts "Last invoice = 100" => This should increment each time the rake tasks is run
Every new invoice still has the same total_price_in_cents (when I'm expecting that it should increment).
What is going on ?
EDIT: Added after code upadte:
In your updated code, it looks like you were calling user.invoices.last after you called user.invoices.new, this is why it always returned the same value.
Create a variable #last_invoice = user.invoices.last before call user.invoices.new.
ORIGINAL ANSWER:
In your original code posting, it looks like your save call on #invoice happened outside the loop -- I believe you're only saving it once.
task :create_invoices => :environment do
# Begin the loop for each use
User.all.each do |user|
#invoice = user.invoices.build
#If last bill < 1000
if user.invoices.last && user.invoices.last.total_price_in_cents < 1000
puts "Last invoice = #{user.invoices.last.total_price_in_cents}"
#invoice.total_price_in_cents += user.invoices.last.total_price_in_cents
#invoice.save
end # end 'if' statement
end # end loop for all users
end # end task definition
So you loop though the users table, but never save updates -- except for the very last time after you exit the loop

Resources