I'm writing a script for a rails app that manages (in a loose sense of the term) files in Dropbox through the dropbox-api gem. Anyway, I'm attempting to delete a file (if it exists) before taking a set of actions.
To delete the file I use the following:
Dropbox::API::Client#destroy
Removes the file specified by path
Returns a Dropbox::API::File object of the deleted file
client.destroy 'file.txt' # => #<Dropbox::API::File>
Where client is
client = Dropbox::API::Client.new(:token => 'xxxxxxxxxxx', :secret => 'yyyyyyyyyyy')
Simply deleting the file is not a problem; it's that I'd like the script not to throw an error if the file doesn't exist. I thought that this might be a job for try() but I haven't been able to get it to work. Is try() appropriate for this application? If not, might there be a good alternative?
You need to write the code in begin and rescue block. Like this:
begin
#code here which may throw error
rescue
#code here to rescue if it throws error
puts "File not found"
end
This is an example. You can do like this. Hope this helps.
Edit:
The try method is used when you need to rescue any exception while calling any object's attributes. May be I am not clear with this sentence, I will explain with an example. Suppose there are users you are not sure about their attributes. So what you can do is:
#user.try(:name)
So if the name exists then it would return the value or else if it doesn't exists it will not throw and error instead it will return nil.
The begin rescue block is useful when you want to handle any exception. Like the above your code might throw an error but you need to handle the error, it should not break the functionality, so you use it. Hope I am clear with this.
Related
I've got an issue with a method shared across a wide number of integration tests.
The problem is, I need to find one of two buttons, and have so far only come up with the following unwieldy syntax for avoiding Capybara's ElementNotFound error:
new_button = begin
find(".button_one")
rescue Capybara::ElementNotFound
begin
find('.button_two')
rescue Capybara::ElementNotFound
raise Capybara::ElementNotFound, "Missing Button"
end
end
new_button.click
This works as expected: if the first button's not found, the second one is, and they're clicked. If neither are present, the error is raised.
Despite this, I really don't like the nested rescues and would like to tidy this up.
The simplest solution which feels like it should exist, though I've not found this anywhere: does anyone know if there's an option to return nil in Capybara's find method, rather than raising the exception?
For example, the following pseudocode...
new_button = find('.button_one', allow_nil: true) || find('.button_two', allow_nil: true)
new_button ? new_button.click : raise(Capybara::ElementNotFound, "Missing Button")
...would be perfect.
Otherwise, any suggestion of how best to rescue the two errors and avoid the horrible nested rescue?
Footnote: this code exists within a large existing structure, which previously worked fine where it shouldn't have. Fixing another issue has caused this problem, which is used widely throughout the suite. I'd love to adjust the calls and use the correct elements (and so avoid this altogether), though that's going to be a big project a little later in the day.
If only one of the buttons is ever on the page, the simplest solution is to just look for both buttons at once using the CSS comma
find(".button_one, .button_two").click
If it's possible for both buttons to be on the page at the same time then that will get you an Ambiguous match error, in which case you could do something like
find(".button_one, .button_two", match: :first).click
or
all(".button_one, .button_two")[0].click
It's also possible to check whether an element exists without raising an exception using the Capybara provided predicates has_css?/has_xpath?/etc. which would give code like
if page.has_css?(".button_one")
find(".button_one")
else
find(".button_two")
end.click
but in this case using the CSS comma would definitely be the better solution.
Try with the x-path //button[contains(#class,'button_one') or contains(#class, 'button_two'] as given below,
new_button = begin
find(:xpath, "//button[contains(#class,'button_one') or contains(#class, 'button_two']")
rescue Capybara::ElementNotFound
raise Capybara::ElementNotFound, "Missing Button"
end
new_button.click
I'm not familiar with Ruby, so I'll leave the link to Ruby docu and Capybara docu. The idea is to use find_elements instead of find_element. Why? find_elements won't throw any exception if there is no elements found. The pseudo code would be like this:
new_button = find_elements('.button_one').size() > 0 ? find('.button_one') || find('.button_two')
And you don't need to handle with exceptions anymore.
In Michael Hartl's rails tutorial chapter 13.3.1, we create a twitter-style micropost like this:
Micropost.create
My question is why use Micropost.create and not Micropost.create!?
It seems that you would always want to raise exceptions in active record if there's a problem so that you can address the exceptions.
Why would want to ever use Micropost.create? He explains that both are options with this table:
But he doesn't really explain why you would choose one or the other. So, why would you choose one over the other?
Well, it depends where you are using the method.
When to use create
If it's a simple create in your controller, generally you will use create to be able to control the flow logic, take this example:
if Micropost.create(micropost_params)
# handle success save, e.g.:
redirect_to :index
else
# handle failure in save, e.g.:
render :new
end
With the above code you control the flow without using exceptions, and that's what you want when you create a record: Handle errors that you know that will be likely to happen without raising exceptions.
So, i prefer to ask it the other way around: why use create! and raise an exception (and get the related overhead) if you could just get a false and handle the error? And why do it if i need more code to handle that exception?
While this is a valid use create, it's not that common, since save and update are more likely to be used (in most cases you will create using new and then saving with save).
When to use create!
If you want to update multiple records within the same action (read transactions), then create! will serve you better; consider the following example:
ActiveRecord::Base.transaction do
user1.update!(balance: user1.balance + 100)
user2.update!(balance: user2.balance - 100)
transfer.create!(amount: 100, receiver: user1, sender: user2)
end
Here you update 2 records and create 1 within the same transaction, but if one of them fails you need to do a rollback to keep you data integrity intact. So, if you use create you will need to handle the rollback yourself to undo the transaction; on the other hand, using create! will raise an exception that triggers the rollback automatically.
Another good use for create! is testing (as in Hartl's tutorial) and debugging, i found them more convenient since they help catch errors a lot faster.
You aren't completely in the dark if a create fails.
my_micropost = Micropost.create
if my_micropost.persisted?
puts "successfully saved"
else
puts "something went wrong"
end
It gives you a simple mechanism to handle the failure condition. You can reference my_micropost.errors to determine what the issue was and handle accordingly, for example.
Whereas to recover from create! without exposing the exception to the end user means you'll need to rescue from the raised exception, which is a more complex process.
I want provide as much feedback as possible to users trying to import data from csv. The problem is on the cases I need to rescue CSV::MalformedCSVError because the exception message is in english(I can't use english) and there is no other way to distinguish one error from another.
What can I do to show the exact problem to the user?
I noticed you tagged this as Rails so that means you have I18n. Why don't you do something like the following:
begin
# csv parsing code
rescue CSV::MalformedCSVError => ex
raise(CSV::MalformedCSVError.new(I18n.t("csv_parser.malformed_csv_error"))
end
Note: this does assume CSV::MalformedCSVError inherits from StandardError which might not be the case but you get the idea, raise an exception and set the message to some translated I18n string.
UPDATE:
If you wanted even more detail you could match against the exception message and have translations for each message type, while capturing the data you want from the error message string - for instance line number etc. I have no idea right now what the error messages look like but say you have something like "error in column 45" then you could do the following
begin
# csv import code
rescue CSV::MalformedCSVError => ex
err_message = case ex.message
when /column (\d+)/
I18n.t("csv_error.column_error_message", column: Regexp.last_match[1])
else
I18n.t("csv_error.generic_message")
end
raise(CSV::MalformedCSVError.new(err_message))
end
UPDATE: The GEM
I've taken this monkey patch and turned it into a gem. Check it out, along with a test suite:
https://github.com/jhubert/csv-i18n
Original Answer
I bumped into this problem and decided that unless I was ok with showing unhelpful error messages to my users, I could go with two options:
Introduce a new class like FriendlyCSV that wrapped the CSV parser, examined the exception message and returned a relevant error message in the right language.
Monkey-patch the CSV library to return translated error messages.
The first way is probably the proper way to go, since I control the source code and can use whatever class I want to process the incoming CSV files.
However, I'm far too reckless for that and I liked the idea that ANY CSV parsing done anywhere in the system returned translated messages, without other people needing to be aware of the FriendlyCSV class.
So, I went with #2.
The Monkey Patch
You can find the patch here:
https://gist.github.com/jhubert/7d75586857d41fb4c45c4491363636e9
The core behavior is that we're overwriting the shift method, which is where the bulk of the string parsing is. We're attempting to translate the error message and then returning the original exception.
def shift
super
rescue ::CSV::MalformedCSVError => exception
raise $!, translated_exception_message(exception.message), $!.backtrace
end
Hope that helps!
Active Record validations throw an error when they fail. What I have in a model is
validate_format_of :field_which_cannot_have_spaces, :with => /^[^\s]+$/, :message => "Some error message"
What I want instead, is for a string replacement to substitute spaces for underscores (snake_case).
The advantages of using validation for me, are that it runs every time the field is changed unless save(validate: false), and that I don't need to repeat the replacement in the create and update controller methods.
Front end javascript solutions won't help if the user hacks the form... a rails solution is needed!
It sounds like you want a callback rather than a validation. This can run each time your object is modified.
So, to remove spaces from your field before the object is saved you can do:
before_save :remove_spaces_from_x
def remove_spaces_from_x
self.field_which_cannot_have_spaces.gsub!("\s","_")
end
Note also that validation do not always raise an error when they fail. If you use save! or create! then an error is raised but if you use the equivalent save or create then no error is raised, false is returned and the object's errors are populated with details of the validation failure.
Co-worker just told me to do the following in the model:
def field_which_cannot_have_spaces=(input_from_form)
super(input_from_form.gsub("\s","_"))
end
This will change the value as it is set.
"Validations are for informing the client there is a problem, and shouldn't be doing something other than throwing an error."
Hope this helps someone else...
In languages that have goto, I like to create an error block at the end of a function (after return) and then when I do error checking within the function, I can just be concise and goto the error handler within each check. As I understand it, this is the one valid use of goto that isn't considered bad practice.
Example in pseudocode:
def example
if (x) goto error
do something
if (y) goto error
do something
If (z) goto error
do something
return
label 'error'
log "error occurred"
begin
redirect_to :back
rescue
redirect_to root_url
end
return;
end
As you can see, in this case, my error block is as long as the function itself, and repeating it 3 times would double the size of my code, and not be very DRY. However, it seems that Ruby doesn't support goto, or at least if it does, as best as I can tell from looking on Google, it's some sort of possibly joke library labeled evil.
Therefore, what are people doing in Ruby in order to handle repeated error checking where the same result should occur in each error?
Callbacks
You should transfer many of these errors into your models, using Callbacks. These apply to errors that are relevant to actions that involve records in your database, i.e. checking whether a data input is appropriate.
Filters
Use before_filters and after_filters to check for errors, especially when you need to perform these checks on multiple controller actions. An example:
before_filter :check_errors
def example
regular code...
end
private
def check_errors
error checking...
end
Case statements
Use Case statements to improve your if statements, particularly when you have multiple checks involved.
Prioritizing the above
Use callbacks in your models whenever you can and definitely whenever data saving/updating/validation is involved.
Use before_filters whenever the code is to be reused across multiple actions (and in my opinion, always whenever you have involved error checking like this).
If you need these checks to occur only once, in this controller action alone, that do not involve records being changed, simply rewrite your code in a valid case statement (but my recommendation would still be to transfer to a before_filter).
Here's a little secret: Exceptions are basically glorified gotos. Also, ruby has a catch/throw syntax, see: http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_exceptions.html
In your case, ask, is it really an error or just an undesirable condition. An error to me is when a belongs_to references a record that doesn't exist, but having an empty belongs_to isn't. This changes from situation to situation.
Looking at your comment above, I think I would be more inclined to add some private methods that set the instance variables and return true of false, and chain them together:
if load_model1 && load_model2 && load_model3
... do regular page view
else
#render error page, use #load_error
end
private
def load_model1
#model1 = ....
if #model1.blank? # or nil? or whatever error condition
#load_error="model 1 failed
return false
else
return true
end
end
def load_model2
...
end
def load_model3
...
end