Как работает определение столбцов в блоках миграции рельсов? - PullRequest
3 голосов
/ 24 апреля 2020

В Rails существует идиома для запроса параметров через блочную переменную.

Например, в миграциях.

Типичная миграция в Rails выглядит примерно так

class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :name
      t.integer :parent_id, foreign_key: true  
    end
  end
end

Важной частью является переменная блока, передаваемая таким методам, как create_table.

Я предполагаю, что Rails использует некоторые ruby метапрограммирующие чёрные маги c для определения столбцов таблицы через блок- переменная |t|.

Теперь Вопрос: Как выглядит конкретный минимальный воспроизводимый пример этого шаблона?

Например ...

Как, например, метод извлекает переменную (ы) ) параметра блока?

Имеет ли ActiveRecord :: Migration, для запроса столбцов таблицы с опциями перезаписи method_missing?

Ваш, фон Спотц

1 Ответ

2 голосов
/ 24 апреля 2020

create_table просто дает, если дан блок.

  def create_table(table_name, **options)
    td = create_table_definition(table_name, options)
    # ... 
    yield td if block_given?
    # ...
    result = execute schema_creation.accept td
    # ...
  end

create_table_definition создает экземпляр ActiveRecord::ConnectionAdapters::TableDefinition.

Представляет схему таблицы SQL абстрактным способом. Этот класс предоставляет методы для управления представлением схемы.

На самом деле это не черные маги c. Это объект DSL, который представляет таблицу и ее столбцы и, в конце концов, создает строку SQL из структуры данных.

Когда вы затем вызываете метод column (и его делегаты, к которым я доберусь) в определении таблицы внутри блока, который вы фактически изменяете объекту td, переданный в аргументах блоку, добавляя объекты ColumnDefinition в список столбцов. Помните, что Ruby передается по ссылке.

Как, например, метод извлекает переменную (и) параметра блока?

Они этого не делают. yield, как и все остальное в Ruby, является выражением и возвращает значение, возвращаемое блоком. Этот метод даже не заботится о возвращаемом значении, поскольку все о побочных эффектах. Любые аргументы, передаваемые в блок, являются локальными для этой области, и локальные переменные будут собираться мусором после завершения блока, если на них нет ссылок вне блока.

Имеет ли ActiveRecord :: Migration, для запроса столбцов таблицы с опциями перезапись method_missing?

Нет. method_missing здесь не используется. Единственное задействованное метапрограммирование - это модуль ColumnMethods , который определяет методы ярлыков, используемые для определения столбцов:

module ActiveRecord
  module ConnectionAdapters #:nodoc:
    # ...
    module ColumnMethods
      # Appends a primary key definition to the table definition.
      # Can be called multiple times, but this is probably not a good idea.
      def primary_key(name, type = :primary_key, **options)
        column(name, type, options.merge(primary_key: true))
      end

      # Appends a column or columns of a specified type.
      #
      #  t.string(:goat)
      #  t.string(:goat, :sheep)
      #
      # See TableDefinition#column
      [
        :bigint,
        :binary,
        :boolean,
        :date,
        :datetime,
        :decimal,
        :float,
        :integer,
        :string,
        :text,
        :time,
        :timestamp,
        :virtual,
      ].each do |column_type|
        module_eval <<-CODE, __FILE__, __LINE__ + 1
          def #{column_type}(*args, **options)
            args.each { |name| column(name, :#{column_type}, options) }
          end
        CODE
      end
      alias_method :numeric, :decimal
    end
    # ...
  end
end

Фактические драйверы базы данных, такие как ActiveRecord::ConnectionAdapters::PostgreSQLAdapter и MysqlAdapter, добавляют больше типов в этот список, такой как json.

Он также фактически не запрашивает таблицу, поскольку он еще не существует, когда выполняется блок.

...