How to cancel evaluating a required Ruby file? (a.k.a. top-level return)
Asked Answered
N

4

11

file1 requires file2, and I want to be able to cancel evaluating file2 under certain conditions without exiting the whole process.

# file1.rb
  puts "In file 1"
  require 'file2'
  puts "Back in file 1"

# file2.rb
  puts "In file 2"
  # return if some_conditional
  puts "Still in file 2"

When running file1, the output I want to see is:

In file 1
In file 2
Back in file 1

The goal is for Still in file 2 to never print, while Back in file 1 does print. Is there anything I can do in file2 to make this possible?

I can't use exit/exit!/abort here because Back in file 1 will never print. I could use raise/fail, but to do that I would have to change file1 and rescue the failed require. I'm hoping to find a way that doesn't involve altering file1.

UPDATE:

A "top-level return" feature has been added.

Neuberger answered 18/12, 2015 at 1:21 Comment(5)
You may have provided a simplified example to illustrate your point. May be if you can tell us what you want to achieve, then, may be someone can assist better. At the least, lets avoid any XY problemGreenlee
There's no X or Y here. I'm used to being able to make Ruby do just about anything, and it strikes me as odd that it can't do this. If it's possible, I'd like to know how.Neuberger
I think the question is not well stated. As in steenslag's answer, you can do anything to make the remaining part after A in file2.rb not to be part of the code. That is trivial. And it does not seem to make sense to have a code written in a file that you don't want to be executed. Is the part after A to be executed under any occasion?Textualism
There is a Ruby issue discussing adding such a feature: bugs.ruby-lang.org/issues/4840. Currently I think you just have to wrap the code in the required file with conditionals.Markhor
@Markhor Thanks for that link. That's exactly the discussion I was looking for.Neuberger
N
4

UPDATE:

A "top-level return" feature has been added.

ORIGINAL:

Commenter matt pointed out that Feature 4840, which would do exactly what I'm asking about, has been in discussion since June 2011. Further, the feature was still being discussed as late as November 2015 in core team meetings regarding new Ruby features.

There are a lot of difficulties involved in designing a feature like this; for a list of the pros and cons, I highly suggest checking out the discussions.

The proposed feature would allow exiting the required file while using any of the the following top-level statements:

if condition
  return
end

while condition
  # ...
  return
end

begin
  # ...
  return
rescue
  # ...
  return
ensure
  # ...
  return
end

And it would not exit the required file in the following statements:

class Foo
  return # LocalJumpError
end

def foo
  return # returns from method, not from required file
end

proc do
  return # LocalJumpError
end

x = -> { return } # returns as from lambda, not from required file

Since the feature remains unimplemented, I have awarded the bounty to steenslag for successfully solving the problem (as originally written) to the letter, if not the spirit.

Neuberger answered 31/12, 2015 at 1:34 Comment(0)
D
4

Lines below __END__ will not be executed.

# file2.rb
puts "In file 2" 
__END__
puts "Still in file 2" # Never gets called
Decay answered 20/12, 2015 at 13:10 Comment(1)
Yeah, and it also won't be parsed.Chaudfroid
N
4

UPDATE:

A "top-level return" feature has been added.

ORIGINAL:

Commenter matt pointed out that Feature 4840, which would do exactly what I'm asking about, has been in discussion since June 2011. Further, the feature was still being discussed as late as November 2015 in core team meetings regarding new Ruby features.

There are a lot of difficulties involved in designing a feature like this; for a list of the pros and cons, I highly suggest checking out the discussions.

The proposed feature would allow exiting the required file while using any of the the following top-level statements:

if condition
  return
end

while condition
  # ...
  return
end

begin
  # ...
  return
rescue
  # ...
  return
ensure
  # ...
  return
end

And it would not exit the required file in the following statements:

class Foo
  return # LocalJumpError
end

def foo
  return # returns from method, not from required file
end

proc do
  return # LocalJumpError
end

x = -> { return } # returns as from lambda, not from required file

Since the feature remains unimplemented, I have awarded the bounty to steenslag for successfully solving the problem (as originally written) to the letter, if not the spirit.

Neuberger answered 31/12, 2015 at 1:34 Comment(0)
S
1

I don't know of any official method for breaking out of required files, especially as there are several require methods (e.g., bundler monkey patches require)

The best solution I could come up with was using rubys throw-catch control flow. I'm not sure conditional you're using to determine if execute should return early, but this should be able to cope with most situations

# file1.rb
puts "In file 1" 
catch(:done) do
    require 'file2' end
puts "Back in file 1"

# file2.rb
puts "In file 2" 
throw :done
puts "Still in file 2" # Never gets called
Sarette answered 18/12, 2015 at 9:19 Comment(1)
This will work, in the same way that raise/rescue will work. Ideally, however, no modifications to file1.rb would be necessary.Neuberger
C
1

Is using a method possible ? It will still parse the method but won't get executed. Something like :

#file1.rb
  puts "In file 1"
  require 'file2'
  puts "Back in file 1"
  a_method

#file2.rb
  puts "In file 2"
  # <= A

  def a_method
    puts "Still in file 2"
  end
Cooper answered 20/12, 2015 at 19:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.