IronRuby and MarkLogic

Hi,

I have used IronPython to successfully connect and issue an xquery to a
MarkLogic database (native XML database) using the following script:

import clr
clr.AddReferenceToFile(“MarklogicXcc.dll”)
import Marklogic.Xcc
import System
print ‘connecting to db…’
uri = System.Uri(‘xcc://adm:adm@localhost:9003’)
contentSource = Marklogic.Xcc.ContentSourceFactory.NewContentSource(uri)
print ‘creating session…’
session = contentSource.NewSession()
print ‘submitting query…’
query = ‘let $r := //Record[@UID eq “20069812”] return $r’
request = session.NewAdhocQuery(query)
resultSequence = session.SubmitRequest(request)
print ‘printing results…’
results = resultSequence.AsString()
print results

Now, I am trying the same thing with IronRuby and here is how far I got:

require ‘System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089’
require “c:\IronRuby\build\release\MarklogicXcc.dll”

all xcc dlls have to be in the same dir as ironruby (except for

MarklogicXcc.dll)
puts ‘connecting to db…’
$uri = System::Uri.new(‘xcc://admin:admin@localhost:9003’) # help from
John L. here…thanks!
$content_source =
Marklogic::Xcc::ContentSourceFactory::NewContentSource($uri)
puts ‘creating session…’
$session = $content_source::NewSession()

For some reason, NewSession is not seen as a valid method. Here is part
of the interpreter’s output:

System.MissingMethodException: undefined local variable or method
NewSession' for #<Marklogic::Xcc::Impl::ContentSourceImpl:0x0000058>:Ruby.Builtins.RubyClass at Ruby.Builtins.Kernel.MethodMissing(CodeContext context, Object self, Block Param block, SymbolId name, Object[] args) at Microsoft.Scripting.Utils.InvokeHelper6.Invoke(Object arg0,
Object arg1,
Object arg2, Object arg3, Object arg4)

The NewSession method is certainly part of the Marklogic DLL. I see it
with Object Browser in Visual Studio, I used it successfully with
IronPython above and with the production code I have, which is in C#.

To make sure that I am not getting anything funny returning to
$content_source, I tried this:
$session = Marklogic::Xcc::ContentSource::NewSession(“someId”)
But, still got the same error message…

Any thoughts???

Thanks!

I think instead of
$content_source::NewSession()

you want
$content_source.NewSession()

:: is a scoping operator.

-Eric

Eric N. wrote:

I think instead of
$content_source::NewSession()

you want
$content_source.NewSession()

:: is a scoping operator.

-Eric

Thanks Eric…but still the same error. Here is the whole error output:

System.MissingMethodException: undefined local variable or method
NewSession' f or #<Marklogic::Xcc::Impl::ContentSourceImpl:0x0000058>:Ruby.Builtins.RubyClass at Ruby.Builtins.Kernel.MethodMissing(CodeContext context, Object self, Block Param block, SymbolId name, Object[] args) at Microsoft.Scripting.Utils.InvokeHelper6.Invoke(Object arg0,
Object arg1,
Object arg2, Object arg3, Object arg4)
at Microsoft.Scripting.Utils.ReflectedCaller.Invoke(Object[] args)
at Microsoft.Scripting.Ast.MethodCallExpression.InvokeMethod(Object
instance,
Object[] parameters)
at
Microsoft.Scripting.Ast.MethodCallExpression.DoEvaluate(CodeContext
contex
t)
at Microsoft.Scripting.Ast.ReturnStatement.DoExecute(CodeContext
context)
at Microsoft.Scripting.Ast.Statement.Execute(CodeContext context)
at Microsoft.Scripting.Actions.StandardRule1.ExecuteTarget(Object site, Code Context context, Object[] args) at Microsoft.Scripting.Actions.ActionBinder.UpdateSiteAndExecute[T](CodeConte xt callerContext, DynamicAction action, Object[] args, Object site, T& target, R uleSet1& rules)
at
Microsoft.Scripting.Actions.FastDynamicSite2.UpdateBindingAndInvoke(T0 ar g0) at Microsoft.Scripting.Actions.DynamicSiteHelpers.UninitializedTargetHelper7
.FastInvoke1(FastDynamicSite2 site, T0 arg0) at Microsoft.Scripting.Actions.FastDynamicSite2.Invoke(T0 arg0)
at helloml$mod_1.main$1() in helloml.rb:line 0
at Ruby.Runtime.RubyOps.RunMain(CodeContext context, Object main)
at helloml$mod_1.Initialize(CodeContext ) in helloml.rb:line 0
at Microsoft.Scripting.ScriptCode.Run(CodeContext codeContext,
Boolean tryEva
luate)
at Microsoft.Scripting.ScriptScope.Execute()
at Ruby.Hosting.RubyCommandLine.RunFile(String path)

Eric N. wrote:

I think instead of
$content_source::NewSession()

you want
$content_source.NewSession()

:: is a scoping operator.

:: can be used to invoke methods just as well:

‘foo’::reverse # => ‘oof’

It just has additional functionality for locating constants in a given
namespace.

  • Charlie

N. F.:

John L. here…thanks!
#Marklogic::Xcc::Impl::ContentSourceImpl:0x0000058:Ruby.Builtins.Ruby
Class

What’s the visibility of ContentSourceImpl type and the NewSession
method? If you could do a quick dump via Reflector that would help.

Thanks,
-John

John L. (DLR) wrote:

N. F.:

John L. here…thanks!
#Marklogic::Xcc::Impl::ContentSourceImpl:0x0000058:Ruby.Builtins.Ruby
Class

What’s the visibility of ContentSourceImpl type and the NewSession
method? If you could do a quick dump via Reflector that would help.

Thanks,
-John

Thanks again John. Here is a dump via Reflector:

internal class ContentSourceImpl : ContentSource
{
// Fields
private readonly ContentSource source;

// Methods
internal ContentSourceImpl(ContentSource source);
public Session NewSession();
public Session NewSession(string contentbaseId);
public Session NewSession(string userName, string password);
public Session NewSession(string userName, string password, string 

contentbaseId);
}

So, this is what I get from the call to…

$content_source =
Marklogic::Xcc::ContentSourceFactory::NewContentSource($uri)

…and here is the dump of that:

public static ContentSource NewContentSource(Uri uri)
{
ContentSource source;
try
{
source = new
ContentSourceImpl(ContentSourceFactory.newContentSource(new
URI(uri.ToString())));
}
catch (XccConfigException exception)
{
throw new XccConfigException(exception);
}
return source;
}

Thanks for the help.

N.F.:

Thanks again John. Here is a dump via Reflector:

internal class ContentSourceImpl : ContentSource {

Pure speculation, but we might be deciding that the method isn’t
callable because it’s on an internal type. We need to search the class
hierarchy to find the visible type so we can emit the call to the method
on the visible type. Maybe there’s a spot where we don’t do that
correctly.

If you’re up for diving into IronRuby’s method binding logic, the place
to put a breakpoint is in ironruby/runtime/RubyActionBinder.cs,
MakeRuleForInvokeMember, in particular the line:

RubyMethodBase method = ec.ResolveMethod(target, action.Name);

At some point we’ll hit that with action.Name == “NewSession”. Then step
into ResolveMethod and see what happens. It should eventually get to
RubyClass.TryGetClrMember with name == “NewSession”. But maybe it gets
lost somewhere along the way…

  • John

N.F.:

  • AND HERE IS THE PROBLEM…I think…
  • inside RubyModule.cs (line 481):
    // skip lookup on types that are not visible or interfaces:
    if (_tracker != null && _tracker.Type.IsVisible &&
    !_tracker.Type.IsInterface) {
    return TryGetClrMember(_tracker.Type, name, out method); } …this if
    returns false and it never calls TryGetClrMember!

Yup, I think you found the problem. I didn’t get a chance to look at it
today, but I’ll take a look at it Monday (unless I can get Tomas to fix
it :slight_smile: ). Hopefully it’s an easy fix

  • John

John M. wrote:

If you’re up for diving into IronRuby’s method binding logic, the place
to put a breakpoint is in ironruby/runtime/RubyActionBinder.cs,
MakeRuleForInvokeMember, in particular the line:

RubyMethodBase method = ec.ResolveMethod(target, action.Name);

At some point we’ll hit that with action.Name == “NewSession”. Then step
into ResolveMethod and see what happens. It should eventually get to
RubyClass.TryGetClrMember with name == “NewSession”. But maybe it gets
lost somewhere along the way…

  • John

Thanks John…I took up the task and here is what I found out (the most
important point is the last one - in uppercase - see below:

  • calls
    GetImmediateClassOf(target).ResolveInstanceMethod(name);

  • target has only non-public members

  • module is null, so it calls
    RubyClass result = TryGetInstanceSingletonOf(obj, out data);

  • calls
    data = TryGetInstanceData(obj);

  • obj (Marklogic.Xcc.Impl.ContentSourceImpl) is null inside of that

  • calls
    TryGetData(object key)

-eventually returns from TryGetInstanceData with result = null

  • calls
    GetClassOf(obj, data);

  • AND HERE IS THE PROBLEM…I think…

  • inside RubyModule.cs (line 481):
    // skip lookup on types that are not visible or interfaces:
    if (_tracker != null && _tracker.Type.IsVisible &&
    !_tracker.Type.IsInterface) {
    return TryGetClrMember(_tracker.Type, name, out method);
    }
    …this if returns false and it never calls TryGetClrMember!

  • Nivaldo

John M. wrote:

Yup, I think you found the problem. I didn’t get a chance to look at it
today, but I’ll take a look at it Monday (unless I can get Tomas to fix
it :slight_smile: ). Hopefully it’s an easy fix

  • John

Hi John, do you (or Tomas) still plan to take a look at this? Thanks!

N.F.:

Hi John, do you (or Tomas) still plan to take a look at this? Thanks!

Thanks for the ping. I think the simple fix actually works fine here.
Let me know if it works for you & I’ll get it checked in.

  • John

Index: src/ironruby/Runtime/RubyModule.cs

— src/ironruby/Runtime/RubyModule.cs (revision 66)
+++ src/ironruby/Runtime/RubyModule.cs (working copy)
@@ -483,8 +483,8 @@
return true;
}

  •        // skip lookup on types that are not visible or interfaces:
    
  •        if (_tracker != null && _tracker.Type.IsVisible && 
    

!_tracker.Type.IsInterface) {

  •        // skip lookup on interfaces:
    
  •        if (_tracker != null && !_tracker.Type.IsInterface) {
               return TryGetClrMember(_tracker.Type, name, out 
    

method);
}

John M. wrote:

Thanks for the ping. I think the simple fix actually works fine here.
Let me know if it works for you & I’ll get it checked in.

Hi John…it worked great! Thanks so very much…I went ahead and pulled
the record from the MarkLogic server and all looks good. Here is the
sample code (very basic…just to show it works) in case anyone may
care:

require ‘System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089’
require “c:\IronRuby\build\release\MarklogicXcc.dll”

all xcc dlls have to be in the same dir as ironruby (except for

MarklogicXcc.dll)

uri = System::Uri.new(‘xcc://adm:adm@localhost:9003’)
content_source =
Marklogic::Xcc::ContentSourceFactory::NewContentSource(uri)
session = content_source::NewSession()
query = ‘let $r := //Record[@UID eq “200608199-060”] return $r’
request = session.NewAdhocQuery(query)
resultSequence = session.SubmitRequest(request)
results = resultSequence.AsString("/n")
puts results

– Nivaldo

Hi,

This problem reported here and fixed by John M. is back in the
IronRuby Alpha release at http://rubyforge.org/projects/ironruby.

So, the patch by John never made it “officially”?

Thanks!

N. F. wrote:

John M. wrote:

Thanks for the ping. I think the simple fix actually works fine here.
Let me know if it works for you & I’ll get it checked in.

Hi John…it worked great! Thanks so very much…I went ahead and pulled
the record from the MarkLogic server and all looks good. Here is the
sample code (very basic…just to show it works) in case anyone may
care:

require ‘System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089’
require “c:\IronRuby\build\release\MarklogicXcc.dll”

all xcc dlls have to be in the same dir as ironruby (except for

MarklogicXcc.dll)

uri = System::Uri.new(‘xcc://adm:adm@localhost:9003’)
content_source =
Marklogic::Xcc::ContentSourceFactory::NewContentSource(uri)
session = content_source::NewSession()
query = ‘let $r := //Record[@UID eq “200608199-060”] return $r’
request = session.NewAdhocQuery(query)
resultSequence = session.SubmitRequest(request)
results = resultSequence.AsString(“/n”)
puts results

– Nivaldo