Создание экземпляра класса ruby, который находится в модуле, из C#

Я пытаюсь повторно использовать некоторые рубиновые классы, которые я написал некоторое время назад в проекте ASP.NET MVC 2. Проблема, с которой я сталкиваюсь, заключается в том, что если класс находится в модуле, я не могу его создать. Если я перемещаю класс за пределы модуля, он работает нормально. Вот уменьшенная версия класса, который я хочу создать:

module Generator
  class CmdLine
    attr_accessor :options

    def initialize(output)
    end

    def run(args=[])
    end
  end
end

Если закомментировать часть модуля, я могу создать объект. Я делаю что-то неправильно? Вот код С#:

var engine  = Ruby.CreateEngine();
var searchPaths = engine.GetSearchPaths().ToList();
searchPaths.Add(@"c:\code\generator\lib");
searchPaths.Add(@"C:\Ruby-ri-192\lib\ruby\1.9.1");
engine.SetSearchPaths(searchPaths);

engine.ExecuteFile(@"c:\code\generator\lib\generator\generator_cmd_line.rb");
var rubyCmdLineObj = engine.Runtime.Globals.GetVariableNames();
// These lines works when I comment out the module
// var genCmdLineObj = engine.Runtime.Globals.GetVariable("CmdLine");
// var cmdLineObj = engine.Operations.CreateInstance(genCmdLineObj);
// var results = engine.Operations.InvokeMember(cmdLineObj, "run");
//  return Content(results);
var sb = new StringBuilder();
foreach (var name in rubyCmdLineObj)
{
  sb.AppendFormat("{0} ", name);
}

return Content(sb.ToString());

У меня есть работа - создание отдельного класса, который я могу вызывать из С#, но если мне не нужно этого делать, я бы предпочел этого не делать. Мы будем очень признательны за любые рекомендации.


person Rob    schedule 24.02.2011    source источник


Ответы (3)


Я знаю, что это своего рода хак/обходной путь, но мне удалось сделать это следующим образом:

Добавьте следующий код в конец файла ruby:

def hack(s)
  eval(s)
end

Теперь ваш код C# будет выглядеть так:

var engine = Ruby.CreateEngine();

var scope = engine.ExecuteFile(@"c:\code\generator\lib\generator\generator_cmd_line.rb");

var genCmdLineObj = engine.Execute(String.Format("hack('{0}::{1}')", "Generator", "CmdLine"), scope);
var cmdLineObj = engine.Operations.CreateInstance(genCmdLineObj);
var results = engine.Operations.InvokeMember(cmdLineObj, "run");
return Content(results);

Вроде хак, но эй, это работает! :)

person Shay Friedman    schedule 25.02.2011
comment
Спасибо Шей! Это немного хак, но он работает. Спасибо, что нашли время ответить! - person Rob; 27.02.2011

Я бы создал новый проект IronRuby, взял ваш исходный код Ruby и перенес/скомпилировал его в библиотеку .NET.

Больше вообще не нужно звонить. У вас есть что-то, что можно вызвать изначально из C#.

person Justin Niessner    schedule 24.02.2011
comment
Я хочу повторно использовать то, что у меня уже есть. Если мне придется переписать/портировать, я найду другой способ получить то, что мне нужно. Я могу ошибаться, но я не верю, что вы можете скомпилировать проект IronRuby в DLL. Я ошибаюсь? - person Rob; 24.02.2011
comment
@Rob - Нет, вы не можете напрямую скомпилировать IronRuby в DLL. Однако вы можете разместить его в C# DLL и обернуть вызовы в DLR. В зависимости от кода Ruby у вас не должно быть особых изменений. - person Justin Niessner; 24.02.2011
comment
Круто проверю. Спасибо, Джастин. - person Rob; 24.02.2011

Решение, предложенное @Shay Friedman, не нужно.

gen.rb

module Generator
  class CmdLine
    attr_accessor :options

    def initialize(output)
        @output = output
    end

    def run(args=[])
        puts "Hello from cmdLine with #{@output} #{args}"
    end
  end
end

кшарп

void Main()
{
    var engine = Ruby.CreateEngine();
    var buffer = new MemoryStream();
    engine.Runtime.IO.SetOutput(buffer, new StreamWriter(buffer));
    engine.ExecuteFile(@"c:\temp\gen.rb");
    ObjectHandle handle = engine.ExecuteAndWrap("Generator::CmdLine.new('the output')");
    var scope = engine.CreateScope();
    scope.SetVariable("myvar", handle);
    engine.Execute("myvar.run", scope);
    engine.Operations.InvokeMember(handle.Unwrap(), "run", "InvokeMember");
    buffer.Position = 0;
    Console.WriteLine(new StreamReader(buffer).ReadToEnd());
}

вывод

Hello from cmdLine with the output []
Hello from cmdLine with the output InvokeMember
person JJS    schedule 07.03.2018