PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Thursday, April 14, 2022

[FIXED] How does column definition in the blocks in rails migrations work?

 April 14, 2022     metaprogramming, migration, ruby-on-rails     No comments   

Issue

Throughout Rails there is an idiom to query parameters through a block-variable.

For instance in migrations.

A typical migration in Rails looks something like this

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

The important part being the variable of the block passed to methods such as create_table.

I guess Rails uses some ruby metaprogramming black magic to define the table columns through the block-variable |t|.

Now Question: How does a concrete minimal reproducable example of this pattern look like?

For instance ...

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

Does ActiveRecord::Migration, for the purpose of querying the table columns with options overwrite method_missing ?

Yours, von Spotz


Solution

create_table just yields if a block is given.

  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 creates an instance of ActiveRecord::ConnectionAdapters::TableDefinition.

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

This is not really black magic. Its a DSL object that represents a table and its columns and in the end creates a SQL string from a data structure.

When you then call the column method (and its delegates which I will get to) on the table definition inside the block you're actually mutating the object td passed in the arguments to the block by adding ColumnDefinition objects to its list of columns. Remember that Ruby is pass by reference.

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

They don't. yield like everything else in Ruby is an expression and returns the value returned by the block. This method does not even care about the return value since its all about side effects. Any arguments passed into a block are local to that scope and the local variables will be garbage collected when the block is finished if they are not referenced outside the block.

Does ActiveRecord::Migration, for the purpose of querying the table columns with options overwrite method_missing ?

No. method_missing is not used here. The only metaprogramming involved is the ColumnMethods module that defines the shortcut methods used to define columns:

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

The actual database drivers such as ActiveRecord::ConnectionAdapters::PostgreSQLAdapter and MysqlAdapter add more types to this list like json.

It also does not actually query the table as it does not exist yet when the block is executed.



Answered By - max
Answer Checked By - Senaida (PHPFixing Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing