My hobbyist coding updates and releases as the mysterious "Mr. Tines"

Showing posts with label Ruby. Show all posts
Showing posts with label Ruby. Show all posts

Saturday, 29 November 2008

Python Swings; Ruby still doesn't

Here's a proof-of-concept of running a Swing UI from Python on the CLR and the JVM, following up on earlier experiments (which I had forgotten about).

try:  
  # IronPython must load the J# redistributable libraries  
  # Using the FullName of the assemblies is probably overkill  
  import clr  
  clr.AddReference( 'vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' )  
  clr.AddReference( 'vjssupuilib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' )  
except ImportError:  
  pass  
     
import javax.swing  
import java.awt.event  
import java.lang  
  
class Exit(java.awt.event.WindowAdapter):  
  def windowClosing(self, evt):  
    evt.getWindow().setVisible(False)
    evt.getWindow().dispose()
  def windowClosed(self, evt):  
    java.lang.System.exit(0) 
    
# With the 'import's above, we can now refer to things that are part of the  
# standard Java platform via their full paths.  
frame = javax.swing.JFrame("Window") # Creating a Java JFrame.  
label = javax.swing.JLabel("Hello")  
     
# We can transparently call Java methods on Java objects, just as if they were defined in Python.  
frame.getContentPane().add(label)  # Invoking the Java method 'getContentPane'.  
frame.addWindowListener(Exit()) # Need to do this for IronPython/J# which is barely JDK 1.2 level  
frame.pack()  
frame.setVisible(True)

which works happily with java -jar jython-complete.jar HelloSwing.py using Jython 2.5β0 and with ipy.exe HelloSwing.py for IronPython2.0RC2 as well.

I was briefly conflicted earlier this week when I saw that IronRuby had reached α2, and wondered about a JRuby/IronRuby UI, but the nigh-equivalent

begin  
  require 'vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'  
  require 'vjssupuilib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'  
rescue LoadError  
  # This is the 'magical Java require line'.  
  require 'java'  
end    
        
class Exit < java.awt.event.WindowAdapter
  def windowClosing evt
    evt.getWindow.setVisible(false)
    evt.getWindow.dispose # make Closed
  end
  def windowClosed evt
    java.lang.System.exit 0 
  end
end

# With the 'require' above, we can now refer to things that are part of the  
# standard Java platform via their full paths.  
frame = javax.swing.JFrame.new("Window") # Creating a Java JFrame.  
label = javax.swing.JLabel.new("Hello")  
     
# We can transparently call Java methods on Java objects, just as if they were defined in Ruby.  
frame.getContentPane.add(label)  # Invoking the Java method 'getContentPane'.  
frame.addWindowListener(Exit.new()) # Need to do this for IronRuby/J# which is barely JDK 1.2 level  
frame.pack  
frame.setVisible(true)  

which works on JRuby 1.1.5, fails in IronRuby α2 when trying to load Swing classes, and I've not found a dodge that will get me at even class metadata for the Swing files. Ruby's casing rules may make it impossible for IronRuby to recognise the lower-case Java namespaces (I had fun with that earlier with Ruby.Net).

LATER: I could do something VM dependent that takes the class name and argument list for construction in {Iron|J}Ruby. What is more tricky is inheritance like

class Exit < java.awt.event.WindowAdapter

which is a major use-case. I wonder if there might be a metaprogramming technique I can use for this...

LATER YET: The problem is that there is no exposed mechanism to wrap a System.Type as a Ruby Class; I could manually subclass everything that is not final that I want in a C# assembly with a My.JavaNamespace and emulate the way that JRuby maps namespace Java::JavaAwtEvent to a value named java.awt.event, but the nearest to automation I can see is to Reflection.Emit a whole ton of stuff.

So, for the moment, consider it a dead end.

Friday, 6 June 2008

Erlang with Python and Ruby

Having played a bit with Erlang for real, in the sense of something non-trivial, even if not a complete product, I find I'm in the mood for more. But while it comes with some UI widgets, it's something more for headless use, and some client(s) in other languages doing the display.

To date, I can easily do Swing or WinForms as the UI toolkit -- but what about wxPython or FXRuby?

There are a number of bridges that I've found, but the closest to jinterface seem to be:

Neither of them have "just worked" for me in the way that the Java and C# versions have done against the simple mathserver example. Some of it might be down to my not figuring how the differences in the APIs are meant to work.

For py_interface, I had to comment out the line

[__import__(item) for item in __all__]
from init.py to get imports to work at all; and even then, the simple

from otp import *

def Handler(Result):
    print Result

cNode = erl_node.ErlNode("clientnode@chloe.ravnaandtines.com", erl_opts.ErlNodeOpts(cookie="cookie"))
print cNode
print cNode.Publish()
mBox = cNode.CreateMBox()
print mBox
mBox.SendRPC("servernode@chloe.ravnaandtines.com", 
    "mathserver", "add", [1, 2], Handler)
raw_input("wait")

just sits there; while the Ruby code

require 'erlang_node'
 
s_node = Erlang::Node.new("servernode@chloe.ravnaandtines.com")
c_node = Erlang::LocalNode.new("clientnode@chloe.ravnaandtines.com", "cookie")
connection = Erlang::Connection.new(s_node, c_node)
connection.sendRPC("mathserver", "add", [1,2])
sum = connection.receiveRPC()
puts "Fail" unless sum == 3

throws a different exception every time I run it, from some place or other inside the connection initializer.

So they both need deeper inspection than this simple kicking of the tyres, maybe modifying for R12 nodes, maybe just reverse engineering the kinks out of the driver programs.

Tuesday, 15 January 2008

Ruby.Net and Ruby.Net — foiled again

A reboot (of the machine, and myself) later, I realised that the obscure "Wrong argument type String (Class expected)" was the result of a sleep-addled 'C'-drenched brain putting include 'Wizard' where require 'Wizard' was expected.

Not so good with the memory issue. After a couple of hours debugging and finally getting the code to interoperate, DevStudio was peaking at over 370Mb of memory, and still thrashing (on a 512Mb machine), while building a project with < 1000 lines of Ruby. NetBeans, by comparison has about 150Mb (if I have a lot of files open)in the IDE and and under 40Mb in the spawned application.

I've put the Interop example code -- showing how to call from shared code JRuby/Ruby.Net to shared-code Java/J# libraries. main.rb calls Wizard.rb which calls through the interop layer defined by .jsl files for Ruby.Net->J#, and Interfaces.rb plus the .java files for JRuby->Java -- on one of my sites, for those who are interested in taking this further.

Until I upgrade my hardware to something that permits more than a toy Ruby.Net layer, though, I don't think I'll be taking the ideal of a shared code/multi-VM project further with Ruby. Which sucks, because that has the best IDE support (I don't even know if the Jython add-in for NetBeans still works at 6.0).

Still a little experimentation is needed with that, and with Scala, to get to an equivalent proof of concept stage.

Monday, 14 January 2008

Ruby.Net and Ruby.Net — first steps

Not a typo, alas.

I was testing calling from a Ruby.Net .exe file into a Ruby.Net .dll, to see if partitioning like that would work; though not in such a simple setup as that : I was trying to test a Ruby implementation of a wizard dialog using the GWT... So I create a Testbed.Net.exe that refers to gwt_ruby.dll (which is built from a single source file Wizard.rb).

That fails with a Key Not Found Exception, and looking at the Ruby.Net code, and ildasm-ing the assembly, I discern that it was looking for a gwt_ruby.rb file having been built into the project. So I give it one to satisfy it

require 'Wizard'
class Gwt_Ruby
end

Later...

The Ruby.Net build is being a voracious memory hog on my 3 year old machine with only 512Mb RAM -- even for only a few hundred lines of Ruby code each build and test is taking more than half an hour of thrashing madly at almost 0% CPU. And I thought that NetBeans was memory hungry -- that I can work on in realtime, with the same code in JRuby. I'm not sure that testing this is going to be practical, even without having to work around packaging quirks.

It's almost enough to drive me to Scala!

Thursday, 20 December 2007

J# and Ruby.Net — string and baling wire

A proof of concept

//Factory.java
public class Factory
{
    private Factory(){}

    public static Object Build(String classname) 
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException
    {
        Class target = Class.forName(classname);
        return target.newInstance();
    }
}
//Utility.java
package Tinesware.Adapter;

public class Utility
{
    private Utility() { }

    public static void exit(int status)
    {
        java.lang.System.exit(status);
    }
}
//WindowAdapter.java
package Tinesware.Adapter;

public class WindowAdapter implements java11.awt.event.WindowListener  
{
    private final Ruby.Object payload;

    public WindowAdapter(Ruby.Object arg)
    {
        payload = arg;
    }

    public void windowActivated(java11.awt.event.WindowEvent e)
    {
    }

    public void windowClosed(java11.awt.event.WindowEvent e)
    {
        System.out.println("window Closed");
        Object[] args = {e};
        Ruby.Runtime.Eval.Call(payload, "windowClosed", args);
    }

    public void windowClosing(java11.awt.event.WindowEvent e)
    {
        System.out.println("window Closing");
        Object[] args = { e };
        Ruby.Runtime.Eval.Call(payload, "windowClosing", args);
    }

    public void windowDeactivated(java11.awt.event.WindowEvent e)
    {
    }

    public void windowDeiconified(java11.awt.event.WindowEvent e)
    {
    }

    public void windowIconified(java11.awt.event.WindowEvent e)
    {
    }

    public void windowOpened(java11.awt.event.WindowEvent e)
    {
    }
}
##Program.rb
require 'Tinesware.Adapter.dll'
require 'gwt221.dll'

window = Tinesware::Adapter::Factory.Build("dtai.gwt.GadgetFrame");
window.setSize (800, 600)
window.setTitle 'Proof of Concept'
window.show

class WindowHandler
  def initialize(w)
    @window = w
   end

  def windowClosing(e)
    @window.hide
  end
  def windowClosed(e)
    Tinesware::Adapter::Utility.exit 0
  end
end

adapter = Tinesware::Adapter::WindowAdapter.new(WindowHandler.new(window))
window.addWindowListener adapter

It only works grace of Ruby.Runtime.Eval.Call() being public, but that's all I need; that the event argument gets through is a bonus.

It is a mild hack, it does involve repetitive boilerplate coding, but you only have to do it for a few interface classes. And it will need some smoothing over on the Ruby side to make it look less awkward with code that's supposed to interoperate with Java as JRuby.

Extra bonus -- if the Call()'d method doesn't exist, it does nothing silently, so you only have to implement the parts of an interface you actually want.

Monday, 17 December 2007

J# and Ruby.Net — foiled again

Carrying on from last post…

// C#
        public static void typeTest(object arg)
        {
            System.Type wtf = arg.GetType();
            Console.WriteLine(wtf.ToString());
            Console.WriteLine("Base = "+wtf.BaseType.ToString());

            System.Type[] iface = wtf.GetInterfaces();
            Console.WriteLine("# interfaces = " + iface.Length);
            foreach (System.Type t in iface)
                Console.WriteLine(":: "+t.ToString())
        }
##Ruby caller
q = Observer.new
JsharpToRuby::ClassDictionary.typeTest q

yields disappointing results

Observer
Base = Ruby.Object
# interfaces = 0

Oh dear! it doesn't seem to work the naïve way for proper interfaces

// C#
namespace JsharpToRuby
{
    public interface ITest
    {
        Boolean isPresent();
    }
##Ruby 
class EyeTest 
  include JsharpToRuby::ITest
  def isPresent
    1
  end
end
q = EyeTest.new
puts q.class
q.class.ancestors.each { |x| puts x }
JsharpToRuby::ClassDictionary.typeTest q

yields

EyeTest
EyeTest
ITest
Object
Kernel
EyeTest
Base = Ruby.Object
# interfaces = 0

This is of course the bit missed out in the interop tutorial, alas; I think for the good and simple reason that it is not yet implemented (from a quick inspection of the code).

*le sigh*

Maybe not all is lost. It would mean more hand-rolled adapter classes to wrap the objects and delegate by reflection.

But later, definitely, later.

Sunday, 16 December 2007

J# and Ruby.Net — further steps

So what do we need Java classes for in a JRuby/Ruby.Net environment?

Things that we have to new because that defines system services in an unambiguous fashion — I'm thinking encodings for I/O and GUI components here, as well as libraries already written that we don't want to port just yet (my algorithms library); and interfaces/abstract classes we want to make concrete (needing to be an ActionListener or similar).

Concrete things are easy; abstractions are trickier, but not impossible.

//C# code to generate a Ruby class type
        public static Ruby.Class getThing()
        {
            object o = new java.awt.Panel();
            return Ruby.Class.CLASS_OF(o);
        }
##Ruby code to use it
thing = JsharpToRuby::ClassDictionary.getThing

puts thing.to_s

puts thing.ancestors

panel = thing.new
puts panel.to_s

which generates

Panel
Container
Component
Serializable
MenuContainer
ImageObserver
Object
Object
Kernel
java.awt.Panel[panel1,0,0,0x0,invalid,layout=java.awt.FlowLayout]

which goes … java.lang.Object, Ruby Object, …

So now try

io = nil
thing.ancestors.each { |x| io = x if x.to_s.eql? "ImageObserver" }
MyImageObserver = io

class Observer 
  include MyImageObserver
  def imageUpdate(img, infoflags, x, y, width, height) 
    nil
   end
end

q = Observer.new
puts q.class.ancestors

to get

Observer
ImageObserver
Object
Kernel

which means the task is not immediately insuperable; but may need some hand-rolling of concrete classes for some of the interesting interfaces, rather than being something that can be totally read-only…

J# and Ruby.Net — first steps

This seems to work

//C# assembly referencing the Ruby.NET.Runtime assembly
namespace Inspect
{
    public class Access
    {
        public static Ruby.Class getRubyPerson()
        {
            ClassLibrary2.nested.Person p = new ClassLibrary2.nested.Person("", 0);
            return Ruby.Class.CLASS_OF(p);
        }
    }
}
## Ruby 
x = Inspect::Access.getRubyPerson()
fred = x.new(name, age)
fred.print

It's a pity that I need an object and cannot just send a System.Type into Interop.CLRClass.Load(type, null, false);, which this ends up doing, but that method is internal to the Runtime DLL.

It should be possible, though, to have a require_jsharp('assembly.dll') that does the tedious running around back and forth through the Ruby/C# boundary in an anutmated fashion, and creates appropriate proxies for lowercase package-names in a tree-like object.

Thursday, 13 December 2007

J# and Ruby.Net — not all is rosy in the garden

Ruby expects modules to be constants (capitalised). Just like .Net namespace naming conventions dictate.

Java, OTOH, uses lower-case package names which map directly to .Net namespaces, which cause Ruby.Net to bork when trying to instantiate classes. In fact, this is a general issue — it affects any .Net module names which are not capitalised, like this C# code

namespace ClassLibrary2.nested
{
    public class Person
    {
        private String name;
        private int age;

        public Person(String name, int age)
        {
            this.name = name;
            this.age = age;
        }

        public void print()
        {
            Console.WriteLine("Hello " + name + " " + age);
        }
    }
}

which won't work when invoked by

require 'ClassLibrary2.dll'

name = 'Fred'
age = 42
fred = ClassLibrary2::nested::Person.new(name, age)
fred.print

But if you change nested to Nested, all is well.

*le sigh* — that means an adapter layer between Java-style names and Ruby/.Net ones. At least that should be doable in Java/J#, even if it makes things more painful than I had hoped.

Tuesday, 27 November 2007

Smelling the coffee…

… before work goes crazy in the 4 weeks left to holidays — a first drop of angerona.redcoffee.zip; what may (ha! ha!) become a hybrid Java/Ruby CTClib equivalent for both JVM and CLR.

This contains code for various symmetric crypto algorithms, strong hashes, erasable big numbers and Zlib compression (based on JZlib 1.0.7); it contains some initial JUnit tests aimed at JUnit 3.8.2 in NetBeans 6.0 (currently using RC1). It also contains a project to build JUnit 3.8.2 (almost) as a J# project generating a command line executable (.exe also included). The "almost" is because the assertEquals() methods for Double and boolean have been taken from 3.8.1 to permit them to compile under J#.

The NetBeans project has been amended to use cobertura 1.9 to perform coverage analysis as the unit tests are run -- assumed to be in C:\cobertura-1.9, adjust build.xml to fit your location; or use it as a prototype for other projects. The VS 2005 solution builds the JUnit ~3.8.2 executable, the angerona.algorithms.dll library, and the unit tests as three projects; and as a post-build step for the last runs the tests under NCover (assumed to be installed to directory C:\Program Files\NCover) -- I'm using 1.5.4, having had some problems with 1.5.8 run over managed C++ code not contained in gc classes.

Also included is an FxCop project; I shall be using that run manually, and PMD as a live plug-in while the code is being groomed; they will get incorporated into the build process later when the noise level has been considerably reduced.

Note that the two build environments are set up to use the same source file structures, though J# has to be hand-held to point it at the files that NetBeans just picks up automagically.

The only files in any sort of stable state at the moment, the proofs of concept, are SHA0.java and SHA1.java, which have unit tests based on the FIPS PUB 180-1 test vectors. These tests all pass and give 100% coverage (including branch coverage) to the base SHA class and the two wrappers. The first phase will be getting the state of no PMD warnings, no FxCop warnings (except where naming conventions are involved, and there, Java conventions set out by PMD will win if there is a conflict), unit tests with ideally 100% coverage; this will overlap with doing some refactoring.

Then with a stable foundation, the plan will be to gradually migrate the rest of CTClib, primarily to Ruby, but probably with a few Java interface types to make the external interface more easily usable by other JVM or CLR projects.

Archive is 257,055 bytes; MD5 7335e89a 171560f3 003f225e d97a3b3c & SHA-1 4d421c00 d4b3b45d be0244e3 649b19bd 04923a94.

Tuesday, 28 August 2007

10 Minute refactor

After a long weekend in the garden...

module Com_ravnaandtines
  module Zlib
    class Adler32
      # largest prime smaller than 65536
      @@ADLER_BASE = 65521
    
      def initialize
        @value = 1
      end
      
      def update(buffer)
        if not buffer
          return @value
        end
        ## build up the checksum
        low = @value & 0xffff
        high = (@value >> 16) & 0xffff
        buffer.each do |x| 
          low += (x.to_i & 0xff)
          high += low
        end

        ## collapse into modular parts
        low %= @@ADLER_BASE
        high %= @@ADLER_BASE
        @value = (high << 16) | low      
      end
      
      def reset
        @value = 1
      end
      
    end
  end
end

and tweak the tests thus:

  def basic_test_engine(seq, expected)
    ## string to array
    ## otherwise expect an each method to yield integers
    if seq.respond_to? :unpack
      seq = seq.unpack("C*")
    end
    adler = Com_ravnaandtines::Zlib::Adler32.new()
    a = adler.update(seq)
    assert_equal(expected, a)
  end

  def test_boundary
    basic_test_engine(nil, 1)  
  end

That feels better. No special cases, no leakage of state. Looking at JZlib, most of the work will be in doing the encapsulation properly.

Next up, though, when time and energy combine, more on the IronPython FTP client.

Friday, 24 August 2007

Selection Factors

I had started porting CTClib to managed C++ to do an Iron<something> piecemeal conversion. But at the moment IronPython doesn't have a good deployment story, IronRuby isn't all there; and I still much prefer Java's UI model to WinForms or WPF… So what about JRuby -- familiar UI style and run from jar -- then?

The stumbling block as always is PhilZ's choice of deflate with a 2^13 bit window (as opposed to Zlib's fixed 2^15 bit window size for Ruby or java.util.zip) for compression. This was where I paused my first Java port -- JZlib which could do the job has appeared since I last looked, around the turn of the century. Even so, for sake of portability I've decided to bite the bullet, and do a minimal zlib/deflate implementation in Ruby for the purpose, to do something meaningful with the language, using JZlib as a guide. Inflate can, of course, have the larger window (as in current CTClib-C builds), and use the built-in version, be it the C version from native Ruby or JRuby's java.util.zip wrapper.

So, the easy bit first -- Adler 32 checksum…

module Com_ravnaandtines
  module Zlib
    # largest prime smaller than 65536
    ADLER_BASE = 65521
    # Adler32 checksum : takes a seed (usually 1), and a byte sequence, 
    # returns 32-bit integer
    def adler32(adler, buffer)
      if not buffer
        return 1
      end
      
      ## string to array
      ## otherwise expect an each method to yield integers
      if buffer.respond_to? :unpack
        buffer = buffer.unpack("C*")
      end
      
      ## build up the checksum
      low = adler & 0xffff
      high = (adler >> 16) & 0xffff
      buffer.each do |x| 
        low += (x.to_i & 0xff)
        high += low
      end

      ## collapse into modular parts
      low %= ADLER_BASE
      high %= ADLER_BASE
      (high << 16) | low
    end
  end
end

Test vectors for the unit tests had to be scavenged from the internet:

require 'test/unit'
require 'tinesware_zlib'
include Com_ravnaandtines::Zlib

class AdlerTest < Test::Unit::TestCase

  def basic_test_engine(seq, expected)
    a = Com_ravnaandtines::Zlib.adler32(1, seq)
    assert_equal(expected, a)
  end

  def test_boundary
    a = Com_ravnaandtines::Zlib.adler32(0, nil)
    assert_equal(1, a)    
  end

  def test_simple_0
    basic_test_engine("Mark Adler", 0x13070394)
  end
  
  def test_simple_1
    basic_test_engine("\x00\x01\x02\x03", 0x000e0007)
  end
 
  def test_simple_2
    basic_test_engine("\x00\x01\x02\x03\x04\x05\x06\x07",  0x005c001d)
  end

  def test_simple_3
    basic_test_engine("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 0x02b80079)
  end

  def test_simple_4
    basic_test_engine("\x41\x41\x41\x41", 0x028e0105)
  end

  def test_simple_5
    basic_test_engine("\x42\x42\x42\x42\x42\x42\x42\x42", 0x09500211)
  end

  def test_simple_6
    basic_test_engine("\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43\x43", 0x23a80431)
  end

  class Vector #arrays filled with value = (byte) index
    def initialize(size)
      @size = size
    end
    def each
      index = 0
      while index < @size
        yield index & 0xff
        index += 1
      end
    end
  end

  def test_total
    index = 0
    results = [  486795068,
                1525910894,
                3543032800,
                2483946130,
                4150712693,
                3878123687,
                3650897945,
                1682829244,
                1842395054,
                 460416992,
                3287492690,
                 479453429,
                3960773095,
                2008242969,
                4130540683,
                1021367854,
                4065361952,
                2081116754,
                4033606837,
                1162071911 ]

    
    while index < 20
      size = 5*index + 1
      xx = Vector.new(1000*size)
      basic_test_engine(xx, results[index])
      index += 1
    end
  end

end

That was one evening. Now, how long will the rest of it take?