Controller can't find object created by worker - ruby-on-rails

Spent all day trying to fix this..
Basically the controller is calling worker to perform the creation of "Balance". The worker performs with success and the record is created but when it returns to the controller it can't find the object created. How to force it looking for the "updated database" records ?
Controller:
balance = BalanceCreator.perform_async(userx.id, market.id)
20.times do
status = Sidekiq::Status::get balance, :exp_status
if ["done"].include?(status)
break
end
sleep(0.2)
end
statux = Sidekiq::Status::get balance, :exp_status
puts statux
# Shows correct status
exp_id = Sidekiq::Status::get balance, :exp_id
puts exp_id
# Shows correct id
user_exp = Balance.find(exp_id)
# Error - WARN: Couldn't find Balance with 'id'=2
Worker
class BalanceCreator
def perform(user, market)
balance = Balance.find_or_create_by(user: user, market: market)
puts balance.id
# Correct id displays
store exp_status: "done"
exp_status = retrieve :exp_status
store exp_id: exposure.id
exp_id = retrieve :exp_id
end
end

Related

How find the distance between two objects?

Im using geocode. The idea is our partners can post products with an address. When they do so it fetches the latitude and longitude. Now when our customers go to buy that product they have to enter in a delivery address to tell us where to deliver the product. However if they delivery address is not within 20 miles of the product they are not allowed to get the product delivered.
Im getting an error message saying this "undefined method `latitude' for nil:NilClass"
Like I said the product.longitude, product.latitude is already set when the users are trying to order.
Not sure if it's because the order.delivery_address(lat, long) is not submitted into the database yet and its trying to check the distance. Here my code below
So My question is how can is how can i find the distance between the product address and order address and I want to show a alert message to the user if the distance between the two is over 20 miles.
def create
product = Product.find(params[:product_id])
if current_user == product.user
flash[:alert] = "You cannot purchase your own property"
elsif current_user.stripe_id.blank? || current_user.phone_number.blank?
flash[:alert] = " Please update your payment method and verify phone number please"
return redirect_to payment_method_path
elsif Geocoder::Calculations.distance_between([product.latitude, product.longitude], [#order.latitude, #order.longitude]) < 20
flash[:alert] = "The delivery address you provided is outside the delivery zone. Please choose a different product."
else
quantity = order_params[:quantity].to_i
#order = current_user.orders.build(order_params)
#order.product = product
#order.price = product.price
#order.total = product.price * quantity + product.delivery_price
# #order.save
if #order.Waiting!
if product.Request?
flash[:notice] = "Request sent successfully... Sit back and relax while our licensed dispensary fulfil your order :)"
else
#order.Approved!
flash[:notice] = "Your order is being filled and it will delivered shortly:)"
end
else
flash[:alert] = "Our licensed dispensary cannot fulfil your order at this time :( "
end
end
redirect_to product
end
You set #order in the following line:
#order = current_user.orders.build(order_params)
But you try to call its longitude and latitude methods above this, before you even set #order variable. To simply fix this problem, you can move this line up, it can even be located at the beginning of create method, since it doesn't depend on product or anything like that:
def create
#order = current_user.orders.build(order_params)
# ...
end
Although, there are number of problems in your code, like method names starting with capital letters (you can do it, but you shouldn't, it's against the convention) or overall complexity of the method.
You should move the business logic to the model where it belongs.
So lets start by creating a validation for the product distance:
class Order < ApplicationRecord
validates :product_is_within_range,
if: -> { product.present? } # prevents nil errors
# our custom validation method
def product_is_within_range
errors.add(:base, "The delivery address you provided is outside the delivery zone. Please choose a different product.") if product_distance < 20
end
def product_distance
Geocoder::Calculations.distance_between(product.coordinates, self.coordinates)
end
end
Then move the calculation of the total into the model:
class Order < ApplicationRecord
before_validation :calculate_total!, if: ->{ product && total.nil? }
def calculate_total!
self.total = product.price * self.quantity + product.delivery_price
end
end
But then you still have to deal with the fact that the controller is very broken. For example:
if current_user == product.user
flash[:alert] = "You cannot purchase your own property"
Should cause the method to bail. You´re not actually saving the record either. I would start over. Write failing tests for the different possible conditions (invalid parameters, valid parameters, user is owner etc) then write your controller code. Make sure you test each and every code branch.

Rails - how to set default values in a model for quantity

I'm trying to allow a User to book events for more than one space at a time, so if one space at an event costs £10 and a User wants to book four spaces then they would need to pay £40.
I've implemented a method in my Booking model to cater for this -
Booking.rb
class Booking < ActiveRecord::Base
belongs_to :event
belongs_to :user
def reserve
# Don't process this booking if it isn't valid
return unless valid?
# We can always set this, even for free events because their price will be 0.
self.total_amount = quantity * event.price_pennies
# Free events don't need to do anything special
if event.is_free?
save
# Paid events should charge the customer's card
else
begin
charge = Stripe::Charge.create(amount: total_amount, currency: "gbp", card: #booking.stripe_token, description: "Booking number #{#booking.id}", items: [{quantity: #booking.quantity}])
self.stripe_charge_id = charge.id
save
rescue Stripe::CardError => e
errors.add(:base, e.message)
false
end
end
end
end
When I try to process a booking I get the following error -
NoMethodError in BookingsController#create
undefined method `*' for nil:NilClass
This line of code is being highlighted -
self.total_amount = quantity * event.price_pennies
I need to check/make sure that quantity returns a value of 1 or more and event.price_pennies returns 0 if it is a free event and greater than 0 if it is a paid event. How do I do this?
I did not set any default values for quantity in my migrations. My schema.rb file shows this for price_pennies -
t.integer "price_pennies", default: 0, null: false
This is whats in my controller for create -
bookings_controller.rb
def create
# actually process the booking
#event = Event.find(params[:event_id])
#booking = #event.bookings.new(booking_params)
#booking.user = current_user
if #booking.reserve
flash[:success] = "Your place on our event has been booked"
redirect_to event_path(#event)
else
flash[:error] = "Booking unsuccessful"
render "new"
end
end
So, do I need a method in my booking model to rectify this or should I do a validation for quantity and a before_save callback for event?
I'm not quite sure how to do this so any assistance would be appreciated.
Just cast to integer, in this case you seem to be done:
self.total_amount = quantity.to_i * event.price_pennies.to_i
Migrations are used to modify the structure of you DB and not the data.
In your case I think you need to seed the DB with default values, and for that purpose you use 'db/seeds.rb' file which is invoked once every time your application is deployed.
You would do something like this in seeds.rb
Booking.find_or_create_by_name('my_booking', quantity:1)
So when the application is deployed the above line of code is executed. If 'my_booking' exists in the table then nothing happens, else it will create a new record with name='my_booking' and quantity=1.
In your localhost you'll execute 'rake db:seed' to seed the DB.

Setting up Twilio client

I'm accessing the Twilio API to send out daily messages to all users in my database. The message should be the same for everyone and is coming from an array of messages I've put together. I'm using the Heroku Scheduler to trigger a rake task I built that will run the task at a certain hour every day. However, because of how my Twilio client model is set up, a new instance of the model is created every time the task is run, therefore always sending the first message in the array (rather than going through the array consecutively as it's set up to do). Does anyone know of a way to rearrange or recode my model or rake task so this doesn't happen? It should ideally run itself.
twilio_client.rb model:
class TwilioClient
require 'twilio-ruby'
def initialize
#client = Twilio::REST::Client.new ENV["TWILIO_ACCOUNT_SID"], ENV["TWILIO_AUTH_TOKEN"]
#index = 0
end
def notify
all_phone_numbers.each do |number|
#client.account.messages.create(
from: '+1401XXXXXXX',
to: number,
body: daily_text
)
end
end
private
def daily_text
#message = [
"1", "2", "3"
]
#index += 1
if #index >= #message.length
#index = 0
end
#message[#index]
end
def all_phone_numbers
User.pluck(:phone_number)
end
end
rake task:
desc "This task is called by the Heroku scheduler add-on"
task notify: :environment do
client = TwilioClient.new
client.notify
end

Rake task - undefined method

I tinkering my way into creating a rake task that grabs the amount of checkins for a given page throw facebook-graph. I usign the koala gem and rails.
I do this by creating a rake task:
task :get_likes => :environment do
require 'koala'
# Grab the first user in the database
user = User.first
# Loop throw every school & and call count_checkins
School.columns.each do |column|
user.facebook.count_checkins(column.name, user)
end
end
# Count like for every school else return 0
def count_checkins(name, u)
a = u.facebook.fql_query('SELECT checkins FROM page WHERE name = "' + name + '"')
if a[0].nil?
return 0
else
return b = a[0]["checkins"]
end
end
# Initialize an connection to the facebook graph
def facebook
#facebook ||= Koala::Facebook::API.new(oauth_token)
end
But I get a error:
private method `count_checkins' called for #<Koala::Facebook::API:0x007fae5bd348f0>
Any ideas or better way to code a rake task would be awesome!
Check the full error here: https://gist.github.com/shuma/4949213
Can't really format this properly in a comment, so I'll put it in an answer. I would put the following into the User model:
# Count like for every school else return 0
def count_checkins(name)
a = self.facebook.fql_query('SELECT checkins FROM page WHERE name = "' + name + '"')
if a[0].nil?
return 0
else
return b = a[0]["checkins"]
end
end
# Initialize an connection to the facebook graph
def facebook
#facebook ||= Koala::Facebook::API.new(oauth_token)
end
Then change the rake task to:
task :get_likes => :environment do
require 'koala'
# Grab the first user in the database
user = User.first
# Loop throw every school & and call count_checkins
School.columns.each do |column|
user.count_checkins(column.name)
end
end
That way count_checkins is defined on the user model, rather than trying to modify a class within Koala -- and you aren't duplicating work by having to pass around more User and Facebook parameters than are necessary.

Rails test involving an object that requires an api update

I am trying to get a Bill object to perform tests on. This is a US congress bill, which I have in xml via rsync on a data directory. My code takes in the name of the bill, say "h1.xml", parses the xml and then gets the full text of the bill from www.govtrack.us. So, in my main app, to create a bill
Get a bill name (e.g. h1) (via globbing in the method def self.update_from_directory)
def self.update_from_directory
Dir.glob("#{Rails.root}/data/bills/small_set/*.xml").each do |bill_path|
bill_name = bill_path.match(/.*\/(.*).xml$/)[1]
b = Bill.find_or_create_by(:govtrack_name => bill_name)
b.update_bill
b.save!
end
end
Update the bill
def update_bill
file_data = File.new("#{Rails.root}/data/bills/#{self.govtrack_name}.xml", 'r')
bill = Feedzirra::Parser::GovTrackBill.parse(file_data)
if bill && (self.introduced_date.nil? || (bill.introduced_date.to_date > self.introduced_date))
self.congress = bill.congress
self.bill_type = bill.bill_type
self.bill_number = bill.bill_number
... and so on . . . until:
get_bill_text
Update the bill record with the bill text
def get_bill_text
bill_object = HTTParty.get("#{GOVTRACK_URL}data/us/bills.text/#{self.congress.to_s}/#{self.bill_type}/#{self.bill_type + self.bill_number.to_s}.html")
self.bill_html = bill_object.response.body
self.text_updated_on = Date.today
Rails.logger.info "Updated Bill Text for #{self.ident}"
end
My goal is very simple, I want to mock a whole bill for a test:
def setup
#my_test_bill = Bill.new(:govtrack_id => "h1")
#my_test_bill.update_bill
end
I am trying to get webmock and vcr working, but all the examples I can find, provide a way to mock a specific call and I don't want to have to retype a whole new update_bill method.
Any thoughts greatly appreciated.
Tim
Consider changing your update_bill method to:
def update_bill
file_data = File.new("#{Rails.root}/data/bills/#{self.govtrack_name}.xml", 'r')
bill = Feedzirra::Parser::GovTrackBill.parse(file_data)
if bill && (self.introduced_date.nil? || (bill.introduced_date.to_date > self.introduced_date))
self.congress = bill.congress
self.bill_type = bill.bill_type
self.bill_number = bill.bill_number
# Yield to a block that can perform arbitrary calls on this bill
if block_given?
yield(self)
end
# Fill bill text if empty
if bill_html.blank? && text_updated_on.blank?
get_bill_text
end
end
Then change your setup method to:
def setup
#my_test_bill = Bill.new(:govtrack_id => "h1")
#my_test_bill.update_bill do |bill|
bill.text_updated_on = Date.today
bill.bill_html = "The mock bill contents"
end
end
That may not be exactly the solution, but this kind of approach--passing the receiver back to a block given to the method, allows you to modify the exact behavior of a given method at runtime.

Resources