Thursday, August 27, 2009

WIN32API

If you plan on doing Ruby programming that needs to access some Windows 32 API functions directly, or that needs to use the entry points in some other DLLs, we've got good news for you-- the Win32API library.

As an example,here's some code that's part of a larger Windows application used by our fulfillment system to download and print invoices and receipts. A Web application generates a PDF file, which the Ruby script running on Windows downloads into a local file. The script then uses the print shell command under Windows to print this file.

arg = "ids=#{resp.intl_orders.join(",")}"
fname = "/temp/invoices.pdf"
site = Net::HTTP.new(HOST, PORT)
site.use_ssl = true
http_resp, = site.get2("/fulfill/receipt.cgi?"+ arg,
'Authorization'=> 'Basic ' +
["name:passwd"].pack('m').strip )
File.open(fname, :wb") {|f| f.puts(http_resp.body) }

shell = Win32API.new("shell32","ShellExecute",
['L','P','P','P','P','L'], 'L' )
shell.Call(0, "print", fname, 0,0, SW_SHOWNORMAL)

You create a Win32API object that represents a call to a particular DLL entry point by specifying the name of the function, the name of the DLL contains the function, and the function signature(argument types and return type). In the previous example, the variable shell wraps the Windows function ShellExecute in the shell32DLL. It takes six parameters (a number, four string pointers, and a number) and returns a number. the resulting object can then be used to make the call to print the file that we downloaded.

Many of the arguments to DLL function are binary structures of some form.Win32API handles this by using Ruby String objects to pass the binary data back and forth. You will need to pack and unpack these strings as necessary.

The Win32API module allows access to any arbitrary Windows 32 functions. Many of these function take or return a Pointer data type- a region of memory corresponding to a C string or structure type.

In Ruby, these pointers are represented using class String, which contains a sequence of 8-bit bytes. It is up to you to pack and unpack the bits in the String.

Parameters 3 and 4 of the new call specify the parameter and return types of the method to be called. The type specifiers are n and 1 for numbers, i for integers, p for pointers to data stored in a string, and v for the void type (used for export parameters only). These strings are case-intensentive. Method parameters are specified as an array of strings, and the return type is a single string.

The functionality of Win32API is also provided using the d1/win32 library. As the DL library is newer, this may be a sign that the original Win32API may be phased out over time.

This example is the from the Ruby distribution, in ext/Win32API

require 'Win32API'

get_cursor_pos = Win32API.new("users32", "GetCursorPos", ['P'], 'V')
lpPoint = "" * 8 # store two LONGs
get_cursor_pos.Call(lpPoint)
x, y = lpPoint.unpack("LL") # get the actual values
print "x: ", x, "\n"
print "y: ", y, "\n"
ods = Win32API.new("kernel32", "OutputDebugString", ['P'], 'V'])
ods.Call("Hello, World\n")

GetDesktopWindow = Win32API.new("user32", "GetDekstopWindow", [], 'L')
GetActiveWindow = Win32API.new("users32", "GetActiveWindow", [], 'L')
SendMessage = Win32.new('users32", "SendMessage", [L'] * 4, 'L')
SendMessage.Call(GetDesktopWindow.Call, 274, 0xf140, 0)

Tuesday, August 18, 2009

Searching a Hash with Regular Expressions

You want to grep a hash: that is, find all keys and/or values in the hash that match a regular expression.

The fastest way to grep the keys of a hash is to get the keys as an array, and grep that:

h = { "apple tree" => "plant", "ficus" => "plant",
"shrew" => "animal", "plesiosaur" => "animal" }
h.keys.grep /p/
# => ["apple tree", "plesiosaur"]

The solution of grepping the values of a hash is similar (substitute Hash#values for Hash#keys), unless you need to map the values back to the keys of the hash. If that's what you need, the fastest way is to use Hash#each to get key-value pairs, and match the regular expression against each value.

h.inject([]) { |res, kv| res << kv if kv[1] =~ /p/; res }
# => [["ficus", "plant"], ["apple tree", "plant"]]

The code is similar if you need to find key-value pairs where either the key or the value matches a regular expression:

 class Hash
      def grep(pattern)
          inject([]) do |res, kv|
           res << kv if kv[0] =~ pattern or kv[1] =~ pattern
           res
       end
     end
   end

h.grep(/p/)
# => [["ficus", "plant"], ["apple tree",plant"], ["plesiosaur", "animal"]]
h.grep(/plant/) # => [["ficus", "plant"], ["apple tree", "plant"]]
h.grep(/i.*u/) # => [["ficus", "plant"], ["plesiosaur", "animal"]]


Hash defines its own grep method,but it will never give you any results. Hash#grep is inherited from Enumerable#grep, which tries to match the output of each against the given regular expression. Hash#each returns a series of two-item arrays containing key value pairs, and an array will never match a regular expression. The Hash#grep implementation above is more useful.

Hash#keys.grep and Hash#values.grep are more efficient than matching a regular expression against each key or value in a hash. If memory usage is your primary concern, iterate over each_key or each_value instead:

res = []
h.each_key { |k| res << k ik k =~ /p/ }
res # => ["apple tree", "plesiosaur"]

Wednesday, August 12, 2009

How to install Ruby and RubyGems

Ruby comes preinstalled on Mac OS X and most Linux installations. Windows doesn't come with Ruby, but it's easy to get it with the One-Click Installer: see http://rubyforge.org/project/rubyinstaller/.

If you're on a Unix/Linux system and you don't have Ruby installed (or you want to upgrade), your distribution's package system may make a Ruby package available. On debian GNU/LINUX, it's available as the package ruby-[version]: for instance, ruby-1.8 or ruby-1.9. Red Hat Linux calls it ruby; so does they macports Mac OS X.

If all else fails, download the Ruby source code and compile it yourself. You can get the Ruby code through FTP or HTTP by visiting http://www.ruby-lang.org

Many recipes in this blog require that you install third party libraries in the form of Ruby gems. In general, we prefer standalone solutions (using only the Ruby standard library) to solutions that use gems, and gem-based solutions to ones that require other kinds of third-party software.

To get started, all you need to know is that you first download the Rubygems library from http://rubyforge.org/projects/rubygems/. Unpack the tarball or ZIP file, change into rubygems-[version] directory, and run this command as the superuser:

$ ruby setup.rb

The RubyGems library in the Windows One-Click Installer, so you don't have to worry about this step on Windows.

Once you've got the Rubygems library installed, it's easy to install many other pieces of Ruby code. When a recipe says something like "Ruby on Rails is available as the rails gem," you can issue the following command from the command line (again, as the superuser):

$ gem install rails --include-dependencies

The RubyGems library will download the rails gem (and any other gems on which it depends) and automatically install them. You should then be able to run the code in the recipe, exactly as it appears.

The three most useful gems for new Ruby installations are rails (if you intend to create Rails applications) and the two provided by the Ruby Facets project: facets_core and facets_more. The Facets Core library extends the classes of the Ruby standard library with generally useful methods. The Facets More library adds entirely new classes and modules. The Ruby Facets homepage (http://facets.rubyforge.org/) has a complete reference.

Some Ruby libraries (especially older ones) are not package as gems. Most of the nongem libraries mentioned in this article have entries in the Ruby Application Archive (http://raa.ruby-lang.org/), a directory of Ruby programs and libraries. In most cases you can download a tarball or ZIP file from RAA, and install it with the technique.

Tuesday, August 11, 2009

Interactive Ruby

One way to run Ruby interactive is simply to type ruby at the shell prompt. Here we typed in the single puts expressions and an end-of-file character (which is Ctrl+D on our system). This process works, but it's painful if you make typo, and you can't really see what's going on as you type.

%ruby
puts "Hello, world!"

For most folks, irb--Interactive Ruby--is the tool of choice for executing Ruby interactive.irb is a Ruby Shell, complete with command-line history,line-editing capabilities, and job control.You run irb from the command line. Once it starts, just type in Ruby code. It will show you the value of each expression as it evaluates it.

%irb

We recommend that you get familiar with the irb so you can try some of our examples interactively.

There's a trick when you want to use irb to try example code that's already in a file. You can do this from within irb loading in the program file and then calling methods it contains. In this case, the program file is in code/rdoc/fib_example.rb.

%irb

Monday, August 10, 2009

Building the Gem File

The MomLog gemspec we just created is runnable as a Ruby Program. Invoking it will create a gem file, MomLog-0.5.0.gem.

%ruby momlog.gemspec

Alternatively, you can use the gem build command to generate the gem file.

%gem build momlog.gemspec


Now that you've got a gem file, you can distribute it like any other package. You can put it on an FTP server or a Web site for download or e-mail it to your friends. Once your friends have got this file on their local computers (downloading from your FTP server if necessary), they can install the gem (assuming they have RubyGems installed too) by calling.

%gem install MomLog-0.5.0.gem

If you would like to release your gem to the Ruby community, the easiest way is to use RubyForge (http://rubyforge.org). RubyForge is an open source project management Web site. It also hosts the central gem repository. Any gem files released using RubyForge's file release feature will be automatically picked up and added to the central gem repository several times each day. The advantage to the potential users of your software is that it will be available via RubyGems'remote query and installion operations, making installation even easier.