Rakefile からタスクと変数を抽出するにはどうすればよいですか?

概要

する必要がある:

これは Rakefile 内で定義されたタスクを見つけるために機能しますが、グローバル名前空間を汚染します (つまり、これを 2 回実行すると、最初のタスクで定義されたすべてのタスクが 2 番目のタスクに表示されます)。

sub_rake = Rake::DefaultLoader.new
sub_rake.load("Rakefile")
puts Rake.application.tasks

Rake では、ここで Makefile がロードされます。

そこにロードされている変数にアクセスするにはどうすればよいですか?

これは私が解析している Rakefile の例です。

load '../common.rake'
@source_dir = 'source'
desc "Run all build and deployment tasks, for continuous delivery"
task :deliver => ['git:pull', 'jekyll:build', 'rsync:push']

ここでは私が試してみてうまくいかなかったことがいくつかあります。 Rakefile で eval を使用する:

safe_object = Object.new
safe_object.instance_eval("Dir.chdir('" + f + "')\n" + File.read(folder_rakefile))
if safe_object.instance_variable_defined?("@staging_dir")
  puts "   Staging directory is " + f.yellow + safe_object.instance_variable_get("@staging_dir").yellow
else
  puts "   Staging directory is not specified".red
end

これは、Rakefile の記述部分を解析するときに失敗しました。私も次のようなことを試しました

puts Rake.instance_variables
puts Rake.class_variables

しかし、これらは私が探している @source_dir を取得できません。

解決策

rakefile_body = <<-RUBY
load '../common.rake'
@source_dir = 'some/source/dir'
desc "Run all build and deployment tasks, for continuous delivery"
task :deliver => ['git:pull', 'jekyll:build', 'rsync:push']
RUBY

def source_dir(ast)
  return nil unless ast.kind_of? AST::Node

  if ast.type == :ivasgn && ast.children[0] == :@source_dir
    rhs = ast.children[1]
    if rhs.type != :str
      raise "@source_dir is not a string literal! #{rhs.inspect}"
    else
      return rhs.children[0]
    end
  end

  ast.children.each do |child|
    value = source_dir(child)
    return value if value
  end

  nil
end

require 'parser/ruby22'
body = Parser::Ruby22.parse(rakefile_body)
source_dir body # => "some/source/dir"