Created
July 21, 2011 14:24
-
-
Save jeroenvandijk/1097294 to your computer and use it in GitHub Desktop.
Dataset hack
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
# 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