Статьи

СУХОЙ Миграции

В Rails 1.1 появился Object#with_options

Например, следующий код маршрутизации:

 
map.connect 'atom.xml', :controller => 'feed', :action => 'everything_atom'
map.connect 'comments-atom.xml', :controller => 'feed', :action => 'comments_atom'

можно использовать магию Object#with_options

 
map.with_options(:controller => 'feed') do |feed|
  feed.connect 'atom.xml', :action => 'everything_atom'
  feed.connect 'comments-atom.xml', :action => 'comments_atom'
end

Хорошо, а?

Давайте посмотрим на миграцию, которую я недавно написал:

 
class AddExcerptSlugAndImageToArticles < ActiveRecord::Migration
  def self.up
    add_column :articles, :excerpt, :text
    add_column :articles, :slug, :string
    add_column :articles, :image, :string
  end

  def self.down
    remove_column :articles, :excerpt
    remove_column :articles, :slug
    remove_column :articles, :image
  end
end

Argh … статьи, статьи везде! Во-первых, давайте подумаем, как бы мы хотели, чтобы API выглядел:

 
class AddExcerptSlugAndImageToArticles < ActiveRecord::Migration
  def self.up
    with_table :articles do |t|
      t.add_column :excerpt, :text
      t.add_column :slug, :string
      t.add_column :image, :string
    end
  end

  def self.down
    with_table :articles do |t|
      t.remove_column :excerpt
      t.remove_column :slug
      t.remove_column :image
    end
  end
end

Ааа … намного лучше. Как мы можем добавить этот метод with_table Во-первых, нам нужно добавить метод with_tableActiveRecord::Migration

 
class ActiveRecord::Migration
  def self.with_table(table_name)
    # Funky magic
  end
end

Метод with_tableyield Самый простой способ сделать это — создать новый Object Мы определим метод method_missing Если мы вызываем прокси с помощью add_column :blurb, :stringadd_column :articles, :blurb, :string

Собирая все это вместе, мы получаем:

 
class ActiveRecord::Migration
  def self.with_table(table_name)
    proxy = Object.new
    proxy.instance_variable_set(:@migration, self)
    proxy.instance_variable_set(:@table_name, table_name)
    def proxy.method_missing(method_name, *args)
      @migration.send(method_name, *(args.to_a.insert(0, @table_name)))
    end
    yield proxy
  end
end

Хорошо а? Попробуйте добавить это в начало одного из ваших файлов миграции и протестировать его, и, если вы хотите узнать больше об этом безумстве method_missing, ознакомьтесь с главой 6 «Почему стоит писать в Ruby» .