-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Brandon Joyce
committed
Mar 18, 2016
0 parents
commit 66de7a7
Showing
14 changed files
with
276 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/.bundle/ | ||
/.yardoc | ||
/Gemfile.lock | ||
/_yardoc/ | ||
/coverage/ | ||
/doc/ | ||
/pkg/ | ||
/spec/reports/ | ||
/tmp/ | ||
footprint.sql |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--format documentation | ||
--color |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
source 'https://rubygems.org' | ||
|
||
# Specify your gem's dependencies in sql_footprint.gemspec | ||
gemspec |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# SqlFootprint | ||
|
||
This gem allows you to keep a "footprint" of the sql queries that your application runs. | ||
It's like logging all the sql you're executing except that we remove all the value parameters | ||
and dedupe similar queries. This footprint should be valuable in determining if changes you've | ||
made will significantly change the way you're querying the database. | ||
|
||
## Installation | ||
|
||
Add this line to your application's Gemfile: | ||
|
||
```ruby | ||
gem 'sql_footprint' | ||
``` | ||
|
||
And then execute: | ||
|
||
$ bundle | ||
|
||
## Usage | ||
|
||
Typically, you would want to run this while you're running your specs. | ||
For example w/ RSpec: | ||
``` | ||
RSpec.configure do |config| | ||
config.before(:suite) { SqlFootprint.start } | ||
config.after(:suite) { SqlFootprint.stop } | ||
end | ||
``` | ||
|
||
After running your specs you'll find a 'footprint.sql' file in your project. | ||
Adding this to your Git repository can be very useful so you can include the diff of the footprint | ||
as part of your code review. |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
require "bundler/gem_tasks" | ||
require "rspec/core/rake_task" | ||
|
||
RSpec::Core::RakeTask.new(:spec) | ||
|
||
task :default => :spec |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#!/usr/bin/env ruby | ||
|
||
require "bundler/setup" | ||
require "sql_footprint" | ||
|
||
# You can add fixtures and/or initialization code here to make experimenting | ||
# with your gem easier. You can also use a different console, if you like. | ||
|
||
# (If you use this, don't forget to add pry to your Gemfile!) | ||
# require "pry" | ||
# Pry.start | ||
|
||
require "irb" | ||
IRB.start |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#!/bin/bash | ||
set -euo pipefail | ||
IFS=$'\n\t' | ||
|
||
bundle install | ||
|
||
# Do any other automated setup that you need to do here |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
require "sql_footprint/version" | ||
|
||
module SqlFootprint | ||
def self.start | ||
@original_logger = ActiveRecord::Base.logger | ||
@logger = Logger.new | ||
ActiveRecord::Base.logger = @logger | ||
end | ||
|
||
def self.stop | ||
ActiveRecord::Base.logger = @original_logger | ||
File.open('footprint.sql', 'w') do |f| | ||
@logger.logs.each do |log| | ||
f.puts log | ||
end | ||
end | ||
end | ||
|
||
class Logger | ||
def initialize | ||
@logs = [] | ||
end | ||
|
||
attr_reader :logs | ||
|
||
def error param | ||
fail param | ||
end | ||
|
||
def debug text | ||
if sql? text | ||
sql = format_sql(text) | ||
logs << sql if !logs.include?(sql) | ||
logs.sort! | ||
end | ||
end | ||
|
||
def sql? text | ||
/SQL/.match(text) || | ||
/Load\s\(/.match(text) | ||
end | ||
|
||
def format_sql text | ||
strip_values(text).split("\e\[0m") | ||
.select { |t| !t.include?('SQL') } | ||
.select { |t| !/Load\s\(/.match(t) } | ||
.first | ||
.gsub(/\e\[1m/, '') | ||
.strip | ||
end | ||
|
||
def strip_values text | ||
text = text.gsub(/\[\[.*\]\]/, '') | ||
text = strip_string_values(text) | ||
text = strip_integer_values(text) | ||
text = strip_in_clause_values(text) | ||
end | ||
|
||
def strip_in_clause_values text | ||
text.gsub(/\sIN\s\((.*)\)/) do |match| | ||
" IN (values-redacted)" | ||
end | ||
end | ||
|
||
def strip_integer_values text | ||
text.gsub(/\s\=\s([0-9]+)/) do |match| | ||
" = number-redacted" | ||
end | ||
end | ||
|
||
def strip_string_values text | ||
text.gsub(/\s\=\s\'(.*)\'/) do |match| | ||
" = 'value-redacted'" | ||
end | ||
end | ||
|
||
def debug? | ||
true | ||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module SqlFootprint | ||
VERSION = "0.1.0" | ||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
class Widget < ActiveRecord::Base | ||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
ActiveRecord::Schema.define do | ||
self.verbose = false | ||
|
||
create_table :widgets, :force => true do |t| | ||
t.string :name | ||
t.integer :quantity | ||
|
||
t.timestamps | ||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) | ||
require 'bundler/setup' | ||
Bundler.setup | ||
require 'pry' | ||
require 'sql_footprint' | ||
require 'active_record' | ||
|
||
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:' | ||
|
||
load File.dirname(__FILE__) + '/schema.rb' | ||
require File.dirname(__FILE__) + '/models.rb' |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
require 'spec_helper' | ||
|
||
describe SqlFootprint do | ||
it 'has a version number' do | ||
expect(SqlFootprint::VERSION).not_to be nil | ||
end | ||
|
||
describe '.start' do | ||
let!(:logger) { described_class.start } | ||
let(:sql) { logger.logs.last } | ||
|
||
it 'logs sql' do | ||
Widget.create! | ||
expect(logger.logs.length).to eq 1 | ||
end | ||
|
||
it 'formats inserts' do | ||
Widget.create! | ||
expect(sql).to eq "INSERT INTO \"widgets\" (\"created_at\", \"updated_at\") VALUES (?, ?)" | ||
|
||
end | ||
|
||
it 'formats selects' do | ||
Widget.where(name: SecureRandom.uuid, quantity: 1).last | ||
expect(sql).to eq "SELECT \"widgets\".* FROM \"widgets\" WHERE \"widgets\".\"name\" = 'value-redacted' AND \"widgets\".\"quantity\" = number-redacted ORDER BY \"widgets\".\"id\" DESC LIMIT 1" | ||
end | ||
|
||
it 'formats IN clauses' do | ||
Widget.where(name: [SecureRandom.uuid, SecureRandom.uuid]).last | ||
expect(sql).to eq "SELECT \"widgets\".* FROM \"widgets\" WHERE \"widgets\".\"name\" IN (values-redacted) ORDER BY \"widgets\".\"id\" DESC LIMIT 1" | ||
end | ||
|
||
it 'dedupes the same sql' do | ||
Widget.create! | ||
Widget.create! | ||
expect(logger.logs.length).to eq 1 | ||
end | ||
|
||
it 'sorts the results' do | ||
Widget.where(name: SecureRandom.uuid, quantity: 1).last | ||
Widget.create! | ||
expect(logger.logs.first).to include('INSERT INTO') | ||
end | ||
end | ||
|
||
describe '.stop' do | ||
it 'writes the footprint' do | ||
described_class.start | ||
Widget.create! | ||
described_class.stop | ||
log = File.read('footprint.sql') | ||
expect(log).to include('INSERT INTO') | ||
end | ||
|
||
it 'removes old results' do | ||
described_class.start | ||
Widget.create! | ||
described_class.stop | ||
described_class.start | ||
Widget.last | ||
described_class.stop | ||
log = File.read('footprint.sql') | ||
expect(log).to_not include('INSERT INTO') | ||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# coding: utf-8 | ||
lib = File.expand_path('../lib', __FILE__) | ||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) | ||
require 'sql_footprint/version' | ||
|
||
Gem::Specification.new do |spec| | ||
spec.name = "sql_footprint" | ||
spec.version = SqlFootprint::VERSION | ||
spec.authors = ["Brandon Joyce"] | ||
spec.email = ["[email protected]"] | ||
|
||
spec.summary = %q{Keeps your DB guy happy.} | ||
spec.description = %q{Check your footprint file into source control} | ||
spec.homepage = "https://github.com/covermymeds/sql_footprint" | ||
|
||
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } | ||
spec.bindir = "exe" | ||
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } | ||
spec.require_paths = ["lib"] | ||
|
||
spec.add_development_dependency "bundler", "~> 1.10" | ||
spec.add_development_dependency "rake", "~> 10.0" | ||
spec.add_development_dependency "rspec" | ||
spec.add_development_dependency "activerecord", "~> 4.0.0" | ||
spec.add_development_dependency "pry" | ||
spec.add_development_dependency "sqlite3" | ||
end |