5

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

Community
  • 1
  • 1
deepak
  • 7,230
  • 5
  • 24
  • 26
  • There is no need to "skip" the rest of the file, which is anyway already "loaded". In case of `rescue LoadError`, you code terminates (with `return` or preferably `exit`) and Ruby will not process the rest of the file. – BernardK Jan 25 '13 at 17:11
  • @BernardK Thanks. edited the question. Want it to be equivalent to wrapping it in a begin-rescue or if-block as asked in the question. The relevant portion is. 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 – deepak Jan 25 '13 at 19:00
  • How is this file loaded ? I don't know Rake. Does it `require` your file ? – BernardK Jan 25 '13 at 19:41
  • 2
    I think this is being considered for Ruby 2: http://bugs.ruby-lang.org/issues/4840. I don’t know if any implementation has made it into the Ruby 2 preview releases though. – matt Jan 25 '13 at 20:16
  • Possible duplicate of [How to cancel evaluating a required Ruby file?](https://stackoverflow.com/questions/34347134/how-to-cancel-evaluating-a-required-ruby-file) – user513951 Feb 01 '19 at 13:38

2 Answers2

3

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

BernardK
  • 3,674
  • 2
  • 15
  • 10
  • did not understand the second example ie. using `LocalJumpError`. I Did not get any error in the first example. I am using ruby 1.9.3p327 – deepak Jan 26 '13 at 18:12
  • Are you using two separate files ? Errors occur only with two files. – BernardK Jan 26 '13 at 18:15
  • yes actually. the actual code (copied from your example) and the output is at https://gist.github.com/4644058. I am using MRI ruby 1.9.3p327 (2012-11-10 revision 37606) [x86_64-darwin10.8.0] Ruby installation does not have any custom patches – deepak Jan 26 '13 at 19:37
  • I have installed ruby-1.9.3-p362. Now the version with catch/throw distributed into two files works, as well as the version with return/rescue. So are you happy now ? Is it what you were looking for ? – BernardK Jan 26 '13 at 21:01
  • @deepak Answer to your first comment : if you run `ruby original.rb` with a `return`, not `throw`, error `in 'rescue in
    ': unexpected return (LocalJumpError)`, but if original.rb is required from another file, we can capture the error, rescue it and continue.
    – BernardK Jan 26 '13 at 21:09
1

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.

user513951
  • 12,445
  • 7
  • 65
  • 82