Saturday, January 23, 2010

Maven, the new Elephant on the Block

Some of you may remember the article, by Bruce Tate, Don't Make Me Eat the Elephant Again.
It was an article about EJB, and Bruce was begging Sun not to make the same mistakes with EJB3 as they had done with EJB, and EJB2. They didn't, Spring came along as better alternative and forced EJB3 to become slimmer and better. If not for Spring, EJB3 would probably look very different from what it looks like today.
Well, guess what, there is a new elephant on the block and its name Maven2.


Just like EJB2, Maven2 was born out of something so unbearable that anything else was bliss. Jelly anyone!


But just as EJB was fundamentally flawed, so is Maven2. Build systems, even advanced ones like Maven is fundamentally about two things.
  1. Check if something that something else depends on has changed
  2. If so, do something.
That's it, that is what is important.
The checking may contain various sophisticated methods for detecting if files, subsets of files, all files, web pages, twitter feeds, etc, has changed, but that is really it.
And the doing can be anything, Copy files, commit files, build websites, run tests, generate code, launch missiles, whatever!
But the key to doing this efficiently is a programming language with easy access to system commands and the ability to create simple abstractions, with methods, variables, and objects. The language should also, preferably, be one without a lot of ceremony, like Ruby, Javascript or Python.
There are already build systems like this out there, Buildr, SCons, and Rake come to mind, but they do not have the momentum of Maven, so a merger between Maven and Buildr would be wonderful.



So, Jason, hear my plea, Don't make me eat the elephant again!

Saturday, January 16, 2010

Scripting in Ruby

I just read, or rather skimmed, the book, called Everyday Scripting with Ruby and it is awful. I had high expectations. I was expecting something like Perl for System Administration, where you right away get into hard core Perl scripting. This book is nothing like that! It is a really basic introduction to Ruby, and if you have any experience programming at all, DON'T BUY THIS BOOK.

OK, enough complaining! I am going to do what this book does not do. I am going to show a good way to write scripts in Ruby. If you want to learn more about Ruby, buy Programming Ruby, The Well-Grounded Rubyist and The Ruby Way.

Ruby is an excellent language. It is, in my opinion, the best scripting language and it beats both Perl and Python face down. If you are not scripting in Ruby, you are missing out. Ok, so here is the script.

sendgmail

The script is called sendgmail and it sends mail via gmail (surprise!).

This is how the command is used.

$ sendgmail --help
/usr/local/bin/sendgmail [options] attachments...
Options are ...
    -t, --to TO                      Send email to recipient
    -m, --message MESSAGE            Include the message.
    -s, --subject SUBJECT            Include the subject.
    -v, --verbose                    Log to standard output.
    -V, --version                    Display the program version.
    -h, -H, --help                   Display this help message.

And here is the code, liberally sprinkled with comments.


#!/usr/bin/env ruby
# Use the currently configured ruby version

# optparse contains OptionParser, ostruct: OpenStruct and growl Growl
require 'optparse'
require 'ostruct'
require 'growl'

# This is the name of the script that is called
# Whatever you name your script will be reflected here.
# This can be useful if you want to have the same script do many things
# Create differently named links to the script 
# and use the program name to select behavior.
PROGRAM_NAME = $0
PROGRAM_VERSION = 1.0

# Create an OpenStruct to save the options.
# OpenStruct allows you to use options.my_option instead of 
# option['my_option'] with a normal hash
# http://ruby-doc.org/stdlib/libdoc/ostruct/rdoc/classes/OpenStruct.html
def options
  @options ||= OpenStruct.new
end


# This is the options of the program, see OptionParser
# http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html
def program_options
  [
          # The values of the array are,
          # [long_option, short_option and parameter, description, code to execute]          
          ['--to', '-t TO', "Send email to recipient",
           lambda { |value| options.to = value }
          ],
          ['--message', '-m MESSAGE', "Include the message.",
           lambda { |value| options.message = value }
          ],
          ['--subject', '-s SUBJECT', "Include the subject.",
           lambda { |value| options.subject = value }
          ],
          ['--verbose', '-v', "Log to standard output.",
           lambda { |value| options.verbose = true }
          ],
          ['--version', '-V', "Display the program version.",
           lambda { |value|
             puts "#{program_name}, version #{PROGRAM_VERSION}"
             exit
           }
          ]
  ]
end

option_parser = OptionParser.new do |opts|
  opts.banner = "#{PROGRAM_NAME} [options] attachments..."
  opts.separator ""
  opts.separator "Options are ..."

  # Add the command on_tail, to make it appear as the last option in the list.
  opts.on_tail("-h", "--help", "-H", "Display this help message.") do
    puts opts
    exit
  end

  program_options.each { |args| opts.on(*args) }
end

begin
  # Parse the options and remove them from the ARGV array
  option_parser.parse!
rescue OptionParser::ParseError => error
  puts error.message
  puts option_parser
  exit
end

unless options.to
  puts 'Missing required argument --to or -t'
  puts option_parser
  exit
end

options.subject = 'No subject' unless options.subject
options.message = '' unless options.message

# Concatenate the options into a proper command
command = 'sendemail -o tls=yes -s smtp.gmail.com -f from@gmail.com -xu from@gmail.com -xp secret'
command += "-t '#{options.to}' -u '#{options.subject}' -m '#{options.message}' "

# Append the filenames with the -a option to send them as attachments
# Only the non options (the filenames) are left in ARGV
unless ARGV.empty?
  command += " -a #{ARGV.join(' ')}"
end

# Print the command to screen if using verbose mode.
puts command if options.verbose
system command

Growl.notify "#{options.subject}\n#{options.message}", :icon => :jpeg, :title => "Email sent to #{options.to}"

The script requires you to install sendemail. It also requires you to install the growlnotify command line utility that is distributed with Growl and the gem that uses this utility, also conveniently named growl.

Install required dependencies on OS X


# Install sendemail and capabilty to send secure mail, required by Gmail
sudo port install sendemail p5-net-ssleay p5-io-socket-ssl

# Install growl command utility from the mounted image
sudo sh /Volumes/Growl-1.2/Extras/growlnotify/install.sh

# Install the growl gem
sudo gem install growl

That’s it, a simple script that will allow you to send messages and files through
gmail from the command line.