ar_attr_lazy 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/LICENSE +20 -0
- data/README.md +35 -0
- data/Rakefile +62 -0
- data/init.rb +1 -0
- data/lib/mcmire/ar_attr_lazy.rb +32 -0
- data/lib/mcmire/ar_attr_lazy/association_preload_ext.rb +55 -0
- data/lib/mcmire/ar_attr_lazy/base_ext.rb +69 -0
- data/lib/mcmire/ar_attr_lazy/belongs_to_association_ext.rb +19 -0
- data/lib/mcmire/ar_attr_lazy/habtm_ext.rb +23 -0
- data/lib/mcmire/ar_attr_lazy/has_many_through_association_ext.rb +9 -0
- data/lib/mcmire/ar_attr_lazy/join_base_ext.rb +14 -0
- data/lib/mcmire/ar_attr_lazy/version.rb +5 -0
- data/test/factories.rb +96 -0
- data/test/helper.rb +107 -0
- data/test/matchers.rb +172 -0
- data/test/not_using_attr_lazy_test.rb +324 -0
- data/test/setup_migration.rb +67 -0
- data/test/setup_sti_models.rb +61 -0
- data/test/setup_tables_for_not_using.rb +91 -0
- data/test/setup_tables_for_using.rb +35 -0
- data/test/using_attr_lazy_sti_test.rb +357 -0
- data/test/using_attr_lazy_test.rb +359 -0
- metadata +136 -0
data/test/helper.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
if ENV["AR_VERSION"]
|
6
|
+
gem 'activerecord', "= #{ENV["AR_VERSION"]}"
|
7
|
+
end
|
8
|
+
require 'activerecord'
|
9
|
+
require 'active_record/version'
|
10
|
+
ActiveRecord::Base.establish_connection(
|
11
|
+
"adapter" => "sqlite3",
|
12
|
+
"database" => ":memory:"
|
13
|
+
)
|
14
|
+
|
15
|
+
gem 'mcmire-protest'
|
16
|
+
require 'protest'
|
17
|
+
gem 'mcmire-matchy'
|
18
|
+
require 'matchy'
|
19
|
+
gem 'mcmire-mocha'
|
20
|
+
require 'mocha'
|
21
|
+
require 'mocha-protest-integration'
|
22
|
+
|
23
|
+
Protest.report_with :documentation
|
24
|
+
#Protest::Utils::BacktraceFilter::ESCAPE_PATHS << %r|test/unit| << %r|matchy| << %r|mocha-protest-integration|
|
25
|
+
Protest::Utils::BacktraceFilter::ESCAPE_PATHS.clear
|
26
|
+
|
27
|
+
#------------------------
|
28
|
+
|
29
|
+
module Protest
|
30
|
+
class TestCase
|
31
|
+
def full_name
|
32
|
+
self.class.description + " " + self.name
|
33
|
+
end
|
34
|
+
|
35
|
+
class TestWrapper #:nodoc:
|
36
|
+
attr_reader :name
|
37
|
+
|
38
|
+
def initialize(type, test_case)
|
39
|
+
@type = type
|
40
|
+
@test = test_case
|
41
|
+
@name = "Global #{@type} for #{test_case.description}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def run(report)
|
45
|
+
@test.send("do_global_#{@type}")
|
46
|
+
end
|
47
|
+
|
48
|
+
def full_name
|
49
|
+
@name
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
module TestWithErrors
|
55
|
+
def file
|
56
|
+
file_and_line[0]
|
57
|
+
end
|
58
|
+
|
59
|
+
def line
|
60
|
+
file_and_line[1]
|
61
|
+
end
|
62
|
+
|
63
|
+
def file_and_line
|
64
|
+
backtrace.find {|x| x =~ %r{^.*/test/(.*_test|test_.*)\.rb} }.split(":")[0..1]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module Utils
|
69
|
+
module Summaries
|
70
|
+
def summarize_errors
|
71
|
+
return if failures_and_errors.empty?
|
72
|
+
|
73
|
+
puts "Failures:"
|
74
|
+
puts
|
75
|
+
|
76
|
+
pad_indexes = failures_and_errors.size.to_s.size
|
77
|
+
failures_and_errors.each_with_index do |error, index|
|
78
|
+
colorize_as = ErroredTest === error ? :errored : :failed
|
79
|
+
# PATCH: test.full_name
|
80
|
+
puts " #{pad(index+1, pad_indexes)}) #{test_type(error)} in `#{error.test.full_name}' (on line #{error.line} of `#{error.file}')", colorize_as
|
81
|
+
# If error message has line breaks, indent the message
|
82
|
+
prefix = "with"
|
83
|
+
unless error.error.is_a?(Protest::AssertionFailed) ||
|
84
|
+
((RUBY_VERSION =~ /^1\.9/) ? error.error.is_a?(MiniTest::Assertion) : error.error.is_a?(::Test::Unit::AssertionFailedError))
|
85
|
+
prefix << " #{error.error.class}"
|
86
|
+
end
|
87
|
+
if error.error_message =~ /\n/
|
88
|
+
puts indent("#{prefix}: <<", 6 + pad_indexes), colorize_as
|
89
|
+
puts indent(error.error_message, 6 + pad_indexes + 2), colorize_as
|
90
|
+
puts indent(">>", 6 + pad_indexes), colorize_as
|
91
|
+
else
|
92
|
+
puts indent("#{prefix} `#{error.error_message}'", 6 + pad_indexes), colorize_as
|
93
|
+
end
|
94
|
+
indent(error.backtrace, 6 + pad_indexes).each {|backtrace| puts backtrace, colorize_as }
|
95
|
+
puts
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
#------------------------
|
103
|
+
|
104
|
+
require 'matchers'
|
105
|
+
require 'factories'
|
106
|
+
|
107
|
+
require 'mcmire/ar_attr_lazy'
|
data/test/matchers.rb
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
module MatchyMatchers
|
2
|
+
# Ported from an RSpec matcher
|
3
|
+
# from https://rspec.lighthouseapp.com/projects/5645/tickets/896-lambda-should-query-matcher
|
4
|
+
# with a few tweaks
|
5
|
+
class ArQuery #:nodoc:
|
6
|
+
cattr_accessor :executed
|
7
|
+
|
8
|
+
@@recording_queries = false
|
9
|
+
def self.recording_queries?
|
10
|
+
@@recording_queries
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(test_case, expecteds, &block)
|
14
|
+
@test_case = test_case
|
15
|
+
@expecteds = expecteds
|
16
|
+
@expecteds = [1] if @expecteds.empty?
|
17
|
+
@block = block
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(given_proc)
|
21
|
+
@eval_block = false
|
22
|
+
@eval_error = nil
|
23
|
+
ArQuery.executed = []
|
24
|
+
@@recording_queries = true
|
25
|
+
|
26
|
+
given_proc.call
|
27
|
+
|
28
|
+
if @expecteds[0].is_a?(Fixnum)
|
29
|
+
@expecteds = @expecteds[0]
|
30
|
+
@actuals = ArQuery.executed.length
|
31
|
+
@matched = (@actuals == @expecteds)
|
32
|
+
else
|
33
|
+
# assume that a block was not given
|
34
|
+
# PATCH: accept multiple queries
|
35
|
+
@expecteds = Array(@expecteds)
|
36
|
+
@actuals = @expecteds.map {|query| [query, ArQuery.executed.detect {|sql| query === sql }] }
|
37
|
+
@matched = @actuals.all? {|e,a| a }
|
38
|
+
end
|
39
|
+
|
40
|
+
eval_block if @block && @matched && !negative_expectation?
|
41
|
+
|
42
|
+
@matched && @eval_error.nil?
|
43
|
+
|
44
|
+
ensure
|
45
|
+
#ArQuery.executed = nil
|
46
|
+
@@recording_queries = false
|
47
|
+
end
|
48
|
+
|
49
|
+
# This is necessary for interoperability with Matchy
|
50
|
+
def fail!(which)
|
51
|
+
@test_case.flunk(which ? failure_message_for_should : failure_message_for_should_not)
|
52
|
+
end
|
53
|
+
|
54
|
+
# This is necessary for interoperability with Matchy
|
55
|
+
def pass!(which)
|
56
|
+
@test_case.assert true
|
57
|
+
end
|
58
|
+
|
59
|
+
def eval_block
|
60
|
+
@eval_block = true
|
61
|
+
begin
|
62
|
+
@block.call(ArQuery.executed)
|
63
|
+
rescue Exception => err
|
64
|
+
@eval_error = err
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def failure_message_for_should
|
69
|
+
if @eval_error
|
70
|
+
@eval_error.message
|
71
|
+
elsif @expecteds.is_a?(Fixnum)
|
72
|
+
"expected #{@expecteds} to be executed, when in fact #{@actuals} were"
|
73
|
+
else
|
74
|
+
# PATCH: better error message
|
75
|
+
msg = ""
|
76
|
+
@actuals.select {|e,a| !a }.each do |expected, _|
|
77
|
+
msg << "expected a query with pattern #{expected.inspect} to be executed, but it wasn't\n"
|
78
|
+
end
|
79
|
+
msg << "All queries executed:\n"
|
80
|
+
ArQuery.executed.each do |query|
|
81
|
+
msg << " - #{query}\n"
|
82
|
+
end
|
83
|
+
msg
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def failure_message_for_should_not
|
88
|
+
if @expecteds.is_a?(Fixnum)
|
89
|
+
"did not expect #{@expecteds} queries to be executed, but they were"
|
90
|
+
else
|
91
|
+
# PATCH: better error message
|
92
|
+
msg = ""
|
93
|
+
@actuals.select {|e,a| a }.each do |_, actual|
|
94
|
+
msg << "expected a query with pattern #{actual.inspect} not to be executed, but it was\n"
|
95
|
+
end
|
96
|
+
msg << "All queries executed:\n"
|
97
|
+
ArQuery.executed.each do |query|
|
98
|
+
msg << " - #{query}\n"
|
99
|
+
end
|
100
|
+
msg
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
#def description
|
105
|
+
# if @expecteds.is_a?(Fixnum)
|
106
|
+
# @expecteds == 1 ? "execute 1 query" : "execute #{@expecteds} queries"
|
107
|
+
# else
|
108
|
+
# "execute query with pattern #{@expecteds.inspect}"
|
109
|
+
# end
|
110
|
+
#end
|
111
|
+
|
112
|
+
# Copied from raise_error
|
113
|
+
def negative_expectation?
|
114
|
+
@negative_expectation ||= !caller.first(3).find { |s| s =~ /should_not/ }.nil?
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# :call-seq:
|
119
|
+
# response.should query
|
120
|
+
# response.should query(expected)
|
121
|
+
# response.should query(expected1, expected2)
|
122
|
+
# response.should query(expected) { |sql| ... }
|
123
|
+
# response.should_not query
|
124
|
+
# response.should_not query(expected)
|
125
|
+
# response.should_not query(expected1, expected2)
|
126
|
+
#
|
127
|
+
# Accepts a Fixnum, a String, a Regexp, or an array of Strings or Regexps as arguments.
|
128
|
+
#
|
129
|
+
# With no args, matches if exactly 1 query is executed.
|
130
|
+
# With a Fixnum arg, matches if the number of queries executed equals the given number.
|
131
|
+
# With a Regexp arg, matches if any query is executed with the given pattern.
|
132
|
+
# With multiple args, matches if all given patterns are matched by all queries executed.
|
133
|
+
#
|
134
|
+
# Pass an optional block to perform extra verifications of the queries matched.
|
135
|
+
# The argument of the block will receive an array of query strings that were executed.
|
136
|
+
#
|
137
|
+
# == Examples
|
138
|
+
#
|
139
|
+
# lambda { @object.posts }.should query # same as `should query(1)`
|
140
|
+
# lambda { @object.valid? }.should query(0)
|
141
|
+
# lambda { @object.save }.should query(3)
|
142
|
+
# lambda { @object.line_items }.should query("SELECT DISTINCT")
|
143
|
+
# lambda { @object.line_items }.should query(/SELECT DISTINCT/)
|
144
|
+
# lambda { @object.line_items }.should query(/SELECT DISTINCT/, /SELECT COUNT\(\*\)/)
|
145
|
+
# lambda { @object.line_items }.should query(1) { |sql| sql[0].should =~ /SELECT DISTINCT/ }
|
146
|
+
#
|
147
|
+
# lambda { @object.posts }.should_not query # same as `should_not query(1)`
|
148
|
+
# lambda { @object.valid? }.should_not query(0)
|
149
|
+
# lambda { @object.save }.should_not query(3)
|
150
|
+
# lambda { @object.line_items }.should_not query(/SELECT DISTINCT/)
|
151
|
+
# lambda { @object.line_items }.should_not query(/SELECT DISTINCT/, /SELECT COUNT\(\*\)/)
|
152
|
+
#
|
153
|
+
def query(*expecteds, &block)
|
154
|
+
ArQuery.new(self, expecteds, &block)
|
155
|
+
end
|
156
|
+
|
157
|
+
unless defined?(IGNORED_SQL)
|
158
|
+
# From active_record/test/cases/helper.rb :
|
159
|
+
::ActiveRecord::Base.connection.class.class_eval do
|
160
|
+
IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /SHOW FIELDS/]
|
161
|
+
def execute_with_query_record(sql, name = nil, &block)
|
162
|
+
if ArQuery.recording_queries?
|
163
|
+
# PATCH: squeeze and strip
|
164
|
+
ArQuery.executed << sql.squeeze(" ").strip unless IGNORED_SQL.any? { |ignore| sql =~ ignore }
|
165
|
+
end
|
166
|
+
execute_without_query_record(sql, name, &block)
|
167
|
+
end
|
168
|
+
alias_method_chain :execute, :query_record
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
Protest::TestCase.class_eval { include MatchyMatchers }
|
@@ -0,0 +1,324 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
# what about STI? (do the lazy attributes carry over?)
|
4
|
+
|
5
|
+
Protest.context "for a model that doesn't have lazy attributes" do
|
6
|
+
global_setup do
|
7
|
+
load File.dirname(__FILE__) + '/setup_migration.rb'
|
8
|
+
load File.dirname(__FILE__) + '/setup_tables_for_not_using.rb'
|
9
|
+
Account.make! do |account|
|
10
|
+
User.make!(:account => account) do |user|
|
11
|
+
Avatar.make!(:user => user)
|
12
|
+
Post.make!(:author => user) do |post|
|
13
|
+
Comment.make!(:post => post)
|
14
|
+
post.tags << Tag.make
|
15
|
+
post.categories << Category.make
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
global_teardown do
|
22
|
+
ObjectSpace.each_object(Class) do |klass|
|
23
|
+
Object.remove_class(klass) if klass < ActiveRecord::Base
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def regex(str)
|
28
|
+
Regexp.new(Regexp.escape(str))
|
29
|
+
end
|
30
|
+
|
31
|
+
context "with no associations involved" do
|
32
|
+
test "find selects all attributes by default" do
|
33
|
+
lambda { Account.find(:first) }.should query(regex(%|SELECT * FROM "accounts"|))
|
34
|
+
end
|
35
|
+
test "accessing any one attribute doesn't do a query" do
|
36
|
+
account = Account.first
|
37
|
+
lambda { account.name }.should_not query
|
38
|
+
end
|
39
|
+
test "find still honors an explicit select option" do
|
40
|
+
lambda { Account.find(:first, :select => "name") }.should query(regex(%|SELECT name FROM "accounts"|))
|
41
|
+
end
|
42
|
+
test "find still honors a select option in a parent scope" do
|
43
|
+
lambda {
|
44
|
+
Account.send(:with_scope, :find => {:select => "name"}) do
|
45
|
+
Account.find(:first)
|
46
|
+
end
|
47
|
+
}.should query(regex(%|SELECT name FROM "accounts"|))
|
48
|
+
end
|
49
|
+
if Mcmire::ArAttrLazy.ar_version >= 2.3
|
50
|
+
test "find still honors a select option in a default scope" do
|
51
|
+
lambda { AccountWithDefaultScope.find(:first) }.should query(regex(%|SELECT name FROM "accounts"|))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "accessing a has_many association" do
|
57
|
+
before do
|
58
|
+
@post = Post.first
|
59
|
+
end
|
60
|
+
test "find selects all attributes by default" do
|
61
|
+
lambda { @post.comments.find(:first) }.should query(regex(%|SELECT * FROM "comments"|))
|
62
|
+
end
|
63
|
+
test "find still honors an explicit select option" do
|
64
|
+
lambda { @post.comments.find(:first, :select => "name") }.should query(
|
65
|
+
regex(%|SELECT name FROM "comments"|)
|
66
|
+
)
|
67
|
+
end
|
68
|
+
test "find still honors a select option in a parent scope" do
|
69
|
+
lambda {
|
70
|
+
Comment.send(:with_scope, :find => {:select => "name"}) do
|
71
|
+
@post.comments.find(:first)
|
72
|
+
end
|
73
|
+
}.should query(
|
74
|
+
regex(%|SELECT name FROM "comments"|)
|
75
|
+
)
|
76
|
+
end
|
77
|
+
if Mcmire::ArAttrLazy.ar_version >= 2.3
|
78
|
+
test "find still honors a select option in a default scope" do
|
79
|
+
lambda { @post.comments_with_default_scope.find(:first) }.should query(
|
80
|
+
regex(%|SELECT name FROM "comments"|)
|
81
|
+
)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
test "find still honors a select option in the association definition itself" do
|
85
|
+
lambda { @post.comments_with_select.find(:first) }.should query(
|
86
|
+
regex(%|SELECT name FROM "comments"|)
|
87
|
+
)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "accessing a belongs_to association" do
|
92
|
+
test "find selects all attributes by default" do
|
93
|
+
post = Post.first
|
94
|
+
lambda { post.author }.should query(regex(%|SELECT * FROM "users"|))
|
95
|
+
end
|
96
|
+
# can't do a find on a belongs_to, so no testing needed for that
|
97
|
+
end
|
98
|
+
|
99
|
+
context "accessing a has_one association" do
|
100
|
+
test "find selects all attributes by default" do
|
101
|
+
account = Account.first
|
102
|
+
lambda { account.user }.should query(regex(%|SELECT * FROM "users"|))
|
103
|
+
end
|
104
|
+
# can't do a find on a has_one, so no testing needed for that
|
105
|
+
end
|
106
|
+
|
107
|
+
context "accessing a has_and_belongs_to_many association" do
|
108
|
+
before do
|
109
|
+
@post = Post.first
|
110
|
+
end
|
111
|
+
test "find selects all attributes by default" do
|
112
|
+
lambda { @post.tags.find(:all) }.should query(regex(%|SELECT * FROM "tags"|))
|
113
|
+
end
|
114
|
+
test "find still honors an explicit select option" do
|
115
|
+
lambda { @post.tags.find(:all, :select => "tags.name") }.should query(
|
116
|
+
regex(%|SELECT tags.name FROM "tags"|)
|
117
|
+
)
|
118
|
+
end
|
119
|
+
test "find still honors a select option in a parent scope" do
|
120
|
+
pending "this fails on Rails 2.3.4"
|
121
|
+
lambda {
|
122
|
+
Tag.send(:with_scope, :find => {:select => "tags.name"}) do
|
123
|
+
@post.tags.find(:all)
|
124
|
+
end
|
125
|
+
}.should query(
|
126
|
+
regex(%|SELECT tags.name FROM "tags"|)
|
127
|
+
)
|
128
|
+
end
|
129
|
+
if Mcmire::ArAttrLazy.ar_version >= 2.3
|
130
|
+
test "find still honors a select option in a default scope" do
|
131
|
+
pending "this fails on Rails 2.3.4"
|
132
|
+
lambda {
|
133
|
+
@post.tags_with_default_scope.find(:all)
|
134
|
+
}.should query(
|
135
|
+
regex(%|SELECT tags.name FROM "tags"|)
|
136
|
+
)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
test "find still honors a select option in the association definition itself" do
|
140
|
+
lambda {
|
141
|
+
@post.tags_with_select.find(:all)
|
142
|
+
}.should query(
|
143
|
+
regex(%|SELECT tags.name FROM "tags"|)
|
144
|
+
)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "accessing a has_many :through association" do
|
149
|
+
before do
|
150
|
+
@post = Post.first
|
151
|
+
end
|
152
|
+
test "find selects all attributes by default" do
|
153
|
+
lambda { @post.categories.find(:all) }.should query(
|
154
|
+
regex(%|SELECT "categories".* FROM "categories"|)
|
155
|
+
)
|
156
|
+
end
|
157
|
+
test "find still honors an explicit select option" do
|
158
|
+
lambda { @post.categories.find(:all, :select => "categories.name") }.should query(
|
159
|
+
regex(%|SELECT categories.name FROM "categories"|)
|
160
|
+
)
|
161
|
+
end
|
162
|
+
test "find still honors a select option in a parent scope" do
|
163
|
+
pending "this fails on Rails 2.3.4"
|
164
|
+
lambda {
|
165
|
+
Category.send(:with_scope, :find => {:select => "categories.name"}) do
|
166
|
+
@post.categories.find(:all)
|
167
|
+
end
|
168
|
+
}.should query(
|
169
|
+
regex(%|SELECT categories.name FROM "categories"|)
|
170
|
+
)
|
171
|
+
end
|
172
|
+
if Mcmire::ArAttrLazy.ar_version >= 2.3
|
173
|
+
test "find still honors a select option in a default scope" do
|
174
|
+
pending "this fails on Rails 2.3.4"
|
175
|
+
lambda {
|
176
|
+
@post.categories_with_default_scope.find(:all)
|
177
|
+
}.should query(
|
178
|
+
regex(%|SELECT categories.name FROM "categories"|)
|
179
|
+
)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
test "find still honors a select option in the association definition itself" do
|
183
|
+
lambda {
|
184
|
+
@post.categories_with_select.find(:all)
|
185
|
+
}.should query(
|
186
|
+
regex(%|SELECT categories.name FROM "categories"|)
|
187
|
+
)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# has_one :through didn't work properly prior to 2.3.4 - see LH #2719
|
192
|
+
if Mcmire::ArAttrLazy.ar_version >= "2.3.4"
|
193
|
+
context "accessing a has_one :through association" do
|
194
|
+
test "find selects all attributes by default" do
|
195
|
+
account = Account.first
|
196
|
+
lambda { account.avatar }.should query(
|
197
|
+
regex(%|SELECT "avatars".* FROM "avatars"|)
|
198
|
+
)
|
199
|
+
end
|
200
|
+
# can't do a find on a has_one, so no testing needed for that
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context "eager loading a has_many association (association preloading)" do
|
205
|
+
test "find selects all attributes by default" do
|
206
|
+
lambda {
|
207
|
+
Post.find(:first, :include => :comments)
|
208
|
+
}.should query(regex(%|SELECT * FROM "posts"|), regex(%|SELECT "comments".* FROM "comments"|))
|
209
|
+
end
|
210
|
+
# can't test for an explicit select since that will force a table join
|
211
|
+
# can't test for a scope select since association preloading doesn't honor those
|
212
|
+
end
|
213
|
+
context "eager loading a has_many association (table join)" do
|
214
|
+
test "find selects all attributes by default" do
|
215
|
+
lambda {
|
216
|
+
Post.find(:first, :include => :comments, :order => "comments.id")
|
217
|
+
}.should query(%r{"posts"\."body"}, %r{"comments"\."body"})
|
218
|
+
end
|
219
|
+
# can't test for an explicit select since that clashes with the table join anyway
|
220
|
+
# can't test for a scope for the same reason
|
221
|
+
end
|
222
|
+
|
223
|
+
context "eager loading a has_one association (association preloading)" do
|
224
|
+
test "find selects all attributes by default" do
|
225
|
+
lambda { Account.find(:first, :include => :user) }.should query(regex(%|SELECT "users".* FROM "users"|))
|
226
|
+
end
|
227
|
+
# can't test for an explicit select since that will force a table join
|
228
|
+
# can't test for a scope select since association preloading doesn't honor those
|
229
|
+
end
|
230
|
+
context "eager loading a has_one association (table join)" do
|
231
|
+
test "find selects all attributes by default" do
|
232
|
+
lambda {
|
233
|
+
Account.find(:first, :include => :user, :order => "users.id")
|
234
|
+
}.should query(%r{"users"\."bio"})
|
235
|
+
end
|
236
|
+
# can't test for an explicit select since that clashes with the table join anyway
|
237
|
+
# can't test for a scope for the same reason
|
238
|
+
end
|
239
|
+
|
240
|
+
context "eager loading a belongs_to association (association preloading)" do
|
241
|
+
test "find selects all attributes by default" do
|
242
|
+
lambda {
|
243
|
+
Post.find(:first, :include => :author)
|
244
|
+
}.should query(regex(%|SELECT * FROM "posts"|))
|
245
|
+
end
|
246
|
+
# can't test for an explicit select since that will force a table join
|
247
|
+
# can't test for a scope select since association preloading doesn't honor those
|
248
|
+
end
|
249
|
+
context "eager loading a belongs_to association (table join)" do
|
250
|
+
test "find selects all attributes by default" do
|
251
|
+
lambda {
|
252
|
+
Post.find(:first, :include => :author, :order => "users.id")
|
253
|
+
}.should query(%r{"posts"\."(body|summary)"}, %r{"users"\."bio"})
|
254
|
+
end
|
255
|
+
# can't test for an explicit select since that clashes with the table join anyway
|
256
|
+
# can't test for a scope for the same reason
|
257
|
+
end
|
258
|
+
|
259
|
+
context "eager loading a has_and_belongs_to_many association (association preloading)" do
|
260
|
+
test "find selects all attributes by default" do
|
261
|
+
lambda {
|
262
|
+
Post.find(:first, :include => :tags)
|
263
|
+
}.should query(regex(%|SELECT * FROM "posts"|), regex(%|SELECT "tags".*, t0.post_id as the_parent_record_id FROM "tags"|))
|
264
|
+
end
|
265
|
+
# can't test for an explicit select since that will force a table join
|
266
|
+
# can't test for a scope select since association preloading doesn't honor those
|
267
|
+
end
|
268
|
+
context "eager loading a has_and_belongs_to_many association (table join)" do
|
269
|
+
test "find selects all attributes by default" do
|
270
|
+
lambda {
|
271
|
+
Post.find(:first, :include => :tags, :order => "tags.id")
|
272
|
+
}.should query(%r{"posts"\."(body|summary)"}, %r{"tags"\."description"})
|
273
|
+
end
|
274
|
+
# can't test for an explicit select since that clashes with the table join anyway
|
275
|
+
# can't test for a scope for the same reason
|
276
|
+
end
|
277
|
+
|
278
|
+
context "eager loading a has_many :through association (association preloading)" do
|
279
|
+
test "find selects all attributes by default" do
|
280
|
+
lambda {
|
281
|
+
Post.find(:first, :include => :categories)
|
282
|
+
}.should query(
|
283
|
+
regex(%|SELECT * FROM "categories"|)
|
284
|
+
)
|
285
|
+
end
|
286
|
+
# can't test for an explicit select since that will force a table join
|
287
|
+
# can't test for a scope select since association preloading doesn't honor those
|
288
|
+
end
|
289
|
+
context "eager loading a has_many :through association (table join)" do
|
290
|
+
test "find selects all attributes by default" do
|
291
|
+
lambda {
|
292
|
+
Post.find(:first, :include => :categories, :order => "categories.id")
|
293
|
+
}.should query(
|
294
|
+
regex(%|SELECT "posts"."id" AS t0_r0, "posts"."type" AS t0_r1, "posts"."author_id" AS t0_r2, "posts"."title" AS t0_r3, "posts"."permalink" AS t0_r4, "posts"."body" AS t0_r5, "posts"."summary" AS t0_r6, "categories"."id" AS t1_r0, "categories"."type" AS t1_r1, "categories"."name" AS t1_r2, "categories"."description" AS t1_r3 FROM "posts"|)
|
295
|
+
)
|
296
|
+
end
|
297
|
+
# can't test for an explicit select since that clashes with the table join anyway
|
298
|
+
# can't test for a scope for the same reason
|
299
|
+
end
|
300
|
+
|
301
|
+
# has_one :through didn't work properly prior to 2.3.4 - see LH #2719
|
302
|
+
if Mcmire::ArAttrLazy.ar_version >= "2.3.4"
|
303
|
+
context "eager loading a has_one :through association (association preloading)" do
|
304
|
+
test "find selects all attributes by default" do
|
305
|
+
lambda { Account.find(:first, :include => :avatar) }.should query(
|
306
|
+
regex(%|SELECT "avatars".* FROM "avatars"|)
|
307
|
+
)
|
308
|
+
end
|
309
|
+
# can't test for an explicit select since that will force a table join
|
310
|
+
# can't test for a scope select since association preloading doesn't honor those
|
311
|
+
end
|
312
|
+
context "eager loading a has_one :through association (table join)" do
|
313
|
+
test "find selects all attributes by default" do
|
314
|
+
pending "this is failing for some reason!"
|
315
|
+
lambda {
|
316
|
+
Account.find(:first, :include => :avatar, :order => "avatars.filename")
|
317
|
+
}.should query(%|SELECT "accounts"."id" AS t0_r0, "accounts"."name" AS t0_r1, "avatars"."id" AS t1_r0, "avatars"."user_id" AS t1_r1, "avatars"."filename" AS t1_r2, "avatars"."data" AS t1_r3 FROM "accounts"|)
|
318
|
+
end
|
319
|
+
# can't test for an explicit select since that clashes with the table join anyway
|
320
|
+
# can't test for a scope for the same reason
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|