Invalid after rescue exception in model - ruby-on-rails

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

Related

Get params sent by a form in my model for validate

In my model, I need to check if the value is sent to validate it,
In my validation part, I have :
validate :validate_book, if: ->(book) { book.author.use_library? }
Then I have
def book_title_value=(title)
self.title = title.blank? ? nil : find_book(title)
end
def book_title_value
book.title
end
def find_book(title)
Book.where(title: title).first
end
def book_title_is_blank?
return true if book_title_value(title).blank? # At this point title is always nil, same with book_title_value
false
end
def validate_book
errors.add(:book, :invalid) if book_title_is_blank? #it return the errors
end
In my part book_title_is_blank? how can I get the value passed in the form to check if its blank or not ?
I dont get it because in my book_title_value=(title) I always have the value but not in the other method

"syntax error, unexpected '\n', expecting '.' or &. or :: or '['" with Ruby on Rails

I want to redirect another page or give alert messages due to user table info in this controller.
but, I get bellow error message.
like this:
class Ir::FactsetUrlsController < Ir::ApplicationController
def show
user = User.find(params[:user_id])
factset_url, option = get_factset_url(user)
redirect_to factset_url, option
end
private
def get_factset_url(investor)
url, option = if !current_user.factset_enabled?
url, {alert: "こちらの閲覧には有料契約が必要です。"}
elsif investor.company.is_fresh?
url, {alert: "現在収録作業中です。申し訳ございませんが、少々お待ち下さい。"}
elsif investor.company.is_fresh?
url, {alert: "申し訳ございませんが、投資家DBに収録がありません 収録されていない理由 ①投資助言会社や投資アドバイザーなど直接保有していない ②HPや開示されている情報がない"}
else
investor.company.factset_url
end
end
end
Simply wrap in square brackets [ and ] within conditions as multiple values are being assigned.
def get_factset_url(investor)
url, option = if !current_user.factset_enabled?
[url, {alert: "こちらの閲覧には有料契約が必要です。"}]
elsif investor.company.is_fresh?
[url, {alert: "現在収録作業中です。申し訳ございませんが、少々お待ち下さい。"}]
elsif investor.company.is_fresh?
[url, {alert: "申し訳ございませんが、投資家DBに収録がありません 収録されていない理由 ①投資助言会社や投資アドバイザーなど直接保有していない ②HPや開示されている情報がない"}]
else
[investor.company.factset_url, nil]
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.

Rails errors[:base] inside class method?

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.

Return save outcome inside begin/ensure block

I've a setter method for an attr_accessor in rails
# setter method of the shopify_p accessor
def shopify_v=(s)
begin
self.product.shop.connect_to_store
#shopify_v = s if s.save
ensure
ShopifyAPI::Base.site = nil
end
end
I'd like it to return true if save is successful or false if the save action doesn't work.
Instead it always outputs the s object (or #shopify_v, I don't know).
How can I make it return true or false depending on the save action?
Thanks,
Augusto
UPDATE #1
Here is the getter method of the same attr_accessor.
Basically it downloads the object from the server only in case it has never done it before.
# getter method of the shopify_v accessor
def shopify_v
if #shopify_v.nil?
begin
self.product.shop.connect_to_store
#shopify_v = ShopifyAPI::Variant.find(self.shopify_id)
ensure
ShopifyAPI::Base.site = nil
end
puts "remote"
return #shopify_v
else
puts "local"
return #shopify_v
end
end
def shopify_v=(s)
begin
self.product.shop.connect_to_store
#shopify_v = s if s.save
ensure
ShopifyAPI::Base.site = nil
end
#shopify_v.present? # will return true or false
end
I would use exceptions, since being unable to save is an exceptional condition. Also, whenever practical, a method should either have a side-effect, or return a value.
For example:
class ShopifyError < StandardError ; end
def shopify_v=(s)
begin
self.product.shop.connect_to_store
raise ShopifyError unless s.save
#shopify_v = s
ensure
ShopifyAPI::Base.site = nil
end
end
and in the caller:
begin
...
model.v = s
...
rescue ShopifyError
# set flash[:notify], or whatever error handling is appropriate
end
Also, there are cleaner ways to structure the getter. Consider doing something like this:
# getter method of the shopify_v accessor
def shopify_v
#shopify_v ||= fetch_v
end
private
def fetch_v
begin
self.product.shop.connect_to_store
ShopifyAPI::Variant.find(self.shopify_id)
ensure
ShopifyAPI::Base.site = nil
end
end
A setter always returns the value being set, no matter what you try to return. So you should use another method name, for example:
def set_shopify_v(s)
self.product.shop.connect_to_store
status = s.save
#shopify_v = s if status
status
rescue => exc
# Rails.logger.error(...)
false
ensure
ShopifyAPI::Base.site = nil
end

Resources