Ruby Voodoo

Deep dives into random corners of my favorite programming language.
  • 24

    OCT
    2014

    The Three Tick Sort

    Yesterday I showed a newer programmer some code like scores.sort_by(&:reverse). This provoked a comment about how they where going to look up sort_by() later to figure out what magic is involved here. It made me sad to realize how many cool tricks they weren't going to see in that bit of documentation.

    Allow me to enumerate those tricks for you, but first let's flesh out an example. Consider this code:

    scores = {
      fifteen:         2,
      five_card_run:   5,
      five_card_flush: 5,
      four_card_run:   4,
      four_card_flush: 4,
      his_nobs:        1,
      pair:            2,
      three_card_run:  3,
    }
    scores.sort_by(&:reverse).each do |name, score|
      puts "Score #{score} for #{name}."
    end
    # >> Score 1 for his_nobs.
    # >> Score 2 for fifteen.
    # >> Score 2 for pair.
    # >> Score 3 for three_card_run.
    # >> Score 4 for four_card_flush.
    # >> Score 4 for four_card_run.
    # >> Score 5 for five_card_flush.
    # >> Score 5 for five_card_run.
    

    In this case, the magic method call (scores.sort_by(&:reverse)) has reordered a list of Cribbage hands first by point value and then alphabetically ("ASCIIabetically" in truth). How this happens is a pretty interesting journey though.

    Read more…

  • 10

    OCT
    2008

    All About Struct

    I build small little data classes all the time and there's a reason for that: Ruby makes it trivial to do so. That's a big win because we all know that what is a trivial data class today will be tomorrow's super object, right? If I start out using a simple Array or Hash, I'll probably end up redoing most of the logic at both ends eventually. Or I can start with the trivial class and grow it naturally.

    The key to all this though is that I don't write those classes myself! That's what Ruby is for. More specifically, you need to learn to love Struct. Allow me to show you what I mean.

    Imagine I need a basic class to represent a Contact. Ruby gives us so many shortcuts that the class could be very small even without Struct:

    class Contact
      def initialize(first, last, email)
        @first = first
        @last  = last
        @email = email
      end
    
      attr_accessor :first, :last, :email
    end
    

    You could shorten that up more with some multiple assignment if you like, but that's the basics. Now using Struct is even easier:

    Read more…

  • 9

    OCT
    2008

    Dual Interface Modules

    I'm guessing we've all seen Ruby's Math Module. I'm sure you know that you can call methods in it as "module (or class) methods:"

    Math.sqrt(4)  # => 2.0
    

    That's just one way to use the Math Module though. Another is to treat it as a mixin and call the same methods as instance methods:

    module MyMathyThing
      extend Math
    
      def self.my_sqrt(*args)
        sqrt(*args)
      end
    end
    
    MyMathyThing.my_sqrt(4)  # => 2.0
    

    Ruby ships with a few Modules that work like this, including the mighty Kernel.

    How is this dual interface accomplished? With the seldom seen module_function() method. You use this much like you would private(), to affect all following method definitions:

    module Greeter
      module_function
    
      def hello
        "Hello!"
      end
    end
    
    module MyGreeter
      extend Greeter
    
      def self.my_hello
        hello
      end
    end
    
    Greeter.hello       # => "Hello!"
    MyGreeter.my_hello  # => "Hello!"
    

    As you can see, it magically gives us the dual interface for the methods beneath it. You can also affect specific methods by name, just as you could with private(). This is equivalent to my definition above:

    Read more…

    In: Ruby Voodoo | Tags: APIs | 2 Comments
  • 8

    OCT
    2008

    Readable Booleans

    There's a great little trick you can do to improve the readability of your code. A common problem is dealing with methods that have a boolean flag arguments. Here's an example I ran into just today in a Rails application:

    def rating_stars(..., clickable = false)
      # ...
    end
    

    The problem with this is that you typically see calls like this scattered around the application:

    <%= rating_stars(..., true) %>
    

    Would you know what true did there if I hadn't shown you the name of the variable first? I didn't. I had to go hunting for that method definition.

    Ironically the opposite problem, a magical dangling false, is much more rare in my experience. That's typically the default for these kind of arguments and it just makes more sense and reads better to leave it out.

    Anyway, the point is that we can typically improve the ease of understanding the common case. Remember that in Ruby false and nil are false while everything else is true. That means that truth is very loosely defined and we can pass a lot of things for our boolean flag value. For example, after looking up the method and understanding what was needed, I chose to call it like this:

    Read more…

    In: Ruby Voodoo | Tags: APIs & Style | 2 Comments
  • 7

    OCT
    2008

    DSL Block Styles

    There's an argument that rages in the Ruby camps: to instance_eval() or not to instance_eval(). Most often this argument is triggered by DSL discussions where we tend to want code like:

    configurable.config do
      width 100
      mode  :wrap
    end
    

    You can accomplish something like this by passing the block to instance_eval() and changing self to an object that defines the width() and mode() methods. Of course changing self is always dangerous. We may have already been inside an object and planning to use methods from that namespace:

    class MyObject
      include Configurable       # to get the config() method shown above
    
      def initialize
        config do
          width calculate_width  # a problem:  may not work with instance_eval()
        end
      end
    
      private
    
      def calculate_width        # the method we want to use
        # ...
      end
    end
    

    In this example, if width() comes from a different configuration object, we're in trouble. The instance_eval() will shift the focus away from our MyObject instance and we will get a NoMethodError when we try to call calculate_width(). This may prevent us from being able to use Configurable in our code.

    Read more…

    In: Ruby Voodoo | Tags: DSLs & Style | 7 Comments
  • 6

    OCT
    2008

    Conversion Methods

    I want to take a step back from all the syntax I've been covering lately and just talk about some simple methods in Ruby's core. Ruby ships with so many great helpers, it's often hard to keep track of what everything can do. Specifically, let's talk about the type conversion methods.

    I assume we all make calls to to_s() and to_i() regularly:

    255.to_s    # => "255"
    "255".to_i  # => 255
    

    There shouldn't be any surprises there. Even these two simple methods can do more though. They make it possible to convert to and from various numeric bases. For example, here are the same conversions into and out of base 16 (hexadecimal):

    255.to_s(16)   # => "ff"
    "ff".to_i(16)  # => 255
    

    Ruby has other ways to do these same conversions. Here are two unusual methods (beginning with capital letters) that are similar:

    String(255)     # => "255"
    Integer("255")  # => 255
    

    I'll be honest and tell you that I don't really find String() useful as it just calls to_s() for you, but Integer() is a different story. First of all, to_i() is very lenient about what it converts while Integer() is more strict:

    Read more…

    In: Ruby Voodoo | Tags: APIs | 0 Comments
  • 3

    OCT
    2008

    I'm Addicted to the Word Array

    Continuing with my recent trend of showing of fun uses of Ruby syntax, I have a confession to make: I'm addicted to Ruby's "word Array." I really am.

    I suspect most of you know this, but the word Array is a shortcut that can lessen the quote-comma-quote syndrome of simple a simple Array like:

    ["a", "wordy", "Array"]
    

    You can create the same Array with the word Array syntax:

    %w[a wordy Array]
    

    That's essentially just a String that will automatically be split() on whitespace to build an Array. You can use any amount of space any place you like, so you can layout the data in whatever way makes the most sense for you:

    require "pp"
    pp %w[ one   two   three
           four  five  six
           seven eight nine
                 zero        ]
    # >> ["one",
    # >>  "two",
    # >>  "three",
    # >>  "four",
    # >>  "five",
    # >>  "six",
    # >>  "seven",
    # >>  "eight",
    # >>  "nine",
    # >>  "zero"]
    

    Note that you can chose the punctuation characters used at either ends of the Array, some of which are paired while others just repeat:

    Read more…

  • 2

    OCT
    2008

    Interpolation and Statements

    I still cringe anytime I see code like:

    "1 + 2 = " + (1 + 2).to_s  # => "1 + 2 = 3"
    

    Some books even advocate the above, which is a real shame for Ruby.

    I imagine most of you know that you can rewrite the above to use String interpolation:

    "1 + 2 = #{1 + 2}"  # => "1 + 2 = 3"
    

    Let's think about that simple code a little bit more than we usually do though. What's really going on here? Obviously #{ … } inserts the result of the embedded code in the String, but it's important to realize that it also calls to_s() on that result to make it fit in the String.

    We can really make use of that knowledge if we try. Here's an example:

    Name = Struct.new(:first, :last) do
      def full
        "#{first} #{last}".strip  # trick 1
      end
      alias_method :to_s, :full   # trick 2
    end
    
    Name.new("James").full                     # => "James"
    Name.new(:James, :Gray).full               # => "James Gray"
    "My name is #{Name.new('James', 'Gray')}." # => "My name is James Gray."
    

    I've built a trivial data class for managing names here. In that, I've tried to make use of interpolation to the fullest.

    Read more…

    In: Ruby Voodoo | Tags: Syntax | 7 Comments
  • 2

    OCT
    2008

    Working With Multiline Strings

    I imagine most Rubyists are aware that Ruby has "heredocs," but do you really know all they can do? Let's find out.

    A "here document" is a literal syntax for a multiline String. In the most basic form, they look like this:

    p <<END_HEREDOC
    This is a
      multiline,
    as is String!
    END_HEREDOC
    # >> "This is a\n  multiline,\nas is String!\n"
    

    The <<NAME syntax introduces the heredoc, but it actually begins at the start of the following line. It continues until NAME occurs again, at the beginning of a line. Note the trailing newline in the example above. All of the data between start and finish is packaged up into a String and dropped in where the original <<NAME designator appeared.

    There are some important details in that description, namely that the String begins on the next line and that it's inserted where the heredoc was started. This means that the rest of the line where the heredoc is started can have normal Ruby code (though your editor may syntax highlight it badly):

    p <<END_SQL.gsub(/\s+/, " ").strip
    SELECT * FROM     users
             ORDER BY users.id DESC
    END_SQL
    # >> "SELECT * FROM users ORDER BY users.id DESC"
    

    Read more…

  • 26

    JUN
    2008

    Summoning Error Classes As Needed

    In the past, I've written a lot of code like this:

    class SomeThing
      class SomeError       < RuntimeError; end
      class AnotherError    < RuntimeError; end
      class YetAnotherError < RuntimeError; end
    
      # some methods that can raise the above errors...
    end
    

    I have a new strategy I've been using for code like this and it has really been working out well. Here is how I do the same thing today:

    class SmarterThing
      def self.const_missing(error_name)  # :nodoc:
        if error_name.to_s =~ /Error\z/
          const_set(error_name, Class.new(RuntimeError))
        else
          super
        end
      end
    
      # error raising methods here...
    end
    

    Let's discuss how this works. The const_missing() method is a hook in Ruby, much like the beloved method_missing(). Note that const_missing() is a class method though, instead of an instance method. When a constant is used and Ruby can't find it, a call to the hook will be triggered.

    In this version, I just check to see if the constant name ends in Error. If it does, I summon an Exception subclass on the fly. Other calls to this hook are forwarded on to Ruby's default error raising implementation via super.

    Read more…