Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Declarative Keyword Paramaters for Ruby

branch: master

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 lib
Octocat-spinner-32 .gitignore
Octocat-spinner-32 .rvmrc
Octocat-spinner-32 Gemfile
Octocat-spinner-32 README
Octocat-spinner-32 Rakefile
Octocat-spinner-32 keyword_params.gemspec
README
# -*- mode: ruby -*-

# Note: this file is also the library test suite. Run `rake test` to
# verify the code is passing.

# Ruby doesn't have keyword arguments, but it fakes them pretty well.

def explain(options={})
  "the #{options[:the]} says #{options[:says]}"
end

explain the: "pig", says: "oink"    # => "the pig says oink"
explain the: "frog", says: "ribbit" # => "the frog says ribbit"

# Which is fine, but it isn't as declarative (and therefore not as
# self-documenting) as proper keyword arguments.

# Also, when using keywords to construct English-like DSLs, as we are
# above, we often would like to assign different names to the
# parameters which are passed by keyword.

def explain2(options={})
  animal = options[:the]
  sound  = options[:says]
  "the #{animal} says #{sound}"
end

# And then there's defaulting for missing paramters...

def explain3(options={})
  animal = options.fetch(:the) { "cow" }
  sound  = options.fetch(:says){ "moo" }
  "the #{animal} says #{sound}"
end

explain3 # => "the cow says moo"

# Of course, it might be nice to offer a positional-argument version
# as well.

def explain4(*args)
  options = args.last.is_a?(Hash) ? args.pop : {}
  animal  = args[0] || options.fetch(:the) { "cow" }
  sound   = args[1] || options.fetch(:says){ "moo" }
  "the #{animal} says #{sound}"
end

explain4 "horse", "neigh"        # => "the horse says neigh"
explain4 "duck", says: "quack"   # => "the duck says quack"
explain4 the: "donkey", :says => "hee-haw" # => "the donkey says hee-haw"

# Once we've written all this parameter-munging machinery, we then
# repeat it in the method's documentation. (Assuming we document it at
# all). This seems a bit un-DRY.

# Let's see if we can improve on the situation.

require 'keyword_params'

class BarnYard
  extend KeywordParams

  keyword(:the)  { "cow" }
  keyword(:says) { "moo" }
  def explain(animal, sound)
    "the #{animal} says #{sound}"
  end
end

b = BarnYard.new

b.explain "horse", "neigh"                  # => "the horse says neigh"
b.explain "duck", says: "quack"             # => "the duck says quack"
b.explain the: "donkey", :says => "hee-haw" # => "the donkey says hee-haw"
b.explain the: "cat"                        # => "the cat says moo"
b.explain                                   # => "the cow says moo"

# Improvement? Well, I'll leave that for you to judge. Certainly
# specifying part of the method's  signature above the method
# definition proper has a nasty "old-style C" feel to it. But I feel
# like it's a lot more self-documenting than doing the keyword
# processing inside the method.
Something went wrong with that request. Please try again.