Gray Soft

The programming blog of James Edward Gray II (JEG2).
  • 16

    JUL
    2006

    String Has Other Methods Besides =~/match() and sub()

    Ask anyone who knows me and they will tell you I'm a huge fan of regular expressions. I use them all the time and my FasterCSV library is a regular expression powered parser. However, even I know they are not for everything, and lately I keep running into almost comical examples of misuse. Here are some of my favorites.

    First, we have:

    str =~ /=/
    

    That snippet is like calling for a military escort (the regular expression engine) to see you safely to the grocery store down the block. That's fun, but probably overkill. In this case, a call to include?() will do the trick:

    str.include?("=")
    

    That may be more like riding your bike to the grocery store, but it gets the job done and is a bit faster to boot.

    Funny example number two. I've seen this before:

    str =~ /\Aquit\Z/
    

    Again, the regular expression engine appreciates the love, but you really just want ==:

    str == "quit"
    

    Even for some of the fancier stuff, you don't need a full blown regular expression. For example, this:

    Read more…

  • 8

    JUL
    2006

    The Books are Wrong About Logger

    I've read several books that introduced the standard Logger library and they all agree on one thing: you can't customize the output. That's so last version in thinking! Behold…

    Here's a trivial Logger script, showing basic functionality:

    #!/usr/bin/env ruby -w
    
    require "logger"
    
    def expensive_error_report
      sleep 3  # Heavy Computation Simulation (patent pending)
      "YOU BROKE IT!"
    end
    
    log       = Logger.new(STDOUT)
    log.level = Logger::INFO  # set out output level above the DEBUG default
    
    log.debug("We're not in the verbose debug mode.")
    log.info("We do see informative logs though.")
    if log.error?  # check that this will be printed, before waste time
      log.error(expensive_error_report)
    end
    

    If you run that you will see:

    I, [2006-07-08T11:17:19.531943 #340]  INFO -- : We do see informative logs though.
    E, [2006-07-08T11:17:22.532424 #340] ERROR -- : YOU BROKE IT!
    

    Now everyone has always known you can format the date and time display using a strftime() compatible pattern:

    #!/usr/bin/env ruby -w
    
    require "logger"
    
    def expensive_error_report
      sleep 3
      "YOU BROKE IT!"
    end
    
    log                 = Logger.new(STDOUT)
    log.level           = Logger::INFO
    log.datetime_format = "%Y-%m-%d %H:%M "  # simplify time output
    
    log.debug("We're not in the verbose debug mode.")
    log.info("We do see informative logs though.")
    if log.error?
      log.error(expensive_error_report)
    end
    

    Read more…

  • 13

    JUN
    2006

    Do I Need (These Parentheses()?)

    If you came to Ruby via the Learn to Program book or just don't yet have a consistent set of rules for when you do and don't need parentheses in Ruby code, this post is for you.

    I have nothing against Learn to Program, just to be clear. A member of my family is learning Ruby from it and it's going pretty well. I recommend it. However, Chris is a little inconsistent with his use of parentheses in the code samples, and worse, he doesn't really give you a good set of rules to decide when to make the choice. No problem. Let me give you the rules.

    I'm a chess player. In learning chess, you really go through two phases. First, you learn the rules of strategy. These will make you good because the rules are designed to help you avoid common mistakes. Now, to get great, you go through the second phase: learning when to break the strategy rules. Ruby is exactly the same.

    Here's the only rule of strategy you need to learn to get good: methods need parentheses around their arguments.

    Read more…

  • 21

    APR
    2006

    Unit Testers Get More Chicks

    Just recently, a developer I respect very much was caught uttering the this surprising statement: "Unit tests just really aren't my thing." Now, I still respect this developer very much and I can tell you that the person single-handedly created one of my very favorite pieces of software. However, I do think the developer is dead wrong on this point. This is my attempt to change the mind of everyone that thinks similar things about unit testing.

    My belief is that unit testing is for everyone and, in fact, I'll go so far as to say that I believe becoming a test-driven developer is the single best change a programmer can make in their day to day routine. Here are just some of the reasons why:

    • Though counterintuitive, I swear that it makes you code faster. No one tends to believe me on this, but most test-driven developers come to this realization eventually. The reason is simple: you spend much less time debugging the 935 errors from your two hour code sprees.
    • The tests are my memory. My head is too full of all the languages, frameworks, APIs, and family birthdays I am expected to know to remember everything I've ever done on top of that. This week at work I've touched three separate projects all with over 1,000 lines of code. I'm sure I had good ideas when I wrote those lines, but now I doubt I can tell you what they are. My tests can though. They remember so I don't have to. If I go into the code and change something I don't remember was needed or why, my tests will remind me immediately.
    • In team development, my test powered memory even travels to the machines of the other developers! How cool is that? When someone goes into the code and says, "Why on Earth did James do this? We don't need this. I'm going to change that…" my tests will look after my interests for me.
    • I'm a lot more confident in my software. I use to say things like, "I just finished this, so it hasn't been used much yet and probably still has plenty of issues." Now by the time I finish something my tests have been using the heck out of it. I won't kid you and tell you that my software now springs fully formed from the head of my tests, but it definitely comes out farther along the track.
    • Environment changes seldom surprise me anymore. When I move my software to a different box and it can't cope for whatever reason, I not only know the first time I run the tests, I have an excellent idea of exactly where the problem is.

    Read more…

  • 12

    APR
    2006

    Ruby Idioms Bundle for TextMate

    [Update: An improved version of Ruby Idioms has been merged into the Ruby bundle in TextMate. Everyone gets Ruby Idioms with TextMate now.]

    If you are a TextMate user (a Mac OS X text editor), we have something in common and you might be interested in the rest of this post. If not, skip it.

    I have made my Ruby Idioms bundle available for anyone who would like to download it and try it out. This bundle contains all the Ruby shortcuts I use to program everyday. I've spent a lot of energy over the last six months refining them down to the keystroke, so hopefully there is a lot of value hidden in there.

    If you just want to get to the goods and start playing, click the above link and run. However, if you want some insight into my thinking with these shortcuts, read on…

    Tab Triggers

    I'm a Unix guy, so I'm very comfortable with two or three letter triggers and that's what you are going to find here. Sometimes I use a one or two caracter trigger (r, w, and rw just make sense for attr_reader, attr_writer, and attr_accessor, for example), but three is the norm.

    Read more…

  • 21

    MAR
    2006

    The Why and How of Iterators

    A friend of mine has been asking some general questions about iterators in private emails we have traded. I wanted to put some of my answers here, in case they appeal to a wider audience.

    Why do we have iterators?

    First, let's invent a little data to play with:

    >> Name = Struct.new(:first, :last)
    => Name
    >> names = [ Name.new("James", "Gray"),
    ?>           Name.new("Dana", "Gray"),
    ?>           Name.new("Caleb", "Nordloh"),
    ?>           Name.new("Tina", "Nordloh") ]
    => [#<struct Name first="James", last="Gray">,
        #<struct Name first="Dana", last="Gray">,
        #<struct Name first="Caleb", last="Nordloh">,
        #<struct Name first="Tina", last="Nordloh">]
    

    Now let's assume we want to print some names. We can use the each() iterator for that, no index:

    >> names.each { |name| puts "#{name.last}, #{name.first}" }
    Gray, James
    Gray, Dana
    Nordloh, Caleb
    Nordloh, Tina
    => [#<struct Name first="James", last="Gray">,
        #<struct Name first="Dana", last="Gray">,
        #<struct Name first="Caleb", last="Nordloh">,
        #<struct Name first="Tina", last="Nordloh">]
    

    Read more…

  • 10

    MAR
    2006

    Java a Bit on the Wordy Side

    I was helping a friend of mine with a Java problem yesterday and couldn't help but notice this totally normal (for Java) file in his project:

    import java.io.Serializable;
    
    public class Contact implements Serializable
    {
       private String firstName;
       private String lastName;
       private String email;
       private String phone;
    
       public Contact()
       {
           this("", "", "", ""); // call four-argument constructor
       } // end no-argument Contact constructor
    
       // initialize a record
       public Contact(String first, String last, String eml, String phn)
       {
           setFirstName(first);
           setLastName(last);
           setEmail(eml);
           setPhone(phn);
       } // end four-argument Contact constructor
    
       // set first name
       public void setFirstName(String first)
       {
          firstName = first;
       } // end method setFirstName
    
       // get first name
       public String getFirstName()
       {
          return firstName;
       } // end method getFirstName
    
       // set last name
       public void setLastName(String last)
       {
          lastName = last;
       } // end method setLastName
    
       // get last name
       public String getLastName()
       {
          return lastName;
       } // end method getLastName
    
       // set email address
       public void setEmail(String eml)
       {
          email = eml;
       } // end method setEmail
    
       // get email address
       public String getEmail()
       {
          return email;
       } // end method getEmail
    
       // set phone number
       public void setPhone(String phn)
       {
          phone = phn;
       } // end method setPhone
    
       // get phone
       public String getPhone()
       {
          return phone;
       } // end method getPhone
    } // end class Contacts
    

    Read more…

  • 22

    FEB
    2006

    Class Level Mix-ins

    A question that comes up pretty often in Ruby circles was posed again today, by Xavier Noria:

    #
    # Is there standard idiom to add module methods to classes that mix them in?
    #
    # That is, I would like class C to croak:
    #
    
    module M
      def self.croak
        puts "Croak!"
      end
    end
    
    class C
      include M
      croak  # doesn't work
    end
    

    This brings up a couple of interesting points about mix-ins. Obviously, class methods are not easily added to things, but we generally want class methods so we can call them directly from the module. Here's how I deal with this issue.

    First, instance methods are the way to go for anything you will be mixing in, period. With instance methods, you can inject them into a class or object. With anything else you have to start hacking. That gives us our first change:

    module M
      def croak
        puts "Croak!"
      end
    end
    

    Now, we really wanted a module method, so I've made things worse. However, as I've said, we have all the choices with this setup. Let's just mix the module into it's own class!

    Read more…

  • 22

    FEB
    2006

    Currying

    All the examples in this chapter are trivially translated (switch sub { ... } to lambda { ... }). Ironically, I have never seen a chunk of idiomatic Ruby do anything like this. Rubyists clearly favor blocks for this sort of work. Have a look at the stream addition and multiplication examples of this chapter, for example. You can also see this when MJD trying to create a suitable inject() for Perl (he calls it reduce/fold).

    Another interesting point about this chapter is how much of it is spent warring with Perl's syntax. MJD really struggles to introduce a block-like syntax for curried methods and is outright defeated in a couple of attempts. I really like how easily Ruby yields to our attempts to reprogram her, in sharp contrast to her sister language.

    Continuing that line of thought, here's my best effort at the Poor Man's Currying library:

    #!/usr/bin/env ruby -w
    
    class Proc
      def curry(&args_munger)
        lambda { |*args| call(*args_munger[args]) }
      end
    end
    
    class Object
      def curry(new_name, old_name, &args_munger)
        ([Class, Module].include?(self.class) ? self : self.class).class_eval do
          define_method(new_name) { |*args| send(old_name, *args_munger[args]) }
        end
      end
    end
    

    Read more…

  • 20

    FEB
    2006

    Infinite Streams

    I've tried to summarize this chapter a couple of times now, but I keep getting tripped up over syntax. So, let's talk about that…

    Functional Perl

    Obviously, the examples in the book can be more or less directly translated. Here's a sample from the first couple of pages:

    #!/usr/local/bin/ruby -w
    
    ### Stream Methods ###
    
    def node(head, tail)
      [head, tail]
    end
    
    def head(stream)
      stream.first
    end
    
    def tail(stream)
      tail = stream.last
      if tail.is_a?(Proc)
        tail.call
      else
        tail
      end
    end
    
    def drop(stream)
      head                 = head(stream)
      stream[0], stream[1] = Array(tail(stream))
      head
    end
    
    def show(stream, limit = nil)
      while head(stream) && (limit.nil? or (limit -= 1) > -1)
        print drop(stream), $, || " "
      end
      print $/
    end
    
    ### Examples ###
    
    def upto(from, to)
      return if from > to
      node(from, lambda { upto(from + 1, to) })
    end
    show(upto(3, 6))  # => 3 4 5 6
    
    def upfrom(start)
      node(start, lambda { upfrom(start + 1) })
    end
    show(upfrom(7), 10)  # => 7 8 9 10 11 12 13 14 15 16
    

    Read more…