Created
September 27, 2011 06:56
-
-
Save FND/1244491 to your computer and use it in GitHub Desktop.
test case for eager loading of nested associations with DataMapper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Gemfile.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# encoding: UTF-8 | |
# test case for eager loading of nested associations with DataMapper | |
require 'rubygems' | |
require 'dm-core' | |
require 'dm-constraints' | |
require 'dm-migrations' | |
require 'eager_loading' | |
DataMapper::Logger.new($stdout, :debug) | |
DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/db.sqlite") | |
class Person | |
include DataMapper::Resource | |
property :id, Serial | |
property :name, String, :required => true | |
has n, :vehicles | |
end | |
class Vehicle | |
include DataMapper::Resource | |
property :id, Serial | |
property :name, String, :required => true | |
has n, :components | |
end | |
class Component | |
include DataMapper::Resource | |
property :id, Serial | |
property :name, String, :required => true | |
belongs_to :manufacturer | |
end | |
class Manufacturer | |
include DataMapper::Resource | |
property :id, Serial | |
property :name, String, :required => true | |
end | |
DataMapper.auto_migrate! | |
# generate test data | |
Person.create(:name => "FND", :vehicles => [ | |
Vehicle.create(:name => "Taurus", :components => [ | |
Component.create(:name => "engine", | |
:manufacturer => Manufacturer.create(:name => "Ford")), | |
Component.create(:name => "radio", | |
:manufacturer => Manufacturer.create(:name => "Bose")) | |
]), | |
Vehicle.create(:name => "fixie", :components => [ | |
Component.create(:name => "frame", | |
:manufacturer => Manufacturer.create(:name => "Campagnolo")), | |
Component.create(:name => "breaks", | |
:manufacturer => Manufacturer.create(:name => "Shimano")) | |
]) | |
]) | |
Person.create(:name => "tillsc", :vehicles => [ | |
Vehicle.create(:name => "Golf", :components => [ | |
Component.create(:name => "engine", | |
:manufacturer => Manufacturer.create(:name => "VW")) | |
]) | |
]) | |
# retrieve data | |
puts "", "[INFO] test case A" | |
person = Person.get!(1) | |
puts person.vehicles.components.manufacturer.map(&:name).join(", ") | |
puts "", "[INFO] test case B" | |
people = Person.all | |
people.each do |person| | |
person.vehicles.each do |vehicle| | |
puts sprintf("%-10s %-10s", person.name, vehicle.name) | |
end | |
end | |
puts "", "[INFO] test case C ===== /!\ n+1 hazard ====" | |
people = Person.all | |
people.each do |person| | |
person.vehicles.each do |vehicle| | |
vehicle.components.each do |component| | |
puts sprintf("%-10s %-10s %-10s", person.name, vehicle.name, component.name) | |
end | |
end | |
end | |
puts "", "[INFO] test case D" | |
people = Person.all | |
people.eager_load(Person.vehicles.components).each do |person| | |
person.vehicles.each do |vehicle| | |
vehicle.components.each do |component| | |
puts sprintf("%-10s %-10s %-10s", person.name, vehicle.name, component.name) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# manual eager loading for DataMapper | |
# adapted from Chris Corbyn: https://gist.github.com/1244491#gistcomment-56797 | |
module EagerLoading | |
def eager_load(query_path) | |
scope = self | |
query_path.relationships.each do |relation| | |
source_key = relation.source_key.first # TODO: rename | |
target_key = relation.target_key.first # TODO: rename | |
# for each level in the query path, collect all the resources referencing | |
# keys at the current scope | |
next_scope = relation.target_model.all(target_key.name => scope. | |
collect(&:"#{source_key.name}")) | |
# map target keys to the resources that exist for them | |
links = next_scope.inject({}) do |map, resource| | |
map.merge(target_key.get(resource) => [resource]) { |k, v1, v2| v1 + v2 } | |
end | |
# now pre-load those from the map | |
scope.each do |parent| | |
if links.key?(source_key.get(parent)) | |
parent.instance_variable_set(:"@#{relation.name}", | |
links[source_key.get(parent)]) | |
end | |
end | |
# and step into the next nesting level | |
scope = next_scope | |
end | |
self | |
end | |
end | |
DataMapper::Collection.send(:include, EagerLoading) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
source :rubygems | |
DM_VERSION = '~> 1.2.0.rc2' | |
gem 'dm-core', DM_VERSION | |
gem 'dm-constraints', DM_VERSION | |
gem 'dm-migrations', DM_VERSION | |
gem 'dm-sqlite-adapter', DM_VERSION |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env sh | |
rm db.sqlite | |
# reformat SQL queries for readability | |
bundle exec ./dm_el.rb | perl -pe 's#SELECT .*? (FROM ".*?" )(.*)#\1SELECT ... \2#' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey guys, I know this is an old thread, but check this out: https://gist.github.com/3100034