refinerycms 0.9.6.22 → 0.9.6.23
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +0 -2
- data/VERSION +1 -1
- data/config/application.rb +1 -1
- data/readme.md +1 -2
- metadata +2 -23
- data/vendor/plugins/slim_scrooge/README.textile +0 -112
- data/vendor/plugins/slim_scrooge/Rakefile +0 -29
- data/vendor/plugins/slim_scrooge/VERSION.yml +0 -5
- data/vendor/plugins/slim_scrooge/ext/Rakefile +0 -42
- data/vendor/plugins/slim_scrooge/ext/extconf.rb +0 -5
- data/vendor/plugins/slim_scrooge/lib/slim_scrooge.rb +0 -16
- data/vendor/plugins/slim_scrooge/lib/slim_scrooge/callsite.rb +0 -96
- data/vendor/plugins/slim_scrooge/lib/slim_scrooge/callsites.rb +0 -70
- data/vendor/plugins/slim_scrooge/lib/slim_scrooge/monitored_hash.rb +0 -103
- data/vendor/plugins/slim_scrooge/lib/slim_scrooge/result_set.rb +0 -38
- data/vendor/plugins/slim_scrooge/lib/slim_scrooge/simple_set.rb +0 -34
- data/vendor/plugins/slim_scrooge/lib/slim_scrooge/slim_scrooge.rb +0 -46
- data/vendor/plugins/slim_scrooge/rails/init.rb +0 -8
- data/vendor/plugins/slim_scrooge/slim_scrooge.gemspec +0 -62
- data/vendor/plugins/slim_scrooge/slim_scrooge_windows.gemspec +0 -59
- data/vendor/plugins/slim_scrooge/test/active_record_setup.rb +0 -3
- data/vendor/plugins/slim_scrooge/test/helper.rb +0 -91
- data/vendor/plugins/slim_scrooge/test/models/course.rb +0 -2
- data/vendor/plugins/slim_scrooge/test/schema/schema.rb +0 -5
- data/vendor/plugins/slim_scrooge/test/setup.rb +0 -5
data/.yardopts
CHANGED
@@ -7,7 +7,6 @@ vendor/plugins/images/**/*.rb
|
|
7
7
|
vendor/plugins/authentication/**/*.rb
|
8
8
|
vendor/plugins/dashboard/**/*.rb
|
9
9
|
vendor/plugins/inquiries/**/*.rb
|
10
|
-
vendor/plugins/news/**/*.rb
|
11
10
|
vendor/plugins/pages/**/*.rb
|
12
11
|
vendor/plugins/refinery/**/*.rb
|
13
12
|
vendor/plugins/refinery_dialogs/**/*.rb
|
@@ -24,7 +23,6 @@ vendor/plugins/images/images.md
|
|
24
23
|
vendor/plugins/authentication/authentication.md
|
25
24
|
vendor/plugins/dashboard/dashboard.md
|
26
25
|
vendor/plugins/inquiries/inquiries.md
|
27
|
-
vendor/plugins/news/news.md
|
28
26
|
vendor/plugins/resources/resources.md
|
29
27
|
vendor/plugins/refinery_settings/settings.md
|
30
28
|
license.md
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.6.
|
1
|
+
0.9.6.23
|
data/config/application.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
RAILS_GEM_VERSION = '2.3.5' unless defined? RAILS_GEM_VERSION
|
3
3
|
|
4
4
|
# Specified gem version of Refinery to use when vendor/plugins/refinery/lib/refinery.rb is not present.
|
5
|
-
REFINERY_GEM_VERSION = '0.9.6.
|
5
|
+
REFINERY_GEM_VERSION = '0.9.6.23' unless defined? REFINERY_GEM_VERSION
|
6
6
|
|
7
7
|
# Boot Rails
|
8
8
|
require File.join(File.dirname(__FILE__), 'boot')
|
data/readme.md
CHANGED
@@ -65,8 +65,7 @@ After your database exists, you'll need to install the gems that Refinery depend
|
|
65
65
|
|
66
66
|
rake gems:install
|
67
67
|
|
68
|
-
|
69
|
-
This is found here:
|
68
|
+
Note: The news engine that was previously in Refinery's core was extracted into a separate gem / plugin to be found here:
|
70
69
|
|
71
70
|
http://github.com/resolve/refinerycms-news
|
72
71
|
|
metadata
CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
|
|
6
6
|
- 0
|
7
7
|
- 9
|
8
8
|
- 6
|
9
|
-
-
|
10
|
-
version: 0.9.6.
|
9
|
+
- 23
|
10
|
+
version: 0.9.6.23
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Resolve Digital
|
@@ -782,27 +782,6 @@ files:
|
|
782
782
|
- vendor/plugins/resources/config/routes.rb
|
783
783
|
- vendor/plugins/resources/rails/init.rb
|
784
784
|
- vendor/plugins/resources/resources.md
|
785
|
-
- vendor/plugins/slim_scrooge/README.textile
|
786
|
-
- vendor/plugins/slim_scrooge/Rakefile
|
787
|
-
- vendor/plugins/slim_scrooge/VERSION.yml
|
788
|
-
- vendor/plugins/slim_scrooge/ext/Rakefile
|
789
|
-
- vendor/plugins/slim_scrooge/ext/callsite_hash.c
|
790
|
-
- vendor/plugins/slim_scrooge/ext/extconf.rb
|
791
|
-
- vendor/plugins/slim_scrooge/lib/slim_scrooge.rb
|
792
|
-
- vendor/plugins/slim_scrooge/lib/slim_scrooge/callsite.rb
|
793
|
-
- vendor/plugins/slim_scrooge/lib/slim_scrooge/callsites.rb
|
794
|
-
- vendor/plugins/slim_scrooge/lib/slim_scrooge/monitored_hash.rb
|
795
|
-
- vendor/plugins/slim_scrooge/lib/slim_scrooge/result_set.rb
|
796
|
-
- vendor/plugins/slim_scrooge/lib/slim_scrooge/simple_set.rb
|
797
|
-
- vendor/plugins/slim_scrooge/lib/slim_scrooge/slim_scrooge.rb
|
798
|
-
- vendor/plugins/slim_scrooge/rails/init.rb
|
799
|
-
- vendor/plugins/slim_scrooge/slim_scrooge.gemspec
|
800
|
-
- vendor/plugins/slim_scrooge/slim_scrooge_windows.gemspec
|
801
|
-
- vendor/plugins/slim_scrooge/test/active_record_setup.rb
|
802
|
-
- vendor/plugins/slim_scrooge/test/helper.rb
|
803
|
-
- vendor/plugins/slim_scrooge/test/models/course.rb
|
804
|
-
- vendor/plugins/slim_scrooge/test/schema/schema.rb
|
805
|
-
- vendor/plugins/slim_scrooge/test/setup.rb
|
806
785
|
- vendor/plugins/themes/app/helpers/themes_helper.rb
|
807
786
|
- vendor/plugins/themes/lib/theme.rb
|
808
787
|
- vendor/plugins/themes/lib/theme_server.rb
|
@@ -1,112 +0,0 @@
|
|
1
|
-
h1. SlimScrooge - serious optimisation 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 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
|
-
SlimScrooge is similar to (and is partly derived from) "Scrooge":http://github.com/methodmissing/scrooge but has many fewer lines of code and is faster.
|
10
|
-
|
11
|
-
h2. Benchmark
|
12
|
-
|
13
|
-
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.
|
14
|
-
|
15
|
-
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.
|
16
|
-
|
17
|
-
Note that this result was for comparing the time taken for running complete requests through rails - of which database accesses are only one part. So the result is better than it at first sounds.
|
18
|
-
|
19
|
-
In future releases I expect further gains.
|
20
|
-
|
21
|
-
h2. Installation
|
22
|
-
|
23
|
-
Requirements: Rails 2.2 or above, Ruby 1.8.6 or above.
|
24
|
-
|
25
|
-
h3. Gem
|
26
|
-
|
27
|
-
<pre>
|
28
|
-
# if you haven't already, add gemcutter to your gem sources
|
29
|
-
sudo gem install gemcutter
|
30
|
-
gem tumble
|
31
|
-
# install slim_scrooge
|
32
|
-
sudo gem install slim_scrooge
|
33
|
-
</pre>
|
34
|
-
|
35
|
-
Note that the C extension will only build in MRI Ruby 1.8 on non-windows at present, but do not worry, for other platforms the backup callsite mechanism is used.
|
36
|
-
|
37
|
-
Next add this to your Rails::Initializer section in environment.rb:
|
38
|
-
|
39
|
-
<pre>
|
40
|
-
config.gem 'slim_scrooge'
|
41
|
-
</pre>
|
42
|
-
|
43
|
-
h3. Plugin
|
44
|
-
|
45
|
-
<pre>
|
46
|
-
script/plugin install git://github.com/sdsykes/slim_scrooge.git
|
47
|
-
</pre>
|
48
|
-
|
49
|
-
h2. What it does
|
50
|
-
|
51
|
-
<pre>
|
52
|
-
# 1st request, sql is unchanged but columns accesses are recorded
|
53
|
-
Brochure Load SlimScrooged 1st time (27.1ms) SELECT * FROM `brochures` WHERE (expires_at IS NULL)
|
54
|
-
|
55
|
-
# 2nd request, only fetch columns that were used the first time
|
56
|
-
Brochure Load SlimScrooged (4.5ms) SELECT `brochures`.expires_at,`brochures`.operator_id,`brochures`.id FROM
|
57
|
-
`brochures` WHERE (expires_at IS NULL)
|
58
|
-
|
59
|
-
# 2nd request, later in code we need another column which causes a reload of all remaining columns
|
60
|
-
Brochure Reload SlimScrooged (0.6ms) `brochures`.name,`brochures`.comment,`brochures`.image_height,`brochures`.id,
|
61
|
-
`brochures`.tel,`brochures`.long_comment,`brochures`.image_name,`brochures`.image_width FROM
|
62
|
-
`brochures` WHERE `brochures`.id IN ('5646','5476','4562','3456','4567','7355')
|
63
|
-
|
64
|
-
# 3rd request
|
65
|
-
Brochure Load SlimScrooged (4.5ms) SELECT `brochures`.expires_at,`brochures`.operator_id,`brochures`.name,
|
66
|
-
`brochures`.id FROM `brochures` WHERE (expires_at IS NULL)
|
67
|
-
</pre>
|
68
|
-
|
69
|
-
h2. Technical discussion
|
70
|
-
|
71
|
-
SlimScrooge hooks in at just one particular place in ActiveRecord - and that place is the find_all_by_sql method. All select queries pass through this method.
|
72
|
-
|
73
|
-
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 the database so that they only select the rows that are actually used by that piece of code.
|
74
|
-
|
75
|
-
How does SlimScrooge know which columns are actually used?
|
76
|
-
|
77
|
-
It tracks them using a monitored hash - the hash is a subclass of Hash that has is instantiated with a proc for its default value. In this proc we handle any attributes that were not fetched from the DB - fetching the remaining columns as needed.
|
78
|
-
|
79
|
-
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 there is no additional overhead.
|
80
|
-
|
81
|
-
h2. Caveats
|
82
|
-
|
83
|
-
* 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).
|
84
|
-
|
85
|
-
This situation is likely to be found rarely because 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.
|
86
|
-
|
87
|
-
* Some users have complained that running their test suite is slower when SlimScrooge is enabled. This is expected - it's doing a little more work for each query, and most queries in tests are only executed once.
|
88
|
-
|
89
|
-
If it proves to be a problem, you can try this in environment.rb:
|
90
|
-
|
91
|
-
<pre>
|
92
|
-
config.gem 'slim_scrooge' unless Rails.env.test?
|
93
|
-
</pre>
|
94
|
-
|
95
|
-
I do recommend you test with SlimScrooge enabled at some point though, to make sure that everything is working as expected.
|
96
|
-
|
97
|
-
h2. Tests
|
98
|
-
|
99
|
-
SlimScrooge performs the full activerecord test suite without errors, except for a couple of tests that check the number of queries performed.
|
100
|
-
|
101
|
-
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:
|
102
|
-
|
103
|
-
<pre>
|
104
|
-
rake test_with_active_record
|
105
|
-
</pre>
|
106
|
-
|
107
|
-
h2. References
|
108
|
-
* "Scrooge":http://github.com/methodmissing/scrooge
|
109
|
-
|
110
|
-
h2. Authors
|
111
|
-
* Stephen Sykes (sdsykes)
|
112
|
-
* 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.
|
@@ -1,29 +0,0 @@
|
|
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 - serious 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 ActiveRecord Models by 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/Rakefile"
|
24
|
-
end
|
25
|
-
Jeweler::GemcutterTasks.new
|
26
|
-
rescue LoadError
|
27
|
-
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://
|
28
|
-
gems.github.com"
|
29
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
task :default=>[:build]
|
2
|
-
|
3
|
-
task :build do
|
4
|
-
# only try to install extension on non windows / MRI 1.8
|
5
|
-
unless /mswin/ =~ RUBY_PLATFORM || RUBY_VERSION >= "1.9" || defined?(RUBY_ENGINE) && RUBY_ENGINE != "ruby"
|
6
|
-
require 'rbconfig'
|
7
|
-
|
8
|
-
ruby = File.join(Config::CONFIG["bindir"],
|
9
|
-
Config::CONFIG["RUBY_INSTALL_NAME"])
|
10
|
-
ruby << Config::CONFIG["EXEEXT"]
|
11
|
-
|
12
|
-
# escape string in case path to ruby executable contain spaces.
|
13
|
-
ruby.sub!(/.*\s.*/m, '"\&"')
|
14
|
-
|
15
|
-
results = `#{ruby} extconf.rb`
|
16
|
-
|
17
|
-
unless File.exist? 'Makefile'
|
18
|
-
raise "Makefile not found:\n\n#{results.join "\n"}"
|
19
|
-
end
|
20
|
-
|
21
|
-
dest_path = ENV["RUBYARCHDIR"] || File.join(File.dirname(__FILE__), "..", "lib")
|
22
|
-
|
23
|
-
mf = File.read('Makefile')
|
24
|
-
mf = mf.gsub(/^RUBYARCHDIR\s*=\s*\$[^$]*/, "RUBYARCHDIR = #{dest_path}")
|
25
|
-
mf = mf.gsub(/^RUBYLIBDIR\s*=\s*\$[^$]*/, "RUBYLIBDIR = #{dest_path}")
|
26
|
-
|
27
|
-
File.open('Makefile', 'wb') {|f| f.print mf}
|
28
|
-
|
29
|
-
make_program = ENV['make']
|
30
|
-
unless make_program
|
31
|
-
make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
|
32
|
-
end
|
33
|
-
|
34
|
-
['', ' install'].each do |target|
|
35
|
-
cmd = "#{make_program}#{target}"
|
36
|
-
results << cmd + "\n"
|
37
|
-
results << `#{cmd}`
|
38
|
-
|
39
|
-
raise "make#{target} failed:\n\n#{results}" unless $?.success?
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# Author: Stephen Sykes
|
2
|
-
begin
|
3
|
-
unless File.exists?(File.join(File.dirname(__FILE__), "../", "ext", "Makefile"))
|
4
|
-
Dir.chdir(File.join(File.dirname(__FILE__), "../", "ext"))
|
5
|
-
`rake`
|
6
|
-
end
|
7
|
-
rescue Exception
|
8
|
-
end
|
9
|
-
|
10
|
-
begin; require 'callsite_hash'; rescue LoadError; end
|
11
|
-
require 'slim_scrooge/simple_set'
|
12
|
-
require 'slim_scrooge/callsites'
|
13
|
-
require 'slim_scrooge/callsite'
|
14
|
-
require 'slim_scrooge/result_set'
|
15
|
-
require 'slim_scrooge/monitored_hash'
|
16
|
-
require 'slim_scrooge/slim_scrooge'
|
@@ -1,96 +0,0 @@
|
|
1
|
-
# Author: Stephen Sykes
|
2
|
-
|
3
|
-
module SlimScrooge
|
4
|
-
# A Callsite contains the list of columns that are accessed when an SQL
|
5
|
-
# query is made from a particular place in the app
|
6
|
-
#
|
7
|
-
class Callsite
|
8
|
-
ScroogeComma = ",".freeze
|
9
|
-
ScroogeRegexJoin = /(?:LEFT|INNER|OUTER|CROSS)*\s*(?:STRAIGHT_JOIN|JOIN)/i
|
10
|
-
|
11
|
-
attr_accessor :seen_columns
|
12
|
-
attr_reader :columns_hash, :primary_key, :model_class_name
|
13
|
-
|
14
|
-
class << self
|
15
|
-
# Make a callsite if the query is of the right type for us to optimise
|
16
|
-
#
|
17
|
-
def make_callsite(model_class, original_sql)
|
18
|
-
if use_scrooge?(model_class, original_sql)
|
19
|
-
new(model_class)
|
20
|
-
else
|
21
|
-
nil
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# Check if query can be optimised
|
26
|
-
#
|
27
|
-
def use_scrooge?(model_class, original_sql)
|
28
|
-
original_sql =~ select_regexp(model_class.table_name) &&
|
29
|
-
model_class.columns_hash.has_key?(model_class.primary_key) &&
|
30
|
-
original_sql !~ ScroogeRegexJoin
|
31
|
-
end
|
32
|
-
|
33
|
-
# The regexp that enables us to replace the * from SELECT * with
|
34
|
-
# the list of columns we actually need
|
35
|
-
#
|
36
|
-
def select_regexp(table_name)
|
37
|
-
%r{SELECT (`?(?:#{table_name})?`?.?\\*) FROM}
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def initialize(model_class)
|
42
|
-
@all_columns = SimpleSet.new(model_class.column_names)
|
43
|
-
@model_class_name = model_class.to_s
|
44
|
-
@quoted_table_name = model_class.quoted_table_name
|
45
|
-
@primary_key = model_class.primary_key
|
46
|
-
@quoted_primary_key = model_class.connection.quote_column_name(@primary_key)
|
47
|
-
@columns_hash = model_class.columns_hash
|
48
|
-
@select_regexp = self.class.select_regexp(model_class.table_name)
|
49
|
-
@seen_columns = SimpleSet.new(essential_columns(model_class))
|
50
|
-
end
|
51
|
-
|
52
|
-
# List of columns that should always be fetched no matter what
|
53
|
-
#
|
54
|
-
def essential_columns(model_class)
|
55
|
-
model_class.reflect_on_all_associations.inject([@primary_key]) do |arr, assoc|
|
56
|
-
if assoc.options[:dependent] && assoc.macro == :belongs_to
|
57
|
-
arr << assoc.association_foreign_key
|
58
|
-
end
|
59
|
-
arr
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Returns suitable sql given a list of columns and the original query
|
64
|
-
#
|
65
|
-
def scrooged_sql(seen_columns, sql)
|
66
|
-
sql.gsub(@select_regexp, "SELECT #{scrooge_select_sql(seen_columns)} FROM")
|
67
|
-
end
|
68
|
-
|
69
|
-
# List if columns what were not fetched
|
70
|
-
#
|
71
|
-
def missing_columns(fetched_columns)
|
72
|
-
(@all_columns - SimpleSet.new(fetched_columns)) << @primary_key
|
73
|
-
end
|
74
|
-
|
75
|
-
# Returns sql for fetching the unfetched columns for all the rows
|
76
|
-
# in the result set, specified by primary_keys
|
77
|
-
#
|
78
|
-
def reload_sql(primary_keys, fetched_columns)
|
79
|
-
sql_keys = primary_keys.collect{|pk| "'#{pk}'"}.join(ScroogeComma)
|
80
|
-
cols = scrooge_select_sql(missing_columns(fetched_columns))
|
81
|
-
"SELECT #{cols} FROM #{@quoted_table_name} WHERE #{@quoted_primary_key} IN (#{sql_keys})"
|
82
|
-
end
|
83
|
-
|
84
|
-
def connection
|
85
|
-
@model_class_name.constantize.connection
|
86
|
-
end
|
87
|
-
|
88
|
-
# Change a set of columns into a correctly quoted comma separated list
|
89
|
-
#
|
90
|
-
def scrooge_select_sql(set)
|
91
|
-
set.collect do |name|
|
92
|
-
"#{@quoted_table_name}.#{connection.quote_column_name(name)}"
|
93
|
-
end.join(ScroogeComma)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
@@ -1,70 +0,0 @@
|
|
1
|
-
# Author: Stephen Sykes
|
2
|
-
|
3
|
-
module SlimScrooge
|
4
|
-
# Contains the complete list of callsites
|
5
|
-
#
|
6
|
-
class Callsites
|
7
|
-
CallsitesMutex = Mutex.new
|
8
|
-
@@callsites = {}
|
9
|
-
ScroogeCallsiteSample = 1..16
|
10
|
-
|
11
|
-
class << self
|
12
|
-
# Whether we have encountered a callsite before
|
13
|
-
#
|
14
|
-
def has_key?(callsite_key)
|
15
|
-
@@callsites.has_key?(callsite_key)
|
16
|
-
end
|
17
|
-
|
18
|
-
# Return the callsite for this key
|
19
|
-
#
|
20
|
-
def [](callsite_key)
|
21
|
-
@@callsites[callsite_key]
|
22
|
-
end
|
23
|
-
|
24
|
-
# Fallback if you can't compile extension for some reason
|
25
|
-
#
|
26
|
-
begin
|
27
|
-
callsite_hash
|
28
|
-
rescue NameError
|
29
|
-
def callsite_hash
|
30
|
-
caller[ScroogeCallsiteSample].hash
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# Generate a key string - uses the portion of the query before the WHERE
|
35
|
-
# together with the callsite_hash generated by callsite_hash.c
|
36
|
-
#
|
37
|
-
def callsite_key(sql)
|
38
|
-
callsite_hash + sql.gsub(/\sWHERE.*/i, "").hash
|
39
|
-
end
|
40
|
-
|
41
|
-
# Create a new callsite
|
42
|
-
#
|
43
|
-
def create(sql, callsite_key, name)
|
44
|
-
begin
|
45
|
-
model_class = name.split.first.constantize
|
46
|
-
rescue NameError, NoMethodError
|
47
|
-
add_callsite(callsite_key, nil)
|
48
|
-
else
|
49
|
-
add_callsite(callsite_key, Callsite.make_callsite(model_class, sql))
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Add a new callsite, wrap in a mutex for safety
|
54
|
-
#
|
55
|
-
def add_callsite(callsite_key, callsite)
|
56
|
-
CallsitesMutex.synchronize do
|
57
|
-
@@callsites[callsite_key] = callsite
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# Record that a column was accessed, wrap in a mutex for safety
|
62
|
-
#
|
63
|
-
def add_seen_column(callsite, seen_column)
|
64
|
-
CallsitesMutex.synchronize do
|
65
|
-
callsite.seen_columns << seen_column
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
@@ -1,103 +0,0 @@
|
|
1
|
-
# Author: Stephen Sykes
|
2
|
-
|
3
|
-
module SlimScrooge
|
4
|
-
# A MonitoredHash allows us to return only some columns into the @attributes
|
5
|
-
# of an ActiveRecord model object, but to notice when an attribute that
|
6
|
-
# wasn't fetched is accessed.
|
7
|
-
#
|
8
|
-
# Also, when a result is first fetched for a particular callsite, we monitor
|
9
|
-
# all the columns so that we can immediately learn which columns are needed.
|
10
|
-
#
|
11
|
-
class MonitoredHash < Hash
|
12
|
-
attr_accessor :callsite, :result_set, :monitored_columns
|
13
|
-
|
14
|
-
# Create a monitored hash. The unmonitored_columns are accessed like a regular
|
15
|
-
# hash. The monitored columns kept separately, and new_column_access is called
|
16
|
-
# before they are returned.
|
17
|
-
#
|
18
|
-
def self.[](monitored_columns, unmonitored_columns, callsite)
|
19
|
-
hash = MonitoredHash.new {|hash, key| hash.new_column_access(key)}
|
20
|
-
hash.monitored_columns = monitored_columns
|
21
|
-
hash.merge!(unmonitored_columns)
|
22
|
-
hash.callsite = callsite
|
23
|
-
hash
|
24
|
-
end
|
25
|
-
|
26
|
-
# Called when an unknown column is requested, through the default proc.
|
27
|
-
# If the column requested is valid, and the result set is not completely
|
28
|
-
# loaded, then we reload. Otherwise just note the column with add_seen_column.
|
29
|
-
#
|
30
|
-
def new_column_access(name)
|
31
|
-
if @callsite.columns_hash.has_key?(name)
|
32
|
-
@result_set.reload! if @result_set && name != @callsite.primary_key
|
33
|
-
Callsites.add_seen_column(@callsite, name)
|
34
|
-
end
|
35
|
-
@monitored_columns[name]
|
36
|
-
end
|
37
|
-
|
38
|
-
# Reload if needed before allowing assignment
|
39
|
-
#
|
40
|
-
def []=(name, value)
|
41
|
-
if has_key?(name)
|
42
|
-
return super
|
43
|
-
elsif @result_set && @callsite.columns_hash.has_key?(name)
|
44
|
-
@result_set.reload!
|
45
|
-
Callsites.add_seen_column(@callsite, name)
|
46
|
-
end
|
47
|
-
@monitored_columns[name] = value
|
48
|
-
end
|
49
|
-
|
50
|
-
# Returns the column names
|
51
|
-
#
|
52
|
-
def keys
|
53
|
-
@result_set ? @callsite.columns_hash.keys : super | @monitored_columns.keys
|
54
|
-
end
|
55
|
-
|
56
|
-
# Check for a column name
|
57
|
-
#
|
58
|
-
def has_key?(name)
|
59
|
-
@result_set ? @callsite.columns_hash.has_key?(name) : super || @monitored_columns.has_key?(name)
|
60
|
-
end
|
61
|
-
|
62
|
-
alias_method :include?, :has_key?
|
63
|
-
|
64
|
-
# Called by Hash#update when reload is called on an ActiveRecord object
|
65
|
-
#
|
66
|
-
def to_hash
|
67
|
-
@result_set.reload! if @result_set
|
68
|
-
@monitored_columns.merge(self)
|
69
|
-
end
|
70
|
-
|
71
|
-
def freeze
|
72
|
-
@result_set.reload! if @result_set
|
73
|
-
@monitored_columns.freeze
|
74
|
-
super
|
75
|
-
end
|
76
|
-
|
77
|
-
# Marshal
|
78
|
-
# Dump a real hash - can't dump a monitored hash due to default proc
|
79
|
-
#
|
80
|
-
def _dump(depth)
|
81
|
-
Marshal.dump(to_hash)
|
82
|
-
end
|
83
|
-
|
84
|
-
def self._load(str)
|
85
|
-
Marshal.load(str)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
# We need to change the update method of Hash so that it *always* calls
|
91
|
-
# to_hash. This is because it normally checks if other_hash is a kind of
|
92
|
-
# Hash, and doesn't bother calling to_hash if so. But we need it to call
|
93
|
-
# to_hash, because otherwise update will not get the complete columns
|
94
|
-
# from a MonitoredHash
|
95
|
-
#
|
96
|
-
# This is not harmful - to_hash in a regular Hash just returns self.
|
97
|
-
#
|
98
|
-
class Hash
|
99
|
-
alias_method :c_update, :update
|
100
|
-
def update(other_hash, &block)
|
101
|
-
c_update(other_hash.to_hash, &block)
|
102
|
-
end
|
103
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# Author: Stephen Sykes
|
2
|
-
|
3
|
-
module SlimScrooge
|
4
|
-
# A ResultSet contains all the rows found by an sql query
|
5
|
-
# A call to reload! will cause all the rows in the set to be fully loaded
|
6
|
-
# from the database - this should be called when a column access that hasn't previously
|
7
|
-
# been seen by SlimScrooge is encountered
|
8
|
-
#
|
9
|
-
class ResultSet
|
10
|
-
attr_reader :rows, :callsite_key
|
11
|
-
|
12
|
-
def initialize(rows, callsite_key, fetched_columns)
|
13
|
-
@rows = rows
|
14
|
-
@callsite_key = callsite_key
|
15
|
-
@fetched_columns = fetched_columns
|
16
|
-
end
|
17
|
-
|
18
|
-
def rows_by_key(key)
|
19
|
-
@rows.inject({}) {|hash, row| hash[row[key]] = row; hash}
|
20
|
-
end
|
21
|
-
|
22
|
-
# Reload all the rows in the sql result at once
|
23
|
-
# Reloads only those columns we didn't fetch the first time
|
24
|
-
#
|
25
|
-
def reload!
|
26
|
-
callsite = Callsites[@callsite_key]
|
27
|
-
rows_hash = rows_by_key(callsite.primary_key)
|
28
|
-
sql = callsite.reload_sql(rows_hash.keys, @fetched_columns)
|
29
|
-
new_rows = callsite.connection.send(:select, sql, "#{callsite.model_class_name} Reload SlimScrooged")
|
30
|
-
new_rows.each do |row|
|
31
|
-
if old_row = rows_hash[row[callsite.primary_key]]
|
32
|
-
old_row.result_set = nil
|
33
|
-
old_row.monitored_columns.merge!(row)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,34 +0,0 @@
|
|
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
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# Author: Stephen Sykes
|
2
|
-
|
3
|
-
module SlimScrooge
|
4
|
-
module FindBySql
|
5
|
-
def self.included(base)
|
6
|
-
ActiveRecord::Base.extend ClassMethods
|
7
|
-
class << base
|
8
|
-
alias_method_chain :find_by_sql, :slim_scrooge
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
module ClassMethods
|
13
|
-
def find_by_sql_with_slim_scrooge(sql)
|
14
|
-
return find_by_sql_without_slim_scrooge(sql) if sql.is_a?(Array) # don't mess with user's custom query
|
15
|
-
callsite_key = SlimScrooge::Callsites.callsite_key(sql)
|
16
|
-
if SlimScrooge::Callsites.has_key?(callsite_key)
|
17
|
-
find_with_callsite_key(sql, callsite_key)
|
18
|
-
elsif callsite = SlimScrooge::Callsites.create(sql, callsite_key, name) # new site that is scroogeable
|
19
|
-
rows = connection.select_all(sql, "#{name} Load SlimScrooged 1st time")
|
20
|
-
rows.collect! {|row| instantiate(MonitoredHash[row, {}, callsite])}
|
21
|
-
else
|
22
|
-
find_by_sql_without_slim_scrooge(sql)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def find_with_callsite_key(sql, callsite_key)
|
29
|
-
if callsite = SlimScrooge::Callsites[callsite_key]
|
30
|
-
seen_columns = callsite.seen_columns.dup # dup so cols aren't changed underneath us
|
31
|
-
rows = connection.select_all(callsite.scrooged_sql(seen_columns, sql), "#{name} Load SlimScrooged")
|
32
|
-
rows.collect! {|row| MonitoredHash[{}, row, callsite]}
|
33
|
-
result_set = SlimScrooge::ResultSet.new(rows.dup, callsite_key, seen_columns)
|
34
|
-
rows.collect! do |row|
|
35
|
-
row.result_set = result_set
|
36
|
-
instantiate(row)
|
37
|
-
end
|
38
|
-
else
|
39
|
-
find_by_sql_without_slim_scrooge(sql)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
ActiveRecord::Base.send(:include, SlimScrooge::FindBySql)
|
@@ -1,8 +0,0 @@
|
|
1
|
-
unless /mswin/ =~ RUBY_PLATFORM || RUBY_VERSION >= "1.9" || defined?(RUBY_ENGINE) && RUBY_ENGINE != "ruby"
|
2
|
-
unless File.exists?(File.join(File.dirname(__FILE__), "..", "ext", "Makefile"))
|
3
|
-
Dir.chdir(File.join(File.dirname(__FILE__), "..", "ext"))
|
4
|
-
`rake`
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
|
-
require 'slim_scrooge.rb'
|
@@ -1,62 +0,0 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
-
# -*- encoding: utf-8 -*-
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = %q{slim_scrooge}
|
8
|
-
s.version = "1.0.5"
|
9
|
-
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Stephen Sykes"]
|
12
|
-
s.date = %q{2010-02-15}
|
13
|
-
s.description = %q{Slim scrooge boosts speed in Rails ActiveRecord Models by only querying the database for what is needed.}
|
14
|
-
s.email = %q{sdsykes@gmail.com}
|
15
|
-
s.extensions = ["ext/Rakefile"]
|
16
|
-
s.extra_rdoc_files = [
|
17
|
-
"README.textile"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
"README.textile",
|
21
|
-
"Rakefile",
|
22
|
-
"VERSION.yml",
|
23
|
-
"ext/Rakefile",
|
24
|
-
"ext/callsite_hash.c",
|
25
|
-
"ext/extconf.rb",
|
26
|
-
"lib/slim_scrooge.rb",
|
27
|
-
"lib/slim_scrooge/callsite.rb",
|
28
|
-
"lib/slim_scrooge/callsites.rb",
|
29
|
-
"lib/slim_scrooge/monitored_hash.rb",
|
30
|
-
"lib/slim_scrooge/result_set.rb",
|
31
|
-
"lib/slim_scrooge/simple_set.rb",
|
32
|
-
"lib/slim_scrooge/slim_scrooge.rb",
|
33
|
-
"test/active_record_setup.rb",
|
34
|
-
"test/helper.rb",
|
35
|
-
"test/models/course.rb",
|
36
|
-
"test/schema/schema.rb",
|
37
|
-
"test/setup.rb"
|
38
|
-
]
|
39
|
-
s.homepage = %q{http://github.com/sdsykes/slim_scrooge}
|
40
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
41
|
-
s.require_paths = ["lib"]
|
42
|
-
s.rubygems_version = %q{1.3.5}
|
43
|
-
s.summary = %q{Slim_scrooge - serious optimisation for ActiveRecord}
|
44
|
-
s.test_files = [
|
45
|
-
"test/active_record_setup.rb",
|
46
|
-
"test/helper.rb",
|
47
|
-
"test/models/course.rb",
|
48
|
-
"test/schema/schema.rb",
|
49
|
-
"test/setup.rb"
|
50
|
-
]
|
51
|
-
|
52
|
-
if s.respond_to? :specification_version then
|
53
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
54
|
-
s.specification_version = 3
|
55
|
-
|
56
|
-
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
57
|
-
else
|
58
|
-
end
|
59
|
-
else
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
@@ -1,59 +0,0 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
-
# -*- encoding: utf-8 -*-
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = %q{slim_scrooge}
|
8
|
-
s.version = "1.0.4"
|
9
|
-
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Stephen Sykes"]
|
12
|
-
s.date = %q{2009-12-26}
|
13
|
-
s.description = %q{Slim scrooge boosts speed in Rails ActiveRecord Models by only querying the database for what is needed.}
|
14
|
-
s.email = %q{sdsykes@gmail.com}
|
15
|
-
|
16
|
-
s.extra_rdoc_files = [
|
17
|
-
"README.textile"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
"README.textile",
|
21
|
-
"Rakefile",
|
22
|
-
"VERSION.yml",
|
23
|
-
"lib/slim_scrooge.rb",
|
24
|
-
"lib/slim_scrooge/callsite.rb",
|
25
|
-
"lib/slim_scrooge/callsites.rb",
|
26
|
-
"lib/slim_scrooge/monitored_hash.rb",
|
27
|
-
"lib/slim_scrooge/result_set.rb",
|
28
|
-
"lib/slim_scrooge/simple_set.rb",
|
29
|
-
"lib/slim_scrooge/slim_scrooge.rb",
|
30
|
-
"test/active_record_setup.rb",
|
31
|
-
"test/helper.rb",
|
32
|
-
"test/models/course.rb",
|
33
|
-
"test/schema/schema.rb",
|
34
|
-
"test/setup.rb"
|
35
|
-
]
|
36
|
-
s.homepage = %q{http://github.com/sdsykes/slim_scrooge}
|
37
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
38
|
-
s.require_paths = ["lib"]
|
39
|
-
s.rubygems_version = %q{1.3.5}
|
40
|
-
s.summary = %q{Slim_scrooge - serious optimisation for ActiveRecord}
|
41
|
-
s.test_files = [
|
42
|
-
"test/active_record_setup.rb",
|
43
|
-
"test/helper.rb",
|
44
|
-
"test/models/course.rb",
|
45
|
-
"test/schema/schema.rb",
|
46
|
-
"test/setup.rb"
|
47
|
-
]
|
48
|
-
|
49
|
-
if s.respond_to? :specification_version then
|
50
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
51
|
-
s.specification_version = 3
|
52
|
-
|
53
|
-
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
54
|
-
else
|
55
|
-
end
|
56
|
-
else
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
@@ -1,91 +0,0 @@
|
|
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
|