I'm trying to implement a file dialog using Tk. This aspect has worked but my error checking isn't working.
Since this file dialog can only take certain extensions I made it raise a LoadError, but I also don't want the program to stop, I want it to reopen to allow the user to pick another file.
Each way I've tried has only ended in an infinite loop or a LoadError stopping the program.
My code is:
module FileExplorer
require 'tk'
require 'tkextlib/tile'
def self.fileDialog
TkClipboard.append(Tk.getOpenFile)
f = TkClipboard.get
begin
unless extenstionCheck(f)
raise LoadError, 'Please select a valid file type'
end
rescue LoadError
fileDialog
end
end
def self.extenstionCheck(file)
filetypes = ['.xlsx', '.xls', '.csv', '.xml']
type = File.extname(file)
true if filetypes.include?(file)
end
end
There's no need to use TkClipboard, nor to use an exception.
Did misspelling the word, 'extension' blind you to your nearby error of checking whether filetypes included file, instead of type?
Your program, minimally changed as follows, works for me:
module FileExplorer
require 'tk'
require 'tkextlib/tile'
def self.fileDialog
while true
f = Tk.getOpenFile
break if extension_okay?(f)
Tk.messageBox message: 'Please select a valid file type!', detail: "Selection was: #{f}"
end
f
end
def self.extension_okay?(file)
filetypes = ['.xlsx', '.xls', '.csv', '.xml']
type = File.extname(file)
filetypes.include?(type)
end
end
p FileExplorer.fileDialog
This is completely inappropriate (and unnecessary) use of LoadError.
Raised when a file required (a Ruby script, extension library, …)
fails to load.
Its a low level error that does not inherit from StandardError and is tied to Kernel#require.
Instead declare your own exceptions in your own namespace:
module FileExplorer
require 'tk'
require 'tkextlib/tile'
FileTypeError = Class.new(::StandardError)
def self.fileDialog
TkClipboard.append(Tk.getOpenFile)
f = TkClipboard.get
begin
unless extenstionCheck(f)
raise FileTypeError, 'Please select a valid file type'
end
rescue FileTypeError
fileDialog
end
end
def self.extenstionCheck(file)
filetypes = ['.xlsx', '.xls', '.csv', '.xml']
type = File.extname(file)
true if filetypes.include?(file)
end
end
Related
I have a module named FileSystem in my app, which executes basic filesystem functionality. Here is the relevant code of it.
module TxtDB
module FileSystem
def self.create_database(db)
fpdb = db_full_path(db)
Dir.mkdir(fpdb) unless ((not valid_parameter?(db)) or (not valid_database?(fpdb)))
end
private
def self.valid_parameter?(db)
raise TxtDB::NIL_PARAMETER_ERROR unless (not db == nil)
raise TxtDB::NOT_A_STRING_ERROR unless (db.is_a? String)
raise TxtDB::EMPTY_PARAMETER_ERROR unless (not db.empty?)
true
end
def self.valid_database?(db)
raise TxtDB::DATABASE_ALREADY_EXISTS_ERROR unless (not Dir.exist?(db_full_path(db)))
true
end
def self.db_full_path(db)
"#{TxtDB::BASE_DIRECTORY}/#{db}"
end
end
end
And this is my Rspec test for this feature
it 'raises a StandardError (Database already exists) if it receives the name of an existing database' do
base_path = TxtDB::BASE_DIRECTORY
if (not Dir.exist?(base_path)) then
Dir.mkdir(base_path)
end
db_path = File.join(TxtDB::BASE_DIRECTORY,'testedb')
if (not Dir.exist?(db_path)) then
Dir.mkdir(db_path)
end
expect {
TxtDB::FileSystem::create_database('testedb')
}.to raise_error(StandardError, TxtDB::DATABASE_ALREADY_EXISTS_ERROR)
end
It happens that when I run my tests I got this error
expected StandardError with "Database already exists", got #<Errno::EEXIST: File exists # dir_s_mkdir - txtdb/testedb>
As I see things, this should not happen, since I'm testing for the existence before calling Dir.mkdir. But I'm obviously wrong, since the error occurs. The question is: Where am I wrong?
==========
According to the suggestion of Peter Alfvin (see answer below), I change my method to
def self.create_database(db)
fpdb = db_full_path(db)
if (valid_parameter?(db) and valid_database?(fpdb)) then
Dir.mkdir(fpdb)
end
end
Now there is no doubt the validations are done beforehand. But I still get the same error.
Dir.exists?(path) returns true if path is a directory, otherwise it returns false. In valid_database?, you are passing it the full path name, which I would guess is pointing to a non-directory file.
See http://ruby-doc.org/core-2.1.2/Dir.html#method-c-exists-3F
Have a small method in a Padrino helper to add partials. There are two main uses for it:
Have an easily configurable enable/disable system via a config file for the components.
Choose language specific layouts IF the requested layout is not found.
This is the code, I am using:
module SZHF
App.helpers do
def add_component component
if disabled?(component)
""
else
begin
partial(component)
rescue
language_specific(component)
end
end
end
def partial_exists?(component)
File.file?(File.expand_path("../../views/#{component}.html.erb", __FILE__))
end
def disabled?(component)
name = component.split("/").last
config["enable_#{name}".to_sym] == false
end
def language_specific(component)
name = component.split("/").last
prefix = component.chomp(name)
partial prefix + I18n.locale.to_s + "/" + name
end
end
end
But somehow the error instead being caught in the rescue block; the page returns an error stating: undefined methodsafe_concat' for nil:NilClass`.
Can't figure out why, any ideas?
I am trying to write a script in Ruby which interactively generate some input data for a program. The idea is use QtRuby when it exists, otherwise console is used. What I tried is to
begin
require "Qt4"
rescue LoadError => load_err
puts "Qt not found, using console"
end
class ConsoleDefine
# console code
end
class QtDefine < Qt::Widget
# GUI code
end
but the interpreter refused my code when Qt4 does not exist. is there a way to deal it similar to C++, like:
#ifdef QT4
class qt4gui
{
// some code
};
#else
class qt4gui
{
// dummy
};
#endif // Qt4
Use require to your advantage:
begin
require "Qt4"
require "my_lib/qt4"
rescue LoadError => load_err
puts "Qt not found, using console"
require "my_lib/console"
end
Create the two files:
# my_lib/console.rb
class ConsoleDefine
# console code
end
# my_lib/qt4.rb
class QtDefine < Qt::Widget
# GUI code
end
As #pst said, you don't need a preprocessor in Ruby, since it is dynamic. So:
begin
require "Qt4"
class QtDefine < Qt::Widget
# GUI code
end
rescue LoadError => load_err
puts "Qt not found, using console"
class ConsoleDefine
# console code
end
end
Is there a way to rescue all exceptions under a certain namespace?
For example, I want to rescue all of the Errno::* exceptions (Errno::ECONNRESET, Errno::ETIMEDOUT). I can go ahead and list them all out on my exception line, but I was wondering if I can do something like.
begin
# my code
rescue Errno
# handle exception
end
The above idea doesn't seem to work, thus is there something similar that can work?
All the Errno exceptions subclass SystemCallError:
Module Errno is created dynamically to map these operating system errors to Ruby classes, with each error number generating its own subclass of SystemCallError. As the subclass is created in module Errno, its name will start Errno::.
So you could trap SystemCallError and then do a simple name check:
rescue SystemCallError => e
raise e if(e.class.name.start_with?('Errno::'))
# do your thing...
end
Here is another interesting alternative. Can be adapted to what you want.
Pasting most interesting part:
def match_message(regexp)
lambda{ |error| regexp === error.message }
end
begin
raise StandardError, "Error message about a socket."
rescue match_message(/socket/) => error
puts "Error #{error} matches /socket/; ignored."
end
See the original site for ruby 1.8.7 solution.
It turns out lambda not accepted my more recent ruby versions. It seems the option is to use what worked in 1.8.7 but that's IM slower (to create a new class in all comparisons. So I don't recommend using it and have not even tried it:
def exceptions_matching(&block)
Class.new do
def self.===(other)
#block.call(other)
end
end.tap do |c|
c.instance_variable_set(:#block, block)
end
end
begin
raise "FOOBAR: We're all doomed!"
rescue exceptions_matching { |e| e.message =~ /^FOOBAR/ }
puts "rescued!"
end
If somebody knows when ruby removed lambda support in rescue please comment.
All classes under Errno are subclasses of SystemCallError. And all subclasses of SystemCallError are classes under Errno. The 2 sets are identical, so just rescue SystemCallError. This assumes that you're not using an external lib that adds to one and not the other.
Verify the identity of the 2 sets (using active_support):
Errno.constants.map {|name|
Errno.const_get(name)
}.select{|const|
Class === const
}.uniq.map(&:to_s).sort ==
SystemCallError.subclasses.map(&:to_s).sort
This returns true for me.
So, applied to your example:
begin
# my code
rescue SystemCallError
# handle exception
end
Here is a more generic solution, in the case you wanted to rescue some Errno types and not others.
Create a custom module to be included by all the error classes we want to rescue
module MyErrnoModule; end
Customize this array to your liking, up to the "each" call.
Errno.constants.map {|name|
Errno.const_get(name)
}.select{|const|
Class === const
}.uniq.each {|klass|
klass.class_eval {
include MyErrnoModule
}
}
Test:
begin
raise Errno::EPERM
rescue MyErrnoModule
p "rescued #{$!.inspect}"
end
Test result:
"rescued #<Errno::EPERM: Operation not permitted>"
I would guess this performs slightly better than a solution that needs to check the name of the exception.
I'm trying to create a local config file to use with compass so we can cope with differing import paths on developers' machines. So far I've tried to import the file inside an exception block, incase it doesn't exist, then use the variable further down:
local_config.rb
VENV_FOLDER = 'venv'
config.rb
VENV_FOLDER = '.'
begin
require 'local_config.rb'
rescue LoadError
end
puts VENV_FOLDER
Normally I'm a Python developer so I'd expect the import to change the value of VENV_FOLDER to venv, however it's still . afterwards.
Is there a way to import local_config.rb in such a way that it overrides the value of VENV_FOLDER?
Other alternatives:
YAML (or JSON)
local_config.yml
venv_folder: 'venv'
config.rb
require 'yaml'
VENV_FOLDER = begin
YAML.load_file('local_config.yml').fetch('venv_folder')
rescue Errno::ENOENT, KeyError
'.'
end
puts VENV_FOLDER
Class instance variable
You could put the value in a class instance variable:
local_config.rb
Config.venv = 'venv'
config.rb
class Config
class << self ; attr_accessor :venv ; end
self.venv = '.'
end
begin
require './local_config.rb'
rescue LoadError
end
puts Config.venv
Constants
Also, sticking to ruby files with constants, the following is perhaps marginally clearer in its intentions and avoids having to catch exceptions.
local_config.rb
VENV_FOLDER = 'venv'
config.rb
config_file = './local_config.rb'
require config_file if File.file? config_file
VENV_FOLDER ||= '.'
puts VENV_FOLDER
All three solutions have different mechanisms for ensuring that the value will be set even if the file is missing or doesn't set the value as expected. Hope it's helpful
The path to the file is wrong. It needs to include a slash if it isn't loaded from $LOAD_PATH.
Your LoadError is being caught silently.
If you do this:
VENV_FOLDER = '.'
begin
require './local_config.rb'
rescue LoadError
end
puts VENV_FOLDER
Then you'll see it works.
Better still:
VENV_FOLDER = '.'
require File.expand_path('../local_config.rb', __FILE__) rescue LoadError
puts VENV_FOLDER
Since the second version doesn't depend on the PWD of the user who invokes the script.
Constant re-assignment is a bad idea however. Ruby will let you do it, but you'll get a warning. I believe your confusion was just with the LoadError though.
Try something like this:
local_config.rb
VENV[:folder] = 'venv'
config.rb
VENV = {:folder => '.'}
begin
load 'local_config.rb'
rescue Exception
end
puts VENV[:folder]
Here's working version for ruby 1.9.3: https://gist.github.com/1501237
for ruby 1.8.7: https://gist.github.com/1501321