Ok, after almost two hours of searching the web and not finding an answer to my question, I will ask it here.
I have this method in my
class MatchesController < ApplicationController
def import
if Match.find_by_match_id(params[:match_id])
redirect_to matches_url, notice: "Match already imported."
else
Match.import(params[:match_id], params[:league_id])
# error handling here?
redirect_to matches_url, :flash => { :success => "Match imported successfully." }
end
end
in my
class Match < ActiveRecord::Base
def self.import(match,league)
begin
transaction do
uri = URI('https://api.steampowered.com/IDOTA2Match_570/GetMatchDetails/V001/')
params = { :match_id => match,
:key => "MY KEY" }
uri.query = URI.encode_www_form(params)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri.request_uri)
resp = http.request(request)
data = resp.body
result = JSON.parse(data)
if result['result']['error']
errors[:base] << "No Match with given ID found"
#self.errors.add_to_base("No Match with given ID found")
break
end
match = result['result']
players = match['players']
pickban = match['picks_bans']
leagueid = match['leagueid']
if league != leagueid
errors.add_to_base("This Match doesn't belong to the selected League")
break
end
end
rescue ActiveRecord::RecordInvalid => invalid
# not yet implemented
end
end
Now when I import a match and it is not found I want to add an error that I can display to the user, but I get this error
undefined local variable or method `errors' for #<Class:0x007fde27cba478>
What am I doing wrong here? And how can I display the error via a flashto the user?
#errors is an instance method, and you're calling it on the Match class. You could write the import method to have a return value of either 'Match imported successfully' or an error message.
Related
I have a rails worker using redis/sidekiq where I send some data to an API (Active Campaign), so I normally use all the http configurations to send data. I want to have it nice and clean, so it's part of a refactor thing. My worker currently looks like this:
class UpdateLeadIdWorker
include Sidekiq::Worker
BASE_URL = Rails.application.credentials.dig(:active_campaign, :url)
private_constant :BASE_URL
API_KEY = Rails.application.credentials.dig(:active_campaign, :key)
private_constant :API_KEY
def perform(ac_id, current_user_id)
lead = Lead.where(user_id: current_user_id).last
url = URI("#{BASE_URL}/api/3/contacts/#{ac_id}") #<--- need this endpoint
https = bindable_lead_client.assign(url)
pr = post_request.assign(url)
case lead.quote_type
when 'renter'
data = { contact: { fieldValues: [{ field: '5', value: lead.lead_id }] } }
when 'home'
data = { contact: { fieldValues: [{ field: '4', value: lead.lead_id }] } }
when 'auto'
data = { contact: { fieldValues: [{ field: '3', value: lead.lead_id }] } }
else
raise 'Invalid quote type'
end
pr.body = JSON.dump(data)
response = JSON.parse(https.request(pr).read_body).symbolize_keys
if response.code == '200'
Rails.logger.info "Successfully updated contact #{ac_id} with lead id #{lead.lead_id}"
else
raise "Error creating contact: #{response.body}"
end
end
def bindable_lead_client
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http
end
def post_request
post_request_ = Net::HTTP::Put.new(url)
post_request_['Accept'] = 'application/json'
post_request_['Content-Type'] = 'application/json'
post_request_['api-token'] = API_KEY
post_request_
end
end
But whenever I run this I get:
2022-07-28T00:52:08.683Z pid=24178 tid=1s1u WARN: NameError: undefined local variable or method `url' for #<UpdateLeadIdWorker:0x00007fc713442be0 #jid="e2b9ddb6d5f4b8aecffa4d8b">
Did you mean? URI
I don't want everything stuck in one method. How could I achieve to make this cleaner?
Thanks.
Pure ruby wise, The reason you get the error is because your method definition bindable_lead_client is missing the url argument. Hence undefined variable.
So def should look something like:
def bindable_lead_client (url)
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http
end
and call:
bindable_lead_client(url)
As for how to make this code better, falls under question being too subjective under StackOverflow guidelines, which encourage you to ask more specific questions.
I am trying save hashes to my database but before I want check request
I am using
require 'net/http'
gem 'http'
This is my controller (hashes I call :hammer)
class PaymentsController < ApplicationController
before_action :logged_in_user, only: [:create]
def create
#payment = current_user.payments.build(payment_params)
aza = ''
uri = URI("https://blockexplorer.com/api/tx/#{:hammer}")
res = Net::HTTP::Post.new(uri)
res1 = res.class.name
aza += Net::HTTP.get(uri)
#go = aza
if aza.include?( '3MGeicHK6P2pUpepsXyTiuA7omMbRZbZx3') #'"addresses":["3MGeicHK6P2pUpepsXyTiuA7omMbRZbZx3"]'
if aza.include? '"value":"0.03072025"'
if aza.include? '"confirmations":0'
flash[:info] = "Wait 15 minutes for confirm"
else
if #payment.save
flash[:success] = "You paid"
redirect_to root_url
else
render 'welcome/index'
end
end
else
flash[:danger] = "You paid less"
end
else
flash[:danger] = "#{res1}"
redirect_to root_url
end
end
def destroy
end
private
def payment_params
params.require(:payment).permit(:hammer)
end
end
When I was try to save it is not check, it is just show error 400
But If I use console it is work
uri = URI("https://blockexplorer.com/api/tx/f484f14ebf9726ab2ab46ffc491786db50fc69ceff737620122e51559a3ea379")
irb(main):003:0> Net::HTTP.get(uri)
I find want can to do
#test = payment_params[:hammer]
# hammer = ''
# hammer += params[:hammer].to_s
aza = ''
uri = URI("https://blockexplorer.com/api/tx/#{#test}")
I think the bug is there:
uri = URI("https://blockexplorer.com/api/tx/#{:hammer}")
in PaymentsController.
Try that instead:
uri = URI("https://blockexplorer.com/api/tx/#{params[:hammer]}")
You missed params[] in your interpolation.
I have a text form to get URL from users.
Controller:
def create
link = Link.new(set_url_params)
if link.save
redirect...
else
flash[:notice] = "error"
end
end
In my model I have a method to add http if a given link does not have it:
before_validation :clean_link
def clean_link
self.url = "http://#{url}" unless self.url =~ /^https?:\/\//
end
Now I'd like to valid the url responds (+ get the final link after redirections) and save it or show an error message:
validate :response?
def response?
begin
self.url = HTTParty.head(self.url).request.last_uri.to_s
rescue SocketError
false
end
end
Example with valid URL:
input: cvcka.cz
clean_link: http://cvcka.cz
respond? https://cvcka.cz
link.valid? True
Invalid URL:
input: dada
clean_link: http://dada
respond?: rescue exception and return false
link.valid? - it returns true instead of false.
Why link.valid? with a string "dada" is true instead of false?
You need to add an entry to the model's errors so that the model's valid? state will be false.
def response?
begin
self.url = HTTParty.head(self.url).request.last_uri.to_s
rescue SocketError
errors.add(:url, "Not a responsive URL")
return false
end
end
I'm writing an application in Rails and I want to get a huge amount of information from an API – which I'm streaming through an Enumerator object as a CSV export. I want to rescue an error that is called within the Enumerator.
CONTROLLER: Enumerator
def csv_lines( url )
Enumerator.new do |y|
per_page = 200
# Parse parameters and get shelf information
_params = BrowseScraper.get_params(url)
shelf = BrowseScraper.get_preso( _params, 0 )
total_items = shelf['response']['total_results']['all'].to_i
total_pages = ( total_items / per_page.to_f ).ceil
shelf_info = BrowseScraper.crawl_ids( shelf['response']['query']['category'] )
y << BrowseScraper.csv_header(url, shelf_info, total_items, ["Tool ID", "Name", "Price", "URL"])
total_pages.times { |i| y << BrowseScraper.csv_body( _params, per_page, i+1) }
end
end
The following functions are raising errors, but I can't catch them outside of the Enumerator:
MODEL: methods
def self.get_params
response = open(url)
raise if response.code != 200
end
CONTROLLER: Display
def export
url = params[:url]
raise StandardError, "Please enter a Browse URL below" if !url || url.empty?
respond_to do |format|
format.csv do
render_csv(url)
end
format.html { render_csv(url) }
end
rescue => e
flash[:error] = e.message
redirect_to scraper_path
end
private
def render_csv( url )
set_file_headers
set_streaming_headers
response.status = 200
# Rails should iterate this enumerator
self.response_body = csv_lines(url)
end
def set_file_headers( name = "browse_export" )
headers["Content-Type"] ||= 'text/csv'
headers["Content-Disposition"] = "attachment; filename=\"#{name}.csv\""
headers["Content-Transfer-Encoding"] = "binary"
headers["Last-Modified"] = Time.now.ctime.to_s
end
def set_streaming_headers
#nginx doc: Setting this to "no" will allow unbuffered responses suitable for Comet and HTTP streaming applications
headers['X-Accel-Buffering'] = 'no'
headers["Cache-Control"] ||= "no-cache"
headers.delete("Content-Length")
end
Rescuing the error raised in export works. Rescuing an error within the Enumerator works (example:
Enumerator do |y|
begin
y << BrowseScraper.get_params(_params)
rescue => e
Rails.logger.error "Failed to get parameters: #{e.message}"
end
end
How can I rescue an exception outside of the Enumerator so I can properly redirect the user with a flash message? How do I pass the exception from within the Enumerator object? What is it about the Enumerator that isn't letting me rescue it with:
def method
Enumerator do |y|
y << BrowseScraper.get_params(_params)
end
rescue => e
Rails.logger.error "Error in Enumerator is #{e.message}"
end
I think I've figured out what's going on here. When you write code in an Enumerator, the block isn't actually executed within the Enumerator. Therefore, if I add a rescue within the Enumerator, it doesn't matter.
This is because the |y| in Enumerator is actually a yielder object which does the yielding (more on that in the Enumerator documentation or the Enumerator::Yielder documentation.
You have to rescue things beforehand.
I have a simple script which checks for bad url's:
def self.check_prod_links
require 'net/http'
results = []
Product.find_each(:conditions =>{:published => 1}) do |product|
url = product.url
id = product.id
uri = URI(url)
begin
response = Net::HTTP.get_response(uri)
rescue
begin
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
rescue
begin
response = Net::HTTP.get_response("http://" + uri)
rescue => e
p "Problem getting url: #{url} Error Message: #{e.message}"
end
end
end
p "Checking URL = #{url}. ID = #{id}. Response Code = #{response.code}"
unless response.code.to_i == 200
product.update_attribute(:published, 0)
results << product
end
end
return results
end
How can I allow incorrectly formatted urls eg: hkbfksrhf.google.com to not crash the script with the following error:
getaddrinfo: nodename nor servname provided, or not known
I just want the task to run till the end, and print any/all errors that are not a 200 and 301 http response.
Thanks!
Is open-uri an option? It throws an exception when 404s or 500s (or other HTTP exceptions) are encountered, in addition to SocketErrors, which allows you to clean up your code a bit
def self.check_prod_links
require 'open-uri'
results = []
Product.where(:published => 1).each do |product|
url = product.url
id = product.id
failed = true
begin
open URI(url)
failed = false
rescue OpenURI::HTTPError => e
error_message = e.message
response_message = "Response Code = #{e.io.status[0]}"
rescue SocketError => e
error_message = e.message
response_message = "Host unreachable"
rescue => e
error_message = e.message
response_message = "Unknown error"
end
if failed
Rails.logger.error "Problem getting url: #{url} Error Message: #{error_message}"
Rails.logger.error "Checking URL = #{url}. ID = #{id}. #{response_message}".
product.update_attribute(:published, 0).
results << product
end
end
results
end