Showing posts with label ironruby. Show all posts
Showing posts with label ironruby. Show all posts

Friday, August 29, 2008

Marshaling the Ruby Way

This post is for the .NET developer who is looking at IronRuby but is otherwise unfamiliar with Ruby. As you learn Ruby, keep in mind a question that Ruby programmers like to ask themselves a lot, "What is the Ruby Way of solving this problem?" Today the problem is marshaling an object and we're going to solve it the Ruby Way.

If you're like most other .NET developers, you're probably thinking about BinaryFormatters or perhaps you prefer XmlSerializers. Maybe you're thinking about DeserializationCallbacks or XmlIgnoreAttributes and where to put them. Stop right there! That's definitely not the Ruby Way.

Let's define a Ruby class:
class Person
attr_reader :first_name, :last_name, :hobbies

def initialize(first_name, last_name, *hobbies)
@first_name = first_name
@last_name = last_name
@hobbies = hobbies
end
end

This is just a simple class with three read-only attributes: first_name, last_name, and hobbies. How would we create an instance of our class and marshal it the Ruby Way?
ray = Person.new("Ray", "Vernagus", "programming", "walking")
yml = ray.to_yaml
puts yml

to_yaml, what's that? YAML stands for "YAML ain't markup language." Did that make you smile? Now that's the Ruby Way! ;) YAML is very simple but also very powerful. It's the kind of notation that you would write yourself if you were just jotting a class down on a piece of paper except that it's structured and standardized. Here's what our marshaled person looks like:
--- !ruby/object:Person
first_name: Ray
hobbies:
- programming
- walking
last_name: Vernagus

Not bad, aye? That's about as ugly as you're likely to see YAML get. I encourage you to learn more about YAML and to use it in place of XML every chance that you get.

We can get our person back just as easily:
p = YAML.load(yml)
puts p.inspect

and this prints:
#<Person:0x0000064 @first_name="Ray", @hobbies=["programming", "walking"], @last_name="Vernagus">


This is all running in IronRuby (batteries-included version).

Wednesday, August 27, 2008

Threading in IronRuby (Part 2)

In my last post I pointed out some issues with the Thread.new construct in IronRuby. One of the advantages of using IronRuby is that we have access to the .NET framework. In this post, we'll take a look at using the ThreadPool class in order to correct some of the issues that came up in the previous post.

The first issue that we saw with the Thread.new construct was that it launched the thread as soon as we declared it. When using the ThreadPool, we first declare a callback object:
require "mscorlib"
include System::Threading

callback = WaitCallback.new { puts "pool thread" }

We declare a callback in a similar manner to a Thread, by passing a block to the constructor. In this case, however, we get a WaitCallback object that we can hold on to until we are ready to execute the block. You can run the code block by queuing it up in the ThreadPool:
ThreadPool.queue_user_work_item callback

You may need to pause the main thread in order to see the line print to the console, giving us this as a full example:
require "mscorlib"
include System::Threading

callback = WaitCallback.new { puts "pool thread" }
ThreadPool.queue_user_work_item callback

Thread.sleep 100


The other issue that I mentioned concerning the Thread.new construct was that it defaulted to a foreground thread. This isn't a problem when using the ThreadPool since all ThreadPool threads are background threads.

A lot of this code is very un-Ruby-like. For instance, it would be nice to just write:
ThreadPool.queue_user_work_item { puts "pool thread" }

Hopefully future versions of IronRuby will make it easier to pass blocks in place of delegates. That would definitely make these tasks feel more like Ruby.

Monday, August 25, 2008

Threading in IronRuby (Part 1)

I couldn't find much at all on the Web about threading in IronRuby. Here's a little introduction to some basic threading in IronRuby.

In contrast to Ruby's less than ideal threading model (which is to change in 1.9), .NET has a rich threading model that we can tap into using IronRuby. We can create a new thread in IronRuby in the same way that we do in MRI Ruby:
Thread.new { puts "child thread" }

In IronRuby, however, we're not talking "green" threads. Instead, we get a real .NET thread as demonstrated in this example:
require "mscorlib"
include System::Threading

puts "##{Thread.current_thread.managed_thread_id} is the main thread."
# #1 is the main thread.

Thread.new { puts "##{Thread.current_thread.managed_thread_id} is a child thread." }
# #3 is a child thread.

You may have noticed that we didn't have to do anything to launch the thread. This is undesirable if you need to, for instance, start a thread as a background thread. Since the default value for the IsBackground property is false, the thread in the example above runs as a foreground thread. If your thread runs long enough, you can do this:
require "mscorlib"
include System::Threading

t = Thread.new { Thread.sleep 1000 }
puts "t.is_background = #{t.is_background}"
# t.is_background = false

t.is_background = true
puts "t.is_background = #{t.is_background}"
# t.is_background = true

Use of Thread.new will probably suffice in only the simplest situations. IronRuby supports more advanced threading scenarios and we'll get started on that in the next post in this series!

Tuesday, October 2, 2007

Extension Methods and IronRuby

You may or may not be excited about C# getting extension methods but this idea is a core part of Ruby. IronRuby carries this spirit into .NET where it's a simple matter to add a method to an existing class--even a BCL class.

As rich as the BCL classes are, there are times where a bit of functionality is just plain missing. In sticking with true object-oriented practices, Ruby makes it easy to add that functionality where it belongs.

Suppose that you are constantly turning your strings into questions. Instead of creating a Questionizer class that takes a string and manipulates it in some way, wouldn't it be nice to just store our questionizing functionality on the strings themselves? Enter the IronRuby prompt and type the following:
>>> class String
... def questionize
... self + ", eh?"
... end
... end
=> nil


You just added a method to all String objects. Now you can use your method anywhere that you have a string object:
>>> "Nice weather".questionize
=> "Nice weather, eh?"


Now go forth and put your functionality where it clearly belongs!

Monday, July 23, 2007

IronRuby Tests

I'm sure there's a lot to be learned from the IronRuby code base but one thing in particular stuck out in my mind: the test code is actually good Ruby code.

This was in stark contrast to the Ruby.NET test code which looks more like LISP to me than Ruby. Don't get me wrong, there's a lot to impress the Ruby enthusiast in the Ruby.NET project especially since it has been moved to an open source contribution model.

My point is simply that I have confidence that IronRuby will be true to the Ruby spirit. For one, they allow for Ruby-casing when calling .NET classes, e.g., Environment.user_name instead of Environment.UserName. For another, they've obviously been studying RSpec (this is a good thing!). This is the kind of code that I like to see behind anything that I use:
describe "range creation" do

it "creates an inclusive range object from integer literals" do
a = 0..1
a.class.name.should == "Range"
a.begin.should == 0
a.end.should == 1
a.exclude_end?.should == false
end

end


The IronRuby test suite would be a great model for the various Ruby specification projects that are out there. It's a from-the-ground-up, behavioral specification for any Ruby language implementation.

It's an exciting time for Ruby programmers who have (or want) to use .NET!

IronRuby & C# Express

Microsoft won my heart today by releasing IronRuby and by announcing that it will indeed be taking outside contributions.

As if this weren't enough, I was able to build the entire IronRuby project with C# Express Edition. This isn't something that can be done with the Ruby.NET project.

It's because of things like this that I've been spending less and less time in Linux lately...