Skip to content

Instantly share code, notes, and snippets.

@betawaffle
Created March 23, 2012 16:32
Show Gist options
  • Save betawaffle/2172507 to your computer and use it in GitHub Desktop.
Save betawaffle/2172507 to your computer and use it in GitHub Desktop.
# in config/initializers
ActiveRecord::Relation.class_eval do
include EachRow
end
module EachRow
def each_row &block
return to_enum(:each_row) unless block_given?
sql = to_sql
res = connection.execute(sql)
res.each do |row|
yield row
end
res.clear
nil
end
# WARNING: This is way too clever for anyone's good.
# Paramaters passed depend on the name you give them in the block.
#
# Example:
# lazy_map_rows do |id, updated_at|
# "#{self.class} with id #{id} was last updated at #{updated_at}"
# end
def lazy_map_rows mapper = nil, &block
return to_enum(:lazy_map_rows, block) unless mapper
return to_enum(:lazy_map_rows, mapper) unless block
table = arel_table
query = except(:select)
cols = mapper.parameters.map do |(_, col)|
query = query.select table[col]
col.to_s
end
proc = lambda do |row|
mapper.call *row.values_at(*cols)
end
sql = query.to_sql
res = connection.execute(sql)
res.each { |row| yield proc.call(row) }
res.clear
nil
end
end
# NOTE: #each_row and #lazy_map_rows probably only work with Postgres.
# NOTE: #lazy_map_rows most likely only works on MRI 1.9.3 or later.
# Assume a model defined as so:
# Tag(id: integer, name: string, created_at: datetime, updated_at: datetime)
class Tag < ActiveRecord::Base
# EachRow methods can be called on any scope.
# Use .scoped or .unscoped if you don't use query methods first.
end
# And the associated table has the following data:
# [#<Tag id: 1, name: "socks", created_at: "2012-02-27 22:40:46", updated_at: "2012-02-27 22:40:46">,
# #<Tag id: 2, name: "hateful", created_at: "2012-02-27 22:40:46", updated_at: "2012-02-27 22:40:46">,
# #<Tag id: 3, name: "streamage", created_at: "2012-03-16 15:39:05", updated_at: "2012-03-16 15:39:05">,
# #<Tag id: 4, name: "empty", created_at: "2012-03-20 13:58:46", updated_at: "2012-03-20 13:58:46">]
# Each of the following examples will perform ONLY 1 iteration of the rows in the table.
tags = Tag.scoped.with_whatever.you_want
tags.lazy_map_rows { |id, name| "#{id} => #{name}" }.to_a
# => ["1 => socks", "2 => hateful", "3 => streamage", "4 => empty"]
tags.lazy_map_rows { |created_at, name| "#{name} was created at #{created_at}" }.to_a
# => ["socks was created at 2012-02-27 22:40:46.426997",
# "hateful was created at 2012-02-27 22:40:46.447398",
# "streamage was created at 2012-03-16 15:39:05.242834",
# "empty was created at 2012-03-20 13:58:46.88798"]
# Please Enjoy Responsibly!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment