Ruby in Practice

October 25, 2007 at 8:32 AMmgordon

In my current contract, it's the typical story with compressed timelines and pulling off miracles, so I haven't had the opportunity to make use of Ruby for a "real" production application.  However, we're in the midst of doing some work in migrating data from a legacy system into a completely new database schema.  All imagined challenges apply, I assure you.  Part of this exercise is repeatedly bouncing the data against the new schema, applying validations to the data to make sure all the new referential integrity holds true in the new system and reporting on data that isn't fitting into the new system properly.  I took this as an opportunity to apply some Ruby scripting as a means of automating this part of the project.

 Having played with the DBI library a bit before starting, I knew that I wanted to abstract some of the interaction with it away so I wrapped it like so.

  ########## Class to abstract the server connection ##########
  class SqlServer
   
    def initialize(connectionString)
        @dbh=DBI.connect("DBI:ADO:Provider=SQLOLEDB;" + connectionString)
        @dbh["AutoCommit"] = true
    end
   
    def execute(sql)
        statement = @dbh.prepare(sql)
        statement.execute
    end
   
    def fetch(sql)
        statement = @dbh.execute(sql)
        statement
    end
   
    def disconnect
        @dbh.disconnect
    end
  end

I needed to be able to pull fresh data from other databases into my conversion database, so I abstracted that task, as well.

 ########## Class to manage pulling data from one db to another ##########
 class Pull
    def initialize(tableName)
        @table = tableName
        @destination = SqlServer.new("Data Source=server1;initial catalog=database1; trusted_connection=yes")
        @source = SqlServer.new("Data Source=server2;initial catalog=database2; trusted_connection=yes")
    end
   
    def CopyTable
       
        @destination.execute("TRUNCATE TABLE " + @table)
               
        statement = @source.fetch("select * from " + @table)
       
        statement.fetch do |row|
            columns = ""
            values = ""
            insert = ""
           
           
            i = 0
            statement.column_names.each do |name|
                columns += "," + name if columns.length > 0
                columns += name if columns.length == 0
                if row[i].nil?
                    values += ", NULL" if values.length > 0
                    values += "NULL" if values.length == 0
                else
                    values += ", '" + row[i].to_s.gsub("\\", "").gsub("'", "''") + "'" if values.length > 0
                    values += "'" + row[i].to_s.gsub("\\", "").gsub("'", "''") + "'" if values.length == 0
                end
                i = i + 1
            end
           
            columns = "(" + columns + ")"
            values = "values(" + values + ")"
            begin
                insert = "insert into " + @table + " " + columns + " " + values
                @destination.execute(insert)
            rescue DBI::DatabaseError => e
                puts "An error occurred"
                puts "Error code: #{e.err}"
                puts "Error message: #{e.errstr}"
                puts "Error SQLSTATE: #{e.state}"

                puts insert
                exit
            end
        end
        statement.finish
        @destination.disconnect
        @source.disconnect
    end
 end

In my case, all the columns were varchar, so I didn't have to fuss with the format of the data in the insert statements.  With DBI, however, it's possible to get the meta data about each column to decide whether the data needs to be quoted or not.  Now, I needed to be able to pull data about how the load went and any exceptions that were encountered.  That data needed to be saved into CSV files so they could be attached to emails.  The next class was written to do this.

 ########## Class to pull statistic about the load into csv files ##########
 class StatFile
 
    def initialize(connString)
        @server = SqlServer.new(connString)
    end
 
    def GetStats(filePath, sql)
        statement = @server.fetch(sql)
       
        output = File.open(filePath, "w")
       
        statement.fetch do |row|
            output.puts row.to_a.join(",")
        end
       
        output.close
    end
 end

 With the above classes, I was able to script the tasks at hand something like this.

Pull.new("tableName").CopyTable #Copies the table from one db to another

 conversion = SqlServer.new("Data Source=server;initial catalog=database; trusted_connection=yes")
 conversion.do("EXEC ConversionSproc")

 stat.Getstats("c:\\Stat_Files\\exceptions.csv", "select foo1, foo2 from exception_table where error='bad' group by description")

As I've stated before, I'm not planning to use Ruby to put food on the table at this point, but I would like to get some experience using the language if only to get the chance to think about things differently.

 UPDATE:
After using the above scripts a bit, I found that when executing some rather long running stored procedures, I was receiving a timeout.  It was then that I realized there was not way to set the CommandTimout property on the database connection through DBI.  After hours of searching, I was able to gleen enough information to overcome the problem.  I added the following line to the bottom of the initialize method of the SqlServer class:

  @dbh.handle.instance_variable_get(:@handle).setproperty('CommandTimeout', 7200)

Of course, you can specify whatever value you like for the timeout or even parametize the value.

Posted in: Productivity | Sql Server 2005 | Database | Ruby

Tags: ,

Getting My Media On

October 23, 2007 at 5:24 AMmgordon

I was lucky enough to receive an Xbox 360 for Father's Day, this year, and it brought me one step closer to my dream of on-demand media from my own media server.  Throughout the past several months, I've been piecing together all the necessary parts.

First I purchased the WIFI add on for the Xbox.  I'm currently running a wireless G network, at home, but the adapter supports A so I have room to increase the bandwidth, if needed (and it probably will be).  I have an old desktop that I installed Windows 2003 on (Pentium, 128 Meg) and I wanted to be able to stream my media from that machine to my Xbox.  Since I wasn't running Media Center on the machine, I went looking for alternatives.  Apparently, if you have Windows Media Player 11, it will let you stream to other devices, but alas it can't be installed on Windows 2003.

Then I found Orb.  Orb includes a service that you install on your media machine that catalogs all your media and serves it up either through a web interface on their site or to devices on your network.  It was a bit slow and I kept running into files encoded in formats that Xbox wouldn't play.  Quite by accident, I then found TVersity.  Tversity works similarly to Orb in that it installs a service on the media machine that keeps an inventory of your media files and serves them up.  However, it is also smart enough to detect what type of device it's serving the content to and transcode it to a format the device can play.  You can also tell it about any video or audio podcasts you like and it will download them and make them available for viewing.

Instead of offering you a web site through which you can access your media, the server on you machine serves up the data on a port that you configure.  If you hit this port with a browser, you get a nice flash application that allows you to browse and play your media files.  One word of caution, though.  The port is not secured by any means, currently, so if you open this port on your router, your media (compromising photos?  videos?) is available to anyone who accesses the port.

I'm happy with TVeristy, but my current hardware chokes when the on-the-fly transcoding kicks in.  The good thing here, is that after the Xbox spring update the system will play many additional formats which means that less transcoding is necessary.  However, for those times when I need the raw power, I've purchased a new Dell PowerEdge server ;-).  As good an excuse as any, I'd say.

Posted in: General | Xbox | Media

Tags: , ,

ASP.Net MVC Framework

October 16, 2007 at 4:15 AMmgordon

Scott Gutherie posted on his blog, Sunday, that Microsoft will be releasing an MVC framework for ASP.Net later this year.  After having spent the last few weeks emerging myself in the Rails framework, I found the list of features for the MVC framework to be very interesting, indeed.  It will provide a clean separation of concerns and allow better testability of the code, will be highly extensible and pluggable, will provide support for URL mapping and pretty URLs, and will allow you to specify "view templates". 

In my last post, I pointed out new language features in the 3.0 version of C# and how they seemed to be inspired by dynamic languages such as Ruby.  Now, I have to say, the MVC Framework seems to include several features that are inspired by the Rails framework. Shrug...looks like .Net developers won't have to go anywhere to enjoy the benefits of dynamic language features and pattern based web development. 

The MVC approach really appeals to my sensibilities, but it was so hard to implement in ASP.Net.  I look forward to getting my hands on this new framework.

Posted in: General | .Net

Tags:

puts "Ruby and Rails"

October 2, 2007 at 3:48 AMmgordon

For the past few weeks I've been kicking the tires on the Ruby language and the Rails framework.  This post is not meant to be a review of either, but I thought I'd note some of my impressions for other .Net developers that have been hearing the buzz and have not had the time to take a look.

First of all, I'm not planning to make a jump from ASP.Net to rails anytime soon.  However, I think there is always benefit in exploring other technologies, approaches and points of view and my look at these things was primarily motivated by curiosity.

The Ruby Language
I love this language.  Scott Hanselman has commented on how easily intent can be expressed in Ruby and Wilco Bauwer likes its rhythm.  Writing logic in it feels really natural, to me, and it seems that whenever I need a particular piece of functionality, it's available and easy to invoke without having to jump through hoops.  Without casting a vote for either dynamic languages or statically typed ones as the be-all-and-end-all, I have to say that if I were to be tasked with creating a domain specific language I'd certainly go for Ruby as my language of choice to implement it in.  It lends itself well to that type of exercise.  After all, to an extent Rails is a DSL for the web application space.  Some day to day tasks would lend themselves well to being implemented in Ruby.  Command line tasks, in particular, could be easily handled in Ruby.  The language (as implemented for Windows) supports calling into the win32 API and COM.  It does not contain any support for building windows UI, though you can do that by calling the windows API (if you're nuts) or by making use of one of the many Ruby .Net bridges that are freely available.  Just don't expect the performance of the bridge to blow you away.

Another thing that impressed me was the number of dynamic language features that are making their way into .Net languages.  Anonymous methods, continuations and extension methods all seem to have their roots in dynamic languages such as Ruby. 

Ruby is easy to learn and the code is easy to read.

Rails Framework
As one who appreciates the value of convention and likes having a process in place to grease the wheels of progress, I find Rails to be a very comfortable approach to use for web development.  As stated on many Rails-Centric sites, Rails is about "Convention over configuration"  and "DRY" or "Don't Repeat Yourself" meaning don't duplicate code.  Typical frameworks offer abstractions to make the most commonly required functionality easier and faster to use.  Rails does this well, but adds code generation and a type of adaptability that could only be achieved by using dynamic language such as Ruby.  There is also a substantial number of tools to aid the developer with ancillary tasks such as deployment and testing.  In general, Rails forces the developer to use the Model-View-Controller pattern to build their web application.  Few would argue with this approach.  ActiveRecord, which is an ORM library, is used for database access.  ActiveRecord is the stated inspiration for the Subsonic library written in .Net and greatly simplifies database access.

Many proponents of Rails claim extraordinary gains in productivity can be achieved.  I can see how productivity can be increased greatly for an experienced Rails developer, but be warned that there is a lot to remember and new Rails developers may find they are spending a great amount of time in the Rails documentation. 

There are a few IDE's out there that support Rails development.  I preferred RadRails and have been using it for the past few weeks with good success.  I found that Rails, as many have claimed, brought some fun back to the development of web pages. 

The one area I found to be a significant pain was in the deployment story.  Rails is not thread safe.  This means that each web request cannot be handled by only a new thread, but a new process.  I searched, for some time, and found some solutions using FastCgi which basically spins up several instances of a web server and feeds requests to them.  There seem to be some better solutions on the horizon , but I was not able to find what I would consider to be a "clean" solution to use immediately.

I've gained a number of insights, so far, from this exercise.  I don't see myself building my next business application in Rails (but, who knows) or even being gainfully employed as a Rails developer, but the lessons learned will continue to open my mind and make me a better developer.

 

Posted in: General | Productivity | SubSonic

Tags: , ,