When doing some meta-programming magic and you want to do something for all attributes of a class, you may need to access connection
or some of its methods (e.g. columns
) during class definition.
While everything will be fine while you are working on a project that is in active development, the application will fail to boot when the database is missing or has no tables. This means that Raketasks like db:create
or db:migrate
fail on a freshly cloned project.
The reason is your environment.rb
which is loaded for Raketasks and calls Rails.application.initialize!
which in turn may/will evaluate classes. If one of those classes is tries to access its database connection, you will encounter fun errors such as:
-
PG::ConnectionBad
(for missing databases on PostgreSQL) -
ActiveRecord::StatementInvalid: PG::UndefinedTable
(when database exists, but has no tables)
Generally speaking, evaluating model columns during class definition is not a bad thing, but you need to make it work when the model has no database or database columns yet. Example:
class Post < ApplicationRecord
begin
# Magically auto-strips all string attributes
columns.each do |column|
next if [:string, :text].exclude?(column.type)
attribute_name = column.name
class_eval <<~RUBY, __FILE__, __LINE__ + 1
def #{attribute_name}=(value)
super(value.to_s.strip.presence)
end
RUBY
end
rescue PG::ConnectionBad, ActiveRecord::StatementInvalid
# When the database does not exist at all or is missing a model's table,
# accessing "columns" raises an error and the application fails to boot.
#
# To allow calling "rake db:create" or "rake db:migrate" on an empty
# database, we swallow such errors.
end
end
For MySQL, rescue Mysql2::Error, ActiveRecord::StatementInvalid
might be fitting.