ActiveRecord#each?
Something that’s struck me as odd lately while working with ActiveRecord is the apparent lack of any iteration support. ActiveRecord results don’t come back flat - they are a tree - and so iterating them generically can be a biaatch. Here’s a little go at it:
##############################################################################
# SCHEMA
##############################################################################
#
# create table company (
# name string,
# company_id integer primary key
# );
#
# create table employee (
# name string,
# employee_id integer primary key
# );
#
# create table company_employee (
# company_id integer,
# employee_id integer
# );
#
# insert into employee values (“ara”, NULL); — 1
# insert into employee values (“dan”, NULL); — 2
#
# insert into company values (“codeforpeople”, NULL); — 1
# insert into company values (“eparklabs”, NULL); — 2
#
# insert into company_employee values ( 1, 1 ); — ara works for codeforpeople
# insert into company_employee values ( 2, 1 ); — ara works for eparklabs
# insert into company_employee values ( 2, 2 ); — dan works for eparklabs
#
##############################################################################
# CONFIG
##############################################################################
#
# ActiveRecord::Base::primary_key_prefix_type = :table_name_with_underscore
# ActiveRecord::Base::pluralize_table_names = false
#
##############################################################################
# DEMO
##############################################################################
require ’config/environment.rb’
require ’config/boot.rb’
records = Employee.find :all, :include => :company
records.each do |record|
crawl record do |key, value|
printf “%-42.42s => %-32.32s\n”, key, value
end
puts
end
##############################################################################
# OUTPUT
##############################################################################
#
# employee[1].company[1].name => codeforpeople
# employee[1].company[1].company_id => 1
# employee[1].company[2].name => eparklabs
# employee[1].company[2].company_id => 2
# employee[1].name => ara
# employee[1].employee_id => 1
#
# employee[2].company[2].name => eparklabs
# employee[2].company[2].company_id => 2
# employee[2].name => dan
# employee[2].employee_id => 2
#
##############################################################################
# THE CODE
##############################################################################
BEGIN{
def crawl record, visited={}, prefix=”, &b
return record if visited[record]
visited[record] = true
tid = record.class.table_name
oid = record.id
### yes this __is__ pure evil
columns = {}.update record.instance_variable_get(’@attributes’)
record.instance_variables.each do |ivar|
next if ivar == ‘@attributes’
k = ivar[1..-1]
v = record.instance_variable_get ivar
columns.update k => v
end
columns.each do |cid, value|
if(value.is_a? Array and value.first.is_a? ActiveRecord::Base)
value.each do |v|
crawl v, visited, “#{ tid }[#{ oid }].”, &b
end
elsif value.is_a? ActiveRecord::Base
crawl value, visited, “#{ tid }[#{ oid }].”, &b
else
yield [”#{ prefix }#{ tid }[#{ oid }].#{ cid }”, value]
end
end
record
end
}
# SCHEMA
##############################################################################
#
# create table company (
# name string,
# company_id integer primary key
# );
#
# create table employee (
# name string,
# employee_id integer primary key
# );
#
# create table company_employee (
# company_id integer,
# employee_id integer
# );
#
# insert into employee values (“ara”, NULL); — 1
# insert into employee values (“dan”, NULL); — 2
#
# insert into company values (“codeforpeople”, NULL); — 1
# insert into company values (“eparklabs”, NULL); — 2
#
# insert into company_employee values ( 1, 1 ); — ara works for codeforpeople
# insert into company_employee values ( 2, 1 ); — ara works for eparklabs
# insert into company_employee values ( 2, 2 ); — dan works for eparklabs
#
##############################################################################
# CONFIG
##############################################################################
#
# ActiveRecord::Base::primary_key_prefix_type = :table_name_with_underscore
# ActiveRecord::Base::pluralize_table_names = false
#
##############################################################################
# DEMO
##############################################################################
require ’config/environment.rb’
require ’config/boot.rb’
records = Employee.find :all, :include => :company
records.each do |record|
crawl record do |key, value|
printf “%-42.42s => %-32.32s\n”, key, value
end
puts
end
##############################################################################
# OUTPUT
##############################################################################
#
# employee[1].company[1].name => codeforpeople
# employee[1].company[1].company_id => 1
# employee[1].company[2].name => eparklabs
# employee[1].company[2].company_id => 2
# employee[1].name => ara
# employee[1].employee_id => 1
#
# employee[2].company[2].name => eparklabs
# employee[2].company[2].company_id => 2
# employee[2].name => dan
# employee[2].employee_id => 2
#
##############################################################################
# THE CODE
##############################################################################
BEGIN{
def crawl record, visited={}, prefix=”, &b
return record if visited[record]
visited[record] = true
tid = record.class.table_name
oid = record.id
### yes this __is__ pure evil
columns = {}.update record.instance_variable_get(’@attributes’)
record.instance_variables.each do |ivar|
next if ivar == ‘@attributes’
k = ivar[1..-1]
v = record.instance_variable_get ivar
columns.update k => v
end
columns.each do |cid, value|
if(value.is_a? Array and value.first.is_a? ActiveRecord::Base)
value.each do |v|
crawl v, visited, “#{ tid }[#{ oid }].”, &b
end
elsif value.is_a? ActiveRecord::Base
crawl value, visited, “#{ tid }[#{ oid }].”, &b
else
yield [”#{ prefix }#{ tid }[#{ oid }].#{ cid }”, value]
end
end
record
end
}
Anyone else got a better idea? If so ping me offline @ [[the address that you can find via google]].