15 December 2007

JRuby as a system programming language

At work, we are starting to refactor some of our system utilities. The scripts interact with these basic components:
  • Postgres databases
  • FTP servers
  • Files on various servers
  • Java applications
  • Specific system calls
My boss, being from an "older generation" ;-), favors Unix scripting (sed, bash, etc.) . Being a Ruby/JRuby enthusiast myself, I wanted to show my team how easy these tasks are in JRuby. While sed might take fewer characters in some cases, I think JRuby strikes an ideal match between terseness and readability.

Note that the following code will not run on your computer, at least not without setting up a Postgres database and changing the FTP server info, etc. But it will give you a good flavor for system programming in JRuby.


#

# Copyright 2007 Effectiveqa.com. All rights reserved

#



def postgres

# Accessing Postgres from JRuby

# http://ruby.scripting.ca/postgres/rdoc/



$LOAD_PATH << 'C:/jruby-1.0.1/lib/ruby/gems/1.8/gems/postgres-pr-0.4.0/lib'

require 'postgres'

con = PGconn.connect('localhost', 5432, nil, nil, 'foo', 'postgres', 'password'

res = con.query("select * from bar")

puts res[0][0]

end



def ftp

# FTP Access

# http://ruby-doc.org/stdlib/libdoc/net/ftp/rdoc/index.html

require 'net/ftp'

Net::FTP.open('effectiveqa.com') do |ftp|

ftp.login('username', 'password')

ftp.chdir('public_html')

ftp.list('*.css').each {|file| puts file}

ftp.gettextfile('main.css', 'local_main.css')

end

end



def ftp_password

# Show how easily JRuby can handle password problem

require 'net/ftp'

begin

ftp = Net::FTP.open('effectiveqa.com')

ftp.login('effectiv', 'BADPASSWORD')

rescue

puts "Here is where I fix the password"

ftp.login('username', 'password')

ftp.chdir('public_html')

ftp.list('*.css').each {|file| puts file}
end

end



def file_tricks

# Different file/directory manipulations

# http://www.ruby-doc.org/stdlib/libdoc/fileutils/rdoc/index.html

require 'fileutils'

include FileUtils



begin cd('dfsdf') rescue puts 'Oops, had a problem...' end

chmod(0777, 'local_main.css')

puts "Don't match!" if ! cmp('local_main.css', 'local_main_1.css')

cp('local_main.css', 'local_main_copy.css')

puts "Do match!" if cmp('local_main.css', 'local_main_copy.css')

puts pwd

rm('local_main_copy.css')

begin rm('DOESNT_EXIST') rescue puts 'Caught one' end

end



def file_tests

# Very nice tests

# http://ruby-doc.org/docs/ProgrammingRuby/html/ref_m_filetest.html



puts FileTest.directory?('example.zip') # false

puts FileTest.size('example.zip') # 1723

puts FileTest.file?('.') # false

puts FileTest.file?('example.zip') # true

puts FileTest.readable?('example.zip') # true

puts FileTest.writable?('example.zip') # true



include FileTest

puts directory?('.') # true; needs include

puts file?('.') # false

end

def use_java

# Show off how easy it is to use Java from JRuby

include Java



zf = java.util.zip.ZipFile.new('example.zip')

puts "There are #{zf.size} files in the zip."

zf.entries.each {|entry| puts " file: #{entry}"}

end



def split_files

# Easy processing of DB dump files



lines = IO.readlines('pipeFile.txt')

lines.each {|line| puts line.split('|')[1]}

end



def system_calls

# JRuby has full access to OS calls

lines = `df`.split("\n")

lines.shift # Read off header line

lines.each do |line|

fields = line.split

next unless fields[0] =~ /C:$/ # Only care about the C: drive

percent = fields[4].to_i

puts "Holy cow, disk almost full" if percent > 95

puts "Disk > 50% full" if percent > 50

end

end



# Some interesting Ruby libraries

# http://beaver.net/slides/ruby/10-easy-pieces.html

# Library doc: http://www.ruby-doc.org/stdlib/

# Pathnames: http://www.ruby-doc.org/stdlib/libdoc/pathname/rdoc/index.html

# MD5, sha1: http://www.ruby-doc.org/stdlib/libdoc/digest/rdoc/index.html

# Waking trees: http://ruby-doc.org/stdlib/libdoc/find/rdoc/index.html



puts "\n**** I can access postgres\n"

postgres

puts "\n**** I can access FTP\n"

ftp

puts "\n**** I can tell if an FTP password failed\n"

ftp_password

puts "\n**** I have all kinds of *nix file commands\n"

file_tricks

puts "\n**** I also have all kinds of cool file tests\n"

file_tests

puts "\n**** I can easily use Java code\n"

use_java

puts "\n**** I can split up Dave's db dump files\n"

split_files

puts "\n**** I can make OS calls with ease\n"

system_calls



The only shortcoming I could find with JRuby is that it doesn't support all the command line options that Ruby does. So you can see that JRuby falls behind in the terseness of its one-liner to rip a particular field out of a pipe-delimited dump file, though I would argue it is more readable.

#> ruby -n -a -F"\|" -e "puts $F[1]" pipeFile.txt

#> perl -n -a -F"\|" -e 'print "$F[1]\n"' pipeFile.txt

#> jruby -e "IO.readlines('pipeFile.txt').each {|ln| puts ln.split('|')[1]}"


No comments: