predictive_load 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9765b8681e6f08d2913adc2e00dd8543221e4104
4
- data.tar.gz: 7c9efbb5370c71cf3209a534e8694ac698f6e282
3
+ metadata.gz: eb9fa507002032502359805bb5a3e97730abba37
4
+ data.tar.gz: 40ad5bd0b8d8aa0b65bdd4d81bd20617a901744f
5
5
  SHA512:
6
- metadata.gz: 36c715e1fc8d8e241e766fdf1fb2df3ca78505b10ca717cdb87f6164020a2527e40ba422657fc53b4dd3322f870b8ffe45879d099c13a7cb6aa9aab36240e034
7
- data.tar.gz: c48502690d43a1a2b93d555fbbbb95bceac4e54d4c2048a570faf98c7bead3b5fb62995fedf624e02af1f9e00c7b566b3e95277a2828ef63cf45753b80461103
6
+ metadata.gz: 785eaf1a3a9a1ad2f7c79ad0603ae92e0366cde058ffe8941ab5e81093df731562f6c90b8724aa5f0613949058c7aa0bbf414992ea8b4d597524fee79b024d70
7
+ data.tar.gz: 3ac672859b89465d53fe8086dad7d82fe3519869452537ac7ab551b7c7216111a1b336c4f746aebb2c9af6e720d01b3e40a680e3b1a575406cf313ca38412d44
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://travis-ci.org/eac/resque-durable.png)](https://travis-ci.org/eac/predictive_load)
1
+ [![Build Status](https://travis-ci.org/eac/predictive_load.png)](https://travis-ci.org/eac/predictive_load)
2
2
 
3
3
  predictive_load
4
4
  ===============
@@ -7,12 +7,16 @@ Observes Active Record collections and notifies when a member loads an associati
7
7
  * automatically preloading the association in a single query for all members of that collection.
8
8
  * N+1 detection logging
9
9
 
10
-
11
-
12
10
  ### Automatic preloading
13
11
 
14
12
 
15
13
  ```ruby
14
+ require 'predictive_load'
15
+ require 'predictive_load/active_record_collection_observation'
16
+ ActiveRecord::Base.send(:include, PredictiveLoad::ActiveRecordCollectionObservation)
17
+
18
+ require 'predictive_load/loader'
19
+
16
20
  ActiveRecord::Relation.collection_observer = PredictiveLoad::Loader
17
21
 
18
22
  Ticket.all.each do |ticket|
@@ -28,10 +32,24 @@ Produces:
28
32
  SELECT `accounts`.* FROM `accounts` WHERE `accounts`.`id` IN (1, 2, 3)
29
33
  ```
30
34
 
35
+ ### Disabling preload
36
+
37
+ Some things cannot be preloaded, use `no_preload`
38
+
39
+ ```
40
+ has_many :foos, no_preload: true
41
+ ```
42
+
31
43
  ### N+1 detection logging
32
44
 
33
45
  There is also a log-only version:
34
46
  ```ruby
47
+ require 'predictive_load'
48
+ require 'predictive_load/active_record_collection_observation'
49
+ ActiveRecord::Base.send(:include, PredictiveLoad::ActiveRecordCollectionObservation)
50
+
51
+ require 'predictive_load/watcher'
52
+
35
53
  ActiveRecord::Relation.collection_observer = PredictiveLoad::Watcher
36
54
 
37
55
  Comment.all.each do |comment|
@@ -1,2 +1,4 @@
1
1
  module PredictiveLoad
2
2
  end
3
+
4
+ ActiveRecord::Associations::Builder::Association.valid_options << :no_preload
@@ -5,7 +5,6 @@ module PredictiveLoad
5
5
  # ActiveRecord::Relation.collection_observer = LazyLoader
6
6
  #
7
7
  class Loader
8
-
9
8
  def self.observe(records)
10
9
  new(records).observe
11
10
  end
@@ -28,6 +27,10 @@ module PredictiveLoad
28
27
  end
29
28
  end
30
29
 
30
+ protected
31
+
32
+ attr_reader :records
33
+
31
34
  def all_records_will_likely_load_association?(association_name)
32
35
  if defined?(Mocha) && association_name.to_s.index('_stub_')
33
36
  false
@@ -37,16 +40,20 @@ module PredictiveLoad
37
40
  end
38
41
 
39
42
  def supports_preload?(association)
40
- return false if association.reflection.options[:conditions].respond_to?(:to_proc)
43
+ return false if association.reflection.options[:no_preload]
44
+ return false if association.reflection.options[:conditions].respond_to?(:to_proc) # rails 3 conditions proc (we do not know if it uses instance methods)
45
+ if ActiveRecord::VERSION::MAJOR > 3
46
+ return false if association.reflection.scope.try(:arity).to_i > 0 # rails 4+ conditions block, if it uses a passed in object, we assume it is not preloadable
47
+ end
41
48
  true
42
49
  end
43
50
 
44
- protected
45
-
46
- attr_reader :records
47
-
48
51
  def preload(association_name)
49
- ActiveRecord::Associations::Preloader.new(records_with_association(association_name), [ association_name ]).run
52
+ if ActiveRecord::VERSION::STRING <= "4.1.0"
53
+ ActiveRecord::Associations::Preloader.new(records_with_association(association_name), [ association_name ]).run
54
+ else
55
+ ActiveRecord::Associations::Preloader.new.preload(records_with_association(association_name), [ association_name ])
56
+ end
50
57
  end
51
58
 
52
59
  def records_with_association(association_name)
@@ -59,10 +66,9 @@ module PredictiveLoad
59
66
 
60
67
  def mixed_collection?
61
68
  @mixed_collection ||= begin
62
- klass = records.first.class
63
- records.any? { |record| record.class != klass }
64
- end
69
+ klass = records.first.class
70
+ records.any? { |record| record.class != klass }
71
+ end
65
72
  end
66
-
67
73
  end
68
74
  end
@@ -8,14 +8,16 @@ module PredictiveLoad
8
8
  def preload(association)
9
9
  grouped_records(association).each do |reflection, klasses|
10
10
  klasses.each do |klass, records|
11
- preloader = preloader_for(reflection).new(klass, records, reflection, options)
11
+ preload_scope = (ActiveRecord::VERSION::MAJOR == 3 ? options : self.preload_scope)
12
+ preloader = preloader_for(reflection).new(klass, records, reflection, preload_scope)
12
13
 
13
14
  if preloader.respond_to?(:through_reflection)
14
15
  log("encountered :through association for #{association}. Requires loading records to generate query, so skipping for now.")
15
16
  next
16
17
  end
17
18
 
18
- preload_sql = preloader.scoped.where(collection_arel(preloader)).to_sql
19
+ scope = (ActiveRecord::VERSION::MAJOR == 3 ? preloader.scoped : preloader.scope)
20
+ preload_sql = scope.where(collection_arel(preloader)).to_sql
19
21
 
20
22
  log("would preload with: #{preload_sql.to_s}")
21
23
  klass.connection.explain(preload_sql).each_line do |line|
@@ -1,3 +1,5 @@
1
+ raise "Not supported on rails 4.1+" if ActiveRecord::VERSION::STRING >= "4.1.0"
2
+
1
3
  require 'predictive_load/loader'
2
4
  require 'predictive_load/preload_log'
3
5
 
metadata CHANGED
@@ -1,15 +1,133 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: predictive_load
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Chapweske
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-15 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2015-09-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 4.3.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 3.2.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 4.3.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: minitest
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: minitest-rg
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: sqlite3
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rake
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: bump
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: wwtd
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ - !ruby/object:Gem::Dependency
118
+ name: query_diet
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
13
131
  description: Predictive loader
14
132
  email:
15
133
  - eac@zendesk.com
@@ -17,25 +135,13 @@ executables: []
17
135
  extensions: []
18
136
  extra_rdoc_files: []
19
137
  files:
20
- - .gitignore
21
- - .travis.yml
22
- - Gemfile
23
138
  - LICENSE
24
139
  - README.md
25
- - Rakefile
26
140
  - lib/predictive_load.rb
27
141
  - lib/predictive_load/active_record_collection_observation.rb
28
142
  - lib/predictive_load/loader.rb
29
143
  - lib/predictive_load/preload_log.rb
30
144
  - lib/predictive_load/watcher.rb
31
- - predictive_load.gemspec
32
- - test/active_record_collection_observation_test.rb
33
- - test/database.yml
34
- - test/helper.rb
35
- - test/loader_test.rb
36
- - test/models.rb
37
- - test/schema.rb
38
- - test/watcher_test.rb
39
145
  homepage: ''
40
146
  licenses:
41
147
  - Apache License Version 2.0
@@ -46,25 +152,18 @@ require_paths:
46
152
  - lib
47
153
  required_ruby_version: !ruby/object:Gem::Requirement
48
154
  requirements:
49
- - - '>='
155
+ - - ">="
50
156
  - !ruby/object:Gem::Version
51
157
  version: '0'
52
158
  required_rubygems_version: !ruby/object:Gem::Requirement
53
159
  requirements:
54
- - - '>='
160
+ - - ">="
55
161
  - !ruby/object:Gem::Version
56
162
  version: '0'
57
163
  requirements: []
58
164
  rubyforge_project:
59
- rubygems_version: 2.0.7
165
+ rubygems_version: 2.2.2
60
166
  signing_key:
61
167
  specification_version: 4
62
168
  summary: ''
63
- test_files:
64
- - test/active_record_collection_observation_test.rb
65
- - test/database.yml
66
- - test/helper.rb
67
- - test/loader_test.rb
68
- - test/models.rb
69
- - test/schema.rb
70
- - test/watcher_test.rb
169
+ test_files: []
data/.gitignore DELETED
@@ -1,2 +0,0 @@
1
- test/db
2
- Gemfile.lock
data/.travis.yml DELETED
@@ -1,4 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 1.9.3
4
- - 2.0.0
data/Gemfile DELETED
@@ -1,7 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gemspec
4
- gem "activerecord", "3.2"
5
- gem "minitest"
6
- gem 'sqlite3'
7
- gem 'rake'
data/Rakefile DELETED
@@ -1,11 +0,0 @@
1
- require './test/helper'
2
- require 'rake'
3
- require 'rake/testtask'
4
-
5
- Rake::TestTask.new(:test) do |test|
6
- test.libs << 'test'
7
- test.pattern = 'test/*_test.rb'
8
- test.verbose = true
9
- end
10
-
11
- task :default => :test
@@ -1,16 +0,0 @@
1
- # encoding: utf-8
2
-
3
- Gem::Specification.new do |gem|
4
- gem.authors = ["Eric Chapweske"]
5
- gem.email = ["eac@zendesk.com"]
6
- gem.description = "Predictive loader"
7
- gem.summary = %q{}
8
- gem.homepage = ""
9
- gem.license = "Apache License Version 2.0"
10
-
11
- gem.files = `git ls-files`.split($\)
12
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
- gem.name = "predictive_load"
15
- gem.version = '0.1.2'
16
- end
@@ -1,44 +0,0 @@
1
- require_relative 'helper'
2
- require 'predictive_load/watcher'
3
-
4
- describe PredictiveLoad::ActiveRecordCollectionObservation do
5
-
6
- describe "Relation#to_a" do
7
- before do
8
- user1 = User.create!(:name => "Rudolph")
9
- user2 = User.create!(:name => "Santa")
10
- end
11
-
12
- after do
13
- User.delete_all
14
- end
15
-
16
- describe "when a collection observer is specified" do
17
- before do
18
- ActiveRecord::Relation.collection_observer = PredictiveLoad::Watcher
19
- end
20
-
21
- it "observes the members of that collection" do
22
- users = User.all
23
- assert_equal 2, users.size
24
- assert users.all? { |user| user.collection_observer }
25
- end
26
-
27
- end
28
-
29
- describe "when a collection observer is not specified" do
30
- before do
31
- ActiveRecord::Relation.collection_observer = nil
32
- end
33
-
34
- it "does not observe the members of that collection" do
35
- users = User.all
36
- assert_equal 2, users.size, users.inspect
37
- assert users.none? { |user| user.collection_observer }
38
- end
39
-
40
- end
41
-
42
- end
43
-
44
- end
data/test/database.yml DELETED
@@ -1,3 +0,0 @@
1
- test:
2
- adapter: sqlite3
3
- database: test/db
data/test/helper.rb DELETED
@@ -1,57 +0,0 @@
1
- require 'rubygems'
2
- require 'bundler/setup'
3
- require 'minitest'
4
- require 'minitest/spec'
5
- require 'minitest/autorun'
6
- require 'active_record'
7
- require 'predictive_load'
8
- require 'predictive_load/active_record_collection_observation'
9
-
10
- ActiveRecord::Base.class_eval do
11
- include PredictiveLoad::ActiveRecordCollectionObservation
12
- end
13
-
14
- database_config = YAML.load_file(File.join(File.dirname(__FILE__), 'database.yml'))
15
- ActiveRecord::Base.establish_connection(database_config['test'])
16
- ActiveRecord::Base.default_timezone = :utc
17
- require_relative 'schema'
18
- require_relative 'models'
19
-
20
- def assert_queries(num = 1)
21
- ActiveRecord::SQLCounter.log = []
22
- yield
23
- ensure
24
- assert_equal num, ActiveRecord::SQLCounter.log.size, "#{ActiveRecord::SQLCounter.log.size} instead of #{num} queries were executed.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
25
- end
26
-
27
- # Yanked from ActiveRecord tests
28
- module ActiveRecord
29
- class SQLCounter
30
- cattr_accessor :ignored_sql
31
- self.ignored_sql = [/^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
32
-
33
- # FIXME: this needs to be refactored so specific database can add their own
34
- # ignored SQL. This ignored SQL is for Oracle.
35
- ignored_sql.concat [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
36
-
37
- cattr_accessor :log
38
- self.log = []
39
-
40
- attr_reader :ignore
41
-
42
- def initialize(ignore = self.class.ignored_sql)
43
- @ignore = ignore
44
- end
45
-
46
- def call(name, start, finish, message_id, values)
47
- sql = values[:sql]
48
-
49
- # FIXME: this seems bad. we should probably have a better way to indicate
50
- # the query was cached
51
- return if 'CACHE' == values[:name] || ignore.any? { |x| x =~ sql }
52
- self.class.log << sql
53
- end
54
- end
55
-
56
- ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
57
- end
data/test/loader_test.rb DELETED
@@ -1,155 +0,0 @@
1
- require_relative 'helper'
2
- require 'predictive_load/loader'
3
-
4
- describe PredictiveLoad::Loader do
5
-
6
- describe "A collection of records" do
7
- before do
8
- ActiveRecord::Relation.collection_observer = PredictiveLoad::Loader
9
- # trigger schema lookup to avoid messing with query count assertions
10
- Photo.columns
11
-
12
- topic = Topic.create!(:title => "Sleigh repairs")
13
- user1 = User.create!(:name => "Rudolph")
14
- user2 = User.create!(:name => "Santa")
15
- user1.emails.create!
16
- comment1 = topic.comments.create!(:body => "meow", :user => user1)
17
- comment2 = topic.comments.create!(:body => "Ho Ho ho", :user => user2)
18
- end
19
-
20
- after do
21
- User.delete_all
22
- Comment.delete_all
23
- Topic.delete_all
24
- Photo.delete_all
25
- Email.delete_all
26
- end
27
-
28
- it "supports nested loading" do
29
- # 3: User, Comment, Topic
30
- assert_queries(3) do
31
- User.all.each do |user|
32
- user.comments.each { |comment| assert comment.topic }
33
- end
34
- end
35
- end
36
-
37
- describe "belongs_to" do
38
-
39
- it "automatically preloads" do
40
- comments = Comment.all
41
- assert_equal 2, comments.size
42
- assert_queries(1) do
43
- comments.each { |comment| assert comment.user.name }
44
- end
45
- end
46
-
47
- it "does not attempt to preload associations with proc conditions" do
48
- comments = Comment.all
49
- assert_equal 2, comments.size
50
- assert_queries(2) do
51
- comments.each { |comment| assert comment.user_by_proc.full_name }
52
- end
53
- end
54
-
55
- end
56
-
57
- describe "has_one" do
58
-
59
- it "automatically preloads" do
60
- users = User.all
61
- assert_equal 2, users.size
62
-
63
- assert_queries(1) do
64
- users.each { |user| user.photo }
65
- end
66
- end
67
-
68
- end
69
-
70
- describe "has_many :through" do
71
-
72
- it "automatically preloads" do
73
- users = User.all
74
- assert_equal 2, users.size
75
-
76
- assert_queries(3) do
77
- users.each do |user|
78
- user.topics.each do |topic|
79
- topic.comments.to_a
80
- end
81
- end
82
- end
83
-
84
- end
85
- end
86
-
87
- describe "has_and_belongs_to_many" do
88
-
89
- it "automatically preloads" do
90
- users = User.all
91
- assert_equal 2, users.size
92
-
93
- assert_queries(1) do
94
- users.each { |user| user.emails.to_a }
95
- end
96
-
97
- end
98
- end
99
-
100
- describe "has_many" do
101
-
102
- it "automatically prelaods" do
103
- users = User.all
104
- assert_equal 2, users.size
105
- assert_queries(1) do
106
- users.each { |user| user.comments.to_a }
107
- end
108
- end
109
-
110
- it "preloads #length" do
111
- users = User.all
112
- assert_equal 2, users.size
113
- assert_queries(1) do
114
- users.each { |user| user.comments.length }
115
- end
116
- end
117
-
118
- describe "unsupported behavior" do
119
- it "does not preload when dynamically scoped" do
120
- users = User.all
121
- topic = Topic.first
122
- assert_queries(2) do
123
- users.each { |user| user.comments.by_topic(topic).to_a }
124
- end
125
- end
126
-
127
- it "does not preload when staticly scoped" do
128
- users = User.all
129
- topic = Topic.first
130
- assert_queries(2) do
131
- users.each { |user| user.comments.recent.to_a }
132
- end
133
- end
134
-
135
-
136
- it "does not preload #size" do
137
- users = User.all
138
- assert_queries(2) do
139
- users.each { |user| user.comments.size }
140
- end
141
- end
142
-
143
- it "does not preload first/last" do
144
- users = User.all
145
- assert_queries(2) do
146
- users.each { |user| user.comments.first }
147
- end
148
- end
149
- end
150
-
151
- end
152
-
153
- end
154
-
155
- end
data/test/models.rb DELETED
@@ -1,36 +0,0 @@
1
- class User < ActiveRecord::Base
2
- has_many :comments, :dependent => :destroy
3
- has_many :topics, :through => :comments
4
- has_one :photo
5
- has_and_belongs_to_many :emails
6
-
7
- def full_name
8
- name
9
- end
10
-
11
- end
12
-
13
- class Email < ActiveRecord::Base
14
- end
15
-
16
- class Topic < ActiveRecord::Base
17
- has_many :comments, :dependent => :destroy
18
- end
19
-
20
- class Comment < ActiveRecord::Base
21
- belongs_to :user
22
- belongs_to :user_by_proc, :class_name => "User", :foreign_key => :user_id,
23
- :conditions => proc { "1 = #{one}" }
24
- belongs_to :topic
25
-
26
- scope :by_topic, lambda { |topic| where(:topic_id => topic.id) }
27
- scope :recent, order('updated_at desc')
28
-
29
- def one
30
- 1
31
- end
32
- end
33
-
34
- class Photo < ActiveRecord::Base
35
- belongs_to :user
36
- end
data/test/schema.rb DELETED
@@ -1,37 +0,0 @@
1
- ActiveRecord::Schema.define(:version => 1) do
2
-
3
- drop_table(:users) rescue nil
4
- drop_table(:emails) rescue nil
5
- drop_table(:photos) rescue nil
6
- drop_table(:topics) rescue nil
7
- drop_table(:comments) rescue nil
8
- drop_table(:emails_users) rescue nil
9
-
10
- create_table(:users) do |t|
11
- t.string :name, :null => false
12
- end
13
-
14
- create_table(:photos) do |t|
15
- t.integer :user_id, :null => false
16
- end
17
-
18
- create_table(:emails_users) do |t|
19
- t.integer :user_id
20
- t.integer :email_id
21
- end
22
-
23
- create_table(:emails) do |t|
24
- end
25
-
26
- create_table(:topics) do |t|
27
- t.string :title, :null => false
28
- end
29
-
30
- create_table(:comments) do |t|
31
- t.string :body, :null => false
32
- t.integer :topic_id, :null => false
33
- t.integer :user_id, :null => false
34
- t.timestamps
35
- end
36
-
37
- end
data/test/watcher_test.rb DELETED
@@ -1,96 +0,0 @@
1
- require_relative 'helper'
2
- require 'predictive_load/watcher'
3
- require 'logger'
4
-
5
- describe PredictiveLoad::Watcher do
6
-
7
- describe "A collection of records" do
8
- before do
9
- ActiveRecord::Relation.collection_observer = PredictiveLoad::Watcher
10
-
11
- topic = Topic.create!(:title => "Sleigh repairs")
12
- user1 = User.create!(:name => "Rudolph")
13
- user2 = User.create!(:name => "Santa")
14
- comment1 = topic.comments.create!(:body => "meow", :user => user1)
15
- comment2 = topic.comments.create!(:body => "Ho Ho ho", :user => user2)
16
- end
17
-
18
- after do
19
- User.delete_all
20
- Comment.delete_all
21
- Topic.delete_all
22
- end
23
-
24
- it "logs what the loader would have done" do
25
- users = User.all
26
- users[0].id = 1
27
- users[1].id = 2
28
- message = "predictive_load: detected n1 call on User#comments
29
- predictive_load: expect to prevent 1 queries
30
- predictive_load: would preload with: SELECT \"comments\".* FROM \"comments\" WHERE \"comments\".\"user_id\" IN (1, 2)
31
- predictive_load: 0|0|0|SCAN TABLE comments (~100000 rows)
32
-
33
- predictive_load: 0|0|0|EXECUTE LIST SUBQUERY 1
34
-
35
- predictive_load: would have prevented all 1 queries
36
- "
37
- timing_pattern = /\d+\.\d+ms/
38
- message.gsub!(timing_pattern, '')
39
- assert_log(message, timing_pattern) do
40
- users.each { |user| user.comments.to_a }
41
- end
42
- end
43
-
44
- it "does not log :through association queries" do
45
- users = User.all
46
- message = "predictive_load: detected n1 call on User#topics
47
- predictive_load: expect to prevent 1 queries
48
- predictive_load: encountered :through association for topics. Requires loading records to generate query, so skipping for now.
49
- predictive_load: would have prevented all 1 queries
50
- "
51
-
52
- timing_pattern = /\d+\.\d+ms/
53
- message.gsub!(timing_pattern, '')
54
- assert_log(message, timing_pattern) do
55
- users.each { |user| user.topics.to_a }
56
- end
57
-
58
- end
59
-
60
- it "does not blow up when preloading associations with proc conditions" do
61
- log = StringIO.new
62
- logger = build_logger(log)
63
- ActiveRecord::Base.logger = logger
64
-
65
- comments = Comment.all
66
- assert_equal 2, comments.size
67
- assert_queries(2) do
68
- comments.each { |comment| assert comment.user_by_proc.full_name }
69
- end
70
- end
71
-
72
- end
73
-
74
- def assert_log(message, gsub_pattern)
75
- original_logger = ActiveRecord::Base.logger
76
- log = StringIO.new
77
- logger = build_logger(log)
78
- ActiveSupport::LogSubscriber.colorize_logging = false
79
- ActiveRecord::Base.logger = logger
80
-
81
- yield
82
- result = log.string
83
- result.gsub!(gsub_pattern, '')
84
- assert_equal message, result
85
- ensure
86
- ActiveRecord::Base.logger = original_logger
87
- end
88
-
89
- def build_logger(log)
90
- Logger.new(log).tap do |logger|
91
- logger.level = Logger::Severity::INFO
92
- logger.formatter = proc { |severity, datetime, progname, msg| "#{msg}\n" }
93
- end
94
- end
95
-
96
- end