Corey Innis's blog



Corey InnisCorey Innis
New York Standup 03/19/2009
edit Posted by Corey Innis on Thursday March 19, 2009 at 02:35PM

Interesting

  • We've created a Pivotal fork of the Ctags bundle for TextMate (the original).

    Ctags provides a mechanism for indexing your source code files for the purpose of locating and navigating between bits of code; something IntelliJ/RubyMine is very good at, while TextMate is found lacking.

    The forked bundle aims to be more inline with Pivotal practices, where developers are often switching between IDE/editor worlds. For example, code completion has been re-mapped to ctrl-space, to be more like IntelliJ.

    Have you played with Ctags? Take a look at the bundle; see what you think.

Help Wanted

  • We're on the hunt for quality burritos in NYC. Ben gave Burrito Loco a try last night; thought the burritos were "supple". Dave is desperately seeking breakfast burritos.

    Suggestions?

Corey InnisCorey Innis
New York Standup 03/12/2009
edit Posted by Corey Innis on Friday March 13, 2009 at 02:59AM

Interesting

  • Ben noted, you can give your Ruby command-line tools some (RDoc::)usage
  • Rails Boost, where one may "generate a ... bootstrapped Rails app", now includes fixjour as an option to the generator
  • Joe and Ryan recently created a Pivotal fork of Pat's acts_as_fu to get it to play nicely with your application and its database connections

Corey InnisCorey Innis
New York Standup 03/11/2009
edit Posted by Corey Innis on Wednesday March 11, 2009 at 06:59PM

Help Wanted

  • We're interested in running some sort of javascript validation and syntax checking suite on one of our projects. JSLint looks reasonable for the framework and can be run from the command line with Rhino.

    If you have experience with or thoughts about the idea, please share.

Corey InnisCorey Innis
New York Standup, Overdue from the Week of 03/02/2009
edit Posted by Corey Innis on Wednesday March 11, 2009 at 01:13PM

Interesting Things

  • An EngineYard-hosted project had an issue with monit attempting to restart mongrel too often. It turned out that the mongrel processes were not dropping pid files soon enough. The EngineYard-suggested fix:

    • Upgrade mongrel to 1.1.5.1 (patched to drop the pid file faster)
    • Upgrade to monit 5.0 beta 6
    • Update to the latest ey-monit-scripts
  • FiveRuns dash is a cool, customizable metrics service. Pat created a plug-in for sending continuous integration stats there: dash-ci.
  • Using Cucumber to test Capistrano deployment:

    cap --dry-run will run Capistrano without completing the actual task (e.g., deployment). Cucumber can then be used to write some nice, story-like deployment expectations that search the Capistrano output to document your project's deployment process and ensure the documentation remains valid. Something like:

    Feature: Deployment
      In order to deploy the application
      As an administrator
      I should run Capistrano commands
    
    
      Scenario: Deploying
        Given I am working from the RAILS_ROOT directory
          And the parent directory is a Git repository
        When  I run a deployment task
        Then  'scm_user' for the deployment should be derived from the Git config for the remote origin
    
    
      Scenario: Deploying to demo
        Given I am working from the RAILS_ROOT directory
        When  I run 'cap demo deploy'
        Then  the deploy should succeed
          And the deployed code matches the latest 'web/stable' tag
          And the deployed code should be marked with a new 'web/demo' tag
    

    More on this to come.

  • Using Selenium to ensure unique IDs in your DOM:

    # ----------------------------------------------------------------------
    # The examples below illustrate the technique with the Prototype and
    # jQuery libraries, respectively.  Both use Pat Nakajima's selenium
    # helper for executing javascript in the tested browser window.
    # 
    # For more on that helper, see:
    # http://pivotallabs.com/users/patn/blog/articles/717-run-javascript-in-selenium-tests-easily-
    # ----------------------------------------------------------------------
    
    
    # ----------------------------------------------------------------------
    # with prototype
    # note the exception catching... prototype chokes on invalid IDs
    # e.g., "invalid_id[][]"
    # ----------------------------------------------------------------------
    def assert_unique_ids
      audit_json = run_javascript <<-JS
        audit_ids = function() {
          var results = {};
          $A($$('*[id]')).each(function(element) {
            if(element.id.replace(' ', '').length > 0) {
              try {
                if($$('#' + element.id ).length > 1) {
                  if( ! results.duplicates) {
                    results.duplicates = {};
                  }
                  var count = results.duplicates[element.id] || 0;
                  count ++;
                  results.duplicates[element.id] = count;
                }
              }
              catch(err) {
                // uncomment to capture invalid IDs
                // var invalid = results.invalid || [];
                // invalid.push(element.id);
                // results.invalid = invalid;
              }
            }
          });
          return ($H(results).toJSON());
        }
        audit_ids();
      JS
      assert_equal({}, JSON.parse(audit_json)), 'Expected no duplicate IDs')
    end
    
    
    # ----------------------------------------------------------------------
    # with jQuery
    # additionally depends on the jquery-json plugin:
    # http://code.google.com/p/jquery-json/
    # ----------------------------------------------------------------------
    def assert_unique_ids
      audit_json = run_javascript <<-JS
        audit_ids = function() {
          var results = {};
          $('*[id]').each(function() {
            if(this.id.replace(' ', '').length > 0) {
              if($('*[id=' + this.id + ']').length > 1) {
                if( ! results.duplicates) {
                  results.duplicates = {};
                }
                var count = results.duplicates[this.id] || 0;
                count ++;
                results.duplicates[this.id] = count;
              }
            }
          });
          return $.toJSON(results);
        }
        audit_ids();
      JS
      assert_equal({}, JSON.parse(audit_json)), 'Expected no duplicate IDs')
    end
    

Help Wanted

  • One project recently moved from Rimu to EngineYard, only to find their Mongrel processes double in memory consumption. Any thoughts on why?

Corey InnisCorey Innis
Standup 05/30/2008
edit Posted by Corey Innis on Friday May 30, 2008 at 06:25PM

Interesting Things

  • ActiveRecord's #method_missing takes precedence over private methods, which means you cannot simply mark "private" database-derived attributes.
    code:

    # File: app/models/rock_star.rb
    #
    # == Schema Information
    # Schema version: 1
    #
    # Table name: rock_stars
    #
    #  id            :integer         not null, primary key
    #  real_name     :string(255)     
    #  band_name     :string(255)     
    #  personal_life :string(255)     
    #
    
    
    class RockStar < ActiveRecord::Base
      def method_missing(method, *arguments, &block)
        puts "I see you've sent my #{method} back and my ActiveRecords and they're all scratched"
        super
      end
    
    
      private
    
    
      def personal_life=(arg)
        puts "Vanish in the air you'll never find me"
        attributes[:personal_life] = arg
      end
    end
    

    script/console:

    Loading development environment (Rails 2.0.2)
    >> sting = RockStar.new(:real_name => 'Gordon Sumner', :band_name => 'The Police')
    I see you've sent my real_name= back and my ActiveRecords and they're all scratched
    => #<RockStar id: nil, real_name: "Gordon Sumner", band_name: "The Police", personal_life: nil>
    >> sting.personal_life = "I'll be watching you"
    I see you've sent my personal_life= back and my ActiveRecords and they're all scratched
    => "I'll be watching you"
    

    Potential solutions:

    • Convention... name "private" database attributes with leading underscores
    • Exception:

      class RockStar < ActiveRecord::Base
        def personal_life=(arg)
          raise "Protest is futile, nothing seems to get through"
        end
      end
      
    • Have another? Post a comment.

  • ||= ("or equal") blows up you have a public "writer", but a private "reader"; makes sense, but still worth a mention.
    code:

    class Model < ActiveRecord::Base
      def field_name=(arg)
        @field_name = arg
      end
    
    
      private
    
    
      def field_name
        @field_name
      end
    end
    

    script/console:

    Loading development environment (Rails 2.0.2)
    >> instance = Model.new
    => #<Model id: nil, field_name: nil>
    >> instance.field_name = 'lala'
    => "lala"
    >> instance.field_name ||= 'dodo'
    NoMethodError: private method `field_name' called for #<Model:0x17df6d0>
      from /Library/Ruby/Gems/1.8/gems/activerecord-2.0.2/lib/active_record/attribute_methods.rb:205:in `method_missing'
      from (irb):4
    
  • ActiveRecord writers always return the passed in argument, even if you define some other return value. This also makes sense -- necessary for chaining, etc., but what the heck...
    code:

    class Model < ActiveRecord::Base
      def field_name=(arg)
        @field_name = arg            
        return "custom return value"
      end
    end
    

    script/console:

    Loading development environment (Rails 2.0.2)
    >> instance = Model.new
    => #<Model id: nil, field_name: nil>
    >> instance.field_name = 'lala'
    => "lala"
    

Corey InnisCorey Innis
Standup 02/11/2008
edit Posted by Corey Innis on Monday February 11, 2008 at 11:57PM

Interesting Things

  • Got Example?

    Don't forget to make use of the reserved (top- and second-level) domain names set aside by RFC 2606... especially if you find yourself writing something like:

    result = @model.do_request('http://www.somebogusdomain.com')
    result.code.should == 1001
    
    
    # NOTE: www.somebogusdomain.com actually exists!
    

Corey InnisCorey Innis
Standup 02/07/2008
edit Posted by Corey Innis on Thursday February 07, 2008 at 05:58PM

Interesting Things

  • There's an IE PNG transparency bug fix as a plugin for jQuery. It looks fairly complete, with more coverage than many others out there and the usage is trivial (once installed):

    <script type="text/javascript">
      $(document).ready(function(){ 
        $(document).pngFix(); 
      }); 
    </script>
    

    The major downside so far: it scales background images.

  • Rails Bug:

    In a failing/rolled back transaction, those ActiveRecord objects that were created prior to the exception still have IDs and respond false to object_name.new_record? (which would seem to indicate that a record for the object has been saved).

    For more info, here are a couple bug tickets.

Corey InnisCorey Innis
Standup 02/05/2008
edit Posted by Corey Innis on Tuesday February 05, 2008 at 10:42PM

Interesting Things

  • In exploring options for RSpec testing of XML responses, one project decided a custom XPath matcher would do the trick nicely:

    items = Item.find(:all)
    get :index
    
    
    response.body.should     have_nodes("//items/item", items.size)
    response.body.should     have_xpath("//items/item[ position() = 1 and @id = '0001' ]")
    response.body.should_not have_xpath("//bogus")
    

    Some other possibilities:

    • Hashes...

      items = Item.find(:all)
      get :index
      
      
      hash = Hash.from_xml(response.body)
      hash['ancestor']['parent']['items']['item'].size.should == items.size
      hash['ancestor']['parent']['items']['item'][0]['id'].should == '0001'
      hash['ancestor']['parent']['bogus'].should be_nil
      
    • Hpricot...

      items = Item.find(:all)
      get :index
      
      
      doc = Hpricot(response.body)
      doc.search("//items/item").size.should == items.size
      doc.at("//items/item[ @id = '0001' ]").position.should == 1
      doc.at("//bogus").should be_nil
      
    • assert_select from Rail's Test::Unit (using CSS-style selectors)...

      items = Item.find(:all)
      get :index
      
      
      assert_select("items > item", :count => 5)
      assert_select("items > item:nth-child(1)[id=?]", '0001')
      assert_select("bogus", false)
      

    What are your favorite techniques for asserting XML/XPath?

  • Something to consider when test-driving controller code:

    You're working with ActionController::TestResponse. So, response.success?, response.redirect?, etc. are available for you there (since they are defined on TestResponse), but not in your actual controllers. That is, things blow up if you try to use @response.success? in your application,

    e.g. to determine whether or not to store the current URL and redirect there after a login.

Corey InnisCorey Innis
Standup 01/28/2008
edit Posted by Corey Innis on Tuesday January 29, 2008 at 06:10PM

A day late, but...

Interesting Things

  • We're re-working some internal Rails plugins. This effort includes a redesign of how those plugins deliver routes to the application.

    Look forward to a post from Nathan on designing plugin-provided routes.

  • Teleport:

    A number of Pivots have experimented with using Synergy -- often configured with QuickSynergy -- as a productivity enhancement tool for pair programming. The basic idea is to have a shared computer set up as a Synergy client of two other, user-driven machines. This allows for collaborative editing in the shared environment as well as individual work, such as reviewing a particular API.

    Jonathon noted that Teleport provides a similar kind of capability which may also be worth checking out. Teleport does differ a bit from Synergy...

    Pluses:

    • Simple, bonjour-enabled configuration via an OS X system preferences pane
    • Bi-directional capabilities. That is, two machines can be both servers and clients of each other
    • Can drag-drop to copy files and folders between machines
    • Pasteboard is synchronized between machines

    Minuses:

    • Pasteboard is synchronized between machines (careful, or you might unintentionally overwrite a copy buffer)
    • OS X only
    • Closed source
    • The one client of two servers setup doesn't seem to work out right, so only one developer can split of to a "research" machine
    • It's a bit buggy regarding configuration
    • Can't do some of the advanced configuration that Synergy can, e.g. re-mapping keys

Corey InnisCorey Innis
Standup 12/28/2007
edit Posted by Corey Innis on Friday December 28, 2007 at 08:42PM

Interesting Things

  • Rails Bug: Test::Unit seems to be broken in Rails 2.0.2 when using multiple levels of test classes (i.e. inheritance). Trunk is fixed, so Rails 2.0.3 will be fine.
  • Chad wrote a small utility for checking/comparing Rails versions which wraps the version checking part of rubygems. This, of course, requires that your Rails project uses at least one gem... that's a pretty good bet.

    The basic technique:

    have = Rails::VERSION::STRING
    requirement = '=1.99.1'
    Gem::Version::Requirement.new([requirement]).satisfied_by?(Gem::Version.new(have))
    
  • One pivot is having trouble finding a rogue puts statement in a gem somewhere. The suggestion comes a bit late in this case, but adding some breadcrumbs may help next time:

    puts "#{__FILE__}:#{__LINE__} find me!"
    

Ask for Help

  • Does anyone have details regarding Google Maps API developer permissions? Localhost-to-localhost requests work fine, but we need to test things between machines (actually, a virtual machine client and virtual machine host).