Can I conditionally skip loading "further" ruby code in the same file? - ruby

Can I conditionally skip loading "further" ruby code in the same file,
if a library (loaded via require) is not found ?
begin
require 'aws-sdk'
rescue LoadError
puts "aws-sdk gem not found"
return #does not work. nor does next
end
# code after here should not be executed as `aws-sdk` gem was not found
puts "=== should not get executed"
namespace :db do
desc "import local postgres database to heroku. user and database name is hardcoded"
task :import_to_heroku => [:environment, "db:dump_for_heroku"] do
# code using aws-sdk gem
end
end
In the above code, can I ask Ruby not to load the file further after
hitting a rescue LoadError.
Like an early return but for loading a file and not for a function.
Need it because i have i have a rake task which needs aws-sdk rubygem but i use it only
on my local machine. If aws-sdk is not found it does not make sense for me to load code afterwards in the same file. I guess i can split the code into smaller files and warp it in
a require call
if Rails.env.development?
require 'import_to_heroku'
end
But do not want to warp or modify my existing code
Also, i can wrap the whole code in an conditional but that is inelegant.
A begin-rescue block is also a form of explicit control flow.
I do not want to wrap or touch the original code is any manner
Maybe an api such as
require_or_skip_further_loading 'aws-ruby`
So i want my code to be functionally equivalent to
begin
require 'aws-sdk'
namespace :db do
desc "import local postgres database to heroku. user and database name is hardcoded"
task :import_to_heroku => [:environment, "db:dump_for_heroku"] do
# code using aws-sdk gem
end
end
rescue LoadError
puts "aws-sdk gem not found"
end
Or via an if conditional
library_found = false
begin
require 'aws-sdk'
library_found = true
rescue LoadError
puts "aws-sdk gem not found"
return #does not work
end
if library_found
namespace :db do
desc "import local postgres database to heroku. user and database name is hardcoded"
task :import_to_heroku => [:environment, "db:dump_for_heroku"] do
# code using aws-sdk gem
end
end
end
Want program execution to continue after LoadError is raised. ie. gracefully handle LoadError and do not load code written after LoadError in the same file. cannot raise exit or abort on LoadError And particularly the code after LoadError should not be executed (or loaded) by the ruby interpreter
Had originally asked How to skip require in ruby?
but i did not ask the question properly. hope this is better worded

I haven't checked the source, but I suppose that, when you run ruby my_file.rb at the console, or require/load it from Ruby code, the file is read entirely into memory before being evaluated. I'm afraid there is no such thing as skipping a part of a file.
I had an idea with catch/throw.
requiring file (for example a Rake task ?) treq1.rb :
catch :aws_sdk do
require_relative 'original'
end
puts '... continued'
The original file that you don't want to modify, original.rb :
puts 'in original, not to be modified'
begin
require 'aws-sdk'
rescue LoadError
puts "aws-sdk gem not found"
throw :aws_sdk
end
puts ">>> to execute only if 'aws-sdk' is found"
# namespace :db do ... etc
#end
Execution :
$ ruby -w treq1.rb
in original, not to be modified
aws-sdk gem not found
treq1.rb:2:in `require_relative': method `backtrace' called on unexpected T_NODE object (0x007fd32b88e900 flags=0x381c klass=0x0) (NotImplementedError)
from treq1.rb:2:in `block in <main>'
from treq1.rb:1:in `catch'
from treq1.rb:1:in `<main>'
Googling with the error : http://www.ruby-forum.com/topic/4406870
Recent post, no answer. It works in a single file, if you want to wrap your code.
Let's try another solution. Supposing that you can change the Rake task, treq2.rb :
begin
require_relative 'original'
rescue LocalJumpError
puts 'rescued LocalJumpError'
end
puts '... continued'
In original.rb, replace throw :aws_sdk by return :
$ ruby -w treq2.rb
in original, not to be modified
aws-sdk gem not found
rescued LocalJumpError
... continued
This way it works.
HTH

The "top-level return" feature has been added.
It is now possible to use the return keyword at the top level, as in your "via an if conditional" example. Further discussion here.

Related

Why undefined method `expect' when checking in at_exit hook

I am trying to check the exitstatus of a command through the below code. It is resulting in an error --->undefined method 'expect'
require 'rspec'
require 'rspec/expectations'
at_exit do
\`cat /etc/redhat-release\`
expect($?.exitstatus).to eq(0)
end
Can any one please help me in solving this problem
Your code is suppose be a test case based on rspec, and you are mixing it with normal ruby code.
In order to test your code with rspec you need to wrap the test scenarios and cases inside a describe and it block, only then you'll have access to the expect method, like so:
# test.rb
require 'rspec'
require 'rspec/expectations'
describe 'Test exit status' do
it "check status" do
`cat /etc/redhat-release`
expect($?.exitstatus).to eq(0)
end
end
and you run this code with rspec command line, like: rspec test.rb
The code you had before, was using the at_exit block. This is only called when you exit the program, or receive a signal for it to be killed.

Resque tasks always fail - uninitialized job constants?

I've tried using Resque before and was met with unmitigated failure. I'm revisiting it again with the same results...
resque.rake:
require "resque/tasks"
task "resque:setup" => :environment
test.rb:
require 'resque'
class FileWorker
#queue = :save_to_file
def self.perform(str)
File.open('./' + Time.now.to_s + '.txt', 'w+') do |f|
f << "test 123"
end
end
end
Resque.enqueue(FileWorker, "12345567".split('').shuffle.join)
Gemfile:
gem 'resque'
gem 'rake'
It seems like running test.rb on its own successfully queues the job:
However, running rake resque:work QUEUE='*' in the same folder results in a warning,
WARNING: This way of doing signal handling is now deprecated. Please
see http://hone.heroku.com/resque/2012/08/21/resque-signals.html for
more info.
As well as the task being added to "failed" queue with the following reason: "exception":"NameError","error":"uninitialized constant FileWorker"
How do I get this to work? Seems like something quite obvious but there's tons of tutorials about Resque spanning many years - some painfully out of date and none explaining how to run workers so they don't fail.
Thanks in advance.
When you enqueue a task with Resque, what is stored on Redis is just the name of the job class (as a string) along with the arguments (again as strings) in a JSON object.
When a worker then tries to perform the task, it needs to be able to create an instance of the job class. It does this by using const_get and const_missing. This is where the error you are seeing occurs, since the worker does not have the definition of FileWorker available to it.
The error is the same as if you tried to get an unknown constant in irb:
> Object.const_missing "FileWorker"
NameError: uninitialized constant FileWorker
The solution is to make sure the definition of FileWorker is available to your workers. The simplest way to do this would be to just require test.rb from your Rakefile (or resque.rake). In your code this would involve adding another task to the queue, so you might want to move the FileWorker code to its own file where it can be required by both the rake file and the code enqueuing jobs.
test.rb:
require 'resque'
require './file_worker'
Resque.enqueue(FileWorker, "12345567".split('').shuffle.join)
Rakefile (note the :environment task only makes sense if you are using Rails and will give errors otherwise):
require "resque/tasks"
require "./file_worker"
file_worker.rb:
class FileWorker
#queue = :save_to_file
def self.perform(str)
File.open('./' + Time.now.to_s + '.txt', 'w+') do |f|
f << "test 123"
end
end
end
Now the workers will be able to create instances of FileWorker to complete the tasks.
The way to avoid the warning about signals is given in the page linked to in the message. Simply set the environment variable TERM_CHILD when calling rake:
$ rake resque:work QUEUE='*' TERM_CHILD=1

Rake tast with cli arguments in Sinatra

I have created a rake file in my Sinatra app to create indexes for a Mongodb collection and I am trying to pass the environment parameter in the rake task db:create_indexes.
Here is my db.rake file:
namespace :db do
task :create_indexes, :environment do |t, args|
puts "Environment : #{args}"
unless args[:environment]
puts "Must provide an environment"
exit
end
yaml = YAML.load_file("./config/mongoid.yml")
env_info = yaml[args[:environment]]
unless env_info
puts "Unknown environment"
exit
end
Mongoid.configure do |config|
config.from_hash(env_info)
end
Bin.mongoid:create_indexes
end
end
Also the Rakefile in the root of app contains:
require 'rake'
require 'rubygems'
require 'bundler/setup'
Dir.glob('lib/tasks/*.rake').each { |r| load r}
But whenver I try to run the rake task using the command rake db:create_indexes[development], I get the following error, no matches found: db:create_indexes[development]
Now I am clueless about how to solve this issue.
So the problem was not with the code but the shell I was using.
I use zsh shell in place of bash and it seems zsh require you to escape the brackets: rake my_task\['arg1'\].
Therefore the code works with rake db:create_indexes\['development'\].

Ruby: cannot load such file - LoadError

I got this 'require' cannot load such file error.
I got this previously and I added
__LIB_DIR__ = File.expand_path(File.join(File.dirname(__FILE__), ".."))
unless $LOAD_PATH.include?(__LIB_DIR__)
$LOAD_PATH.unshift(__LIB_DIR__)
end
I would like to know what this does? I added this in a main 'require' file of my project.
Now I write a test case,
$:.unshift File.join(File.dirname(__FILE__), ".")
I try to run it, I get the LoadError. I also tried require_relative no luck.
Structure:
Main
Git
lib
files.rb
base.rb
test
test1.rb
I have the first code block above in base.rb where I do all 'requires'
and when i try to run the test. I get LoadError.
'Please',Explain the first and second code blocks also give me a solution
For clarity, instead of
__LIB_DIR__ = File.expand_path(File.join(File.dirname(__FILE__), ".."))
use
__LIB_DIR__ = File.expand_path('..', File.dirname(__FILE__))
What does this do?
unless $LOAD_PATH.include?(__LIB_DIR__)
$LOAD_PATH.unshift(__LIB_DIR__)
end
Consider this:
ary = %w[a b]
ary # => ["a", "b"]
ary.unshift('c')
ary # => ["c", "a", "b"]
I try to run it, I get the LoadError. I also tried require_relative no luck.
That could be for a number of reasons, but, unfortunately you didn't share the code where it occurs. require and require_relative are both used to load code, but have different syntax in the parameter passed. We'd need to know what you're trying to load, and where it is in the file hierarchy in relation to your calling script.
Perhaps one of these, or their related questions, would help:
"ruby `require': cannot load such file"
"Ruby 'require' error: cannot load such file"
"require cannot load such file"
"Cannot load files using require"

Reload rubygems in irb?

I've this script right now.
def r(this)
require this
puts "#{this} is now loaded."
rescue LoadError
puts "The gem '#{this}' is missing."
puts "Should I install it? [y/n]"
data = gets
if data =~ /yes|y/i
puts "Installing #{this}, hold on."
if `gem install #{this}` =~ /Successfully/i
load this
end
else
puts "Okey, goodbye."
end
end
That makes it possible to require libs on the fly.
Like this: r "haml".
The problem is that I can't load the gem after it has been installed.
Using load this or load File.expand_path("~/.irbrc") does not work.
Here is an example.
>> r "absolutize"
The gem 'absolutize' is missing.
Should I install it? [y/n]
y
Installing absolutize, hold on
LoadError: no such file to load -- absolutize
>> require "absolutize"
LoadError: no such file to load -- absolutize
>> exit
$ irb
>> require "absolutize"
=> true
Is there a way to reload rubygems or irb on the fly?
I did not try, but I think you might be looking for Gem.clear_paths
Reset the dir and path values. The next time dir or path is requested, the values will be calculated from scratch. This is mainly used by the unit tests to provide test isolation.
You can reset irb by calling exec('irb')
Just remove the file from ´$"´:
require 'erb' # Loaded.
require 'erb' # Does nothing.
$".delete_if {|e| e =~ /erb\.(?:rb|so)/} # Remove "erb" from loaded libraies.
require 'erb' # Reloaded (with warnings if the first require was successful).
See http://www.zenspider.com/Languages/Ruby/QuickRef.html#19

Resources