Controller actions not running

December 28th, 2011

Every year or so I scratch my head for a half hour doing this…. I’m trying to debug something in my cucumber tests, and somehow code that I think must be running is not running. I insert breakpoints and they just never get hit. I slowly step up the call chain until I put a breakpoint at the top of the controller action, and it’s still not getting hit!

I check the routes. I check the paths. Everything seems fine. I’m supposed to be in that controller, on that action. What the heck!

And then, AHA! I’m getting stuck in a before_filter. Of course. Silly me.

And then I go another year before it happens again…

Minimal templating DSL in pure Ruby

December 12th, 2011

For whatever reason, I felt like working on my metaprogramming brain a little bit last night. So I implemented an as-simple-as-possible templating DSL in pure Ruby. Only 34 lines of code and lets you generate pretty much arbitrary HTML. It ends up being basically a shitty version of Markaby.

Here’s the code and an example:

class Tmpl
  def initialize(tag, *args, &block)
    @tag = tag
    @content = args.find {|a| a.instance_of? String}
    @attributes = args.find{|a| a.instance_of? Hash}
    self.instance_eval &block if block_given?
  end
 
  def to_html
    attr_string = " " << @attributes.map {|k,v| "#{k}=#{v.to_s.inspect}" }.join(" ") if @attributes
    "<#{@tag}#{attr_string}>#{@content}#{children.map &:to_html}</#{@tag}>"
  end
 
  def children
    @children ||= []
  end
 
  # Some of these are Kernel or Object methods or whatever that we need to explicitly override
  [:p, :select].each do |name|  
    define_method name do |*args, &block|
      send :method_missing, name, *args, &block
    end
  end
 
  def method_missing(tag, *args, &block)
    child = Tmpl.new(tag.to_s, *args, &block)
    children << child
    child
  end
 
  def self.method_missing(tag, *args, &block)
    Tmpl.new(tag.to_s, *args, &block)
  end
end
 
 
puts Tmpl.html {
  head do
    title "Hello, World!"
  end
  body do
    h1 "Hi", :class => "foo"
    p "This is an awesome thing. " do
      b "Hot "
      a "Sauce", :href => "/awesome"
    end
    select do
      option "Not me"
      option "There can be only one", :selected => true
    end
  end
}.to_html

Different views for the first element in a list

December 9th, 2011

On SproutRobot, I’m often showing a calendar and wanting to display this week slightly different than all of the weeks after it. My calendar is just an OrderedHash, with Weeks as keys and then arrays of Events as values. This, with some Ruby and HAML goodness, allows some pretty clean code in the view:

- @weeks.shift.tap do |week, events|
  / do stuff with the first week and it's events here
- @weeks.each do |week, events|
  / and then the rest of the weeks here

On the first line, “shift” pops the first item off of the hash, which is a key-value (week and events) pair. Then “tap” takes that key-value pair and lets me shove it into a block and with attribute names. Then, on the next line the “each” iterates through the remaining items.

I just love how elegantly all of these pieces fit together.

Save some keystrokes when rendering partials

November 21st, 2011

I’ve always found the syntax for rendering partials to be particularly disgusting. Instead of:

render :partial => "dish", :locals => {:name => "falafel", :price => 599}

I wrote a helper that lets me do:

partial "dish", :name => "falafel", :price => 599

Here’s the code:

# app/helpers/application_helper.rb
def partial(path, locals={})
  render :partial => path, :locals => locals
end

Pretty straightforward. In some ways, I wish that partials were just ruby methods. Like, what if I could create a haml file like this:

- def dish(name, price=99)
  %h1= name
  %p This is a very tasty dish!
  .price It only costs #{number_to_currency price}

And it would work just like a helper. I could just call dish(”falafel”, 599) in my other views! Wouldn’t that be great! Then we could use all of the nice, well thought out ways ruby methods can be parameterized. Maybe they could even take blocks, so I could do:

- def confirm_dialog
  .modal
    yield
    %button OK
    %button Cancel

And then I could do something like:

- confirm_dialog do
  %h1 Are you sure you want to close your bank account?
  %p I won't get a bonus if you do!

Wouldn’t that be lovely? This is explored a bit on Err, the blog… but it seems fundamental enough that it should be built in to the templating system. I think HAML already compiles to Ruby, so it would seem like it shouldn’t be that hard. Maybe something to dig into on a rainy day.

Regardless, this hack combined with my default locals hack gets us pretty close to that vision.

Automatically create your rvm gemsets

November 3rd, 2011

Here’s a little gem I noticed in some code from Kathryn Aaker. Whenever I start a new project with rvm, I always get this gemset error:

ERROR: Gemset ‘blah’ does not exist, rvm gemset create ‘blah’ first

Super annoying. But in Kathryn’s .rvmrc I noticed:

rvm --create ruby-1.8.7@eggs

Apparently that –create automatically creates the gemset for you! Super!

Autoload ruby-debug, with autoeval set to true, automatically!

November 3rd, 2011

I’ve been fed up for years with the annoyance of including ruby-debug in various places, having Heroku yell at me for putting it in my Gemfile, having apps crash because a debugger was loaded in production, and on and on and on.

I’ve finally got it sorted. First of all, you don’t want to load the ruby-debug gem in production:

# Gemfile
group :test, :development do
  gem "ruby-debug"
end

If you’re using Ruby 1.9, it’s ruby-debug19. Then, you want to load ruby-debug smartly in your environment.rb:

# config/environment.rb
unless Rails.env == "production"
  require "ruby-debug" 
  Debugger.settings[:autoeval] = true
end

Viola! The debugger is accessible in your tests and development environment, with autoeval set to true.

Debugging nirvana.

Optional locals in your Rails partials

June 12th, 2011

Edit: Updated to fix issue with method_missing barfing when we hadn’t set any defaults. Edit 2: Fix bug where I was storing locals in the wrong variable. Edit 3: Realized that the locals would get applied to any and all views rendered after the default_locals call. Turned it into a nestable block helper to keep the scope narrow.

There’s not a great way to do default values in your Rails partials. For a long time I’ve been throwing blocks like this at the top of my views: (these are HAML, but ERB is similar…)

- columns = 5 unless local_assigns.keys.include? :size
- title = "Home" unless local_assigns.keys.include? :home
 
%h1= title
- columns.times do
  .column This is a column

I finally got fed up and figured out a more DRY solution. I threw this in my ApplicationHelper:

# app/helpers/application_helper.rb
module ApplicationHelper
  def with_default_locals(new_defaults)
    @default_locals ||= []
    @default_locals.unshift new_defaults
    yield
    @default_locals.shift
  end
 
  def method_missing(sym, *args, &block)
    if @default_locals
      @default_locals.each do |locals|
        if locals.has_key?(sym)
          return locals[sym]
        end
      end
    end
    super
  end
end

Now my view can be much less repetitive:

- with_default_locals :columns => 5, :title => "Home" do
  %h1= title
  - columns.times do
    .column This is a column

And I’m then free to override those locals however I want to (with my partial shortcut):

= partial "calendar", :title => "This Week", :columns => 2
= partial "calendar"

Seems pretty clean and dry.

Bizarro Delayed::Job/ActiveRecord/Callback/Validation bug

June 3rd, 2011

Been dealing with at truly weird bug. I just upgraded to Rails 3 and my rake tasks are failing with:

wrong number of arguments (0 for 1)

With no line numbers, no help, no nothing even if I run “rake jobs:work –trace”.

After much agony, I narrowed the issue down to the :valid? method on my ActiveRecord objects. The actual Rails code, not my code. The interwebs seemed to suggest that there could be some issue with using deprecated validation apis (overwriting validate, for example) but I wasn’t doing that.

I eventually debugged into ActiveRecord, and discovered the problem. Check this out:

# activerecord-3.0.7/lib/active_record/validations.rb:53
def valid?(context = nil)
  context ||= (new_record? ? :create : :update)
  output = super(context)
 
  deprecated_callback_method(:validate)
  deprecated_callback_method(:"validate_on_#{context}")
 
  errors.empty? && output
end

In there we call deprecated_callback_method(:validate), but deprecated_callback_method will just execute whatever symbol you give it. So it sends :validate with no parameters. But new versions of ActiveRecord::Base expect validate to come with some sort of parameters.

On what planet is that supposed to work?

Well, it turns out if I run the exact code in my rails console, it works just fine. In a Delayed::Job task, User.first.valid? raises an ArgumentError, but in the rails console it just returns true.

Seriously. What the fuck.

My hackety-hack monkeypatch is just to throw this in my environment:

module ActiveRecord
  module Callbacks
    def deprecated_callback_method(symbol)
    end
  end
end

Which seems like it could be potentially bad, but if the deprecated callback method is deprecated, why would I want to use it anyway? Like I said, I don’t think I’m actually using any deprecated callbacks, but who really knows.

My job queue works again. I’m moving on.

Rails 3 “No route matches” error when it frickin’ matches!

May 31st, 2011

I’m upgrading SproutRobot to Rails 3, and have been battling a mountain of bugs that resulted from the upgrade. I’ve been feeling a bit guilty about not posting all my findings here, but there are just so MANY little things, and I’ve got so much crap to get though.

But this one I had to post. I was getting this error in my tests when submitting a form that updates my User model, via the :zip action:

No route matches "/users/100/zip"

Of course my `rake routes` shows a perfectly good route!

zip_user POST /users/:id/zip(.:format) {:action=>"zip", :controller=>"users"}

It turns out you need to be careful not to use POST in your routes file when you really mean PUT:

resources :users do
  member do
    post :zip # will not work
    put  :zip # will work just super!
  end
end

This sort of baffles me, because the form itself says method=”POST” in the HTML. I’ve never quite understood the wizardry associated with POST forms that somehow become PUT actions in Rails. How does it know I didn’t actually want to do a POST?

Regardless. That’s one more stupid bug squashed.

ActiveRecord instance defaults (that stay out of the database)

April 4th, 2011

There a decent amount of debate out there about how to set defaults for ActiveRecord objects.

There are a variety of suggestions out there, but for SproutRobot I need a few unique properties. First, the defaults need to stay out of the database. The database is for values that we are sure of. Defaults are guesses. I don’t want to mix the two up.

Second, when I’m editing objects, I need to tell which fields haven’t been set. That means to the edit form, the object has to pretend like it doesn’t know about the defaults.

And third, I want to do this as simply as possible, without getting into weird field names, like “custom_such_and_such” and “such_and_such_or_default”.

So, what I ended up doing was adding a default? flag to the object, and rewriting the read accessors to conditionally use the defaults:

class Variety
  def without_defaults
    @no_defaults = true
    self
  end
 
  {
    :days_to_thinning => 20,
    :days_to_sprout => 14,
    :days_to_harvest => 70
  }.each do |symbol,value|
    define_method symbol do
      self[symbol] || (value unless @no_defaults)
    end
  end
end

This means on my form I can just do:

variety = Variety.find_by_id(params["id"]).without_defaults

And that instance won’t use any defaults, but all other instances will. And my code can just use the standard field names.