Wednesday, December 02, 2009

Hosting IronRuby in .Net applications - Part1

This post provides necessary information to host IronRuby in .Net applications. I assume no knowledge of Ruby or DLR from reader. It starts with basic introduction to IronRuby and then provides information on DLR and hosting options.

Ruby is a dynamic, open source programming language with a focus on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write. IronRuby is a Open Source implementation of the Ruby programming language for .NET, heavily relying on Microsoft's Dynamic Language Runtime.

This is the first part of this series. This part familiarizes the ruby language as much required for this series. For better understanding of ruby language reader can refer to Ruby Programming, IronRuby Documentation, Ruby Learning Tutorial, Ruby Manual.

Getting started

Though prior knowledge of Ruby is an added advantage a novice can still follow this post to get started with Ruby.

Before we can start Download IronRuby 0.9.2 msi. After installing the msi you should find “~\IronRuby 0.9.2\IronRuby Console” in Start –> Programs. This is an interactive console. We can also run a script using “ir.exe <script>”. ir.exe can be found at “<installation path>\IronRuby 0.9.2\bin\ir.exe".

Enter following lines in a notepad and save the file with .rb extension.

ex01.rb

puts 'Hello World'
print "Hello World\n"
puts "Hello World"
puts "Hello World";

Now run this example from command prompt. “ir.exe ex01.rb”. You can test these three lines from interactive console also.

Observations from this example are


  • puts or print either one works to print a message

  • semi-colon at the end of a line is optional

  • Single quote or double quotes, either one works. There is subtle difference between them regarding allowed escape sequences line new line etc.

  • There is no need of main or class to start a hello world program

OK we have just written a ruby script. There is no main/Main here. OK that doesn’t mean we can’t have functions. Let us see them.

ex02.rb

def sayHello
puts 'Hello World'
end

sayHello
sayHello()
sayHello();

Place this code in ex02.rb and run “ir.exe ex02.rb”. All three statements produces same result.

Observations from this example are


  • functions are defined as def and end blocks

  • there is no return type specified. In ruby we don’t specify a return type

  • definitions must be declared prior to calling them

  • all definitions should start with small letter. Though a capital letter as starting character works sometimes, that is not a good practice. Avoid it

  • definition can be called is all three forms. It there are no arguments braces are optional. Again last semi-colon is optional

Fine. Let us pass an argument and get a return parameter.

ex03.rb

def getSQRT(arg)
Math.sqrt(arg)
end

sqrt = getSQRT(ARGV[0]);

puts sqrt

Place this code in ex03.rb and run “ir.exe ex03.rb 4”. Square root of 4, 2.0 is printed on screen.

Observations from this example are

  • command arguments are in ARGV array starting from index zero

  • arguments doesn’t take any type parameter

  • return call is optional. If no return statement is coded, result of last statement is passed as return value

  • sqrt parameter is a local variable, but there is no type specified. It hold return value of getSQRT call

  • Math is a module which is implicitly available. We didn’t write anything line using/import Math

Now let us see how comparison works.

ex04.rb

def compareValues()
return $i1 > $i2
end

$i1 = ARGV[0]
$i2 = ARGV[1]

comp = compareValues();

puts comp

Place this code in ex04.rb and run “ir.exe ex04.rb 2 1”. ‘true’ is printed on screen

Observations from this example are

  • return keyword is specified though it is optional

  • global variables as prefixed with $. This ‘$’ sign specifies that the variable is a global variable.

  • Variable scopes are

    • Local variable : Defined with a block. Starts with lower case letter or ‘_’. ex: var1, _var2

    • Instance variable : Defined within a class (classes are covered later) as a member. Starts with ‘@’ ex: @inst1, @inst2

    • Class variable: These are like static variable in a class. Only one instance is allowed and the same is shared among all objects. Starts with ‘@@’. ex: @@cls1, @@cls2

    • Global variables: These are accessible for all definitions. Starts with ‘$’. eg: $g1, $g2

    • Constants are started with a upper case letter. ex: PI, SpecificGravity

  • There are some commonly used built-ins as mentioned below

    • self - Execution context of the current method

    • nil - Expresses nothing

    • true - Expresses true

    • false – Expresses false. nil also is considered to be false, and every other value is considered to be true

Its time to take big bite.

ex05.rb

class ClassA
attr_accessor :dict

def initialize()
@dict = System::Collections::Generic::Dictionary[String,Object].new
self.dict.Add 'Argument', 12.243
end

def addElements
begin
if @dict['Argument'] == 12.243
@dict.Add 'Key1', 1
self.dict.Add('Key2','TWO')
@dict.Add('Key3', 8.765);
end
rescue StandardError => ste
puts 'StandardError occurred : ' + ste
end
end
end

clsA = ClassA.new

clsA.addElements



for i in clsA.dict.Keys
puts clsA.dict[i]
end

Place this code in ex05.rb and run “ir.exe ex05.rb”. Following results are printed on screen

12.243
1
TWO
8.765

We covered lot of ground here.

Let us start with a small pie

  • Class name to start with a capital letter.
  • attr_accessor creates a get set accessors like properties. We can declare multiple accessors in one statement like
    attr_accessor :dict, :hach, :lict
  • def initialize() is a paramterless constructor

Instance variable

  • As mentioned earlier instance variables are declared with prefix ‘@’. They can be accessed using ‘@’ notation or self.<variable> notation. Observe two lines in constructor and usage of either notation

Using CLR types and object creation

  • CLR types can be used in IronPython with syntax mentioned here.

    Observe minor difference in scope resolution operator ‘::’ and generic template notation with square brackets.

  • Also observe the notation to create objects with <Type>.new method. eg: Dictionary[String,Object].new and ClassA.new

  • We can use all methods of CLR type as is. Observe usage of ‘Keys’ property and ‘Add’ methods

Exception handling

  • ‘begin’, ‘rescue’, and ‘else’ sequences are exception handling blocks. Exceptions are raised using ‘raise’ keyword

  • rescue is like catch while raise is like throw. => operator provides a reference to current exception into the variable on right side.

  • there is option of else to catch all other exceptions

    begin
    raise 'Error occured'
    rescue Exception1 => ex1
    rescue Exception2 => ex2
    else
    Other exceptions
    end

for loop

  • Here for loop is used with iterator.
  • There is no need to specify type for iteration variable. eg. for i in clsA.dict.Keys

There is so much in it. Now let us see what ruby offers for delegation of control

ex06.rb

def AuthenticateUser(l)
if l.call('ruby', 'gem')
puts 'Authentication Successful'
else
puts 'Authentication Failed'
end
end

#Function pointer
fp = proc { |username, password|
if username == 'ruby' && password == 'gem'
return true
end
}

AuthenticateUser(fp)

#Method pointer
class Authenticate
def initialize(username, password)
@username = username
@password = password
end
def validateCredentials(username, password)
if username == @username && password == @password
return true
end
end
end

a = Authenticate.new('ruby','gem')

mp = a.method( :validateCredentials )

AuthenticateUser(mp)

Place this code in ex06.rb and run “ir.exe ex06.rb”. Two ‘Authentication Successful’ messages are printed on screen.

Here Authenticate method takes a delegate as argument. This argument is a pointer to a routine on which method can invoke a call. Above code demonstrates passing the routine as function pointer and method pointer within a class.

Observations from this example are

  • Invocation of a routine is done with .call method
  • Function pointers are obtained using ‘proc’ keyword. It can be done using keyword ‘lambda’ also. There is a subtle difference between them regarding invocation context. More details can be found at link
  • The usage within | | is arguments for routine being called followed by implementation using these arguments. It is like anonymous method in C#
  • To get the reference to a method within a class we use ‘.method’ function.

Some more examples in Part-2. And then part-3, I will cover about integrating ruby scripting feature in .Net applications.

No comments: