I am writing a ruby DSL that will be used to code-generate a number of Objective-C++ functions. I would like the name of each function to be derived from the name of its ruby DSL source file.
For example, given this source file clusterOptions.rb
:
require './vMATCodeMonkey'
VMATCodeMonkey.new(:print).options_processor <<EOS
-cutoff: flag: set('useCutoff', true), arg: vector('double')
-depth: flag: set('useInconsistent', true), arg: scalar('double', default: 2.0)
-maxclust: flag: set('useCutoff', false), arg: vector('index')
EOS
When the VMATCodeMonkey.new(:print)
expression is evaluated I would ideally somehow like the new object to capture the clusterOptions.rb
source filename. Is that possible?
And if (as I suspect) it is not, is there a good idiom for accomplishing this functionality [e.g. making the source file name effectively part of the specification captured by a DSL] in ruby?
[While I suspect it's not possible to do exactly as I've described, I ask anyway, because I've been surprised by ruby's obscure capabilities more than once.]
EDIT: I'm aware of __FILE__
; what I'm looking for is some DSL-centric way of capturing the name of a DSL source file without explicitly mentioning __FILE__
in the DSL source. Hmm, and now that I'm trying to explain it, maybe crawling up a stack trace from the class initialize
method?
With thanks to tadman, here is my VMATCodeMonkey#initialize
method:
def initialize(out_opt = :print)
@caller_file = caller(1)[0].split(':')[0]
case out_opt
when :pbcopy
@out = IO.popen('pbcopy', 'w')
when :print
@out = $stdout
else
raise ArgumentError, "#{out_opt} is not an option!"
end
@out.puts "// vMATCodeMonkey's work; do not edit by hand!\n\n"
initialize_options_processor
end
And here's what it captures:
@caller_file = "/Users/Shared/Source/vMAT/ruby/clusterOptions.rb"
The full path to the source file being evaluated is stored in __FILE__
. If you want just the filename, you'd use:
File.basename(__FILE__)
The __FILE__
constant is common to C, C++, Perl and Python, among others.
If you need to know what file made the call to the currently running routine, this could work:
caller(1)[0].split(':')[0]
This presumes your filenames do not have :
in them, but in most cases that should be a fairly safe assumption. You'll also need to call this at the entry point into your library. If it's a method deeper in the stack, test caller(2)
and so on.