Posts Tagged ‘rails’

Script for opening the latest migration

Wednesday, April 17th, 2013

Often after generating a Rails migration I want to edit it, but it’s annoying to have to type in those dang timestamps. So I wrote a little script that just opens the newest one:

#!/bin/sh
s db/migrate/`ls db/migrate | tail -n 1`

Put that in ~/bin/latest-migration, chmod u+x it, and add ~/bin to your PATH and you can just run latest-migration from within any Rails folder. Of course you have to replace ’s’ with the command for invoking your text editor of choice.

Invoking methods on optional objects

Friday, May 18th, 2012

Lately I’ve been using pretty heavily an extension I wrote to Ruby’s Object class:

# lib/core_extensions/object.rb
class Object
  def defined_and(key=nil)
    return nil if nil?
    block_given? ? yield(self) : self[key]
  end
end

This lets me invoke methods on objects that may or may not exist. It’s really useful for the frequent situations where an optional child object has a method that also applies to it’s parent:

class ZipCode
  def start_for(vegetable)
    planting_specs.for(vegetable).defined_and &:start
  end
end

As opposed to the ugly alternative:

def start_for(vegetable)
  spec = planting_specs.for(vegetable)
  spec.start if spec
end

That will either return a start, or it’ll return nil if no planting_spec was found. It works with a block (&:start is a shortcut for {|x| x.start}) or an enumerable key:

{ :a => 1}.defined_and(:a) #=> 1
nil.defined_and(:a) #=> nil

To use stuff in the lib/core_extensions folder in your Rails app, as defined above, you need an initializer:

# config/initializers/core_extensions.rb
Dir[Rails.root.join("lib/core_extensions/*")].each {|f| require f}

Explain ActiveRecord queries in Rails console

Tuesday, May 15th, 2012

I keep missing this in Google somehow, but if you want ActiveRecord to explain your queries in the console, do:

ActiveRecord::Base.logger = Logger.new(STDOUT)

via shime on StackOverflow

Memcache on Rails with Dalli not expiring

Thursday, March 15th, 2012

This was a little bit of a stumper. In testing, when I tried to cache objects in Memcache with Dalli, by setting expires_in to, say, 5.seconds, the cache didn’t seem to expire.

After debugging into Dalli, I now see why:

# dalli-1.1.4/lib/active_support/cache/dalli_store.rb:131
if expires_in > 0 && !options[:raw]
  # Set the memcache expire a few minutes in the future to support race condition ttls on read
  expires_in += 5.minutes
end

Dalli adds 5 minutes to the expires_in for all cache writes.

Stupid Rails Tricks: Using find and find_all on ActiveRecord collections

Sunday, February 5th, 2012

ActiveRecord collections are enumerable, but they override Enumerable’s find and find_all methods to hit the database. But the versions on Enumerable are totally different. They feed objects to a block and return the ones for which the block returns true. Sometimes that’s what you want.

I used to convert the collection to an array so I could use the Enumerable methods, but that is far from optimal in terms of memory use. Then I figured out Enumerable has the detect and select aliases which work the same way, and work on AR collections:

# Find all the broody chickens that weigh at least 5 lbs:
Chickens.broody.find(:first, :conditions => ["weight >= ?", 5])
 
# Find all the broody chickens whose favoriate food is snails:
Chickens.broody.detect do |chicken|
  chicken.meals.map(&:food).mode == "snail"
end
 
# Instead of having to do 
Chickens.broody.to_a.find_all do |chicken|
  ...

Previous trick: Range to the end of an array

Controller actions not running

Wednesday, 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…

Save some keystrokes when rendering partials

Monday, 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.

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

Thursday, 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

Sunday, 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

Friday, 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.