rails 迁移中块中的列定义如何工作?

How does column definition in the blocks in rails migrations work?

通篇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 元编程黑魔法通过块变量 |t| 定义 table 列。

现在问题:此模式的具体最小可重现示例是什么样的?

例如...

例如,方法如何检索块参数的变量?

是否 ActiveRecord::Migration,为了查询带有选项的 table 列覆盖 method_missing

你的,冯·斯波茨

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.

的实例

Represents the schema of an SQL table in an abstract way. This class provides methods for manipulating the schema representation.

这并不是真正的黑魔法。它是一个 DSL 对象,表示 table 及其列,最后从数据结构创建 SQL 字符串。

然后当您在块内的 table 定义上调用 column 方法(及其委托)时,您实际上是在改变参数中传递给的对象 td通过将 ColumnDefinition 对象添加到其列列表来创建该块。请记住,Ruby 是通过引用传递的。

How for example does a method retrieve the variable(s) of a block parameter?

他们没有。 yield 与 Ruby 中的其他所有内容一样,是一个表达式,return 是块 return 的值。此方法甚至不关心 return 值,因为它全都与副作用有关。 传递到块中的任何参数都是该范围的局部变量,如果局部变量未在块外引用,则在块完成时将被垃圾回收。

Does ActiveRecord::Migration, for the purpose of querying the table columns with options overwrite 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::PostgreSQLAdapterMysqlAdapter 向此列表添加更多类型,如 json。

它实际上也不会查询 table,因为在执行块时它还不存在。