简体   繁体   中英

Catch-22: Rails db migration doesn't run when class constants make db calls

I have a class similar to the following:

class FruitKinds < ActiveRecord::Base
  Apple = FruitKinds.find(:all).find { |fk|
    fk.fruit_name == :apple.to_s
  }

  # ... other fruits

  # no methods
end

Apple and other specific Fruits are commonly used as default values elsewhere in my application, so I want a handy means to refer to them in an enumerable, static-ish way.

However, there's a problem. There is a database migration to create the FruitKinds table and populate it with the special Fruits like Apple . When the database migration runs to initialize FruitKinds, rake fails to start because it first loads FruitKinds , which then makes a call to the database, which of course fails since the FruitKinds table is not yet there.

The workaround is to comment out the FruitKinds::* fields while the migration runs, but that is awful and hacky. What's the best way to do this?

That's a pretty common gotcha, to the point that I now consider any database access at the class definition level an antipattern. The alternative is to make it a lazy property. The simplest way to do this is simply to make it a class-level method instead of a constant. Note that you can have capitalised methods in Ruby, so you can make it look like a constant if you want:

class FruitKinds < ActiveRecord::Base
  def self.Apple 
    @apple ||= FruitKinds.find(:all).find { |fk|
      fk.fruit_name == :apple.to_s
    }
  end

  # ... other fruits

  # no methods
end

Or if you want to get fancy you can use const_missing to dynamically create the constant the first time it is accessed.

As a side note, that's about the most inefficient way possible to find a record by name ;-)

As Avdi mentioned, you'll want to avoid interacting with the database when the class is loaded. If you're wanting to cache database records in local memory, I recommend using the Memoization feature added in Rails 2.2. See my post here for details.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM