Ruby Tuesday #18 : Ruby meets .NET

12 08 2008

A couple of weeks ago, I posted that John Lam had announced a binary release of IronRuby.  So, I downloaded the zip file and extracted it to the root of my C drive.  That created a folder called IronRuby on my C drive containing all the IronRuby goodness.  In the IronRuby folder, there’s a bin folder in which is ir.exe, so I added C:\IronRuby\bin to my system PATH variable.  I opened a command window and typed ir and saw this:

image

Now that I had IronRuby running, I did the obvious stuff – putting Hello World, adding some numbers and so on.  Next, I tried calling .NET from IronRuby.  I typed the following lines into the console;

require ‘mscorlib’ require ‘System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′ require ‘System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ Form = System::Windows::Forms::Form $f = Form.new $f.Text = “Hello World!” System::Windows::Forms::Application.run $f

I’m using $f (a global variable for those new to Ruby) because, as the console says, local variables aren’t supported yet in console mode.  And, this is what I saw:

image

.NET written in Ruby.  There’s a lot more here to explore.





Ruby Tuesday #17 : Ain’t no party like an HTTParty

5 08 2008

This week, I noticed on RubyInside that there’s a new gem called HTTParty that simplifies calling APIs over HTTP.  Since this is exactly what I have been doing with Twitter and SSDS I thought I’d have a look at it.  First port of call is the example on RubyForge.  There’s some more examples on github - the Twitter example is here.

The next step is to take the Twitter client I wrote before and refactor it to use HTTParty.  Here’s the resulting code:

require 'httparty'

class Twitter
  include HTTParty
  base_uri 'twitter.com'
  format :xml

  def download_public_timeline
    download_timeline('statuses/public_timeline.xml')
  end

  def download_friends_timeline(username, password)
    download_timeline("statuses/friends_timeline/#{username}.xml", username, password)
  end

  def download_user_timeline(username)
    download_timeline("statuses/user_timeline/#{username}.xml")
  end

  def update(update_text, username, password)
    self.class.post("update.xml", { :query => {:status => update_text}, :basic_auth => {:username => username, :password => password}})
  end

  private

  def download_timeline(path, username=nil, password=nil)
    self.class.get(path, {:basic_auth => {:username => username, :password => password}}
  end
end

Add a little code to call the class to make sure it works:

require "Twitter"
require 'pp'
$KCODE = "u"
username = 'Put your username here'password= 'Put your password here'
client = Twitter.new
puts "Public Timeline\r\n"
puts "***********************"
pp client.download_public_timeline
puts "***********************"
puts "Friends Timeline\r\n"
puts "***********************"
pp client.download_friends_timeline(username, password)
puts "***********************"
puts "User Timeline\r\n"
puts "***********************"
pp client.download_user_timeline(username)
puts "***********************"
client.update("Tweeting with Ruby via HTTParty", username, password)

And it all works - apart from a few of the usual timeouts and grumbles from Twitter.  Less code and simpler code.  This time around I’m using pp (pretty-printer) to format the output - this gives a good view of what HTTParty is returning.  My next step with HTTParty is to use it for the SSDS code I wrote.





Ruby Tuesday #16 Part 2 : IronRuby news

29 07 2008

At OSCON last week, John Lam made a number of announcements about IronRuby (and speaking of OSCON, you should check out the announcements Sam Ramji made, too.)  In summary:

There’s a binary release of IronRuby, which includes the standard Ruby libraries.

An ironruby-contrib project has been established – another way to get involved in the IronRuby community.

A set of changes to RubySpec have been submitted.

The full blog post on the IronRuby announcements is here.  Well worth reading in full.





Ruby Tuesday #16 Part 1 : A Little Light Metaprogramming

29 07 2008

One of the reasons I was interested in learning about Ruby is metaprogramming.  For those new to Ruby, consider the following code:

class Test
 (0..5).each do |i|
    define_method "method_#{i}" do
      puts "Hello World #{i}"
    end
  end
end

Ok, it may not be the most useful piece of code in that form, but I think it shows just how metaprogrammable Ruby is.  When you remember that you can modify classes at runtime, alias methods and so on, the possibilities start to become clear.  If this has got you thinking about metaprogramming and what you could use it to achieve, this post goes into more detail on metaprogramming techniques in Ruby.





Ruby Tuesday #15 : Ruby Round Up

23 07 2008

There’s a few things I’ve noticed over the last week in the Rubyverse that I thought it was worth highligting.  In no particular order, here we go.

In my Ruby doodlings, I’ve spent quite a bit of time with REXML.  GIven that I’m focussed on learning Ruby rather than deploying code, there’s no issue.  However, this post from RubyInside shows there are alternatives such as libxml-ruby.

Testing, both TDD and BDD style, are prominent in the Ruby community.  This post examines the reasons to unit test, questions the emphasis placed on unit testing and highlights the importance of remembering why you test.  Well worth reading and relevant regardless of your preferred language.

Finally, I came across a couple of resources that those who, like me, are learning Ruby may find valuable.  The first is the news that there is a new chapter in the Book of Ruby.  The second is a couple of posts about learning Ruby for C#ers.  These posts also mention IronRuby, so well worth reading.





Ruby Tuesday #14 : Creating an Authority

15 07 2008

This week I decided to put my new-found Ruby knowledge to practical use.  Marc has embarked on a micro quest to build a game in WPF.  I’m going to contribute to the project along the way - the first contribution was to write basic save and load routines for maps.  The next step is to enhance that to support SQL Server Data Services - (I’ll blog about the non-Ruby elements to this another time.)  SQL Server Data Services (SSDS) uses an authority as the highest level of storage.  Authorities contain containers, which in turn contain entities.  The first step, then, is to create an authority.  This step is a one-off operation, so it lends itself to an administrative script - I figured Ruby would be a good choice for this task.

As it turns out, someone else must also figure Ruby is a good choice because there’s documentation on MSDN about how to create an authority using Ruby here.  In fact, it looks like there’s Ruby samples for all of the REST interface to SSDS.  Instead of just copying the code and creating the authority, I thought I’d take some of the code I’d written for the Twitter client and re-use it (copy and paste style) for this task.  I wanted the code to be as simple as possible and I ended up with this:

require 'net/https'
require 'rexml/document'

username = 'your user name goes here'
password = 'your password goes here'
authority_id = 'and here is where you put your authority id';

req_xml = "<s:Authority xmlns:s='http://schemas.microsoft.com/sitka/2008/03/'><s:Id>#{authority_id}</s:Id></s:Authority>";

response = ""

http = Net::HTTP.new('data.beta.mssds.com', 443)
http.use_ssl = true
http.start do |http|
  request = Net::HTTP::Post.new("/v1/")
  request.basic_auth(username, password)
  request['Content-Type'] = 'application/xml'
  request['Content-Length'] = req_xml.to_s.size.to_s
  request.basic_auth(username, password)
  response = http.request(request, req_xml)
end

case response
when Net::HTTPSuccess then
  puts authority_id + ' created'
  error = false
when Net::HTTPForbidden then
  puts "SSDS Access denied"
  error = true
when Net::HTTPBadRequest then
  puts "Request is not valid"
  error = true
when Net::HTTPConflict then
  puts "SSDS Authority already exists"
  error = true
else
  puts "Unexpected Error"
  error = true
end
if(error)
  # process the http response body
  xml = REXML::Document.new(response.body)
  puts "Error: #{xml.root().elements[1].name} => #{xml.root().elements[1].text}"
  puts "Error: #{xml.root().elements[2].name} => #{xml.root().elements[2].text}"
end

The big difference between this and the Twitter code is the use of SSL.  That’s led to a few differences in the use of the Net::HTTP library.  The code in the MSDN example can also handle proxies, which my code can’t, so it’s worth a look at how that’s accomplished.  The code works and I now have a freshly minted authority - I fell foul of the naming rules a couple of times (e.g. authorities can only contain lowercase characters, numbers and hyphens - more info here), but the error reporting is great at pointing out exactly what you’ve done wrong.  And with the code for creating an authority complete, creating a container should be straightforward.





Ruby Tuesday #13 : Testing

8 07 2008

Having completed a rudimentary Twitter client, I thought it was high time I figured out how unit testing works in Ruby.  The good news is that there’s a framework (called Test::Unit) distributed with Ruby.  To create a class of tests, simply create a new class and inherit from Test::Unit::TestCase.  Any method in the class that begin with test will be executed as tests.  Here’s a simple test for the translator in my Twitter client:

require 'test/unit'
require 'Twitter'
require 'rexml/document'

include REXML

class Test_Translator < Test::Unit::TestCase

    def test_element_to_user
        xml = '<user>
        <id>99999999</id>
        <name>Username</name>
        <screen_name>Screen Name</screen_name>
        <location>Location</location>
        <description>Description</description>
        <profile_image_url>image_url</profile_image_url>
        <url>url</url>
        <protected>false</protected>
        <followers_count>404</followers_count>
        </user>'
        translator = Translator.new
        element = get_user_element(xml)
        user = translator.element_to_user(element)
        assert_equal('99999999', user.id)
        assert_equal('Username', user.name)
        assert_equal('Screen Name', user.screen_name)
        assert_equal('Location', user.location)
        assert_equal('Description', user.description)
        assert_equal('image_url', user.image_url)
        assert_equal('url', user.url)
        assert_equal('false', user.protected)
        assert_equal('404', user.followers_count)
    end

    private

    def get_user_element(xml)
        xml_document = Document.new(xml)
        xml_document.root
    end
end

Run that test and it tells me there are 8 assertions in 1 test.  And there’s an error.  The error is a NoMethodError - tells me that there is no method followers_count on the User instance.  And looking at the class it’s clear why:

class User
    attr_reader :id, :name, :screen_name, :location, :description, :image_url, :url, :protected

    def initialize(id, name, screen_name, location, description, image_url, url, protected, followers_count)
        @id = id
        @name = name
        @screen_name = screen_name
        @location = location
        @description = description
        @image_url = image_url
        @url = url
        @protected = protected
        @followers_count = followers_count
    end
end

There’s an instance variable that gets set when an instance is created, but there’s no property defined.  All that’s needed is to update the first line of the class like so:

attr_reader :id, :name, :screen_name, :location, :description, :image_url, :url, :protected, :followers_count

And the test passes.  Lots more to learn in Test::Unit, but that feels like a good start.

When I started this series of Ruby Tuesdays, Matt recommended ZenTest, so I thought I’d look at that, too.  You can install it as a gem.  Once it’s installed, I called it with the following command:

zentest Twitter.rb >Test_Twitter.rb

That creates a file called Test_Twitter.rb that contains tests for the methods in Twitter.rb.  Here’s a brief excerpt:

class TestClient < Test::Unit::TestCase
    def test_download_friends_timeline
        raise NotImplementedError, 'Need to write test_download_friends_timeline'
    end

As you can see, for each method an error is raised to prompt you to write the test code.  There’s some other goodness in there like the autotest daemon that automatically runs your tests as you make changes.  And if you like to have your tests running automatically and you use Growl - this might be for you.





Ruby Tuesday #12 : Twitter on Shoes

1 07 2008

Last week I hoped I would have finished my Twitter client in Shoes by now.  Unfortunately, I ran into some issues with Shoes (such as redraws where every control ends up superimposed on top of itself but pushed out to the left and down.)  The most serious of these issues is its frequent reluctance to allow me to enter text into text boxes (or edit_lines in shoe parlance.)  I’ve found that if I click off the Shoes window and back in, it will let me enter text and click buttons.  Not ideal, but it seems to work - I even managed to tweet using my new client.  So, without further ado, here is the code:

require 'Twitter'

Shoes.app :title => "Twitter Shoes", :height => 750, :width => 750 do
    flow :margin => 10 do
         stack :width => 700, :height => 90, :top => 10, :left => 10 do
            flow :margin => 5 do
                para "Username"
                @username = edit_line :height => 30, :width => 120, :left => 100
            end
            flow :margin => 5 do

            end
            flow :margin => 5 do
                para "Password"
                @password = edit_line :secret => true, :height => 30, :width => 120, :left => 100
            end
        end
        stack :height => 90, :top => 100, :left =>10, :width => 700 do
            flow :margin => 5 do
                para "Status", :top => 10
                @status = edit_line :height => 30, :width => 270, :left => 100, :top => 10
                button "Update", :height => 30, :width => 100, :left => 400, :top => 10 do
                    username = @username.text
                    password = @password.text
                    status = @status.text
                    if username.empty?
                        alert "You must enter a username."
                    elsif password.empty?
                        alert "You must enter a password."
                    elsif status.empty?
                        alert "You must provide a status."
                    else
                        client.update(status, username, password)
                    end
                end
                button "Update Timeline", :height => 30, :width => 120, :left => 550, :top => 10 do
                    username = @username.text
                    password = @password.text
                    if username.empty?
                        alert "You must enter a username."
                    elsif password.empty?
                        alert "You must enter a password."
                    else
                        timeline = ""
                        translator.xml_to_tweets(client.download_friends_timeline(username, password)).each do |tweet|
                            timeline << "#{tweet.user.screen_name} says #{tweet.text}\n\n"
                        end
                        @tweets.replace timeline
                    end
                end
            end
        end
        stack :height => 550, :top => 200, :width => 700, :margin => 10, :left => 10 do
            background "#eee"
            @tweets = para ""
        end
    end
end

You can also download it here.  Most of the changes are in the layout.  I started the layout from scratch.  I’d suggest reading the manual that you can find here.  It’s not exactly comprehensive, but it points you in the right direction.  If you’re like me, you’ll find that changing one value has unexpected and seemingly unconnected consequences.  Still, by changing one thing at a time and sticking to a very basic design I got there.

So, you can tweet from Shoes.  Shoes uses Ruby and has some intriguing ideas (like the HTMLish layout concepts), but there’s a lot to be said for a good visual designer with drag and drop.   Gets the same job done more quickly.  Add the ability to edit by hand and we’re all happy.  I’m still in search of my ideal Twitter client, though…





Ruby Tuesday #11 Update : First Steps with Shoes

24 06 2008

When I left my last post, I had been encountering some crashes with Shoes.  So, I took the Ruby code I’d written, installed Shoes on a Mac (running Leopard) and, hey presto, it worked.  I haven’t tested another Vista PC to see if it’s Shoes or the PC I was using.  Here’s the code:

require 'Twitter'

translator = Translator.new
client = Client.new

Shoes.app :title => "Twitter Shoes", :height => 750, :width => 750 do
  background rgb(240, 250, 208 )
  stack :width => 700, :height => 1.0, :margin => 20 do
    background "#eee", :radius => 12
    border "#00D0FF", :strokewidth => 3, :radius => 12
    stack :margin => 20 do
      button "Get Timeline" do
        alert("Username is {#@username.text} and password is {#@password.text}")
        timeline = ""
        translator.xml_to_tweets(client.download_friends_timeline(@username.text, @password.text)).each do |tweet|
        timeline << "#{tweet.user.screen_name} says #{tweet.text}\n\n"
      end
      @label.replace timeline
      #alert "done"
    end
  end
  stack :height => 550, :margin => 20 do
    background "#eee"
    @label = para ""
  end
  stack :margin =>20 do
    @username = edit_line :height =>50, :width => 100
    @password = edit_line :secret => true, :height =>50, :width => 100
   end
 end
end

So, what needs to be done next now is to provide a way to tweet from the Shoes form.  And it could do with some tidying up.

To help if you’re following this series, I’ve posted the source files for Twitter.rb and App.rb.

Oh, and there’s a space between 208 and the closing bracket in the line beginning background because if I don’t put the space in, WordPress converts the sequence 8 followed by a bracket to a smiley face wearing sunglasses.  Like this 8) .  Which looks a bit weird in code.





Ruby Tuesday #11 : First Steps with Shoes

24 06 2008

Last week, I decided that Shoes was the toolkit I’d use to build a GUI for the Twitter client I’ve built in Ruby. So, I downloaded Shoes and installed it on a machine running Vista. There’s a bunch of samples that come with Shoes - to run the Timer, for example, type Shoes samples\timer.rb.

Before I got any further, I checked my feedreader and noticed that Scott Hanselman had blogged about Shoes - and said that it’d be a grat idea to build a Twitter client in Shoes. Timing is everything. Let’s put our shoes on.

I like to start simple. In fact I pretty much like to end simple, too. Anyway, I thought I’d start out with a button that downloads the public timeline when you press it. Here’s the code:

require 'Twitter'

translator = Translator.new
client = Client.new 

Shoes.app :height => 150, :width => 250 do
   background rgb(240, 250, 208 )
   stack :margin => 10 do
     button "Get Public Timeline" do
       timeline = ""
       translator.xml_to_tweets(client.download_public_timeline).each do |tweet|
         timeline << "#{tweet.user.screen_name} says #{tweet.text\n"
       end
       @label.replace timeline
     end
     @label = para ""
   end
end

OK, a couple of confessions. The first is that it looks pretty awful. The second is that on my machine the require was causing an exception in Shoes, so I replaced it with the code from Twitter.rb. I’ve shown the code here with require because it should work and it’s much simpler to read.

Let’s see if we can tidy that up a bit. There’s a couple of issues. There’s no scrollbar for the tweets and all the tweets run into each other. By putting stacks inside a containing stack, we should be able to sort that out. Here goes:

require 'Twitter'

translator = Translator.new
client = Client.new

Shoes.app :title => "Twitter Shoes", :height => 750, :width => 750 do
   background rgb(240, 250, 208 )   stack :width => 700, :margin => 20 do
     background "#eee", :radius => 12
     border "#00D0FF", :strokewidth => 3, :radius => 12
     stack :margin => 20 do
       button "Get Public Timeline" do
         timeline = ""
         translator.xml_to_tweets(client.download_public_timeline).each do |tweet|
           timeline << "#{tweet.user.screen_name} says #{tweet.text\n\n"
         end
         @label.replace timeline
       end
     end
     stack :height => 650, :margin => 20 do
        background "#eee"
        @label = para ""
     end
   end
end

I borrowed the colours and border from the jot example in the samples that come with Shoes. I think that looks much better. What would be even better is if it downloaded your friends timeline (i.e. you and those you follow.) I put some text boxes (edit_box) in to enter the username and password and even figured out how to mask the password (:secret => true) but started to have some issues with Shoes crashing. So, next week’s task will be to see if I can overcome the crashing and get a more functional client.