Forum: IronRuby Accessing a Module's Classes from C# with dynamic

Posted by Joshua M. (joshua_m)
on 2011-01-13 21:51
Hey guys.

I am currently experimenting with IronRuby as an extensibility solution
for my application. However, I have hit a roadblock. What I would like
to do is call Runtime.UseFile("script.rb"), then access a global module
in the script, and instantiate a specific class in that module.



Here is the script file for reference:
module RbScriptApp
    class Foo
        def Bar()
            return (rand(100) + 1).to_s();
        end
    end
end



Initially, I thought it would be as easy as typing
dynamic globals = myRuntime.Globals;
dynamic myClass = globals.RbScriptApp.Foo.@new();
myClass.Bar();
...

but I soon found out this was not the case, as the builtin RubyModule
class does not support this dynamic syntax. Is there a way around this
or is it just plain impossible? I was unable to find *any* information
about how to do this (not even about RubyModule itself!) through Google.

The exception in question is a RuntimeBinderException:
'IronRuby.Builtins.RubyModule' does not contain a definition for 'Foo'

Right now my workaround is a call to RubyModule.EnumerateConstants and
manual lookup of the class name I want, but it doesn't have the
syntactic elegance of the dynamic keyword... Any thoughts?

EDIT: Just thought I'd add, I'd like to stay away from Engine.Execute()
if at all possible, unless someone can give me a reasonable case for not
doing so.

Thanks.
Posted by Joshua M. (joshua_m)
on 2011-01-15 08:55
It turns out that the problem is not accessing a module's class, but
accessing the contents of a module in a require'd file.

global.rb
  require 'app'

app.rb
  module RbScriptApp
      class Foo
          def Bar()
              return (rand(100) + 1).to_s()
          end
      end
  end

No matter what I do I can only access RbScriptApp and not Foo if I call
Runtime.UseFile("global.rb"). So far I have two other work arounds: 1.)
create wrapper methods for everything I want to access in app.rb (ugh),
or 2.) use Engine.Execute("RbScriptApp::Foo.new"). The latter is more
preferrable, I suppose, but it seems strange that I can't access Foo by
dynamic.

Any more information about this issue would be very helpful and
appreciated.
Posted by Will Green (hotgazpacho)
on 2011-01-15 14:08
(Received via mailing list)
On Jan 15, 2011, at 2:55 AM, Joshua M. wrote:

> No matter what I do I can only access RbScriptApp and not Foo if I call
> Runtime.UseFile("global.rb"). So far I have two other work arounds: 1.)
> create wrapper methods for everything I want to access in app.rb (ugh),
> or 2.) use Engine.Execute("RbScriptApp::Foo.new"). The latter is more
> preferrable, I suppose, but it seems strange that I can't access Foo by
> dynamic.

That's how Modules work in Ruby. Just like namespaces in C#.

Try adding

include RbScriptApp

to your global.rb file. You need to include the Module if you want to 
access the class without qualifying it.


> Any more information about this issue would be very helpful and
> appreciated.
>
> --
> Posted via http://www.ruby-forum.com/.
> _______________________________________________
> Ironruby-core mailing list
> Ironruby-core@rubyforge.org
> http://rubyforge.org/mailman/listinfo/ironruby-core

--
Will Green
will@hotgazpacho.org
Posted by Tomas Matousek (Guest)
on 2011-01-15 19:11
(Received via mailing list)
Another option would be

RbScriptApp.const_get("Foo")

I haven't tried that, but I think it should work. Neither solution is 
indeed ideal. CLR interop with Modules isn't quite polished yet.

Tomas
Posted by Joshua M. (joshua_m)
on 2011-01-15 22:15
>You need to include the Module if you want to
>access the class without qualifying it.

I have tried this but it doesn't work (RuntimeBinderException:
'Microsoft.Scripting.Hosting.ScriptScope' does not contain a definition
for 'Foo').

In my original post I had specified the fully qualified name.

>RbScriptApp.const_get("Foo")

InvalidOperationException: Empty scope has no global scope.

>I haven't tried that, but I think it should work. Neither solution is
>indeed ideal. CLR interop with Modules isn't quite polished yet.

Ah, that would explain it. I guess I'll hold off on integrating IronRuby
just yet.

--

Thank you both for your time.
Posted by Rob R. (rob_r)
on 2011-02-24 15:41
to add directories to your search path from within C# you can do 
something like this:


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);

Let me know if you find out how to access classes that are within 
modules from C#.  I'm having no luck.
Posted by Shay Friedman (shayfriedman)
on 2011-02-25 07:43
(Received via mailing list)
I got it working using a kind of a hack/workaround.

I know this is kind of a hack/workaround, but I managed to do it this 
way:

I added the next code to the end of the ruby file:

    def hack(s)
      eval(s)
    end

And then used it from the C# code to get the class object:

    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);

Kind of a hack, but hey, it works! :)

Shay.
--------------------------------------------------------
Shay Friedman |  Co-Founder @ CodeValue <http://codevalue.net/> |
C#/IronRuby MVP | Author of IronRuby Unleashed
Blog: http://IronShay.com <http://ironshay.com/> | Twitter:
http://twitter.com/ironshay
Posted by Tomas Matousek (Guest)
on 2011-02-25 09:38
(Received via mailing list)
Wouldn't this work?

dynamic globalConstants = engine.Runtime.Globals

globalConstants.MyModule.const_get('MyClass')

assuming

module MyModule
  class MyClass
  end
end

Haven't tried. Let me know if it works.

Tomas

From: ironruby-core-bounces@rubyforge.org 
[mailto:ironruby-core-bounces@rubyforge.org] On Behalf Of Shay Friedman
Sent: Thursday, February 24, 2011 10:38 PM
To: ironruby-core@rubyforge.org
Subject: Re: [Ironruby-core] Accessing a Module's Classes from C# with 
dynamic

I got it working using a kind of a hack/workaround.

I know this is kind of a hack/workaround, but I managed to do it this 
way:

I added the next code to the end of the ruby file:

    def hack(s)
      eval(s)
    end

And then used it from the C# code to get the class object:

    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);

Kind of a hack, but hey, it works! :)

Shay.
--------------------------------------------------------
Shay Friedman |  Co-Founder @ CodeValue<http://codevalue.net/> | 
C#/IronRuby MVP | Author of IronRuby Unleashed
Blog: http://IronShay.com<http://ironshay.com/> | Twitter: 
http://twitter.com/ironshay



On Thu, Feb 24, 2011 at 4:41 PM, Rob R. 
<lists@ruby-forum.com<mailto:lists@ruby-forum.com>> wrote:
to add directories to your search path from within C# you can do
something like this:


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);

Let me know if you find out how to access classes that are within
modules from C#.  I'm having no luck.

--
Posted via http://www.ruby-forum.com/.
Posted by Shay Friedman (shayfriedman)
on 2011-02-25 13:12
(Received via mailing list)
const_get doesn't work, but you can do
globalConstants.MyModule.constants()[0] but then you get back an object
which you can't initialize...

Shay.



On Fri, Feb 25, 2011 at 10:25 AM, Tomas Matousek <
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.