Skip to content
Declarative Keyword Paramaters for Ruby
Ruby
Find file
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
lib
.gitignore
.rvmrc
Gemfile
README
Rakefile
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.