store_base_sti_class 0.0.1
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.
- data/Appraisals +25 -0
- data/CHANGELOG +2 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +45 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +63 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/db/storebasestiname_unittest.sql +0 -0
- data/gemfiles/rails_3.0.10.gemfile +11 -0
- data/gemfiles/rails_3.0.10.gemfile.lock +43 -0
- data/gemfiles/rails_3.0.11.gemfile +11 -0
- data/gemfiles/rails_3.0.11.gemfile.lock +43 -0
- data/gemfiles/rails_3.0.12.gemfile +11 -0
- data/gemfiles/rails_3.0.12.gemfile.lock +43 -0
- data/gemfiles/rails_3.0.3.gemfile +11 -0
- data/gemfiles/rails_3.0.3.gemfile.lock +43 -0
- data/gemfiles/rails_3.0.4.gemfile +11 -0
- data/gemfiles/rails_3.0.4.gemfile.lock +43 -0
- data/gemfiles/rails_3.0.5.gemfile +11 -0
- data/gemfiles/rails_3.0.5.gemfile.lock +43 -0
- data/gemfiles/rails_3.0.6.gemfile +11 -0
- data/gemfiles/rails_3.0.6.gemfile.lock +43 -0
- data/gemfiles/rails_3.0.7.gemfile +11 -0
- data/gemfiles/rails_3.0.7.gemfile.lock +43 -0
- data/gemfiles/rails_3.0.8.gemfile +11 -0
- data/gemfiles/rails_3.0.8.gemfile.lock +43 -0
- data/gemfiles/rails_3.0.9.gemfile +11 -0
- data/gemfiles/rails_3.0.9.gemfile.lock +43 -0
- data/gemfiles/rails_3.1.0.gemfile +11 -0
- data/gemfiles/rails_3.1.0.gemfile.lock +47 -0
- data/gemfiles/rails_3.1.1.gemfile +11 -0
- data/gemfiles/rails_3.1.1.gemfile.lock +45 -0
- data/gemfiles/rails_3.1.2.gemfile +11 -0
- data/gemfiles/rails_3.1.2.gemfile.lock +45 -0
- data/gemfiles/rails_3.1.3.gemfile +11 -0
- data/gemfiles/rails_3.1.3.gemfile.lock +45 -0
- data/gemfiles/rails_3.1.4.gemfile +11 -0
- data/gemfiles/rails_3.1.4.gemfile.lock +45 -0
- data/gemfiles/rails_3.2.0.gemfile +11 -0
- data/gemfiles/rails_3.2.0.gemfile.lock +45 -0
- data/gemfiles/rails_3.2.1.gemfile +11 -0
- data/gemfiles/rails_3.2.1.gemfile.lock +45 -0
- data/gemfiles/rails_3.2.2.gemfile +11 -0
- data/gemfiles/rails_3.2.2.gemfile.lock +45 -0
- data/gemfiles/rails_3.2.3.gemfile +11 -0
- data/gemfiles/rails_3.2.3.gemfile.lock +45 -0
- data/lib/store_base_sti_class.rb +7 -0
- data/lib/store_base_sti_class_for_3_0.rb +470 -0
- data/lib/store_base_sti_class_for_3_1_and_above.rb +304 -0
- data/store_base_sti_class.gemspec +111 -0
- data/storebasestiname_unittest.sql +0 -0
- data/test/connection.rb +22 -0
- data/test/helper.rb +63 -0
- data/test/models.rb +48 -0
- data/test/schema.rb +35 -0
- data/test/test_store_base_sti_class.rb +161 -0
- metadata +205 -0
@@ -0,0 +1,304 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
if ActiveRecord::VERSION::STRING =~ /^3\.(1|2)/
|
4
|
+
module ActiveRecord
|
5
|
+
|
6
|
+
class Base
|
7
|
+
class_attribute :store_base_sti_class
|
8
|
+
self.store_base_sti_class = true
|
9
|
+
end
|
10
|
+
|
11
|
+
module Associations
|
12
|
+
class Association
|
13
|
+
|
14
|
+
def creation_attributes
|
15
|
+
attributes = {}
|
16
|
+
|
17
|
+
if reflection.macro.in?([:has_one, :has_many]) && !options[:through]
|
18
|
+
attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
|
19
|
+
|
20
|
+
if reflection.options[:as]
|
21
|
+
# START PATCH
|
22
|
+
# original:
|
23
|
+
# attributes[reflection.type] = owner.class.base_class.name
|
24
|
+
|
25
|
+
attributes[reflection.type] = ActiveRecord::Base.store_base_sti_class ? owner.class.base_class.name : owner.class.name
|
26
|
+
|
27
|
+
# END PATCH
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attributes
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
class JoinDependency # :nodoc:
|
37
|
+
class JoinAssociation < JoinPart # :nodoc:
|
38
|
+
def join_to(relation)
|
39
|
+
|
40
|
+
tables = @tables.dup
|
41
|
+
foreign_table = parent_table
|
42
|
+
foreign_klass = parent.active_record
|
43
|
+
|
44
|
+
# The chain starts with the target table, but we want to end with it here (makes
|
45
|
+
# more sense in this context), so we reverse
|
46
|
+
chain.reverse.each_with_index do |reflection, i|
|
47
|
+
table = tables.shift
|
48
|
+
|
49
|
+
case reflection.source_macro
|
50
|
+
when :belongs_to
|
51
|
+
key = reflection.association_primary_key
|
52
|
+
foreign_key = reflection.foreign_key
|
53
|
+
when :has_and_belongs_to_many
|
54
|
+
# Join the join table first...
|
55
|
+
relation.from(join(
|
56
|
+
table,
|
57
|
+
table[reflection.foreign_key].
|
58
|
+
eq(foreign_table[reflection.active_record_primary_key])
|
59
|
+
))
|
60
|
+
|
61
|
+
foreign_table, table = table, tables.shift
|
62
|
+
|
63
|
+
key = reflection.association_primary_key
|
64
|
+
foreign_key = reflection.association_foreign_key
|
65
|
+
else
|
66
|
+
key = reflection.foreign_key
|
67
|
+
foreign_key = reflection.active_record_primary_key
|
68
|
+
end
|
69
|
+
|
70
|
+
constraint = build_constraint(reflection, table, key, foreign_table, foreign_key)
|
71
|
+
|
72
|
+
conditions = self.conditions[i].dup
|
73
|
+
|
74
|
+
# START PATCH
|
75
|
+
# original:
|
76
|
+
# conditions << { reflection.type => foreign_klass.base_class.name } if reflection.type
|
77
|
+
|
78
|
+
if ActiveRecord::Base.store_base_sti_class
|
79
|
+
conditions << { reflection.type => foreign_klass.base_class.name } if reflection.type
|
80
|
+
else
|
81
|
+
conditions << { reflection.type => ([foreign_klass] + foreign_klass.descendants).map(&:name) } if reflection.type
|
82
|
+
end
|
83
|
+
|
84
|
+
# END PATCH
|
85
|
+
|
86
|
+
unless conditions.empty?
|
87
|
+
constraint = constraint.and(sanitize(conditions, table))
|
88
|
+
end
|
89
|
+
|
90
|
+
relation.from(join(table, constraint))
|
91
|
+
|
92
|
+
# The current table in this iteration becomes the foreign table in the next
|
93
|
+
foreign_table, foreign_klass = table, reflection.klass
|
94
|
+
end
|
95
|
+
|
96
|
+
relation
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc:
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def replace_keys(record)
|
107
|
+
super
|
108
|
+
# START PATCH
|
109
|
+
# original: owner[reflection.foreign_type] = record && record.class.base_class.name
|
110
|
+
unless ActiveRecord::Base.store_base_sti_class
|
111
|
+
owner[reflection.foreign_type] = record && record.class.sti_name
|
112
|
+
else
|
113
|
+
owner[reflection.foreign_type] = record && record.class.base_class.name
|
114
|
+
end
|
115
|
+
#END PATCH
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
module Associations
|
120
|
+
class Preloader
|
121
|
+
class Association
|
122
|
+
private
|
123
|
+
def build_scope
|
124
|
+
|
125
|
+
scope = klass.scoped
|
126
|
+
|
127
|
+
scope = scope.where(process_conditions(options[:conditions]))
|
128
|
+
scope = scope.where(process_conditions(preload_options[:conditions]))
|
129
|
+
|
130
|
+
scope = scope.select(preload_options[:select] || options[:select] || table[Arel.star])
|
131
|
+
scope = scope.includes(preload_options[:include] || options[:include])
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
if options[:as]
|
136
|
+
scope = scope.where(
|
137
|
+
klass.table_name => {
|
138
|
+
#START PATCH
|
139
|
+
#original: reflection.type => model.base_class.sti_name
|
140
|
+
reflection.type => ActiveRecord::Base.store_base_sti_class ? model.base_class.sti_name : model.sti_name
|
141
|
+
#END PATCH
|
142
|
+
|
143
|
+
}
|
144
|
+
)
|
145
|
+
end
|
146
|
+
|
147
|
+
scope
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
module ThroughAssociation
|
152
|
+
def through_options
|
153
|
+
through_options = {}
|
154
|
+
if options[:source_type]
|
155
|
+
#START PATCH
|
156
|
+
#original: through_options[:conditions] = { reflection.foreign_type => options[:source_type] }
|
157
|
+
through_options[:conditions] = { reflection.foreign_type => ([options[:source_type].constantize] + options[:source_type].constantize.descendants).map(&:to_s) }
|
158
|
+
#END PATCH
|
159
|
+
else
|
160
|
+
if options[:conditions]
|
161
|
+
through_options[:include] = options[:include] || options[:source]
|
162
|
+
through_options[:conditions] = options[:conditions]
|
163
|
+
end
|
164
|
+
|
165
|
+
through_options[:order] = options[:order]
|
166
|
+
end
|
167
|
+
through_options
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
class AssociationScope
|
173
|
+
def add_constraints(scope)
|
174
|
+
|
175
|
+
tables = construct_tables
|
176
|
+
|
177
|
+
chain.each_with_index do |reflection, i|
|
178
|
+
table, foreign_table = tables.shift, tables.first
|
179
|
+
|
180
|
+
if reflection.source_macro == :has_and_belongs_to_many
|
181
|
+
join_table = tables.shift
|
182
|
+
|
183
|
+
scope = scope.joins(join(
|
184
|
+
join_table,
|
185
|
+
table[reflection.association_primary_key].
|
186
|
+
eq(join_table[reflection.association_foreign_key])
|
187
|
+
))
|
188
|
+
|
189
|
+
table, foreign_table = join_table, tables.first
|
190
|
+
end
|
191
|
+
|
192
|
+
if reflection.source_macro == :belongs_to
|
193
|
+
if reflection.options[:polymorphic]
|
194
|
+
# START PATCH
|
195
|
+
# This line exists to support multiple versions of AR 3.1
|
196
|
+
# original in 3.1.3: key = reflection.association_primary_key
|
197
|
+
|
198
|
+
key = (reflection.method(:association_primary_key).arity == 0) ? reflection.association_primary_key : reflection.association_primary_key(klass)
|
199
|
+
# END PATCH
|
200
|
+
else
|
201
|
+
key = reflection.association_primary_key
|
202
|
+
end
|
203
|
+
|
204
|
+
foreign_key = reflection.foreign_key
|
205
|
+
else
|
206
|
+
key = reflection.foreign_key
|
207
|
+
foreign_key = reflection.active_record_primary_key
|
208
|
+
end
|
209
|
+
|
210
|
+
conditions = self.conditions[i]
|
211
|
+
|
212
|
+
if reflection == chain.last
|
213
|
+
scope = scope.where(table[key].eq(owner[foreign_key]))
|
214
|
+
|
215
|
+
if reflection.type
|
216
|
+
# START PATCH
|
217
|
+
# original: scope = scope.where(table[reflection.type].eq(owner.class.base_class.name))
|
218
|
+
|
219
|
+
unless ActiveRecord::Base.store_base_sti_class
|
220
|
+
scope = scope.where(table[reflection.type].eq(owner.class.name))
|
221
|
+
else
|
222
|
+
scope = scope.where(table[reflection.type].eq(owner.class.base_class.name))
|
223
|
+
end
|
224
|
+
|
225
|
+
# END PATCH
|
226
|
+
end
|
227
|
+
|
228
|
+
conditions.each do |condition|
|
229
|
+
if options[:through] && condition.is_a?(Hash)
|
230
|
+
condition = { table.name => condition }
|
231
|
+
end
|
232
|
+
|
233
|
+
scope = scope.where(interpolate(condition))
|
234
|
+
end
|
235
|
+
else
|
236
|
+
constraint = table[key].eq(foreign_table[foreign_key])
|
237
|
+
|
238
|
+
if reflection.type
|
239
|
+
# START PATCH
|
240
|
+
# original: type = chain[i + 1].klass.base_class.name
|
241
|
+
# constraint = constraint.and(table[reflection.type].eq(type))
|
242
|
+
|
243
|
+
if ActiveRecord::Base.store_base_sti_class
|
244
|
+
type = chain[i + 1].klass.base_class.name
|
245
|
+
constraint = constraint.and(table[reflection.type].eq(type))
|
246
|
+
else
|
247
|
+
klass = chain[i + 1].klass
|
248
|
+
constraint = constraint.and(table[reflection.type].in(([klass] + klass.descendants).map(&:name)))
|
249
|
+
end
|
250
|
+
|
251
|
+
# END PATCH
|
252
|
+
end
|
253
|
+
|
254
|
+
scope = scope.joins(join(foreign_table, constraint))
|
255
|
+
|
256
|
+
unless conditions.empty?
|
257
|
+
scope = scope.where(sanitize(conditions, table))
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
scope
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
end
|
267
|
+
module Reflection
|
268
|
+
class ThroughReflection < AssociationReflection
|
269
|
+
|
270
|
+
def conditions
|
271
|
+
@conditions ||= begin
|
272
|
+
conditions = source_reflection.conditions.map { |c| c.dup }
|
273
|
+
|
274
|
+
# Add to it the conditions from this reflection if necessary.
|
275
|
+
conditions.first << options[:conditions] if options[:conditions]
|
276
|
+
|
277
|
+
through_conditions = through_reflection.conditions
|
278
|
+
|
279
|
+
if options[:source_type]
|
280
|
+
# START PATCH
|
281
|
+
# original: through_conditions.first << { foreign_type => options[:source_type] }
|
282
|
+
|
283
|
+
unless ActiveRecord::Base.store_base_sti_class
|
284
|
+
through_conditions.first << { foreign_type => ([options[:source_type].constantize] + options[:source_type].constantize.descendants).map(&:to_s) }
|
285
|
+
else
|
286
|
+
through_conditions.first << { foreign_type => options[:source_type] }
|
287
|
+
end
|
288
|
+
|
289
|
+
# END PATCH
|
290
|
+
end
|
291
|
+
|
292
|
+
# Recursively fill out the rest of the array from the through reflection
|
293
|
+
conditions += through_conditions
|
294
|
+
|
295
|
+
# And return
|
296
|
+
conditions
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "store_base_sti_class"
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Andrew Mutz"]
|
12
|
+
s.date = "2012-05-09"
|
13
|
+
s.description = "\n ActiveRecord has always stored the base class in polymorphic _type columns when using STI. This can have non-trivial\n performance implications in certain cases. This gem adds 'store_base_sti_class' configuration options which controls\n whether ActiveRecord will store the base class or the actual class. Default to true for backwards compatibility.\n "
|
14
|
+
s.email = "andrew.mutz@appfolio.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"Appraisals",
|
21
|
+
"CHANGELOG",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.rdoc",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"db/storebasestiname_unittest.sql",
|
29
|
+
"gemfiles/rails_3.0.10.gemfile",
|
30
|
+
"gemfiles/rails_3.0.10.gemfile.lock",
|
31
|
+
"gemfiles/rails_3.0.11.gemfile",
|
32
|
+
"gemfiles/rails_3.0.11.gemfile.lock",
|
33
|
+
"gemfiles/rails_3.0.12.gemfile",
|
34
|
+
"gemfiles/rails_3.0.12.gemfile.lock",
|
35
|
+
"gemfiles/rails_3.0.3.gemfile",
|
36
|
+
"gemfiles/rails_3.0.3.gemfile.lock",
|
37
|
+
"gemfiles/rails_3.0.4.gemfile",
|
38
|
+
"gemfiles/rails_3.0.4.gemfile.lock",
|
39
|
+
"gemfiles/rails_3.0.5.gemfile",
|
40
|
+
"gemfiles/rails_3.0.5.gemfile.lock",
|
41
|
+
"gemfiles/rails_3.0.6.gemfile",
|
42
|
+
"gemfiles/rails_3.0.6.gemfile.lock",
|
43
|
+
"gemfiles/rails_3.0.7.gemfile",
|
44
|
+
"gemfiles/rails_3.0.7.gemfile.lock",
|
45
|
+
"gemfiles/rails_3.0.8.gemfile",
|
46
|
+
"gemfiles/rails_3.0.8.gemfile.lock",
|
47
|
+
"gemfiles/rails_3.0.9.gemfile",
|
48
|
+
"gemfiles/rails_3.0.9.gemfile.lock",
|
49
|
+
"gemfiles/rails_3.1.0.gemfile",
|
50
|
+
"gemfiles/rails_3.1.0.gemfile.lock",
|
51
|
+
"gemfiles/rails_3.1.1.gemfile",
|
52
|
+
"gemfiles/rails_3.1.1.gemfile.lock",
|
53
|
+
"gemfiles/rails_3.1.2.gemfile",
|
54
|
+
"gemfiles/rails_3.1.2.gemfile.lock",
|
55
|
+
"gemfiles/rails_3.1.3.gemfile",
|
56
|
+
"gemfiles/rails_3.1.3.gemfile.lock",
|
57
|
+
"gemfiles/rails_3.1.4.gemfile",
|
58
|
+
"gemfiles/rails_3.1.4.gemfile.lock",
|
59
|
+
"gemfiles/rails_3.2.0.gemfile",
|
60
|
+
"gemfiles/rails_3.2.0.gemfile.lock",
|
61
|
+
"gemfiles/rails_3.2.1.gemfile",
|
62
|
+
"gemfiles/rails_3.2.1.gemfile.lock",
|
63
|
+
"gemfiles/rails_3.2.2.gemfile",
|
64
|
+
"gemfiles/rails_3.2.2.gemfile.lock",
|
65
|
+
"gemfiles/rails_3.2.3.gemfile",
|
66
|
+
"gemfiles/rails_3.2.3.gemfile.lock",
|
67
|
+
"lib/store_base_sti_class.rb",
|
68
|
+
"lib/store_base_sti_class_for_3_0.rb",
|
69
|
+
"lib/store_base_sti_class_for_3_1_and_above.rb",
|
70
|
+
"store_base_sti_class.gemspec",
|
71
|
+
"storebasestiname_unittest.sql",
|
72
|
+
"test/connection.rb",
|
73
|
+
"test/helper.rb",
|
74
|
+
"test/models.rb",
|
75
|
+
"test/schema.rb",
|
76
|
+
"test/test_store_base_sti_class.rb"
|
77
|
+
]
|
78
|
+
s.homepage = "http://github.com/appfolio/store_base_sti_class"
|
79
|
+
s.licenses = ["MIT"]
|
80
|
+
s.require_paths = ["lib"]
|
81
|
+
s.rubygems_version = "1.8.21"
|
82
|
+
s.summary = "Modifies ActiveRecord 3.0.5+ with the ability to store the actual class (instead of the base class) in polymorhic _type columns when using STI"
|
83
|
+
|
84
|
+
if s.respond_to? :specification_version then
|
85
|
+
s.specification_version = 3
|
86
|
+
|
87
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
88
|
+
s.add_runtime_dependency(%q<appraisal>, [">= 0"])
|
89
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 0"])
|
90
|
+
s.add_runtime_dependency(%q<sqlite3>, [">= 0"])
|
91
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
92
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
93
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
94
|
+
else
|
95
|
+
s.add_dependency(%q<appraisal>, [">= 0"])
|
96
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
97
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
98
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
99
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
100
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
101
|
+
end
|
102
|
+
else
|
103
|
+
s.add_dependency(%q<appraisal>, [">= 0"])
|
104
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
105
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
106
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
107
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
108
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
Binary file
|
data/test/connection.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
ActiveRecord::Base.logger = Logger.new("debug.log")
|
4
|
+
|
5
|
+
# GRANT ALL PRIVILEGES ON storebasestiname_unittest.* to 'root'@'localhost';
|
6
|
+
|
7
|
+
# ActiveRecord::Base.configurations = {
|
8
|
+
# 'unittest' => {
|
9
|
+
# :adapter => 'mysql2',
|
10
|
+
# :username => 'root',
|
11
|
+
# :encoding => 'utf8',
|
12
|
+
# :database => 'storebasestiname_unittest',
|
13
|
+
# }
|
14
|
+
# }
|
15
|
+
ActiveRecord::Base.configurations = {
|
16
|
+
'unittest' => {
|
17
|
+
:adapter => 'sqlite3',
|
18
|
+
:database => 'db/storebasestiname_unittest.sql',
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
ActiveRecord::Base.establish_connection 'unittest'
|
data/test/helper.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
require 'store_base_sti_class'
|
15
|
+
|
16
|
+
require 'connection'
|
17
|
+
|
18
|
+
# silence verbose schema loading
|
19
|
+
original_stdout = $stdout
|
20
|
+
$stdout = StringIO.new
|
21
|
+
begin
|
22
|
+
require "schema.rb"
|
23
|
+
ensure
|
24
|
+
$stdout = original_stdout
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'models'
|
28
|
+
|
29
|
+
# the following is needed because ActiveRecord::TestCase uses ActiveRecord::SQLCounter, which is
|
30
|
+
# not bundled as part of the gem
|
31
|
+
if ActiveRecord::VERSION::STRING =~ /^3\.2/
|
32
|
+
module ActiveRecord
|
33
|
+
class SQLCounter
|
34
|
+
cattr_accessor :ignored_sql
|
35
|
+
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/]
|
36
|
+
|
37
|
+
# FIXME: this needs to be refactored so specific database can add their own
|
38
|
+
# ignored SQL. This ignored SQL is for Oracle.
|
39
|
+
ignored_sql.concat [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
|
40
|
+
|
41
|
+
cattr_accessor :log
|
42
|
+
self.log = []
|
43
|
+
|
44
|
+
attr_reader :ignore
|
45
|
+
|
46
|
+
def initialize(ignore = self.class.ignored_sql)
|
47
|
+
@ignore = ignore
|
48
|
+
end
|
49
|
+
|
50
|
+
def call(name, start, finish, message_id, values)
|
51
|
+
sql = values[:sql]
|
52
|
+
|
53
|
+
# FIXME: this seems bad. we should probably have a better way to indicate
|
54
|
+
# the query was cached
|
55
|
+
return if 'CACHE' == values[:name] || ignore.any? { |x| x =~ sql }
|
56
|
+
self.class.log << sql
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
data/test/models.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
class Author < ActiveRecord::Base
|
2
|
+
has_many :posts
|
3
|
+
|
4
|
+
has_many :tagging, :through => :posts # through polymorphic has_one
|
5
|
+
has_many :taggings, :through => :posts, :source => :taggings # through polymorphic has_many
|
6
|
+
has_many :tags, :through => :posts # through has_many :through
|
7
|
+
end
|
8
|
+
|
9
|
+
class Post < ActiveRecord::Base
|
10
|
+
belongs_to :author
|
11
|
+
|
12
|
+
has_one :tagging, :as => :taggable
|
13
|
+
has_many :taggings, :as => :taggable
|
14
|
+
has_many :tags, :through => :taggings
|
15
|
+
end
|
16
|
+
|
17
|
+
class SpecialPost < Post
|
18
|
+
end
|
19
|
+
|
20
|
+
class Tagging < ActiveRecord::Base
|
21
|
+
belongs_to :tag, :include => :tagging
|
22
|
+
belongs_to :polytag, :polymorphic => true
|
23
|
+
belongs_to :taggable, :polymorphic => true, :counter_cache => true
|
24
|
+
end
|
25
|
+
|
26
|
+
class Tag < ActiveRecord::Base
|
27
|
+
has_one :tagging
|
28
|
+
|
29
|
+
has_many :taggings
|
30
|
+
has_many :taggables, :through => :taggings
|
31
|
+
has_many :tagged_posts, :through => :taggings, :source => :taggable, :source_type => 'Post'
|
32
|
+
|
33
|
+
has_many :polytaggings, :as => :polytag, :class_name => 'Tagging'
|
34
|
+
has_many :polytagged_posts, :through => :polytaggings, :source => :taggable, :source_type => 'Post'
|
35
|
+
|
36
|
+
has_many :authors, :class_name => "Author", :finder_sql => proc {
|
37
|
+
<<-SQL
|
38
|
+
SELECT authors.* FROM authors
|
39
|
+
INNER JOIN posts p ON authors.id = p.author_id
|
40
|
+
INNER JOIN taggings tgs ON tgs.taggable_id = p.id AND tgs.taggable_type = "Post"
|
41
|
+
WHERE tgs.tag_id = #{self.id}
|
42
|
+
SQL
|
43
|
+
}
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
class SpecialTag < Tag
|
48
|
+
end
|
data/test/schema.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
|
3
|
+
# Please keep these create table statements in alphabetical order
|
4
|
+
# unless the ordering matters. In which case, define them below
|
5
|
+
|
6
|
+
create_table :authors, :force => true do |t|
|
7
|
+
t.string :name, :null => false
|
8
|
+
end
|
9
|
+
|
10
|
+
create_table :posts, :force => true do |t|
|
11
|
+
t.string :type
|
12
|
+
|
13
|
+
t.integer :author_id
|
14
|
+
t.string :title, :null => false
|
15
|
+
t.text :body, :null => false
|
16
|
+
t.integer :taggings_count, :default => 0
|
17
|
+
end
|
18
|
+
|
19
|
+
create_table :taggings, :force => true do |t|
|
20
|
+
t.integer :tag_id
|
21
|
+
|
22
|
+
t.integer :polytag_id
|
23
|
+
t.string :polytag_type
|
24
|
+
|
25
|
+
t.string :taggable_type
|
26
|
+
t.integer :taggable_id
|
27
|
+
end
|
28
|
+
|
29
|
+
create_table :tags, :force => true do |t|
|
30
|
+
t.string :type
|
31
|
+
t.string :name
|
32
|
+
t.integer :taggings_count, :default => 0
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|