Gray Soft

The programming blog of James Edward Gray II (JEG2).
  • 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…

  • 1

    OCT
    2008

    Lone Star Rubyconf Slides

    I've had a couple of requests for my slides from Lone Star Rubyconf. Here are those links for those who want them:

    • At LSRC 2007 I gave a Ruby as a Glue Language speech. My slides are available.
    • I also gave a Hidden Gems talk at LSRC 2008. Those slides are available as well.
    • For LSRC 2009 I talked mainly about Japan, but also mentioned Ruby's modules a little. The talk was called Module Magic. I've made those slides available.
  • 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…

  • 25

    JUN
    2008

    The One Method Config

    I've used this technique a couple of times now for dirt-simple configurations. The idea is to provide a trivial way to read and write configuration values with just a single method. Let me show you what I mean:

    module Configurable
      module_function
    
      def config(new_config = nil)
        if new_config.nil?
          @config ||= { }
        else
          config.merge!(new_config)
        end
      end
    end
    
    include Configurable
    
    config                    # => {}
    
    config :a => 1, :b => 2
    config                    # => {:a=>1, :b=>2}
    config[:a]                # => 1
    
    config :a => -1, :c => 3
    config                    # => {:a=>-1, :b=>2, :c=>3}
    
    config.clear
    config                    # => {}
    

    There's no deep magic here, obviously. The method has two function: read and write for the configuration. Read is handled with what I like refer to as Ruby's "caching operator" (||=). The first time that line is triggered, it will cache an empty Hash in the variable. Thereafter, the same call is just a cache hit to get the same Hash back.

    Read more…

  • 8

    JUN
    2008

    Getting Git Thanks to PeepCode

    It's pretty clear that Git has landed. More and more projects are migrating to Git for their version control needs and developers have another system we need to get up to speed on.

    I'm usually a traditional dead-tree book learner, but this time I decided to try the PeepCode approach. I had heard good things about them and I can now tell you that reputation is well deserved.

    PeepCode has two resources available for those wanting to learn more about Git. First, there is a one hour screencast called Git covering everything from installing through basic usage. There's also a newer 120 page PDF book called Git Internals, that ships with some different screencast material. You can purchase either product for $9, which really is a steal in my opinion. Both are well done, but let me break down their strengths for you below.

    The original screencast is a great resource for the developer who wants to get up to speed on Git in a hurry. You just watch for an hour, perhaps try a few examples as you go, and you should come out the other side with basic Git usage skills. An added bonus is that the screencast covers Git's Subversion integration which is a topic you don't want to miss, but it's sadly missing in the PDF book. It also has some good tips on configuring the gitk browser under Mac OS X.

    Read more…

  • 8

    JUN
    2008

    Programming Amazon Web Services

    I really wanted to love Programming Amazon Web Services and it does have some things going for it, but there are enough minuses to keep me from giving it full marks. Let me start by talking about what the book covers, then we will take a look at what it did well and not so well.

    This book provides full coverage of Amazon's suite of Web services. You'll find detailed chapters on Amazon's file storage service S3, their cloud computing service EC2, their messaging service SQS, their payment gateway FPS, and their document database service SimpleDB. The book begins by explaining Amazon's philosophies for these services, how they affect the suite as a whole, and why that should be important to you. For each service you will find detailed information about the design and intent of the service, how to interact with the API (including a full client implementation), and example applications making use of the service. The larger and more complicated services span multiple chapters to make sure all key aspects of using that service are covered.

    Read more…

  • 23

    APR
    2008

    The Nice New Italian Restaurant of Server Monitoring

    David Heinemeier Hanson, the creator of Rails, has made one of his business strategy talks available recently. This is a great talk about how we all might be trying just a little too hard at what can be a fairly simple task.

    We all hear advice like this over and over again, but we seem to forget it so fast that we need that constant reminding. Just today I saw a site redesigned to improve a section users haven't even seen yet (as that section isn't yet public). Is that really the top use of resources for an unlaunched portion of the site? How do we know they wouldn't have liked it? How do we know they will like the new version better? I'm sure it won't surprise readers to learn that this site is over budget on time and money.

    The point of all of this is that I want to tell you about a new server monitoring tool the company I work for has recently launched. The new service is called Scout and we've done our dead-level best to keep to the simplicity principal both because we agree with David and because we flat out need it to work that way.

    Read more…

    In: Rails | Tags: Monitoring | 2 Comments
  • 10

    APR
    2008

    Five ActiveRecord Tips

    This article was written for the Railcasts 100th Episode Contest. I think the idea is great and I look forward to reading great tips from all who decide to participate.

    1. create_or_find_by_…

    I imagine most of you know that ActiveRecord can handle finders like:

    MyARClass.find_or_create_by_name(some_name)
    

    This will attempt to find the object that has some_name in its name field or, if the find fails, a new object will be created with that name. It's important to note that the order is exactly as I just listed it: find then create. Here are the relevant lines from the current Rails source showing the process:

    record = find_initial(options)
    
    if record.nil?
      record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) }
      #{'yield(record) if block_given?'}
      #{'record.save' if instantiator == :create}
      record
    else
      record
    end
    

    The above code is inside a String literal fed to class_eval(), which is why you see interpolation being used.

    Unfortunately, this process is subject to race conditions because the object could be created by another process (or Thread) between the find and the creation. If that happens, you are likely to run into another hardship in that calls to create() fail quietly (returning the unsaved object). These are some pretty rare happenings for sure, but they can be avoided under certain conditions.

    Read more…

  • 18

    MAR
    2008

    Death and Spam

    The popular expression warns us that death and taxes are the only two certainties, but I'm worried we may need to add spam to the list. Publish any material that draws readership on the Web and invites reader feedback and you can be certain sure you will be swimming in spam soon enough.

    One of the biggest reasons I switched to my own blogging engine was to fine tune my spam control strategy. Until today, the system was that I received an email anytime a comment was posted to this blog and it was a single keystroke to remove any offensive content. While that was as simple as I can think to make a process, the fact was that it still wasn't good enough.

    The spammers ramped up their efforts until I was facing about 50 useless posts every 12 hours. While I didn't mind clearing them, the fact was that visitors were probably seeing spam due to the regularity of the postings and the time between the post and my clearing it. Obviously, there was at least spam on the blog while I slept each night.

    Read more…