Ruby + MySQL + Windows = SUCCESS! (Finally)

If you do a Google for “ruby mysql windows” you will find, among other things, lots of people trying to use Ruby to access MySQL on a Windows system. I’ve been trying for some time, and have finally gotten things going.

About a year ago I found this article in which he fought the same fight. He was able to get a working .so file, using the .Net C++ compiler, and offered his binary, but that won’t work for everyone. Specifically, a file compiled with VC7 isn’t usable by VC6 (at least not that I could see).

Anyway, I followed his advice to a point, and then started experimenting. I opened up irb and started poking MKMF to see what I could accomplish. What I finally ended up with is a hacked extconf.rb that can be edited easily to get the library built on your own system. It will work with both VC6 and VC7, by changing one line in the file. Assuming that you have MySQL 4.1 installed in the default location, and you are using VC6, and Ruby 1.8.2 is installed in the default location, you should be able to do this

    ruby extconf.rb
    nmake
    nmake install

and have everything compile and install. Notice that I said should. You should still take a look at the file, near the top, to see if the settings are appropriate for your system.

Once installed, you can use this module directly, or install DBI for that “standard” interface. DBI will use the library you just built and installed, so all you have to do is download the DBI module, then do this

    cd ruby-dbi-all
    ruby setup.rb config --with=dbi,dbd_mysql
    ruby setup.rb setup
    ruby setup.rb install

If you want to install other DBD drivers, add them after dbd_mysql. That should be it.

Here’s the hacked extconf.rb file.

There are only two lines you might need to change, and they are at the top of the file with comments around them.

I have tested this slightly using the binary I built with VC6 and it seems to work fine. I have not tested the VC7-compiled binary, because I don’t have a Ruby distro that was compiled with VC7. Let me know if these directions don’t work for you. Not that I can do anything about it, but I could put a note here for others.

Also, notice that I used 8.3 pathnames, which is really ugly. I would have been happy to use the pathnames with spaces, but VC6 doesn’t like spaces in pathnames. VC7 can grok them fine, but not 6. Both understand the 8.3, so it was just easier to use that.

One more thing. I don’t believe that installing Visual Studio.Net installs the necessary header and library files for building non-.Net binaries. IOW, you might not have windows.h which will cause things not to work. If you download and install the Platform SDK, then you’ll be fine. My extconf.rb will add the default Platform SDK directory to the necessary paths, so if you have that installed, and try to do a VC7 build, you should be ok.

I Am Bushed…

Today is Day Three of RubyConf2004 and I’m tired. This has been a good conference. Lots of stuff to think about and play with. Lots of code flying around and lots of laptops all in one room. I was up until 02:00 this morning working on an ADO adapter for Active Record in order to talk to SQL Server. I’ve got about 40 failing tests right now (out of 143) so I’m closing in on finishing… It’s been interesting. I only just began looking at Active Record yesterday morning, so I think I’m doing pretty well. Active Record is an OR framework in Ruby that is really simple to work with. Very cool. It’s part of the Rails suite, which is an MVC framework for building websites in Ruby. Very intriguing.

Atlanta Ruby User Group

If you read my stuff very often then you know that I am a huge fan of the Ruby programming language. I write a ton of Ruby code and went to RubyConf 2003, so there was only one other direction to take my love of Ruby: start a user group. So, I have started the Atlanta Ruby User Group. Our first meeting will be Tuesday, March 30 2004, at my office in Norcross. There is a map at the web site and a mailing list. Rather than try to restate the meeting announcement, I’m just going to repost the email I sent to the mailing list here. If you want more info, drop me an email and/or visit the group site. If you are within range of Atlanta, please come on out for the meeting.

Our first meeting will be Tuesday March 30 at 7:00 PM. The meeting location is at my office in Norcross, on Peachtree Industrial Blvd. There is a map on the website. We have classrooms, so it will be a comfortable environment.

This meeting will be a "get to know each other" type meeting where we can discuss what, if anything, we've done with Ruby so far. I will then present the first part of an introduction to Ruby, and then discuss the future direction of the group. The meeting should be finished at 8:30 PM.

Things we need to discuss are meeting frequency, topics, presenters and commitments from group members as far as attendance goes. I'm not suggesting that everyone must commit to being at every meeting, but with a small group, if several people miss each meeting, there won't be much "group" to the group, if you know what I mean. As for frequency, I'm currently thinking a meeting every other month (6 per year) but I'm open to suggestions.

Anyway, Sams Publishing has agreed to send me a few copies of The Ruby Way and Teach Yourself Ruby in 21 Days, so I will have those to hand out. (The Ruby Way is an excellent book [thanks, Hal]; I haven't seen the 21 Days book.) I will provide drinks and some snacks for the first meeting, but if we decide to have food at subsequent meetings, then we need to also decide how we are going to pay for it. Lots to discuss, eh?

So, be sure to mark Tuesday, March 30 on your calendar as we kick off the Atlanta Ruby User Group!

SSH Util Ruby Script

OpenSSH, that ships with Cygwin has a nice utility called ssh-agent. This program is a daemon that will hold on to your keys so that hosts you are authorized to log on to will not continually ask for your password. This is especially useful when working with CVS repositories over SSH. When you run ssh-agent, it spits out text that should be evaluated to set two environment variables that you will need in order to run ssh-add, to actually add your keys to it. That output looks like this:

  
SSH_AUTH_SOCK=/tmp/ssh-mjKjm512/agent.512; 
export SSH_AUTH_SOCK;  
SSH_AGENT_PID=1600; 
export SSH_AGENT_PID;  
echo Agent pid 1600;

On a Unix system, it’s easy to capture the output of ssh-agent and evaluate it, but under the ‘command shell’ that ships with Windows systems, which is so pathetically crippled that my old Commodore 64’s BASIC-based shell is looking pretty good, you can’t do that.

So what are those poor saps, myself included, who are using Windows systems to do? Ruby to the rescue. I wrote a simple script that executes ssh-agent, capturing the output, massaging it a little, and then creating a batch file that I can run to set the necessary variables. Simply redirecting the output to a batch file won’t work because there are some Unix-isms, such as exporting the variables, that don’t work on Windows. So, here’s the script:

#!ruby   
lines = %x{ssh-agent}   
File.open("c:/tmp/sde.bat", "w") do |file|   
  lines.each do |line|   
    chunks = line.chomp.split ';'   
    if chunks[0] =~ /^SSH/  
      file.puts "SET #{chunks[0]}"  
    end  
  end  
end  

You can see that the script sends the captured and massaged output to a batch file called “sde.bat” located in C:/tmp. You can change both of these to suit your preference. Once the script runs, I simply execute the generated batch file and then run ssh-add. The nice thing about the generated batch file is that it lingers until the next time I run the Ruby script. Thus as I open new console Windows I can re-run it to get the environment variables set properly in each one.

Mixer Remixed

After reading several threads about the text scrmabler and various implementations, I revised my Ruby version and it’s now 21 lines shorter and much more Ruby-like. It makes far less of an attempt to deal with punctuation, but I think that’s OK. This is just a lark, after all.

  1  class Mixer  2      private  3      def randomize(str)  4          return str if str.length < 4  5  6          str =~ /B.*B/  7          first = $`  8          last = $'  9          first + ($&.split(//).sort_by {rand}.join) + last 10      end 11 12      public 13      def mix_file(filename) 14          lines = IO.readlines(filename) 15 16          mix_lines(lines) 17      end 18 19      def mix_string(str) 20          mix_lines(str.split).join(" ") 21      end 22 23      def mix_lines(lines) 24          lines.collect! do |line| 25              words = Array.new 26 27              line.split(/W/).each do |word| 28                  words << randomize(word) 29              end 30 31              words.join(" ") 32          end 33      end 34  end 

By Popular Demand, I Give You… Mixer!

“Popular demand,” yeah… right… That’s the ticket. Anyway, I saw this blog entry by Jamie this morning which caused me to write a Ruby program to mix up the letters of words, leaving the first and last letter as they were. I started out with a simple script, then I added better punctuation support, then I converted it to a class, then I wrote unit tests to run it. Anyway, somebody wanted to see it, so here it is. Is it great code? Probably not. Do I care? Not really.

  1  class Mixer  2      private  3      def randomize(str)  4          if str.length < 4  5              return str  6          end  7  8          letters = str.split(//)  9 10          first = letters[0] 11          mid = letters[1..(letters.length - 2)] 12          last = letters[letters.length - 1] 13 14          new_letters = "" 15 16          while mid && mid.length > 0 17              len = mid.length 18              r = rand(len) 19              new_letters << mid.delete_at(r) 20          end 21 22          first + new_letters + last 23      end 24 25      public 26      def mix_file(filename) 27          lines = IO.readlines(filename) 28 29          mix_lines(lines) 30      end 31 32      def mix_string(str) 33          new_str = "" 34 35          mix_lines(str.split).each do |line| 36              new_str << line 37          end 38 39          new_str 40      end 41 42      def mix_lines(lines) 43          lines.collect! do |line| 44              new_line = "" 45 46              line.split(/s+|,|.|!|(|)|"|'/).each do |word| 47                  new_line << randomize(word) << " " 48              end 49 50              new_line 51          end 52 53          lines 54      end 55  end 

And the unit tests, which don’t actually test anything.

  1  require 'test/unit'  2  require 'mix'  3  4  class MixTest < Test::Unit::TestCase  5      def setup()  6          @mixer = Mixer.new  7      end  8  9      def test_word() 10          x = @mixer.mix_string("testing") 11          puts x 12          assert_not_equal("testing", x, "String not randomized") 13      end 14 15      def test_string() 16          x = @mixer.mix_string("this is a humongous test, dangit") 17          puts x 18      end 19 20      def test_file() 21          article = @mixer.mix_file("testfile.txt") 22          #puts article 23      end 24  end 

I’m sure someone will find this useful… Again, “yeah, right.” Ah well, it was a mildly amusing diversion…

Simplicity and Consistency

Mike Clark this morning has a bit of a nudge for Rael to give Ruby a try. Mike makes the following statement that I completely agree with

The beauty of Ruby is its simplicity and consistency. With Ruby, I find myself writing code to get the job done rather than to appease the compiler.

So true! Since Ruby is a dynamic language, there are no variable types to declare, no static checking; variables are just slots. The number of lines of Ruby code to do something is far less than the equivalent Java code, and I would argue more readable. You don’t have to jump through hoops to make the compiler happy, you just write your code to do what you need done. That’s it. It’s a beautiful thing.

The fact that regular expressions are baked right into the language is also a giant plus. This is how Perl does it, and Matz basically lifted this approach when he created Ruby. Python‘s regex support is not nearly as nice since you have to create a regex and call methods on it instead of using a regex literal and using special variables to get the groups, etc. Where having baked-in regex support really shines is in not having to escape backslashed atoms in the regex. Regexen in Java are even more difficult to read than usual because every backslash is doubled to keep the Java string parser from barfing on unknown escapes.

Kata 6

I took a swipe at implementing Dave Thomas’ Kata 6 which is an assignment dealing with anagrams. The goal is to parse a list of 45000-ish words, finding all the words that are anagrams of other words in the file. Dave claims there are 2,530 sets of anagrams, but I only got 2,506. I’m not sure where the disconnect is, but here’s my solution. I welcome any comments and critiques.

 words = IO.readlines("wordlist.txt")  anagrams = Hash.new([])  words.each do |word|     base = Array.new     word.chomp!.downcase!      word.each_byte do |byte|         base << byte.chr     end      base.sort!      anagrams[base.to_s] |= [word] end  # Find the anagrams by eliminating those with only one word anagrams.reject! {|k, v| v.length == 1}  values = anagrams.values.sort do |a, b|     b.length  a.length end  File.open("anagrams.txt", "w") do |file|     values.each do |line|         file.puts(line.join(", "))     end end  largest = anagrams.keys.max do |a, b|     a.length  b.length end  puts "Total: #{anagrams.length}" # puts "Largest set of anagrams: #{values[0].inspect}" #  print "Longest anagram: #{anagrams[largest].inspect} " #  puts "at #{largest.length} characters each" 

Update: Of course, 10 seconds after uploading the code, I see something I could change. Instead of sorting the anagram hash descending by array length, I could have done the following:

 longest = anagrams.to_a.max do |a, b|     a[1].length  b[1].length end 

This will sort and pull the largest one off. The key is bucket 0 and the interesting array is in bucket 1.

First Cut At Kata 8

Dave Thomas of the Pragmatic Programmers has started publishing programming problems, calling them Kata. He’s just published Kata 8 this morning and I’ve had a go at a solution. The problem is to take a supplied list of words and go through it finding all the six letter words that are constructed from shorter words in the file. The full problem is to write the program in three different ways: one optimized for human consumption, one optimized for speed, and one that is highly extensible.

Presented below is my first cut at this kata. I think it is fairly readable, at 79 lines, so this probably will count as my “fit for human consumption” version. It’s relatively fast, completing in 11 seconds.

Comments? Critiques?

 #!/usr/local/bin/ruby  start = Time.now  # Arrays for each class of word fourLetters = Array.new threeLetters = Array.new twoLetters = Array.new sixLetters = Array.new  # Loop over the word list, segregating the words #  to their respective array IO.foreach("wordlist.txt") do |line|     line.chomp!.downcase!      case line.length     when 2         twoLetters << line     when 3         threeLetters << line     when 4         fourLetters << line     when 6         sixLetters << line     end end  candidates = Array.new  # Build up all combinations of four letters + two letters #  and store in them as candidates fourLetters.each do |four|     twoLetters.each do |two|         wc = four + two          candidates << wc     end end  # Build up all combinations of three letters + three #  letters and store them as candidates threeLetters.each do |three|     threeLetters.each do |otherThree|         wc = three + otherThree         candidates << wc     end end  # Finally, all combinations of two letters + two letters #  + two letters and store those as candidates twoLetters.each do |firstTwo|     twoLetters.each do |secondTwo|         twoLetters.each do |thirdTwo|             wc = firstTwo + secondTwo + thirdTwo             candidates << wc         end     end end  # Now get rid of dups and sort in place candidates.uniq!.sort! puts "Candidates = #{candidates.length}" #  # And the two arrays together leaving only those words #  that appear in both lists matches = sixLetters & candidates  # Now write the matches to a file File.open("matches.txt", "w") do |file|     matches.each do |word|         file.puts(word)     end end  finish = Time.now  puts "Started at #{start}" puts "Finished at #{finish}" puts "Total time #{finish.to_i - start.to_i}"