I have a number of Ruby files, each of which declares a Class, but each of which could conceivably be run from the command line.
I’d like to put the following functionality at the bottom of each file with the least duplication possible:
if __FILE__ == $0 # instantiate the class and pass ARGV to instance.run end
My first instinct was to do this:
# /lib/scriptize.rb: Kernel.class_eval do def scriptize(&block) block.call(ARGV) if __FILE__ == $0 end end # /lib/some_other_file.rb: include 'scriptize' class Foo # ... end scriptize { |args| Foo.new.run(args) }
But that doesn’t work because __FILE__ is evaluated in scriptize.rb, so it’s never Foo.
I imagine the solution is to literally inline the contents of scriptize.rb, but I don’t know the syntax. I could use eval, but that’s still quite a bit of duplication — it can’t really be reduced to a method I add to Kernel.
Use
callerto determine how close you are to the top of the call stack:This gives this definition for
scriptizeNow, as an example, we can use two libraries/executables that require each other
So when we run from the command line:
So this shows the equivalence of using scriptize and
if $0 == __FILE__However, consider that:
if $0 == __FILE__ ... endis a standard ruby idiom, easily recognized by others reading your coderequire 'scriptize'; scriptize { |args| ... }is more typing for the same effect.In order for this to really be worth it, you’d need to have more commonality in the body of scriptize – initializing some files, parsing arguments, etc. Once it gets complex enough, you might be better off with factoring out the changes in a different way – maybe passing scriptize your class, so it can instantiate them and do the main script body, or have a main script that dynamically requires one of your classes depending on what the name is.