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?

Solution

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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top