Rails errors[:base] inside class method? - ruby-on-rails

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

Refactoring an API request on Rails

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.

Save to database post,but before check http request in Ruby on Rails

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.

Invalid after rescue exception in model

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

How do I rescue an exception from inside an Enumerator?

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.

Alllow for "bad" urls in in Rails

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

Resources