Joey Gibson's Blog Dean sàor, dean sàor an spiórad. Is seinn d'orain beo.  
Home/tech/ruby
 


Fri, Jun 17 2005

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 in.

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.

[/tech/ruby] # | |

Sun, Oct 03 2004

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.

[/tech/ruby] # | |

Thu, Apr 22 2004

AtlRUG: Second Meeting

The second meeting of the Atlanta Ruby User Group will be next Tuesday, April 27 at 7:00PM. We will continue with my introduction to Ruby presentation and discuss what others have been working on since the last meeting. I also have a few fairly cool demos to show.

[/tech/ruby] # | |

Mon, Mar 08 2004

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!

[/tech/ruby] # | |

Wed, Jan 07 2004

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 patheticly 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:
 1  #!ruby
 2
 3  lines = %x{ssh-agent}
 4
 5  File.open("c:/tmp/sde.bat", "w") do |file|
 6      lines.each do |line|
 7          chunks = line.chomp.split ';'
 8
 9          if chunks[0] =~ /^SSH/
10              file.puts "SET #{chunks[0]}"
11          end
12      end
13  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.

[/tech/ruby] # | |

Wed, Sep 17 2003

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

[/tech/ruby] # | |

Mon, Sep 15 2003

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...

[/tech/ruby] # | |

Tue, Jul 08 2003

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.

[/tech/ruby] # | |

Fri, Jun 27 2003

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.

[/tech/ruby] # | |

Fri, Jun 20 2003

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}"

[/tech/ruby] # | |

 



Make free, make free your spirit. Sing the song of life.


Copyright © 1998 - 2008 Joey Gibson