slim_scrooge 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +85 -0
- data/Rakefile +30 -0
- data/VERSION.yml +5 -0
- data/ext/callsite_hash.c +67 -0
- data/ext/extconf.rb +7 -0
- data/lib/slim_scrooge/callsite.rb +68 -0
- data/lib/slim_scrooge/callsites.rb +44 -0
- data/lib/slim_scrooge/monitored_hash.rb +39 -0
- data/lib/slim_scrooge/result_set.rb +31 -0
- data/lib/slim_scrooge/simple_set.rb +34 -0
- data/lib/slim_scrooge/slim_scrooge.rb +31 -0
- data/lib/slim_scrooge.rb +10 -0
- data/test/active_record_setup.rb +3 -0
- data/test/helper.rb +91 -0
- data/test/models/course.rb +2 -0
- data/test/schema/schema.rb +5 -0
- data/test/setup.rb +5 -0
- metadata +84 -0
data/README.textile
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
h1. SlimScrooge - serious optimisation of mysql for activerecord
|
2
|
+
|
3
|
+
h2. What is it?
|
4
|
+
|
5
|
+
It's an optimization layer to ensure your application only fetches the database content needed to minimize wire traffic, excessive SQL queries and reduce conversion overheads to native Ruby types.
|
6
|
+
|
7
|
+
SlimScrooge implements both lazy loading of attributes fetched from mysql, as well as inline query optimisation, automatically restricting the columns fetched based on what was used during previous passes through the same part of your code.
|
8
|
+
|
9
|
+
h2. Benchmark
|
10
|
+
|
11
|
+
SlimScrooge performs best when the database is not on the same machine as your rails app. In this case the overhead of fetching unnecessary data from the database can become more important.
|
12
|
+
|
13
|
+
I ran a benchmark that consisted of fetching 400 real urls (culled from the log file) from our complex web app. In this test I found a consistent 12% improvement in performance over plain active record. Not earth-shattering, but worthwhile. In future releases I expect further gains.
|
14
|
+
|
15
|
+
h2. Installation
|
16
|
+
|
17
|
+
<pre>
|
18
|
+
# if you haven't already, add gemcutter to your gem sources
|
19
|
+
sudo gem install gemcutter
|
20
|
+
gem tumble
|
21
|
+
# install slim_scrooge
|
22
|
+
sudo gem install slim_scrooge
|
23
|
+
</pre>
|
24
|
+
|
25
|
+
h3. Requirements
|
26
|
+
|
27
|
+
* "Slim-attributes":http://github.com/sdsykes/slim-attributes
|
28
|
+
|
29
|
+
h3. What it does
|
30
|
+
|
31
|
+
<pre>
|
32
|
+
# 1st request, sql is unchanged but columns accesses are recorded
|
33
|
+
Brochure Load SlimScrooged 1st time (27.1ms) SELECT * FROM `brochures` WHERE (expires_at IS NULL)
|
34
|
+
|
35
|
+
# 2nd request, only fetch columns that were used the first time
|
36
|
+
Brochure Load SlimScrooged (4.5ms) SELECT `brochures`.expires_at,`brochures`.operator_id,`brochures`.id FROM
|
37
|
+
`brochures` WHERE (expires_at IS NULL)
|
38
|
+
|
39
|
+
# 2nd request, later in code we need another column which causes a reload of all remaining columns
|
40
|
+
Brochure Reload SlimScrooged (0.6ms) `brochures`.name,`brochures`.comment,`brochures`.image_height,`brochures`.id,
|
41
|
+
`brochures`.tel,`brochures`.long_comment,`brochures`.image_name,`brochures`.image_width FROM
|
42
|
+
`brochures` WHERE `brochures`.id IN ('5646','5476','4562','3456','4567','7355')
|
43
|
+
|
44
|
+
# 3rd request
|
45
|
+
Brochure Load SlimScrooged (4.5ms) SELECT `brochures`.expires_at,`brochures`.operator_id,`brochures`.name,
|
46
|
+
`brochures`.id FROM `brochures` WHERE (expires_at IS NULL)
|
47
|
+
</pre>
|
48
|
+
|
49
|
+
h3. Technical discussion
|
50
|
+
|
51
|
+
SlimScrooge hooks in at just one particular place in the mysql adapter - and that place is the select_all method. All select queries pass through this method.
|
52
|
+
|
53
|
+
SlimScrooge is able to record each call (and where it came from in your code), and to modify queries that do SELECT * FROM en-route to mysql so that they only select the rows that are actually used by that piece of code.
|
54
|
+
|
55
|
+
How does SlimScrooge know which columns are actually used?
|
56
|
+
|
57
|
+
Enter Slim-attributes. Slim-attributes also hooks in at one place, the all_hashes method - and in particular it returns a proxy for the hash for each row, and doesn't actually instantiate any ruby objects for the columns in each row until they are needed.
|
58
|
+
|
59
|
+
Clearly slim-attributes knows when each column is accessed, and this information can be collected by SlimScrooge for future use.
|
60
|
+
|
61
|
+
In fact for efficiency, column accesses are only recorded when they are for newly accessed columns. Columns we already know about are accessed completely normally, and slim-attributes provides its usual lazy-loading benefit without any additional overhead.
|
62
|
+
|
63
|
+
h3. Caveats
|
64
|
+
|
65
|
+
It is possible to delete an object and then to try to use its attributes to access another object that perhaps must be also deleted (like the option :dependent=>:destroy in Rails associations).
|
66
|
+
|
67
|
+
SlimScrooge particularly checks for columns used by :dependent=>:destroy, but if you are doing something similar manually in your code then you may run into problems. Your attempt to access the key of the dependent object could cause a reload if the column is not already noted by SlimScrooge, and the reload will fail if you have already destroyed the parent object.
|
68
|
+
|
69
|
+
h2. Tests
|
70
|
+
|
71
|
+
SlimScrooge performs the full activerecord test suite without errors, except for a couple of tests that check the number of queries performed.
|
72
|
+
|
73
|
+
To run the tests you need to set your database up with appropriate access for the rails user, and make activerecord_unittest and activerecord_unittest2 databases. Then run:
|
74
|
+
|
75
|
+
<pre>
|
76
|
+
rake test_with_active_record
|
77
|
+
</pre>
|
78
|
+
|
79
|
+
h2. References
|
80
|
+
* "Scrooge":http://github.com/methodmissing/scrooge
|
81
|
+
* "Slim-attributes":http://github.com/sdsykes/slim-attributes
|
82
|
+
|
83
|
+
h2. Authors
|
84
|
+
* Stephen Sykes (sdsykes)
|
85
|
+
* Special thanks to Lourens Naudé (methodmissing) for the original idea, and the C implementation of callsite_hash as well as some other bits of code that I borrowed from the original project.
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'test/helper'
|
4
|
+
|
5
|
+
Rake::TestTask.new(:test_with_active_record) do |t|
|
6
|
+
t.libs << SlimScrooge::ActiveRecordTest::AR_TEST_SUITE
|
7
|
+
t.libs << SlimScrooge::ActiveRecordTest.connection
|
8
|
+
t.test_files = SlimScrooge::ActiveRecordTest.test_files
|
9
|
+
t.ruby_opts = ["-r #{File.join(File.dirname(__FILE__), 'test', 'active_record_setup')}"]
|
10
|
+
t.verbose = true
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |s|
|
16
|
+
s.name = "slim_scrooge"
|
17
|
+
s.summary = "Slim_scrooge - lazy instantiation of attributes and query optimisation for ActiveRecord"
|
18
|
+
s.email = "sdsykes@gmail.com"
|
19
|
+
s.homepage = "http://github.com/sdsykes/slim_scrooge"
|
20
|
+
s.description = "Slim scrooge boosts speed in Rails/Mysql ActiveRecord Models by lazily instantiating attributes as needed, and only querying the database for what is needed."
|
21
|
+
s.authors = ["Stephen Sykes"]
|
22
|
+
s.files = FileList["[A-Z]*", "{ext,lib,test}/**/*"]
|
23
|
+
s.extensions = "ext/extconf.rb"
|
24
|
+
s.add_dependency('slim-attributes', '>= 0.7.0')
|
25
|
+
end
|
26
|
+
Jeweler::GemcutterTasks.new
|
27
|
+
rescue LoadError
|
28
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://
|
29
|
+
gems.github.com"
|
30
|
+
end
|
data/VERSION.yml
ADDED
data/ext/callsite_hash.c
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
/* Author: Lourens Naudé */
|
2
|
+
|
3
|
+
#include "ruby.h"
|
4
|
+
#include "node.h"
|
5
|
+
#include "env.h"
|
6
|
+
|
7
|
+
static int strhash(register const char *string) {
|
8
|
+
register int c;
|
9
|
+
|
10
|
+
register int val = 0;
|
11
|
+
|
12
|
+
while ((c = *string++) != '\0') {
|
13
|
+
val = val*997 + c;
|
14
|
+
}
|
15
|
+
|
16
|
+
return val + (val>>5);
|
17
|
+
}
|
18
|
+
|
19
|
+
static VALUE rb_f_callsite( VALUE obj ) {
|
20
|
+
struct FRAME *frame = ruby_frame;
|
21
|
+
NODE *n;
|
22
|
+
int lev = -1;
|
23
|
+
int csite = 0;
|
24
|
+
|
25
|
+
if (frame->last_func == ID_ALLOCATOR) {
|
26
|
+
frame = frame->prev;
|
27
|
+
}
|
28
|
+
if (lev < 0) {
|
29
|
+
ruby_set_current_source();
|
30
|
+
if (frame->last_func) {
|
31
|
+
csite += strhash(ruby_sourcefile) + ruby_sourceline + frame->last_func;
|
32
|
+
}
|
33
|
+
else if (ruby_sourceline == 0) {
|
34
|
+
csite += strhash(ruby_sourcefile);
|
35
|
+
}
|
36
|
+
else {
|
37
|
+
csite += strhash(ruby_sourcefile) + ruby_sourceline;
|
38
|
+
}
|
39
|
+
if (lev < -1) return INT2FIX(csite);
|
40
|
+
}
|
41
|
+
else {
|
42
|
+
while (lev-- > 0) {
|
43
|
+
frame = frame->prev;
|
44
|
+
if (!frame) {
|
45
|
+
csite = 0;
|
46
|
+
break;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
for (; frame && (n = frame->node); frame = frame->prev) {
|
51
|
+
if (frame->prev && frame->prev->last_func) {
|
52
|
+
if (frame->prev->node == n) {
|
53
|
+
if (frame->prev->last_func == frame->last_func) continue;
|
54
|
+
}
|
55
|
+
csite += strhash(n->nd_file) + nd_line(n) + frame->prev->last_func;
|
56
|
+
}
|
57
|
+
else {
|
58
|
+
csite += strhash(n->nd_file) + nd_line(n);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
return INT2FIX(csite);
|
63
|
+
}
|
64
|
+
|
65
|
+
void Init_callsite_hash() {
|
66
|
+
rb_define_global_function("callsite_hash", rb_f_callsite, 0);
|
67
|
+
}
|
data/ext/extconf.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# Author: Stephen Sykes
|
2
|
+
|
3
|
+
module SlimScrooge
|
4
|
+
class Callsite
|
5
|
+
ScroogeComma = ",".freeze
|
6
|
+
ScroogeRegexJoin = /(?:LEFT|INNER|OUTER|CROSS)*\s*(?:STRAIGHT_JOIN|JOIN)/i
|
7
|
+
|
8
|
+
attr_accessor :seen_columns
|
9
|
+
attr_reader :columns_hash, :primary_key, :model_class
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def make_callsite(model_class, original_sql)
|
13
|
+
if use_scrooge?(model_class, original_sql)
|
14
|
+
new(model_class)
|
15
|
+
else
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def use_scrooge?(model_class, original_sql)
|
21
|
+
original_sql =~ select_regexp(model_class.table_name) &&
|
22
|
+
model_class.columns_hash.has_key?(model_class.primary_key) &&
|
23
|
+
original_sql !~ ScroogeRegexJoin
|
24
|
+
end
|
25
|
+
|
26
|
+
def select_regexp(table_name)
|
27
|
+
%r{SELECT (`?(?:#{table_name})?`?.?\\*) FROM}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(model_class)
|
32
|
+
@all_columns = SimpleSet.new(model_class.column_names)
|
33
|
+
@model_class = model_class
|
34
|
+
@quoted_table_name = model_class.quoted_table_name
|
35
|
+
@primary_key = model_class.primary_key
|
36
|
+
@columns_hash = model_class.columns_hash
|
37
|
+
@select_regexp = self.class.select_regexp(model_class.table_name)
|
38
|
+
@seen_columns = SimpleSet.new(essential_columns)
|
39
|
+
end
|
40
|
+
|
41
|
+
def essential_columns
|
42
|
+
@model_class.reflect_on_all_associations.inject([@model_class.primary_key]) do |arr, assoc|
|
43
|
+
if assoc.options[:dependent] && assoc.macro == :belongs_to
|
44
|
+
arr << assoc.association_foreign_key
|
45
|
+
end
|
46
|
+
arr
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def scrooged_sql(seen_columns, sql)
|
51
|
+
sql.gsub(@select_regexp, "SELECT #{scrooge_select_sql(seen_columns)} FROM")
|
52
|
+
end
|
53
|
+
|
54
|
+
def missing_columns(fetched_columns)
|
55
|
+
(@all_columns - SimpleSet.new(fetched_columns)) << @primary_key
|
56
|
+
end
|
57
|
+
|
58
|
+
def reload_sql(primary_keys, fetched_columns)
|
59
|
+
sql_keys = primary_keys.collect{|pk| "'#{pk}'"}.join(ScroogeComma)
|
60
|
+
cols = scrooge_select_sql(missing_columns(fetched_columns))
|
61
|
+
"SELECT #{cols} FROM #{@quoted_table_name} WHERE #{@quoted_table_name}.#{@primary_key} IN (#{sql_keys})"
|
62
|
+
end
|
63
|
+
|
64
|
+
def scrooge_select_sql(set)
|
65
|
+
set.collect{|name| "#{@quoted_table_name}.#{name}"}.join(ScroogeComma)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Author: Stephen Sykes
|
2
|
+
|
3
|
+
module SlimScrooge
|
4
|
+
class Callsites
|
5
|
+
CallsitesMutex = Mutex.new
|
6
|
+
@@callsites = {}
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def has_key?(callsite_key)
|
10
|
+
@@callsites.has_key?(callsite_key)
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](callsite_key)
|
14
|
+
@@callsites[callsite_key]
|
15
|
+
end
|
16
|
+
|
17
|
+
def callsite_key(callsite_hash, sql)
|
18
|
+
callsite_hash + sql.gsub(/WHERE.*/i, "").hash
|
19
|
+
end
|
20
|
+
|
21
|
+
def create(sql, callsite_key, name)
|
22
|
+
begin
|
23
|
+
model_class = name.split.first.constantize
|
24
|
+
rescue NameError, NoMethodError
|
25
|
+
add_callsite(callsite_key, nil)
|
26
|
+
else
|
27
|
+
add_callsite(callsite_key, Callsite.make_callsite(model_class, sql))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_callsite(callsite_key, callsite)
|
32
|
+
CallsitesMutex.synchronize do
|
33
|
+
@@callsites[callsite_key] = callsite
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_seen_column(callsite, seen_column)
|
38
|
+
CallsitesMutex.synchronize do
|
39
|
+
callsite.seen_columns << seen_column
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Author: Stephen Sykes
|
2
|
+
|
3
|
+
module SlimScrooge
|
4
|
+
class MonitoredHash < Hash
|
5
|
+
attr_accessor :callsite, :result_set
|
6
|
+
|
7
|
+
def self.[](original_hash, callsite, result_set)
|
8
|
+
hash = super(original_hash)
|
9
|
+
hash.callsite = callsite
|
10
|
+
hash.result_set = result_set
|
11
|
+
hash
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](name)
|
15
|
+
if @callsite.columns_hash.has_key?(name)
|
16
|
+
@result_set.reload! if @result_set && name != @callsite.primary_key
|
17
|
+
Callsites.add_seen_column(@callsite, name)
|
18
|
+
end
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def []=(name, value)
|
23
|
+
if @result_set && @callsite.columns_hash.has_key?(name)
|
24
|
+
@result_set.reload!
|
25
|
+
end
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
def keys
|
30
|
+
@result_set ? @callsite.columns_hash.keys : super
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_key?(name)
|
34
|
+
@result_set ? @callsite.columns_hash.has_key?(name) : super
|
35
|
+
end
|
36
|
+
|
37
|
+
alias_method :include?, :has_key?
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Author: Stephen Sykes
|
2
|
+
|
3
|
+
module SlimScrooge
|
4
|
+
class ResultSet
|
5
|
+
attr_reader :rows, :callsite_key
|
6
|
+
|
7
|
+
def initialize(rows, callsite_key, fetched_columns)
|
8
|
+
@rows = rows
|
9
|
+
@callsite_key = callsite_key
|
10
|
+
@fetched_columns = fetched_columns
|
11
|
+
end
|
12
|
+
|
13
|
+
def rows_by_key(key)
|
14
|
+
@rows.inject({}) {|hash, row| hash[row[key]] = row; hash}
|
15
|
+
end
|
16
|
+
|
17
|
+
def reload!
|
18
|
+
callsite = Callsites[@callsite_key]
|
19
|
+
rows_hash = rows_by_key(callsite.primary_key)
|
20
|
+
sql = callsite.reload_sql(rows_hash.keys, @fetched_columns)
|
21
|
+
model_class = callsite.model_class
|
22
|
+
new_rows = model_class.connection.send(:select, sql, "#{model_class.name} Reload SlimScrooged")
|
23
|
+
new_rows.each do |row|
|
24
|
+
if old_row = rows_hash[row[callsite.primary_key]]
|
25
|
+
old_row.real_hash.result_set = nil
|
26
|
+
row.each {|col, value| old_row[col] = value}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module SlimScrooge
|
2
|
+
class SimpleSet < Hash
|
3
|
+
class << self
|
4
|
+
# Creates a new set containing the given objects
|
5
|
+
def [](*ary)
|
6
|
+
new(ary)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Create a new SimpleSet containing the unique members of _arr_
|
11
|
+
def initialize(arr = [])
|
12
|
+
Array(arr).each {|x| self[x] = true}
|
13
|
+
end
|
14
|
+
|
15
|
+
# Add a value to the set, and return it
|
16
|
+
def <<(value)
|
17
|
+
self[value] = true
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
# Invokes block once for each item in the set. Creates an array
|
22
|
+
# containing the values returned by the block.
|
23
|
+
def collect(&block)
|
24
|
+
keys.collect(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
alias_method :to_a, :keys
|
28
|
+
|
29
|
+
# Returns set after elements in other have been removed
|
30
|
+
def -(other)
|
31
|
+
SimpleSet.new(collect {|k| other[k] ? nil : k}.compact)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Author: Stephen Sykes
|
2
|
+
|
3
|
+
module SlimScrooge
|
4
|
+
module SelectAll
|
5
|
+
def self.included(base)
|
6
|
+
base.alias_method_chain :select_all, :slim_scrooge
|
7
|
+
end
|
8
|
+
|
9
|
+
def select_all_with_slim_scrooge(sql, name = nil)
|
10
|
+
callsite_key = SlimScrooge::Callsites.callsite_key(callsite_hash, sql)
|
11
|
+
if SlimScrooge::Callsites.has_key?(callsite_key)
|
12
|
+
if callsite = SlimScrooge::Callsites[callsite_key]
|
13
|
+
seen_columns = callsite.seen_columns.dup
|
14
|
+
rows = select_all_without_slim_scrooge(callsite.scrooged_sql(seen_columns, sql), name + " SlimScrooged")
|
15
|
+
result_set = SlimScrooge::ResultSet.new(rows.dup, callsite_key, seen_columns)
|
16
|
+
rows.each {|row| row.real_hash = MonitoredHash[{}, callsite, result_set]}
|
17
|
+
else
|
18
|
+
select_all_without_slim_scrooge(sql, name)
|
19
|
+
end
|
20
|
+
elsif callsite = SlimScrooge::Callsites.create(sql, callsite_key, name)
|
21
|
+
rows = select_all_without_slim_scrooge(sql, name + " SlimScrooged 1st time")
|
22
|
+
rows.each {|row| row.real_hash = MonitoredHash[row.to_hash, callsite, nil]}
|
23
|
+
rows
|
24
|
+
else
|
25
|
+
select_all_without_slim_scrooge(sql, name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:include, SlimScrooge::SelectAll)
|
data/lib/slim_scrooge.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Author: Stephen Sykes
|
2
|
+
|
3
|
+
require 'callsite_hash'
|
4
|
+
require 'slim_attributes'
|
5
|
+
require 'slim_scrooge/simple_set'
|
6
|
+
require 'slim_scrooge/callsites'
|
7
|
+
require 'slim_scrooge/callsite'
|
8
|
+
require 'slim_scrooge/result_set'
|
9
|
+
require 'slim_scrooge/monitored_hash'
|
10
|
+
require 'slim_scrooge/slim_scrooge'
|
data/test/helper.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'setup')
|
2
|
+
require 'active_support/test_case'
|
3
|
+
|
4
|
+
module SlimScrooge
|
5
|
+
class Test
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def setup
|
9
|
+
setup_constants
|
10
|
+
make_sqlite_config
|
11
|
+
make_sqlite_connection
|
12
|
+
load_models
|
13
|
+
load(SCHEMA_ROOT + "/schema.rb")
|
14
|
+
require 'test/unit'
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_files
|
18
|
+
glob("#{File.dirname(__FILE__)}/**/*_test.rb")
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_model_files
|
22
|
+
%w{course}
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def setup_constants
|
28
|
+
set_constant('TEST_ROOT') {File.expand_path(File.dirname(__FILE__))}
|
29
|
+
set_constant('SCHEMA_ROOT') {TEST_ROOT + "/schema"}
|
30
|
+
end
|
31
|
+
|
32
|
+
def make_sqlite_config
|
33
|
+
ActiveRecord::Base.configurations = {
|
34
|
+
'db' => {
|
35
|
+
:adapter => 'sqlite3',
|
36
|
+
:database => 'test_db',
|
37
|
+
:timeout => 5000
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def load_models
|
43
|
+
test_model_files.each {|f| require File.join(File.dirname(__FILE__), "models", f)}
|
44
|
+
end
|
45
|
+
|
46
|
+
def make_sqlite_connection
|
47
|
+
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['db'])
|
48
|
+
end
|
49
|
+
|
50
|
+
def set_constant(constant)
|
51
|
+
Object.const_set(constant, yield) unless Object.const_defined?(constant)
|
52
|
+
end
|
53
|
+
|
54
|
+
def glob(pattern)
|
55
|
+
Dir.glob(pattern)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class ActiveRecordTest < Test
|
61
|
+
class << self
|
62
|
+
def setup
|
63
|
+
setup_constants
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_files
|
67
|
+
glob("#{AR_TEST_SUITE}/cases/**/*_test.rb").sort
|
68
|
+
end
|
69
|
+
|
70
|
+
def connection
|
71
|
+
File.join(AR_TEST_SUITE, 'connections', 'native_mysql')
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def setup_constants
|
77
|
+
set_constant('MYSQL_DB_USER') {'rails'}
|
78
|
+
set_constant('AR_TEST_SUITE') {find_active_record_test_suite}
|
79
|
+
end
|
80
|
+
|
81
|
+
def find_active_record_test_suite
|
82
|
+
ts = ($:).grep(/activerecord/).last.split('/')
|
83
|
+
ts.pop
|
84
|
+
ts << 'test'
|
85
|
+
ts.join('/')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
AR_TEST_SUITE = find_active_record_test_suite
|
90
|
+
end
|
91
|
+
end
|
data/test/setup.rb
ADDED
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: slim_scrooge
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stephen Sykes
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-23 00:00:00 +03:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: slim-attributes
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.7.0
|
24
|
+
version:
|
25
|
+
description: Slim scrooge boosts speed in Rails/Mysql ActiveRecord Models by lazily instantiating attributes as needed, and only querying the database for what is needed.
|
26
|
+
email: sdsykes@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions:
|
30
|
+
- ext/extconf.rb
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.textile
|
33
|
+
files:
|
34
|
+
- README.textile
|
35
|
+
- Rakefile
|
36
|
+
- VERSION.yml
|
37
|
+
- ext/callsite_hash.c
|
38
|
+
- ext/extconf.rb
|
39
|
+
- lib/slim_scrooge.rb
|
40
|
+
- lib/slim_scrooge/callsite.rb
|
41
|
+
- lib/slim_scrooge/callsites.rb
|
42
|
+
- lib/slim_scrooge/monitored_hash.rb
|
43
|
+
- lib/slim_scrooge/result_set.rb
|
44
|
+
- lib/slim_scrooge/simple_set.rb
|
45
|
+
- lib/slim_scrooge/slim_scrooge.rb
|
46
|
+
- test/active_record_setup.rb
|
47
|
+
- test/helper.rb
|
48
|
+
- test/models/course.rb
|
49
|
+
- test/schema/schema.rb
|
50
|
+
- test/setup.rb
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://github.com/sdsykes/slim_scrooge
|
53
|
+
licenses: []
|
54
|
+
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options:
|
57
|
+
- --charset=UTF-8
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
version:
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: "0"
|
71
|
+
version:
|
72
|
+
requirements: []
|
73
|
+
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 1.3.5
|
76
|
+
signing_key:
|
77
|
+
specification_version: 3
|
78
|
+
summary: Slim_scrooge - lazy instantiation of attributes and query optimisation for ActiveRecord
|
79
|
+
test_files:
|
80
|
+
- test/active_record_setup.rb
|
81
|
+
- test/helper.rb
|
82
|
+
- test/models/course.rb
|
83
|
+
- test/schema/schema.rb
|
84
|
+
- test/setup.rb
|