Run Ruby code from within a shell script

If you need to run Ruby code from within a shell script, you could easily execute a Ruby script with a simple bash command. This means you will have two files – one bash script and one Ruby script.

However, what if you want to combine the two file into a single file?

Simple, using a heredoc in your bash script, we simply pass the entire heredoc to the ruby executable. Create a file called hybrid which contains the following code:

#!/usr/bin/env sh

echo "This is bash!"

/usr/bin/env ruby <<-EORUBY

  puts 'This is ruby!'

EORUBY

Running the above bash script will produce this:

$ . hybrid
This is bash!
This is ruby!

Hope this helps.

Active Record Migrations without a Rails application… sort of.

If you’ve ever wanted to take advantage of Active Record’s powerful migrations functionality without the need for a Rails application, here’s how to do it.

Rather than go through all the details here, I’ve created a small github repository with a fully-functional demo. It’s easy to follow and has comments throughout the code. In fact, there’s very little code at all!

Here’s what you’ll get:

  • Active Record migrations without a Rails web application
  • A stripped-down list of available Rake tasks – only the ones you need. No filler.
  • A clean, DSL-like syntax for generating new migrations, all through the use of Rake tasks.

Hopefully this will prove to be useful – we’re about to roll this out to our production environments (with a few mods).

Here’s the code: https://github.com/edjames/ar_migrations

Simples.

Passing parameters to a Rake task

I’ve been building a few custom rake tasks of late and I wanted to pass a parameter to one of my tasks. Typically you would use a named variable to do this. However, I wanted a cleaner approach, something which was closer to the syntax used for a Rails generator.

So instead of this…

$ rake say_hello NAME=eddie

…or, god forbid, this…

$ rake say_hello[eddie]

…I wanted something more like this…

$ rake say_hello eddie

Hmm. Seems easy enough, but there’s a catch. Let’s look at an example.

Method 1: Using a named variable

task :say_hello do
  name = ENV['NAME']
  puts "Hello, #{name}."
end

This is the typical approach, examples of which you will find everywhere. Running the task is easy and the intention of the command is clear:

$ rake say_hello NAME=eddie
=> Hello, eddie.

Nothing new here.

Method 2: Using arguments

task :say_hello, :name do |t, args|
  name = args.name
  puts "Hello, #{name}."
end

You would run this task like so…

$ rake say_hello[eddie]
=> Hello, eddie.

This is a slightly more cryptic approach, which I don’t like at all. The syntax is unintuitive and totally obscures the intention of the command, not to mention the use of square parenthesis to wrap the argument. Ugly!

Method 3: Custom black magic!

task :say_hello do
  name = ARGV.last
  puts "Hello, #{name}."
  task name.to_sym do ; end
end

For me, this method gives us a much more DSL-like syntax, which seems cleaner and more intuitive (this is obviously entirely dependant on your own set of circumstances). To run the task using this method, you would do this…

$ rake say_hello eddie
=> Hello, eddie.

A few things are happening here that are worth an explanation.

Firstly, we are using the ARGV collection of command line arguments to grab the value for our argument. Standard Ruby stuff. Beware though, that this collection will contain all the arguments for Rake, not our task. That’s why we grab the last element in the collection because the first element is the name of our rake task. Modifying the task as follows will illustrate this point:

task :say_hello do
  puts ARGV.inspect
  name = ARGV.last
  task name.to_sym do ; end
end
$ rake say_hello eddie
=> ["say_hello", "eddie"]

As you can see, the ARGV collection contains two elements: the name of our rake task and the value of the argument we are trying to set. Grab the last one and we’re halfway there.

Secondly, we have to define a new rake task on the fly with the same name as the value of our argument. If we don’t do this, rake will attempt to invoke a task for each command line argument.

$ rake say_hello eddie

By default, the above command will first invoke a task called “say_hello”, and then try to invoke a task called “eddie”. This behaviour is hard-coded into rake. So, we simply define a “no op” task with the corresponding name (“no op” means “no operation”). This avoids an exception being thrown when rake inevitably doesn’t find a defined task of that name.

There you have it. A DSL-like syntax for passing arguments to a rake task.

Simples.

Easy grid tables and forms using pivot_table

One of the most frequently requested features I’m faced with is the ability to display tabular data in a grid which resembles an Excel pivot table. If you’re not familiar with the pivot table concept, take a look here.

I have yet to find a gem that makes this easy, so I’ve created my own. Introducing pivot_table.

The gem is self-explanatory, but still fairly young, so feel free to fork and update to suite your own needs.

Here’s a screenshot of what you can easily accomplish with this gem.

The gem will evolve along with my application’s needs, but I’d appreciate any additional help or feedback.

Simples.

Use instance_eval to clean up your API

I recently wrote a small application to parse Apache log files. The output is this context is not really important, but rather the API of the application that I wrote.

Two things were required to parse a log file:

  1. the log file format
  2. the location of the log file

I wanted the initialize the object with block notation. So starting off, this is what we wrote, which is faily typical in terms of most internal DSLs:

LogParser::Application.new do |p|
  p.format = "%V %h %l %u %t \"%r\" %s"
  p.log_file = '/apache_path/access_log'
end

This would then result in a constructor that looks something like this…

module LogParser
  class Application
    
    def initialize(&block)
      block.call(self) if block_given?
    end

  end
end

That’s perfectly acceptable and it will work just fine. But it’s not a winner in terms of elegance. Enter instance_eval.

Just like call, instance_eval will execute a block, but it will change the value of self as it executes the block.

So, to get an API that looks like this…

LogParser::Application.new do
  format "%V %h %l %u %t \"%r\" %s"
  log_file '/apache_path/access_log'
end

…we must change the constructor to look like this…

module LogParser
  class Application
    
    def initialize(&block)
      instance_eval(block) if block_given?
    end

  end
end

The example above has a few caveats though. In the first example, we are assigning values to format and log_file using setter methods: format= and log_file=. These can be created manually or by using attr_accessor.

In the second example, we are calling methods format and log_file. It’s a subtle difference but something that should be considered when developing your own API.

Here’s the final code for the Application class used above…

module LogParser
  class Application

    def initialize(&block)
      instance_eval(&block) if block_given?
    end

    def format regex_pattern
      @format = regex_pattern
    end

    def log_file filepath
      @log_file = filepath
    end

    # rest of code omitted

  end
end

Hope this helps.

Git flow steps for a new project

I use in all my projects, and it makes my life worth living.

However, whenever I start a new project I can never seem to remember the initial steps that needs to be taken to create the remote develop branch, which include tracking the branch locally.

So for my own sanity, here is what you need to do when starting a new git-flow repo…

Step 1:

Create your repo on github. This will create the default master branch only.

Step 2:

In you shiny new repo, create the develop branch locally.

git flow init

Step 3:

Create the remote develop branch.

git push origin develop

Step 4:

Track the remote develop branch.

git branch --set-upstream develop origin/develop

Done.

Now write some code.

When checking out an existing repo, you only need Step 4 in order to track the remote develop branch.

Now write some code.

Follow

Get every new post delivered to your Inbox.