Developmentastic

  1. Simple Presenter

    John Nunemaker inspired me in several ways this morning. He described his experience building an API for Gauges which echoes much of my own experience with the CloudApp API. Especially his third point about linking resources instead of expecting your clients to construct their own URLs. That road will only end in tears.

    Where it really hit home was his use of a presenter to provide a JSON representation of a model. I wanted to share some code that I’ve been spending some time with lately. There were a few refactorings that have made the code much easier to follow and reason about.

    There’s one main commit that shows the majority of the presenter implementation. It freed the main Sinatra app from having to concern itself with how to render a thing and now it simply asks the thing to format itself. Big difference.

    def fetch_and_render_drop(slug)
      drop = fetch_drop slug
      respond_to do |format|
    
        # Redirect to the bookmark's link, render the image view
        # for an image, or render the generic download view for
        # everything else.
        format.html do
          if drop.bookmark?
            redirect_to_api
          else
            erb drop_template(drop),
                :locals => { :drop    => drop,
                             :body_id => body_id(drop) }
          end
        end
    
        # Handle a JSON request for a **Drop**. Return the same
        # data received from the CloudApp API.
        format.json do
          Yajl::Encoder.encode drop.data
        end
      end
    end
    

    You can tell I knew the code was awful all along by the presence of comments. Compare that with today’s version.

    def fetch_and_render_drop(slug)
      drop = DropPresenter.new fetch_drop(slug), self
      respond_to do |format|
        format.html { drop.render_html }
        format.json { drop.render_json }
      end
    end
    

    I think this code’s job is pretty clear. Fetch the drop and ask for the correct representation based on the format the client requested. The DropPresenter is where the magic happens.

    class DropPresenter < SimpleDelegator
      def initialize(drop, template)
        @template = template
    
        super drop
      end
    
      def render_html
        if bookmark?
          @template.redirect_to_api
        else
          @template.erb template_name,
                        locals: { drop: self, body_id: body_id }
        end
      end
    
      def render_json
        Yajl::Encoder.encode data
      end
    
      # Some trivial private methods omitted.
    end
    

    My favorite part of this refactoring is passing a DropPresenter to existing views in place of a Drop. Everything still works and they’re none the wiser. Having this middleman in place gives the freedom to incrementally refactor logic out of the controller and views.

    For example, here’s the logic to add a logo to the page if the owner has a Free account. It’s ripe for refactoring.

    <% unless drop.subscribed? %>
      <h1><a href="http://store.getcloudapp.com/">Simple sharing</a></h1>
    <% end %>
    
    <% unless drop.subscribed? %>
      <footer id="footer">
        <h1><a href="http://store.getcloudapp.com/">Simple sharing</a></h1>
      </footer>
    <% end %>
    

    Refactoring begets refactoring.

  2. Mimetic Poly-Alloy

    Mimetic Poly-Alloy: A Quicksilver-inspired JavaScript search that favors consecutive matching characters.

    Quicksilver is great to search using acronyms or shorthand. Where it’s not a good fit is matching a string against a list of keywords. Here’s when I realized I needed to take a different approach:

    >>> "this is a test".score("this")
    0.9285714285714286
    >>> "this is a test".score("test")
    0.7821428571428569

    In my application, I don’t care where a match occurs; I only care about the length of the match and if the search matched the beginning of a word.

    Taking some obvious inspiration from Quicksilver, I took a naive approach and computed a score by raising 2 to the power of the number of matching characters and threw in an extra point if it matched the beginning of a word. In the previous example, both searches would score 32 (2(4+1)).

    Here are some examples from the test suite:

    var input = "this is a test";
    
    // Really long match.
    equals(input.score("this is a test"), 32768.0, "14-character match");
    
    // Beginning of a word
    equals(input.score("test"), 32.0,   "4-character match");
    equals(input.score("thit"), 19.6,   "3-character and 1-character matches");
    equals(input.score("tiat"), 13.756, "4 1-character matches");
    equals(input.score("th"),   8.0,    "2-character match");
    equals(input.score("ti"),   7.6,    "2 1-character matches");
    
    // Middle of a word
    equals(input.score("hi"), 4.0, "2-character match");
    equals(input.score("he"), 3.8, "2 1-character matches");

    If this type of search makes sense for you, give it a shot and fork it. If this is a solved problem, yell at me for not doing enough research.

  3. Using Slime To Execute Cucumber Features

    I learned of the wonders of slime from David Whittington during his CPOSC talk on Clojure. He would write a line of code and, with a single keystroke, it would be whisked away and executed in a terminal window. This proved especially useful in his demonstration because he was able to write and manipulate blocks of code in vim and have it execute in the REPL. I tried it for myself when playing with Ruby and lisp to found it far more productive than monkeying around at the command line.

    You can get up and running with slime.vim in a few minutes. Jonathan Palardy wrote a detailed tutorial describing slime’s merits, his setup, and how to use it with irb. Absolutely worth a quick read.

    I’ve been meaning to find a decent solution to execute my tests from within vim instead of having to constantly switch between it and a terminal. I came across a post by Paul Battley outlining his solution using autocmd to define a few simple mappings any time a Ruby test file or Cucumber feature is opened. He’s updated the script since writing that post, so be sure to check out the living copy on GitHub. I spend a great deal of time writing Cucumber features so anything that makes executing them less painful gets my attention.

    I’m sure it’s obvious where I’m going with this. Using slime.vim, the opened feature or focused scenario can be sent to a terminal window to run in the background instead of running in the foreground of vim. Here’s what I ended up with:

    augroup Cucumber
      au!
      autocmd BufNewFile,BufReadPost,BufEnter *.feature,*.story
        \ set filetype=cucumber|
        \ :nmap <leader>r :call Send_to_Screen("cucumber -r features " . expand("%") . "\n")<CR>|
        \ :nmap <leader>R :call Send_to_Screen("cucumber -r features " . expand("%") . "\:<C-R>=line(".")<CR>\n")<CR>|
    augroup END

    When editing the file features/account.feature, the mapping ,r (assuming comma is your leader) will send the command cucumber -r features features/account.feature to the screen session. ,R functions similar but appends the current line number so only the scenario your cursor is on will be executed.

    After using these mappings for a few minutes, I realized many times I’m inside a step definition or somewhere else outside of the feature and want to re-run the last scenario. A simple mapping to execute !! is all you need.

    :nmap <leader>l :call Send_to_Screen("!!\n")<CR>

    A sample workflow looks something like this:

    1. Setup a screen session as Paul describes.
    2. Open a feature and write a scenario.
    3. Mash ,R to run it.
    4. Tell slime.vim the names of your session and window.
    5. Watch it fail.
    6. Implement each step one-by-one and use ,l liberally to re-run the scenario.
    7. Commit.
    8. Rejoice.
    9. Repeat.

    Some things worth mentioning:

    • I tweaked slime.vim to default the session and window names to “s0” and “w0” respectively.
    • Only using screen a handful of times, it threw me off that it has its own scrollback. It makes sense, but I only run one session per terminal tab so I don’t mind disabling that feature.
    • It’s also nice to default the window title to the same value configured in slime.vim.

    Here are the relevant lines from my ~/.screenrc:

    termcapinfo xterm* ti@:te@
    shelltitle 'w0'

    And an alias I threw into ~/.bashrc:

    alias s="screen -S s0"

    I’ll definitely be expanding these tools to better fit my workflow. If you have a clever addition or another approach to the problem, I’d love to see it.

    Update: I’ve been advised to clarify one point. slim.vim isn’t technically slime; it’s just a way to get some similar functionality in vim. Thanks, David, for pointing that out before the angry tweets started streaming in from emacs users.

  4. Cucumber Features In Subdirectories

    I’m working on a project where we’ve amassed a decent amount of Cucumber features. Dumping them in the root directory makes it difficult to find the feature you’re looking for and running related features is impossible.

    I’ve fooled with organizing my cucumber features in the past, but didn’t have much luck. Grouping related features into subdirectories was fine when running the entire suite, but running an individual feature failed because Cucumber doesn’t know to load step definitions from the parent directory. As I was looking through cucumber --help the other day, I happened to notice the --require flag.

    -r, --require LIBRARY|DIR
        Require files before executing the features.

    Running the following command was exactly what I needed.

    cucumber --require features features/users/sign_in.feature

    This tells Cucumber to load all .rb files under features/ which will find the step definitions and support files. To save some typing, I created an alias in ~/.bashrc.

    alias cuc='cucumber -r features'

    Where I’ve really enjoyed this setup is when running all related features. After touching a step definition, I’d like to run the features that depend on it quickly without running the entire suite. Now that’s as simple as:

    cuc features/users/*
  5. MongoMapper Looks Promising

    I’ve been intrigued by schema-free document-oriented databases after tinkering with CouchDB, but haven’t found a way to integrate it into my current projects. I’ve heard John Nunemaker sing the praises of MongoDB in the past and today he has officially released his ActiveRecored inspired database adapter MongoMapper.

    It’s got validations, callbacks, a robust find API, support for easily embedding models, and much more. The code looks almost too good to be true.

    class Person
      include MongoMapper::Document
    
      key :first_name, String
      key :last_name, String
      key :age, Integer
      key :born_at, Time
      key :active, Boolean
      key :fav_colors, Array
    end
    

    I hope this project becomes a huge success in the Rails community.

  6. my_blog.refresh

    Not a visual refresh–although the design was rebuilt using Sass and the Blueprint CSS framework with the help of the amazing Compass project (Chris Eppstein, you rock. Seriously.)–but more of a purpose refresh. It’s apparent I’m not able to write long-winded blog posts with any level of consistency, but I have no trouble tweeting 0.62 times daily. I’ll attempt to strike a balance somewhere between and hope for the best.

    Part of the problem can be attributed to coding my own blog. It was my first deployed Rails project and I thoroughly enjoyed the experience, but I soon realized I have no desire whatsoever in maintaining the code base. I’ve been keeping my eye on Jekyll for a while and with the release of GitHub Pages I think it’s about time I make the switch. I’m still undecided how I feel about the dynamic content on the site having to be loaded ajaxily, but with any luck I’ll manage to get over it.