Rubies in the Rough

This is where I try to teach how I think about programming.
  • 22

    AUG
    2014

    Sleepy Programs

    When we think of real multiprocessing, our thoughts probably drift more towards languages like Erlang, Go, Clojure, or Rust. Such languages really focus on getting separate "processes" to communicate via messages. This makes it a lot easier to know when one process is waiting on another, because calls to receive messages typically block until one is available.

    But what about Ruby? Can we do intelligent process coordination in Ruby?

    Yes, we can. The tools for it are more awkward though. It's easy to run into tricky edge cases and hard to code your way out of them correctly.

    Let's play with an example to see how good we can make things. Here's what we will do:

    1. We will start one parent process that will fork() a single child process
    2. The child will push three messages onto a RabbitMQ queue and exit()
    3. The parent will listen for three messages to arrive, then exit()

    Here's a somewhat sloppy first attempt at solving this:

    #!/usr/bin/env ruby
    
    require "benchmark"
    
    require "bunny"
    
    QUEUE_NAME = "example"
    MESSAGES   = %w[first second third]
    
    def send_messages(*messages)
      connection = Bunny.new.tap(&:start)
      exchange   = connection.create_channel.default_exchange
    
      messages.each do |message|
        exchange.publish(message, routing_key: QUEUE_NAME)
      end
    
      connection.close
    end
    
    def listen_for_messages(received_messages)
      connection = Bunny.new.tap(&:start)
      queue      = connection.create_channel.queue(QUEUE_NAME, auto_delete: true)
    
      queue.subscribe do |delivery_info, metadata, payload|
        received_messages << payload
      end
    
      time_it("Received #{MESSAGES.size} messages") do
        yield
      end
    
      connection.close
    end
    
    def time_it(name)
      elapsed = Benchmark.realtime do
        yield
      end
      puts "%s: %.2fs" % [name, elapsed]
    end
    
    def wait_for_messages(received_messages)
      until received_messages == MESSAGES
        sleep 0.1  # don't peg the CPU while we wait
      end
    end
    
    def send_and_receive
      pid = fork do
        sleep 3  # make sure we're receiving before they are sent
        send_messages(*MESSAGES)
      end
      Process.detach(pid)
    
      received_messages = [ ]
      listen_for_messages(received_messages) do
        wait_for_messages(received_messages)
      end
    end
    
    send_and_receive
    

    Read more…

  • 20

    JUL
    2014

    Dave's No Tests Challenge

    I've mentioned before my difficulties in the 2014 IPSC. But taking one beating is no reason not to try again. The first loss just showed me that the contest still had more to teach me.

    A buddy of mine has spent some time with the crossword problem and told me that he enjoyed it. I didn't try this problem during the actual event, but I was a little familiar with it from my friend's description.

    To add to the fun, I decided this would be a great excuse to take up the recent challenge Dave Thomas gave to the Ruby Rogues: "Stop writing tests."

    Step 1: Feedback Loops

    Without tests to guide me, I really want to see what's going on. One of the biggest advantages of tests, in my opinion, is the feedback loop it provides. So I set out to provide my own feedback.

    Since the problem at hand involves filling in a crossword board, the easiest feedback loop I could think of was to see the board as it fills in. The final board is also the required output. Therefor, I decided a good first step would just be to read the board into some data structure and write it back out. Once I had that, I could insert code between those steps to fill it in. And constantly seeing the board evolve would let me eyeball things for obvious mistakes.

    Read more…

  • 30

    MAY
    2014

    Objectified Beer

    I got another thing out of my recent conversation with Katrina Owen: a will-not-let-go itch to try a programming exercise that she mentioned. I'm such a sucker for a challenge.

    Katrina spoke of an assignment that her and Sandi Metz have used in their object orientation trainings. She said that they build an OO implementation of the 99 Bottles of Beer song, "mainly removing ifs and such." There may be more to the actual task than this, but I ran with that brief explanation.

    As I often say, you'll probably learn more by trying the exercise for yourself before you read through my thoughts about it. Give it a go if you can spare the time.

    Diff Driven Development

    I decided to throw some scaffolding into the master branch of a Git repository. I figured I could then branch off of that with each idea, to keep trying things out.

    I started by constructing a trivial framework for running and verifying the song. That consisted of an executable:

    #!/usr/bin/env ruby -w
    
    require_relative "../lib/bottles_of_beer"
    
    verses = ARGV.first =~ /\A\d+\z/ ? ARGV.shift.to_i : 99
    BottlesOfBeer::Song.new(verses).sing($stdout)
    

    Read more…

  • 21

    MAY
    2012

    Decorators Verses the Mix-in

    It is a neat time to be involved in the Ruby community, if you ask me. A large portion of us are currently studying the techniques for doing good object oriented development. We are looking at the ideas that have come before and trying to decide the best ways to apply those ideas to our favorite language. This leads to blog posts, forum threads, and conference talks about what we are learning. No matter what, we all gain from explorations like this. Everybody wins as our collective knowledge grows. We all deserve gold stars.

    So far, there's one point pretty much everyone agrees on: composition should typically be preferred to inheritance. The trickier part of that discussion though is deciding what composition looks like in Ruby. Generally you see Rubyists comparing the merits of decorators and mix-ins. [Note: the comments correctly pointed out that this was a bad use of the word "composition" on my part, to describe mix-ins.] There's a very representative thread on the excellent Objects on Rails mailing list.

    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…

  • 1

    MAY
    2012

    Single Method Classes

    [Update: I've changed my mind about some of the following due to this excellent counter argument.]

    In the words of Dennis Miller, "I don't want to get off on a rant here, but…"

    There's something that drives me crazy and I see it in so much Ruby code. I see it in the documentation for our key projects; I see Rubyists of all skill levels doing it; it's just everywhere.

    Let's talk about when the use of a Class is and is not appropriate.

    The Chained new()

    Here's an example of one form of code that bugs me:

    class Adder
      def add(n)
        40 + n
      end
    end
    p Adder.new.add(2)
    

    The problem here is that a Class has been used, probably because as Rubyists that's always our default choice, but it's the wrong fit for this code. A Class is for state and behavior. The example above is just using behavior. No state is maintained.

    A handy tip for sniffing out this problem is watching for a call to new() in the middle of method chaining as we have here. If you always use a Class like that, it's not really a Class. Put another way, if an instance never gets assigned to a variable, something has likely gone wrong with the design.

    Read more…

  • 21

    APR
    2012

    Let's Patch Rails

    In celebration of my first ever trip to RailsConf next week I wanted to be a good open source citizen and contribute a patch.

    I'm giving a presentation at the conference on various random features that the framework provides. I selected features from many places like blog posts, books, and some that I just remembered from years of working with the software.

    One of the features I decided to show was an old feature that I never see anyone use. It turns out that there's a good reason for that. When I tried it on a modern Rails, it didn't work anymore. Rails has undergone some changes under the hood and this feature was likely removed in that process. I figured out a workaround so I could still show it in my presentation, but it would be nice if I contributed the fix back to Rails so others could use it.

    In this article, I will walk through the entire process of doing that.

    What's Missing?

    ERb has always had an under appreciated syntax tweak. Everyone knows you can write code like this:

    Read more…

  • 11

    APR
    2012

    Riding the Testrocket

    I say it a lot, but programming is about ideas. More specifically, it's about not running out of ideas.

    Along these lines, I read the results of an interesting study recently. It was a study about how we think and solve problems. When I was in school, the belief was that we needed to cram our brain full of facts. We were pushed to memorize, memorize, memorize.

    The more modern view of learning is that facts don't matter as much. Nowadays we just try to teach children how to think. The assumption is that they can find the facts they need, because information is so universally available, and their thinking skills will allow them to work their way to other bits of knowledge.

    The study looked at people taught using both of these techniques and found some surprising results: us memorizers can often out think the thinkers. A leading theory about why that's the case is that the memorizers have more of a foundation to build their ideas off of. Thinkers may be starting closer to scratch each time. If so, they have further to go to get to the needed solution. Memorizers, on the other hand, may already have a lot of knowledge that puts them closer to the solution before they even need to start thinking.

    Read more…

  • 1

    APR
    2012

    A Stylish Critique

    Before getting started, I feel compelled to point out that my dictionary defines a critique as "a detailed analysis and assessment of something." It seems like we often assume the worst of that word, but that's not how I intend it here.

    The Ruby community seems to be talking about style guides lately. So let's talk about them.

    The fact is that you will have many choices if you go looking for style guides for our favorite language. You can pick from:

    It's obvious that Rubyists care about this topic. Let's see what's out there and consider what really is and is not useful from these guides.

    What is a style guide really, and why do we even have them?

    Defining style guides is surprisingly tough. I suspect they started out as formatting rules for code, but they have evolved pretty far beyond that now.

    Most guides include general conventions that the author feels are important when writing the language in question. This can go all the way down to how to use certain constructs, opinions on what the author considers idiomatic, and syntax to outright avoid.

    Read more…

  • 21

    MAR
    2012

    Learn to Love Mix-ins

    The road to mastering Ruby is paved with understanding some key Ruby concepts. Mix-ins are one of those concepts. I'm sure everyone reading this knows the mechanics of how mix-ins work, but it pays to spend some time really thinking about all that mix-ins imply. Let's do just that.

    Adding a Type

    One of the primary reasons that Ruby needs mix-ins is that it does not support multiple inheritance. That leaves mix-ins as our only option for modeling hybrid objects. It's the way Ruby programmers can add another type.

    That's a good way to think about it too: adding a type.

    Take pagination, for example. Pagination methods are usually defined to return an object like this:

    class PaginatedCollection < Array
      # ... paginated helpers defined here ...
    end
    

    That's never really felt right to me though.

    First, inheriting from Ruby's core classes can come back to bite you in some scenarios. The reason is that Ruby makes some performance tradeoffs to keep the core classes fast, but those tradeoffs mean that those classes don't always perfectly follow Ruby's rules.

    Read more…