Hidden Features

Posts tagged with "Hidden Features."
  • 23

    MAY
    2015

    Rich Methods

    Some APIs provide collections of dirt simple methods that just do one little thing.

    This approach in less common in Ruby though, especially in the core and standard library of the language itself. Ruby often gives us rich methods with lots of switches we can toggle and half hidden behaviors.

    Let's look at some examples of what I am talking about.

    Get a Line at a Time

    I suspect most Rubyists have used gets() to read lines of input from some kind of IO. Here's the basic usage:

    >> require "stringio"
    => true
    >> f = StringIO.new(<<END_STR)
    <xml>
      <tags>Content</tags>
    </xml>
    END_STR
    => #<StringIO:0x007fd5a264fa08>
    >> f.gets
    => "<xml>\n"
    >> f.gets
    => "  <tags>Content</tags>\n"
    

    I didn't want to mess with external files for these trivial examples, so I just loaded StringIO from the standard library. It allows us to wrap a simple String (defined in this example using the heredoc syntax) in the IO interface. In other words, I'm calling gets() here for a String just as I could with a File or $stdin.

    Read more…

  • 30

    JAN
    2015

    Random Access Terminal

    I've recently been playing around with fancy terminal output in Ruby. I've learned quite a bit about this arcane magic. I've also realized that the documentation is pretty spotty. I want to see if I can improve that with a few blog posts, so let's dive right in.

    Output

    Program output typically happens in a linear order from top to bottom. For example, this code:

    puts "onez"
    puts "twos"
    puts "threes"
    

    generates this output:

    onez
    twos
    threes
    

    But what if you need to change some output? Could you replace the z above with an s if you needed to? Yes, but it can get a little involved.

    ANSI Escape Codes

    In many cases, we just push some characters to $stdout (the stream Kernel#puts is writing to above) and your terminal program happily shows them to the user. However, your terminal is watching these characters for special sequences that it understands. Some of those sequences of characters can cause your terminal to take actions other than just writing some output to the screen.

    Read more…

  • 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…

  • 11

    MAY
    2012

    Delaying Decisions

    I love playing with Ruby's Hash. I think it has a neat API and experimenting with it can actually help you understand how to write good Ruby. Let's dig into this idea to see what I mean.

    The nil Problem

    In Destroy All Software #9 Gary chooses to show an example in Python because, unlike Ruby's Hash, it will raise an error for a non-existent key. Ruby just returns nil, he explains.

    What Gary said isn't really true, but I'm guessing he just didn't know that at the time. He was in the process of switching to Ruby from Python and I'm guessing he just didn't have a deep enough understanding of Ruby's Hash yet. I bet he does know how it works now.

    But assume he was right. What's he saying and why does it matter? Consider some code like this:

    class SearchesController < ApplicationController
      def show
        terms = params[:terms]
        SomeModel.search(terms)
        # ...
      end
    end
    

    This is what Gary doesn't like, and rightfully so. Because I indexed into params here with the []() method, I will indeed get a nil if the :terms key wasn't in params.

    Read more…

  • 13

    OCT
    2008

    The Secret Shell Helper

    Someone pops onto the Ruby Talk mailing list fairly regularly asking how to break up content like:

    one "two" "a longer three"
    

    They expect to end with a three element Array, where the third item will contain spaces. They generally expect the quotes will have been removed as well.

    If your needs are very, very simple you may be able to handle this with a regular expression:

    data = 'one "two" "a longer three"'
    p data.scan(/"([^"]*)"|(\S+)/).flatten.compact
    # >> ["one", "two", "a longer three"]
    

    That just searches for either a set of quotes with some non-quote characters between them or a run of non-whitespace characters. Those are the two possibilities for the fields. Note that the two separate capture here mean scan() will returns contents in the form:

    [[nil, "one"], ["two", nil], ["a longer three", nil]]
    

    That's why I added a flatten() and compact() to get down to the actual matches.

    The regular expression approach can get pretty complex though if any kind of escaping for quotes is involved. When that happens, you may need to step up to a parser.

    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…

  • 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

    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…

  • 1

    OCT
    2006

    I Just Want One Character!

    Every so often a person asks the question on Ruby Talk, "How can I get just one character from the keyboard (without needing the user to hit return)?" Everyone is always quick to post solutions, but sadly there are some issues with almost every one of them.

    The general consensus is that this is a tough problem to solve correctly. I say that's the exact reason to let HighLine handle this for you:

    #!/usr/bin/env ruby -w
    
    require "highline/system_extensions"
    include HighLine::SystemExtensions
    
    print "Enter one character:  "
    char = get_character
    puts char.chr
    

    That doesn't look too tough, does it?

    What's terrific about this solution is that under-the-hood HighLine will check your platform and libraries and then try to use the solution that makes the most sense for your environment. The code is really pretty robust too, because people a lot smarter than me have been sending in patches for over a year, slowly eliminating all of those tricky edge cases.

    As you can see, I've split this functionality of HighLine into a separate module so you don't even need to load the full HighLine system. This was done just because this is such a real and common problem. This section of HighLine is one pure Ruby file, so feel free to vendor it if the external dependency is an issue.

    Read more…

  • 30

    JUL
    2006

    PStore Meets YAML

    I love the PStore standard library. It's a very graceful interface to get some fairly robust serialized mini-database handling in just a few lines. With it you get:

    1. Transactions with commit and rollbacks (automatic on exception).
    2. File locking, shared and exclusive.
    3. Multiprocessing safety.

    PStore does even more, including some file mode checking and MD5 hashing to avoid unneeded writes, but the above are the major selling points for me.

    Now, if I had to level any one complaint at PStore, it would be that because it uses Marshal under the hood it doesn't create files you can easily browse or tweak by hand. (Marshal is a feature, don't get me wrong. It's fast, which is very helpful.) Sometimes though I want PStore protection with the YAML file format.

    I'm embarrassed to admit that I use to use a hack for this:

    require "pstore"
    require "yaml"
    class PStore; Marshal = YAML; end
    

    That just redefines the Marshal constant in a scope that should only alter PStore. The library only uses dump() and load() and those methods work the same with Marshal and YAML.

    Read more…