vim-spectacular

You might not have heard but I maintain a vim plugin for running tests. Its called vim-spectacular and the unique feature is that you can use it with any language (not just Ruby with Rspec). Its how I run my tests every day in languages such as Ruby, Haskell, Java, JavaScript, and SML.

I've recently been adding some nice refinements:

  • Better feedback when using it with an unknown file type. It'll now give you a clear error inside Vim, rather than a confusing error in the shell.
  • Make it aware of tmux sessions. If you have a session open named "test" it will redirect the output to that session, if you have tmux integration turned on.
  • Reuse the same test runners across file types. This is great for Rails because it lets you run Ruby tests directly from within ERB templates.

So if you work with multiple languages in Vim, I suggest you go check it out.

A small shell scripting exercise

Most of the stuff I write is English, but occasionally I also have to write some things in Danish. Since I'm most comfortable writing in Vim I would like to be able to write Danish within Vim, and while thats not really a problem it requires a different keyboard layout to write Danish since we have three special letters. They are æ, ø, and å. Sadly its pretty much impossible for me to use Vim with a Danish keyboard layout because I use Ctrl-[ to get out of insert mode and the position of [ is different in the Danish layout and I don't wanna retrain my fingers just to write Danish.

I thought I could fix this by writing some insert mode mappings that would automatically type æ when I type ae, ø when I type oe, and å when I type aa. I throught those combinations of letters were uncommon enough that it wouldn't hurt me too much. I have since changed my mind about that and here is why.

I would like to know how common those three combinations of letters are to get an idea of many words I would have problems typing. Here is how I accomplished that:

There is a file in most Unix systems located at /usr/share/dict/words that contains 235.886 English words. Finding the number of words within that file that contain any of the three letter combinations from before can be done like so:

cat /usr/share/dict/words | ag -i "(ae|aa|oe)" | wc -l

This command will read the entire file, search for words containing any of the three combinations, and lastly count the number of results it got.

The total number of words in the file can be computed like so:

cat /usr/share/dict/words | wc -l

Now we just need a way to do arithmetic on the command line, so we can divide those two numbers and we are done.

After some quick Googling around for ways to do arithmetic on the command line and hitting several weird issues with floating point division, I decided to write my own little calculator script. It looks like this:

#!/usr/bin/env ruby

expression = STDIN.read.gsub("/", "*1.0/")
puts eval(expression)

This script works by reading what came on standard in, converting all numbers used in divisions to floats and then evaling the expression. Using this script looks something like this

$ echo "2 + 2" | ./calc
2

So now all thats left to do I build an expression that will calculate the percentage for words containing "ae", "aa", or "oe" and pipe that expression to calc. Doing that looks like this:

$ echo "`cat /usr/share/dict/words | ag -i "(ae|aa|oe)" | wc -l` / `cat /usr/share/dict/words | wc -l` * 100" | ./calc
2.749633297440289

And as you can see its almost 3% of the words in that file that I wouldn't be able to type if I had setup those mappings. That number is too high for me.

Three helper classes for object decoration

The blog you're reading right now is made with Ruby on Rails. One of the reasons I chose to go with Rails is because I wanted to build a ruby project and try to follow SOLID. Following SOLID means you have to think a lot about how to break your objects up into smaller parts, each with only a single responsibility. This means that you'll likely end up with a lot of decorators, because they provide an easy way to give an object additional functionality without adding more responsibilities to the class itself. If you're not familiar with decorators, read this and come back.

While working with these decorators I discovered some utility classes that have turned out to be quite useful. Here is a high level overview.

  • DecoratedCollection for decorating a collection of objects.
  • CompositeDecorator for composing multiple decorators into one.
  • CurriedDecorator for building decorators with prespecified dependencies

And next I would like to show you the code for each of them, as well as how to use them:

DecoratedCollection

Its common in Rails to have a view that for example shows all your posts or all your users. That often means that you have some sort of collection of objects, which you're sending to your view to render. If you would like to apply a decorator to each object within that collection DecoratedCollection is your guy.

Here is what the code looks like:

class DecoratedCollection
  include Enumerable

  def initialize(objects, decorator)
    @objects = objects
    @decorator = decorator
  end

  delegate :present?, to: :objects

  def each(&block)
    decorated_objects.each(&block)
  end

  private

  attr_reader :objects

  def decorated_objects
    @decorated_objects ||= @objects.map { |object| @decorator.new(object) }
  end
end

And here is an example of how to user it:

class PostsController < ApplicationController
  def index
    @posts = DecoratedCollection.new(Post.all, PostPresenter)
  end
end

The view can now use @posts as if it was a collection of posts, but they'll each be decorated with PostPresenter.

CompositeDecorator

Once you get a lot of decorators you'll probably wanna use several of them at the same time to decorate the same object. This could be done like this:

@post = PostWithPrettyDate.new(TruncatedPost.new(PostWithMoreStuff.new(Post.find(params[:id]))))

And while that works its not very clean. Using the composite pattern we can clean this up. We'll compose several decorators so we can treat them as one. Here is an example:

decorator = CompositeDecorator.new([
  PostWithPrettyDate,
  TruncatedPost,
  PostWithMoreStuff
])

@post = decorator.decorate(post)

And here the code:

class CompositeDecorator
  def initialize(decorators)
    @decorators = decorators
  end

  def decorate(obj)
    @decorators.inject(obj) do |acc, decorator|
      decorator.new(acc)
    end
  end

  def new(obj)
    decorate(obj)
  end
end

Notice that I'm aliasing new to decorate. That means we could have called new instead of decorate in the previous example:

@post = decorator.new(Post.find(params[:id])

The reason this is neat is because we can now treat a composite decorator as if it was a regular decorator and for example use it to decorate a collection like so:

composite = CompositeDecorator.new([
  PostWithPrettyDate,
  TruncatedPost,
  PostWithMoreStuff
])

@posts = CompositeDecorator.new(Post.all, composite)

CurriedDecorator

Following SOLID means you'll inject all your dependencies whenever possible. We don't like to reference direct constants because that violates "Open Close Principle" and "Dependency Inversion Principle". So what do we do if our decorators have dependencies that need to be injected. We could of course just write a decorator that takes more than one argument in its initializer:

@posts = PostWithParsedMarkdown.new(post, markdown_parser: MarkdownParser)

While this works fine it doesn't play well with the other two classes we've looked at. Because they both expect classes that only take one argument in their initializer.

One way to fix this is this is to use currying. Currying is when you have a function that takes for example two arguments, and you "partially apply" that to get a function that takes only one argument while remembering the first argument it got. Here is an example:

adder = -> (a,b) { a + b }

# Calling the proc without currying
adder.call(1, 2) # => 3

# Calling the proc with currying, supplying the arguments one by one
adder.curry.call(1).call(2) # => 3

We could apply the concept of currying to make a class that takes a collection of dependencies and builds a decorator using those dependencies and an object to decorate.

Such a class could look like this:

class CurriedDecorator
  def initialize(decorator, *args)
    @decorator = decorator
    @args = args
  end

  def new(*args)
    @decorator.new(*args.concat(@args))
  end
end

And we could use it like this, remember that the PostWithParsedMarkdown decorator takes two arguments, the post to decorate and the parser to use.

curried_markdown_decorator = CurriedDecorator.new(PostWithParsedMarkdown, markdown_parser: MarkdownParser)

@post = curried_markdown_decorator.new(Post.find(params[:id]))

Notice how we supplied the markdown_parser argument ahead of time and supplied the final post argument later. That means we can put all three classes together and do stuff like this:

curried_markdown_decorator = CurriedDecorator.new(PostWithParsedMarkdown, markdown_parser: MarkdownParser)

composite = CompositeDecorator.new([
  curried_markdown_decorator,
  PostWithPrettyDate
])

@posts = DecoratedCollection.new(Post.all, composite)

If you're interested in seeing these classes in use check out the repo for this blog.

Shell commands with loops

Lets say you were working on a Rails (or any code base for that matter) app and you wanted to know something like "Are there any files in this app that might too long?". If a class is too long its a good indication that it might be violating SRP. So this is an important question to be asking.

This question can be answered with the following shell script

find . |
grep ".rb" |
egrep "(app|lib)" |
while read file
do
  count=`cat $file | wc -l`
  echo $count $file
done |
sort

The way it works is:

  • Find all files in this project
  • Filter those so only ruby files remain
  • Filter those so only files from "app" or "lib" folders remain
  • For each file do
  • Count the number of lines in the file
  • Print out the count followed by the filename
  • Sort the output

The output might look something like this:

 2 ./app/helpers/application_helper.rb
 5 ./app/helpers/title_helper.rb
 5 ./app/models/tag_with_dom_id.rb
 7 ./app/controllers/api/markdown_parser_controller.rb
 7 ./app/models/post_with_pretty_date.rb
 8 ./app/controllers/archives_controller.rb
 8 ./app/models/admin_dashboard.rb
 8 ./app/views/shared/_google_analytics.html.erb
 9 ./app/services/publisher.rb
10 ./app/controllers/admin_controller.rb
11 ./app/controllers/application_controller.rb
12 ./app/models/new_post_form.rb
13 ./config/application.rb
15 ./lib/composite_decorator.rb
22 ./app/models/tag.rb
22 ./lib/decorated_collection.rb
27 ./app/controllers/tags_controller.rb
34 ./lib/markdown_parser.rb
39 ./app/models/post.rb
75 ./app/controllers/posts_controller.rb

This shows that pretty much all files in this project are nice and small, expect the posts controller... Maybe I should look into that.

This is a simple pattern for writing shell scripts. Generate some output each on one line, then do something for each line and maybe format the resulting output.

With this pattern you could also do things like:

  • See how the number of files in a project grows over time using git revlist --all
  • Find all the classes in a system and then find out which classes are referenced most often
  • Track how long time it takes to boot your app over time again using git revlist --all

Bonus
You can even write the script on one line like this:

find . | grep ".rb" | egrep "(app|lib)" | while read file; do count=`cat $file | wc -l`; echo $count $file; done | sort

Dealing with HTTP Basic Authentication in tests

In Rails, using HTTP Basic Authentication to secure an API or a simple admin backend is a good idea. Thats how I'm securing the admin backend of this blog.

Its really easy to setup on the controller side but getting it to play well with the tests requires a little more work. Here is how I did it.

First of all testing that a certain controller action requires authentication can be done like so:

First I have a method like this in ApplicationController:

describe AdminController do
  describe '#index' do
    it 'does not let people through without authenticating' do
      get :index
      expect(response.status).to eq 401
    end

    it 'does let people through with authenticating' do
      # Login somehow??!!
      get :index
      expect(response.status).to eq 200
    end
  end
end

Actually logging in from the controller test requires setting an HTTP_AUTHORIZATION header to the encoded credentials. Rails has a convenient helper for encoding the credentials:

ActionController::HttpAuthentication::Basic.encode_credentials(username, password)

And we can set the header like so

request.env['HTTP_AUTHORIZATION'] = encoded_auth_credentials

Now all thats left to do is add a helper method in "spec/support/some_file.rb" so we can use this across controller tests. That might look something like this:

def encoded_auth_credentials
  username = ENV['admin_username']
  password = ENV['admin_password']

  ActionController::HttpAuthentication::Basic
    .encode_credentials(username, password)
end

def http_login
  request.env['HTTP_AUTHORIZATION'] = encoded_auth_credentials
end

We can now call http_login from any controller test and it should work.

Our final test looks like this:

describe AdminController do
  describe '#index' do
    it 'does not let people through without authenticating' do
      get :index
      expect(response.status).to eq 401
    end

    it 'does let people through with authenticating' do
      http_login
      get :index
      expect(response.status).to eq 200
    end
  end
end

Now all thats left to do is write the implementation code. We can do that by putting a method like this in ApplicationController

def require_authentication
  authenticate_or_request_with_http_basic do |username, password|
    username == ENV["admin_username"] && password == ENV["admin_password"]
  end
end

Then inside any controller we can restrict access using a before_filter:

before_filter :require_authentication, only: [:index]

Note: I found that getting this to work with Capybara feature tests (with and without js: true) required some different code. I added this method to the test helper file we made earlier:

def authenticate
  page.driver.header 'Authorization', encoded_auth_credentials
end

← Archive