predictive_load 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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