Skip to content

Instantly share code, notes, and snippets.

@jeroenvandijk
Created July 21, 2011 14:24
Show Gist options
  • Save jeroenvandijk/1097294 to your computer and use it in GitHub Desktop.
Save jeroenvandijk/1097294 to your computer and use it in GitHub Desktop.
Dataset hack
# Based on the discussion here: https://github.com/aiwilliams/dataset/issues/3
# Used to optimized the datasets being created in your tests.
# Currently works in a test suite for a production app with Postgresql
#
# TODO
# - gemify
# - add tests
# - be able to reuse datasets accross files (declare them in a shared context)
# - support other databases than postgres
# - file access optimization (only read once during a test run)
# - query optimization (change INSERTS into COPY statements for Postgres) - not sure what the gain will be
# - Remember what is being inserted so it is easier to refer to the created objects (no manual queries)
#
module DatasetHack
FIXTURE_DIR = Rails.root.join("tmp/datasets")
module Helpers
# Capture the sql using active support's notifications
def capture_sql(filter = nil)
capture = []
subscription = ActiveSupport::Notifications.subscribe('sql.active_record') do |name, start, finish, id, payload|
if filter && payload[:sql] =~ Regexp.new(filter)
capture << payload[:sql]
end
end
yield
ActiveSupport::Notifications.unsubscribe(subscription)
capture
end
end
include Helpers
extend Helpers
# Hook to reset the database sequences (for Postgres at least)
# This is needed because otherwise id's will change and then the dataset is possibly not compatible
# anymore
# Database cleaner does this by default on truncation
#
# Use this in e.g. your spec_helper
#
# config.after(:each) do
# DatasetHack.reset!
# end
#
def self.reset!
capture_sql(/^ALTER/) do
(ActiveRecord::Base.connection.tables - ["schema_migrations"]).each do |table|
ActiveRecord::Base.connection.execute("TRUNCATE TABLE #{table} RESTART IDENTITY")
end
end
end
# In order to not get stuck with old fixtures it is often nice to clean up all
# the fixtures e.g. in your spec_helper
#
# config.after(:suite) do
# DatasetHack.clean!
# end
#
def self.clean!
Dir[FIXTURE_DIR.join("*")].each { |f| File.delete(f) if File.file?(f) }
end
def self.included(klass)
klass.extend(ClassMethods)
end
# Add dataset as a macro for rspec
module ClassMethods
def dataset(label, &block)
before(:each) do
dataset(label) { instance_eval(&block) }
end
end
end
# Call dataset with a label and a block to cache the generate queries e.g.
#
# dataset :very_expensive_operation do
# 1000.times { User.create! }
# end
#
def dataset(label)
sql_file = FIXTURE_DIR.join("data_#{label}.sql")
if File.exist?(sql_file)
ActiveRecord::Base.connection.execute(File.read(sql_file))
else
captured_sql = capture_sql(/^INSERT/) { yield }
transformed_sql = captured_sql.join(';')
File.open(sql_file, 'w') {|f| f.write(transformed_sql) }
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment