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)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.