activerecord 1.15.3 → 1.15.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +29 -0
- data/Rakefile +1 -1
- data/lib/active_record/acts/list.rb +9 -0
- data/lib/active_record/acts/tree.rb +7 -0
- data/lib/active_record/associations.rb +21 -9
- data/lib/active_record/associations/has_many_association.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -0
- data/lib/active_record/base.rb +12 -3
- data/lib/active_record/calculations.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +2 -2
- data/lib/active_record/deprecated_finders.rb +3 -3
- data/lib/active_record/fixtures.rb +1 -1
- data/lib/active_record/timestamp.rb +0 -9
- data/lib/active_record/version.rb +1 -1
- data/test/associations/eager_test.rb +13 -0
- data/test/associations/join_model_test.rb +10 -1
- data/test/associations_test.rb +24 -3
- data/test/base_test.rb +17 -4
- data/test/fixtures/author.rb +1 -0
- data/test/fixtures/binaries.yml +437 -0
- data/test/fixtures_test.rb +9 -5
- data/test/migration_test.rb +9 -10
- metadata +146 -145
data/CHANGELOG
CHANGED
@@ -1,3 +1,32 @@
|
|
1
|
+
*1.15.4* (October 4th, 2007)
|
2
|
+
|
3
|
+
* Fix #count on a has_many :through association so that it recognizes the :uniq option. Closes #8801 [lifofifo]
|
4
|
+
|
5
|
+
* Don't clobber includes passed to has_many.count [danger]
|
6
|
+
|
7
|
+
* Make sure has_many uses :include when counting [danger]
|
8
|
+
|
9
|
+
* Save associated records only if the association is already loaded. #8713 [blaine]
|
10
|
+
|
11
|
+
* Changing the :default Date format doesn't break date quoting. #6312 [bshand, Elias]
|
12
|
+
|
13
|
+
* Allow nil serialized attributes with a set class constraint. #7293 [sandofsky]
|
14
|
+
|
15
|
+
* belongs_to assignment creates a new proxy rather than modifying its target in-place. #8412 [mmangino@elevatedrails.com]
|
16
|
+
|
17
|
+
* Fix column type detection while loading fixtures. Closes #7987 [roderickvd]
|
18
|
+
|
19
|
+
* Document deep eager includes. #6267 [Josh Susser, Dan Manges]
|
20
|
+
|
21
|
+
* Oracle: extract column length for CHAR also. #7866 [ymendel]
|
22
|
+
|
23
|
+
* Small additions and fixes for ActiveRecord documentation. Closes #7342 [jeremymcanally]
|
24
|
+
|
25
|
+
* SQLite: binary escaping works with $KCODE='u'. #7862 [tsuka]
|
26
|
+
|
27
|
+
* Improved cloning performance by relying less on exception raising #8159 [Blaine]
|
28
|
+
|
29
|
+
|
1
30
|
*1.15.3* (March 12th, 2007)
|
2
31
|
|
3
32
|
* Allow a polymorphic :source for has_many :through associations. Closes #7143 [protocool]
|
data/Rakefile
CHANGED
@@ -151,7 +151,7 @@ spec = Gem::Specification.new do |s|
|
|
151
151
|
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
152
152
|
end
|
153
153
|
|
154
|
-
s.add_dependency('activesupport', '= 1.4.
|
154
|
+
s.add_dependency('activesupport', '= 1.4.3' + PKG_BUILD)
|
155
155
|
|
156
156
|
s.files.delete "test/fixtures/fixture_database.sqlite"
|
157
157
|
s.files.delete "test/fixtures/fixture_database_2.sqlite"
|
@@ -74,6 +74,7 @@ module ActiveRecord
|
|
74
74
|
# lower in the list of all chapters. Likewise, <tt>chapter.first?</tt> would return true if that chapter is
|
75
75
|
# the first in the list of all chapters.
|
76
76
|
module InstanceMethods
|
77
|
+
# Insert the item at the given position (defaults to the top position of 1).
|
77
78
|
def insert_at(position = 1)
|
78
79
|
insert_at_position(position)
|
79
80
|
end
|
@@ -118,6 +119,7 @@ module ActiveRecord
|
|
118
119
|
end
|
119
120
|
end
|
120
121
|
|
122
|
+
# Removes the item from the list.
|
121
123
|
def remove_from_list
|
122
124
|
decrement_positions_on_lower_items if in_list?
|
123
125
|
end
|
@@ -162,6 +164,7 @@ module ActiveRecord
|
|
162
164
|
)
|
163
165
|
end
|
164
166
|
|
167
|
+
# Test if this record is in a list
|
165
168
|
def in_list?
|
166
169
|
!send(position_column).nil?
|
167
170
|
end
|
@@ -178,21 +181,26 @@ module ActiveRecord
|
|
178
181
|
# Overwrite this method to define the scope of the list changes
|
179
182
|
def scope_condition() "1" end
|
180
183
|
|
184
|
+
# Returns the bottom position number in the list.
|
185
|
+
# bottom_position_in_list # => 2
|
181
186
|
def bottom_position_in_list(except = nil)
|
182
187
|
item = bottom_item(except)
|
183
188
|
item ? item.send(position_column) : 0
|
184
189
|
end
|
185
190
|
|
191
|
+
# Returns the bottom item
|
186
192
|
def bottom_item(except = nil)
|
187
193
|
conditions = scope_condition
|
188
194
|
conditions = "#{conditions} AND #{self.class.primary_key} != #{except.id}" if except
|
189
195
|
acts_as_list_class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
|
190
196
|
end
|
191
197
|
|
198
|
+
# Forces item to assume the bottom position in the list.
|
192
199
|
def assume_bottom_position
|
193
200
|
update_attribute(position_column, bottom_position_in_list(self).to_i + 1)
|
194
201
|
end
|
195
202
|
|
203
|
+
# Forces item to assume the top position in the list.
|
196
204
|
def assume_top_position
|
197
205
|
update_attribute(position_column, 1)
|
198
206
|
end
|
@@ -227,6 +235,7 @@ module ActiveRecord
|
|
227
235
|
)
|
228
236
|
end
|
229
237
|
|
238
|
+
# Increments position (<tt>position_column</tt>) of all items in the list.
|
230
239
|
def increment_positions_on_all_items
|
231
240
|
acts_as_list_class.update_all(
|
232
241
|
"#{position_column} = (#{position_column} + 1)", "#{scope_condition}"
|
@@ -70,16 +70,23 @@ module ActiveRecord
|
|
70
70
|
nodes
|
71
71
|
end
|
72
72
|
|
73
|
+
# Returns the root node of the tree.
|
73
74
|
def root
|
74
75
|
node = self
|
75
76
|
node = node.parent while node.parent
|
76
77
|
node
|
77
78
|
end
|
78
79
|
|
80
|
+
# Returns all siblings of the current node.
|
81
|
+
#
|
82
|
+
# subchild1.siblings # => [subchild2]
|
79
83
|
def siblings
|
80
84
|
self_and_siblings - [self]
|
81
85
|
end
|
82
86
|
|
87
|
+
# Returns all siblings and a reference to the current node.
|
88
|
+
#
|
89
|
+
# subchild1.self_and_siblings # => [subchild1, subchild2]
|
83
90
|
def self_and_siblings
|
84
91
|
parent ? parent.children : self.class.roots
|
85
92
|
end
|
@@ -352,7 +352,15 @@ module ActiveRecord
|
|
352
352
|
# for post in Post.find(:all, :include => [ :author, :comments ])
|
353
353
|
#
|
354
354
|
# That'll add another join along the lines of: LEFT OUTER JOIN comments ON comments.post_id = posts.id. And we'll be down to 1 query.
|
355
|
-
#
|
355
|
+
#
|
356
|
+
# To include a deep hierarchy of associations, using a hash:
|
357
|
+
#
|
358
|
+
# for post in Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ])
|
359
|
+
#
|
360
|
+
# That'll grab not only all the comments but all their authors and gravatar pictures. You can mix and match
|
361
|
+
# symbols, arrays and hashes in any combination to describe the associations you want to load.
|
362
|
+
#
|
363
|
+
# All of this power shouldn't fool you into thinking that you can pull out huge amounts of data with no performance penalty just because you've reduced
|
356
364
|
# the number of queries. The database still needs to send all the data to Active Record and it still needs to be processed. So it's no
|
357
365
|
# catch-all for performance problems, but it's a great way to cut down on the number of queries in a situation as the one described above.
|
358
366
|
#
|
@@ -734,6 +742,7 @@ module ActiveRecord
|
|
734
742
|
deprecated_association_comparison_method(reflection.name, reflection.class_name)
|
735
743
|
end
|
736
744
|
|
745
|
+
# Create the callbacks to update counter cache
|
737
746
|
if options[:counter_cache]
|
738
747
|
cache_column = options[:counter_cache] == true ?
|
739
748
|
"#{self.to_s.underscore.pluralize}_count" :
|
@@ -871,6 +880,12 @@ module ActiveRecord
|
|
871
880
|
end
|
872
881
|
|
873
882
|
private
|
883
|
+
# Generate a join table name from two provided tables names.
|
884
|
+
# The order of names in join name is determined by lexical precedence.
|
885
|
+
# join_table_name("members", "clubs")
|
886
|
+
# => "clubs_members"
|
887
|
+
# join_table_name("members", "special_clubs")
|
888
|
+
# => "members_special_clubs"
|
874
889
|
def join_table_name(first_table_name, second_table_name)
|
875
890
|
if first_table_name < second_table_name
|
876
891
|
join_table = "#{first_table_name}_#{second_table_name}"
|
@@ -880,7 +895,7 @@ module ActiveRecord
|
|
880
895
|
|
881
896
|
table_name_prefix + join_table + table_name_suffix
|
882
897
|
end
|
883
|
-
|
898
|
+
|
884
899
|
def association_accessor_methods(reflection, association_proxy_class)
|
885
900
|
define_method(reflection.name) do |*params|
|
886
901
|
force_reload = params.first unless params.empty?
|
@@ -901,7 +916,7 @@ module ActiveRecord
|
|
901
916
|
|
902
917
|
define_method("#{reflection.name}=") do |new_value|
|
903
918
|
association = instance_variable_get("@#{reflection.name}")
|
904
|
-
if association.nil?
|
919
|
+
if association.nil? || association.target != new_value
|
905
920
|
association = association_proxy_class.new(self, reflection)
|
906
921
|
end
|
907
922
|
|
@@ -911,10 +926,7 @@ module ActiveRecord
|
|
911
926
|
instance_variable_set("@#{reflection.name}", association)
|
912
927
|
else
|
913
928
|
instance_variable_set("@#{reflection.name}", nil)
|
914
|
-
return nil
|
915
929
|
end
|
916
|
-
|
917
|
-
association
|
918
930
|
end
|
919
931
|
|
920
932
|
define_method("set_#{reflection.name}_target") do |target|
|
@@ -981,8 +993,8 @@ module ActiveRecord
|
|
981
993
|
|
982
994
|
after_callback = <<-end_eval
|
983
995
|
association = instance_variable_get("@#{association_name}")
|
984
|
-
|
985
|
-
if association.respond_to?(:loaded?)
|
996
|
+
|
997
|
+
if association.respond_to?(:loaded?) && association.loaded?
|
986
998
|
if @new_record_before_save
|
987
999
|
records_to_save = association
|
988
1000
|
else
|
@@ -992,7 +1004,7 @@ module ActiveRecord
|
|
992
1004
|
association.send(:construct_sql) # reconstruct the SQL queries now that we know the owner's id
|
993
1005
|
end
|
994
1006
|
end_eval
|
995
|
-
|
1007
|
+
|
996
1008
|
# Doesn't use after_save as that would save associations added in after_create/after_update twice
|
997
1009
|
after_create(after_callback)
|
998
1010
|
after_update(after_callback)
|
@@ -50,7 +50,7 @@ module ActiveRecord
|
|
50
50
|
options[:conditions] = options[:conditions].nil? ?
|
51
51
|
@finder_sql :
|
52
52
|
@finder_sql + " AND (#{sanitize_sql(options[:conditions])})"
|
53
|
-
options[:include]
|
53
|
+
options[:include] ||= @reflection.options[:include]
|
54
54
|
|
55
55
|
@reflection.klass.count(column_name, options)
|
56
56
|
end
|
@@ -138,7 +138,7 @@ module ActiveRecord
|
|
138
138
|
elsif @reflection.options[:counter_sql]
|
139
139
|
@reflection.klass.count_by_sql(@counter_sql)
|
140
140
|
else
|
141
|
-
@reflection.klass.count(:conditions => @counter_sql)
|
141
|
+
@reflection.klass.count(:conditions => @counter_sql, :include => @reflection.options[:include])
|
142
142
|
end
|
143
143
|
|
144
144
|
@target = [] and loaded if count == 0
|
@@ -101,6 +101,16 @@ module ActiveRecord
|
|
101
101
|
def sum(*args, &block)
|
102
102
|
calculate(:sum, *args, &block)
|
103
103
|
end
|
104
|
+
|
105
|
+
def count(*args)
|
106
|
+
column_name, options = @reflection.klass.send(:construct_count_options_from_legacy_args, *args)
|
107
|
+
if @reflection.options[:uniq]
|
108
|
+
# This is needed becase 'SELECT count(DISTINCT *)..' is not valid sql statement.
|
109
|
+
column_name = "#{@reflection.klass.table_name}.#{@reflection.klass.primary_key}" if column_name == :all
|
110
|
+
options.merge!(:distinct => true)
|
111
|
+
end
|
112
|
+
@reflection.klass.send(:with_scope, construct_scope) { @reflection.klass.count(column_name, options) }
|
113
|
+
end
|
104
114
|
|
105
115
|
protected
|
106
116
|
def method_missing(method, *args, &block)
|
data/lib/active_record/base.rb
CHANGED
@@ -575,7 +575,7 @@ module ActiveRecord #:nodoc:
|
|
575
575
|
|
576
576
|
# Specifies that the attribute by the name of +attr_name+ should be serialized before saving to the database and unserialized
|
577
577
|
# after loading from the database. The serialization is done through YAML. If +class_name+ is specified, the serialized
|
578
|
-
# object must be of that class on retrieval or +SerializationTypeMismatch+ will be raised.
|
578
|
+
# object must be of that class on retrieval, or nil. Otherwise, +SerializationTypeMismatch+ will be raised.
|
579
579
|
def serialize(attr_name, class_name = Object)
|
580
580
|
serialized_attributes[attr_name.to_s] = class_name
|
581
581
|
end
|
@@ -1188,6 +1188,9 @@ module ActiveRecord #:nodoc:
|
|
1188
1188
|
#
|
1189
1189
|
# It's even possible to use all the additional parameters to find. For example, the full interface for find_all_by_amount
|
1190
1190
|
# is actually find_all_by_amount(amount, options).
|
1191
|
+
#
|
1192
|
+
# This also enables you to initialize a record if it is not found, such as find_or_initialize_by_amount(amount)
|
1193
|
+
# or find_or_create_by_user_and_password(user, password).
|
1191
1194
|
def method_missing(method_id, *arguments)
|
1192
1195
|
if match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(method_id.to_s)
|
1193
1196
|
finder, deprecated_finder = determine_finder(match), determine_deprecated_finder(match)
|
@@ -1957,7 +1960,7 @@ module ActiveRecord #:nodoc:
|
|
1957
1960
|
def unserialize_attribute(attr_name)
|
1958
1961
|
unserialized_object = object_from_yaml(@attributes[attr_name])
|
1959
1962
|
|
1960
|
-
if unserialized_object.is_a?(self.class.serialized_attributes[attr_name])
|
1963
|
+
if unserialized_object.is_a?(self.class.serialized_attributes[attr_name]) || unserialized_object.nil?
|
1961
1964
|
@attributes[attr_name] = unserialized_object
|
1962
1965
|
else
|
1963
1966
|
raise SerializationTypeMismatch,
|
@@ -2156,7 +2159,13 @@ module ActiveRecord #:nodoc:
|
|
2156
2159
|
|
2157
2160
|
def clone_attribute_value(reader_method, attribute_name)
|
2158
2161
|
value = send(reader_method, attribute_name)
|
2159
|
-
|
2162
|
+
|
2163
|
+
case value
|
2164
|
+
when nil, Fixnum, true, false
|
2165
|
+
value
|
2166
|
+
else
|
2167
|
+
value.clone
|
2168
|
+
end
|
2160
2169
|
rescue TypeError, NoMethodError
|
2161
2170
|
value
|
2162
2171
|
end
|
@@ -242,8 +242,8 @@ module ActiveRecord
|
|
242
242
|
options.assert_valid_keys(CALCULATIONS_OPTIONS)
|
243
243
|
end
|
244
244
|
|
245
|
-
#
|
246
|
-
#
|
245
|
+
# Converts a given key to the value that the database adapter returns as
|
246
|
+
# as a usable column name.
|
247
247
|
# users.id #=> users_id
|
248
248
|
# sum(id) #=> sum_id
|
249
249
|
# count(distinct users.id) #=> count_distinct_users_id
|
@@ -24,7 +24,7 @@ module ActiveRecord
|
|
24
24
|
when Float, Fixnum, Bignum then value.to_s
|
25
25
|
# BigDecimals need to be output in a non-normalized form and quoted.
|
26
26
|
when BigDecimal then value.to_s('F')
|
27
|
-
when Date then "'#{value.to_s}'"
|
27
|
+
when Date then "'#{value.to_s(:db)}'"
|
28
28
|
when Time, DateTime then "'#{quoted_date(value)}'"
|
29
29
|
else "'#{quote_string(value.to_yaml)}'"
|
30
30
|
end
|
@@ -68,7 +68,7 @@ module ActiveRecord
|
|
68
68
|
class SQLiteColumn < Column #:nodoc:
|
69
69
|
class << self
|
70
70
|
def string_to_binary(value)
|
71
|
-
value.gsub(/\0|\%/) do |b|
|
71
|
+
value.gsub(/\0|\%/n) do |b|
|
72
72
|
case b
|
73
73
|
when "\0" then "%00"
|
74
74
|
when "%" then "%25"
|
@@ -77,7 +77,7 @@ module ActiveRecord
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def binary_to_string(value)
|
80
|
-
value.gsub(/%00|%25/) do |b|
|
80
|
+
value.gsub(/%00|%25/n) do |b|
|
81
81
|
case b
|
82
82
|
when "%00" then "\0"
|
83
83
|
when "%25" then "%"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
class Base
|
3
3
|
class << self
|
4
|
-
# This method is deprecated in favor of find with the :conditions option.
|
4
|
+
# DEPRECATION NOTICE: This method is deprecated in favor of find with the :conditions option.
|
5
5
|
#
|
6
6
|
# Works like find, but the record matching +id+ must also meet the +conditions+.
|
7
7
|
# +RecordNotFound+ is raised if no record can be found matching the +id+ or meeting the condition.
|
@@ -12,7 +12,7 @@ module ActiveRecord
|
|
12
12
|
end
|
13
13
|
deprecate :find_on_conditions => "use find(ids, :conditions => conditions)"
|
14
14
|
|
15
|
-
# This method is deprecated in favor of find(:first, options).
|
15
|
+
# DEPRECATION NOTICE: This method is deprecated in favor of find(:first, options).
|
16
16
|
#
|
17
17
|
# Returns the object for the first record responding to the conditions in +conditions+,
|
18
18
|
# such as "group = 'master'". If more than one record is returned from the query, it's the first that'll
|
@@ -24,7 +24,7 @@ module ActiveRecord
|
|
24
24
|
end
|
25
25
|
deprecate :find_first => "use find(:first, ...)"
|
26
26
|
|
27
|
-
# This method is deprecated in favor of find(:all, options).
|
27
|
+
# DEPRECATION NOTICE: This method is deprecated in favor of find(:all, options).
|
28
28
|
#
|
29
29
|
# Returns an array of all the objects that could be instantiated from the associated
|
30
30
|
# table in the database. The +conditions+ can be used to narrow the selection of objects (WHERE-part),
|
@@ -412,7 +412,7 @@ class Fixture #:nodoc:
|
|
412
412
|
klass = @class_name.constantize rescue nil
|
413
413
|
|
414
414
|
list = @fixture.inject([]) do |fixtures, (key, value)|
|
415
|
-
col = klass.columns_hash[key] if klass.
|
415
|
+
col = klass.columns_hash[key] if klass.respond_to?(:ancestors) && klass.ancestors.include?(ActiveRecord::Base)
|
416
416
|
fixtures << ActiveRecord::Base.connection.quote(value, col).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r")
|
417
417
|
end
|
418
418
|
list * ', '
|
@@ -5,15 +5,6 @@ module ActiveRecord
|
|
5
5
|
# Timestamping can be turned off by setting
|
6
6
|
# <tt>ActiveRecord::Base.record_timestamps = false</tt>
|
7
7
|
#
|
8
|
-
# Keep in mind that, via inheritance, you can turn off timestamps on a per
|
9
|
-
# model basis by setting <tt>record_timestamps</tt> to false in the desired
|
10
|
-
# models.
|
11
|
-
#
|
12
|
-
# class Feed < ActiveRecord::Base
|
13
|
-
# self.record_timestamps = false
|
14
|
-
# # ...
|
15
|
-
# end
|
16
|
-
#
|
17
8
|
# Timestamps are in the local timezone by default but can use UTC by setting
|
18
9
|
# <tt>ActiveRecord::Base.default_timezone = :utc</tt>
|
19
10
|
module Timestamp
|
@@ -168,6 +168,12 @@ class EagerAssociationTest < Test::Unit::TestCase
|
|
168
168
|
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.title = 'magic forest'")
|
169
169
|
assert_equal 0, posts.size
|
170
170
|
end
|
171
|
+
|
172
|
+
def test_eager_count_performed_on_a_has_many_association_with_multi_table_conditional
|
173
|
+
author = authors(:david)
|
174
|
+
author_posts_without_comments = author.posts.select { |post| post.comments.blank? }
|
175
|
+
assert_equal author_posts_without_comments.size, author.posts.count(:all, :include => :comments, :conditions => 'comments.id is null')
|
176
|
+
end
|
171
177
|
|
172
178
|
def test_eager_with_has_and_belongs_to_many_and_limit
|
173
179
|
posts = Post.find(:all, :include => :categories, :order => "posts.id", :limit => 3)
|
@@ -271,6 +277,13 @@ class EagerAssociationTest < Test::Unit::TestCase
|
|
271
277
|
assert_not_nil f.account
|
272
278
|
assert_equal companies(:first_firm, :reload).account, f.account
|
273
279
|
end
|
280
|
+
|
281
|
+
def test_eager_with_multi_table_conditional_properly_counts_the_records_when_using_size
|
282
|
+
author = authors(:david)
|
283
|
+
posts_with_no_comments = author.posts.select { |post| post.comments.blank? }
|
284
|
+
assert_equal posts_with_no_comments.size, author.posts_with_no_comments.size
|
285
|
+
assert_equal posts_with_no_comments, author.posts_with_no_comments
|
286
|
+
end
|
274
287
|
|
275
288
|
def test_eager_with_invalid_association_reference
|
276
289
|
assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
@@ -29,7 +29,16 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
|
|
29
29
|
assert_equal 2, authors(:mary).categorized_posts.size
|
30
30
|
assert_equal 1, authors(:mary).unique_categorized_posts.size
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
|
+
def test_has_many_uniq_through_count
|
34
|
+
author = authors(:mary)
|
35
|
+
assert !authors(:mary).unique_categorized_posts.loaded?
|
36
|
+
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count }
|
37
|
+
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count(:title, {}) }
|
38
|
+
assert_queries(1) { assert_equal 0, author.unique_categorized_posts.count(:title, { :conditions => "title is NULL" }) }
|
39
|
+
assert !authors(:mary).unique_categorized_posts.loaded?
|
40
|
+
end
|
41
|
+
|
33
42
|
def test_polymorphic_has_many
|
34
43
|
assert posts(:welcome).taggings.include?(taggings(:welcome_general))
|
35
44
|
end
|