activerecord 1.14.0 → 1.14.1

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 CHANGED
@@ -1,3 +1,58 @@
1
+ *1.14.1* (April 6th, 2005)
2
+
3
+ * Fix type_name_with_module to handle type names that begin with '::'. Closes #4614. [Nicholas Seckar]
4
+
5
+ * Fixed that that multiparameter assignment doesn't work with aggregations (closes #4620) [Lars Pind]
6
+
7
+ * Enable Limit/Offset in Calculations (closes #4558) [lmarlow@yahoo.com]
8
+
9
+ * Fixed that loading including associations returns all results if Load IDs For Limited Eager Loading returns none (closes #4528) [Rick]
10
+
11
+ * Fixed HasManyAssociation#find bugs when :finder_sql is set #4600 [lagroue@free.fr]
12
+
13
+ * Allow AR::Base#respond_to? to behave when @attributes is nil [zenspider]
14
+
15
+ * Support eager includes when going through a polymorphic has_many association. [Rick]
16
+
17
+ * Added support for eagerly including polymorphic has_one associations. (closes #4525) [Rick]
18
+
19
+ class Post < ActiveRecord::Base
20
+ has_one :tagging, :as => :taggable
21
+ end
22
+
23
+ Post.find :all, :include => :tagging
24
+
25
+ * Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many [Rick]
26
+
27
+ * Added support for going through a polymorphic has_many association: (closes #4401) [Rick]
28
+
29
+ class PhotoCollection < ActiveRecord::Base
30
+ has_many :photos, :as => :photographic
31
+ belongs_to :firm
32
+ end
33
+
34
+ class Firm < ActiveRecord::Base
35
+ has_many :photo_collections
36
+ has_many :photos, :through => :photo_collections
37
+ end
38
+
39
+ * Multiple fixes and optimizations in PostgreSQL adapter, allowing ruby-postgres gem to work properly. [ruben.nine@gmail.com]
40
+
41
+ * Fixed that AssociationCollection#delete_all should work even if the records of the association are not loaded yet. [Florian Weber]
42
+
43
+ * Changed those private ActiveRecord methods to take optional third argument :auto instead of nil for performance optimizations. (closes #4456) [Stefan]
44
+
45
+ * Private ActiveRecord methods add_limit!, add_joins!, and add_conditions! take an OPTIONAL third argument 'scope' (closes #4456) [Rick]
46
+
47
+ * DEPRECATED: Using additional attributes on has_and_belongs_to_many associations. Instead upgrade your association to be a real join model [DHH]
48
+
49
+ * Fixed that records returned from has_and_belongs_to_many associations with additional attributes should be marked as read only (fixes #4512) [DHH]
50
+
51
+ * Do not implicitly mark recordss of has_many :through as readonly but do mark habtm records as readonly (eventually only on join tables without rich attributes). [Marcel Mollina Jr.]
52
+
53
+ * Fixed broken OCIAdapter #4457 [schoenm@earthlink.net]
54
+
55
+
1
56
  *1.14.0* (March 27th, 2005)
2
57
 
3
58
  * Replace 'rescue Object' with a finer grained rescue. Closes #4431. [Nicholas Seckar]
@@ -177,7 +232,7 @@
177
232
 
178
233
  ...and you can configure with:
179
234
 
180
- topic.to_xml(:skip_instruct => true, :skip_attributes => [ :id, bonus_time, :written_on, replies_count ])
235
+ topic.to_xml(:skip_instruct => true, :except => [ :id, bonus_time, :written_on, replies_count ])
181
236
 
182
237
  ...that'll return:
183
238
 
@@ -44,6 +44,18 @@ module ActiveRecord
44
44
  end
45
45
  end
46
46
 
47
+ class HasManyThroughSourceAssociationMacroError < ActiveRecordError #:nodoc
48
+ def initialize(reflection)
49
+ @reflection = reflection
50
+ @through_reflection = reflection.through_reflection
51
+ @source_reflection = reflection.source_reflection
52
+ end
53
+
54
+ def message
55
+ "Invalid source reflection macro :#{@source_reflection.macro}#{" :through" if @source_reflection.options[:through]} for has_many #{@reflection.name.inspect}, :through => #{@through_reflection.name.inspect}. Use :source to specify the source reflection."
56
+ end
57
+ end
58
+
47
59
  class EagerLoadPolymorphicError < ActiveRecordError #:nodoc:
48
60
  def initialize(reflection)
49
61
  @reflection = reflection
@@ -623,7 +635,7 @@ module ActiveRecord
623
635
  # is used on the associate class (such as a Post class). You can also specify a custom counter cache column by given that
624
636
  # name instead of a true/false value to this option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
625
637
  # * <tt>:include</tt> - specify second-order associations that should be eager loaded when this object is loaded.
626
- # # <tt>:polymorphic</tt> - specify this association is a polymorphic association by passing true.
638
+ # * <tt>:polymorphic</tt> - specify this association is a polymorphic association by passing true.
627
639
  #
628
640
  # Option examples:
629
641
  # belongs_to :firm, :foreign_key => "client_of"
@@ -698,11 +710,10 @@ module ActiveRecord
698
710
  # an option, it is guessed using the lexical order of the class names. So a join between Developer and Project
699
711
  # will give the default join table name of "developers_projects" because "D" outranks "P".
700
712
  #
701
- # Any additional fields added to the join table will be placed as attributes when pulling records out through
702
- # has_and_belongs_to_many associations. This is helpful when have information about the association itself
703
- # that you want available on retrieval. Note that any fields in the join table will override matching field names
704
- # in the two joined tables. As a consequence, having an "id" field in the join table usually has the undesirable
705
- # result of clobbering the "id" fields in either of the other two tables.
713
+ # Deprecated: Any additional fields added to the join table will be placed as attributes when pulling records out through
714
+ # has_and_belongs_to_many associations. Records returned from join tables with additional attributes will be marked as
715
+ # ReadOnly (because we can't save changes to the additional attrbutes). It's strongly recommended that you upgrade any
716
+ # associations with attributes to a real join model (see introduction).
706
717
  #
707
718
  # Adds the following methods for retrieval and query.
708
719
  # +collection+ is replaced with the symbol passed as the first argument, so
@@ -714,7 +725,7 @@ module ActiveRecord
714
725
  # * <tt>collection.push_with_attributes(object, join_attributes)</tt> - adds one to the collection by creating an association in the join table that
715
726
  # also holds the attributes from <tt>join_attributes</tt> (should be a hash with the column names as keys). This can be used to have additional
716
727
  # attributes on the join, which will be injected into the associated objects when they are retrieved through the collection.
717
- # (collection.concat_with_attributes is an alias to this method).
728
+ # (collection.concat_with_attributes is an alias to this method). This method is now deprecated.
718
729
  # * <tt>collection.delete(object, ...)</tt> - removes one or more objects from the collection by removing their associations from the join table.
719
730
  # This does not destroy the objects.
720
731
  # * <tt>collection=objects</tt> - replaces the collections content by deleting and adding objects as appropriate.
@@ -951,14 +962,20 @@ module ActiveRecord
951
962
  end
952
963
 
953
964
  def count_with_associations(options = {})
954
- join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
955
- return count_by_sql(construct_counter_sql_with_included_associations(options, join_dependency))
965
+ catch :invalid_query do
966
+ join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
967
+ return count_by_sql(construct_counter_sql_with_included_associations(options, join_dependency))
968
+ end
969
+ 0
956
970
  end
957
971
 
958
972
  def find_with_associations(options = {})
959
- join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
960
- rows = select_all_rows(options, join_dependency)
961
- return join_dependency.instantiate(rows)
973
+ catch :invalid_query do
974
+ join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
975
+ rows = select_all_rows(options, join_dependency)
976
+ return join_dependency.instantiate(rows)
977
+ end
978
+ []
962
979
  end
963
980
 
964
981
  def configure_dependency_for_has_many(reflection)
@@ -1140,6 +1157,8 @@ module ActiveRecord
1140
1157
  def add_limited_ids_condition!(sql, options, join_dependency)
1141
1158
  unless (id_list = select_limited_ids_list(options, join_dependency)).empty?
1142
1159
  sql << "#{condition_word(sql)} #{table_name}.#{primary_key} IN (#{id_list}) "
1160
+ else
1161
+ throw :invalid_query
1143
1162
  end
1144
1163
  end
1145
1164
 
@@ -1381,7 +1400,7 @@ module ActiveRecord
1381
1400
  @aliased_prefix = "t#{ join_dependency.joins.size }"
1382
1401
  @aliased_table_name = table_name # start with the table name
1383
1402
  @parent_table_name = parent.active_record.table_name
1384
-
1403
+
1385
1404
  if !parent.table_joins.blank? && parent.table_joins.to_s.downcase =~ %r{join(\s+\w+)?\s+#{aliased_table_name.downcase}\son}
1386
1405
  join_dependency.table_aliases[aliased_table_name] += 1
1387
1406
  end
@@ -1434,24 +1453,38 @@ module ActiveRecord
1434
1453
  aliased_table_name, primary_key, aliased_join_table_name, options[:foreign_key] || reflection.klass.to_s.classify.foreign_key
1435
1454
  ]
1436
1455
  else
1437
- case source_reflection.macro
1438
- when :belongs_to
1439
- first_key = primary_key
1440
- second_key = options[:foreign_key] || klass.to_s.classify.foreign_key
1441
- when :has_many
1442
- first_key = through_reflection.klass.to_s.classify.foreign_key
1443
- second_key = options[:foreign_key] || primary_key
1456
+ if source_reflection.macro == :has_many && source_reflection.options[:as]
1457
+ " LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [
1458
+ table_alias_for(through_reflection.klass.table_name, aliased_join_table_name), aliased_join_table_name,
1459
+ through_reflection.primary_key_name,
1460
+ parent.aliased_table_name, parent.primary_key] +
1461
+ " LEFT OUTER JOIN %s ON %s.%s = %s.%s AND %s.%s = %s " % [
1462
+ table_name_and_alias,
1463
+ aliased_table_name, "#{source_reflection.options[:as]}_id",
1464
+ aliased_join_table_name, options[:foreign_key] || primary_key,
1465
+ aliased_table_name, "#{source_reflection.options[:as]}_type",
1466
+ klass.quote(source_reflection.active_record.base_class.name)
1467
+ ]
1468
+ else
1469
+ case source_reflection.macro
1470
+ when :belongs_to
1471
+ first_key = primary_key
1472
+ second_key = options[:foreign_key] || klass.to_s.classify.foreign_key
1473
+ when :has_many
1474
+ first_key = through_reflection.klass.to_s.classify.foreign_key
1475
+ second_key = options[:foreign_key] || primary_key
1476
+ end
1477
+
1478
+ " LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [
1479
+ table_alias_for(through_reflection.klass.table_name, aliased_join_table_name), aliased_join_table_name,
1480
+ through_reflection.primary_key_name,
1481
+ parent.aliased_table_name, parent.primary_key] +
1482
+ " LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [
1483
+ table_name_and_alias,
1484
+ aliased_table_name, first_key,
1485
+ aliased_join_table_name, second_key
1486
+ ]
1444
1487
  end
1445
-
1446
- " LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [
1447
- table_alias_for(through_reflection.klass.table_name, aliased_join_table_name), aliased_join_table_name,
1448
- through_reflection.primary_key_name,
1449
- parent.aliased_table_name, parent.primary_key] +
1450
- " LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [
1451
- table_name_and_alias,
1452
- aliased_table_name, first_key,
1453
- aliased_join_table_name, second_key
1454
- ]
1455
1488
  end
1456
1489
 
1457
1490
  when reflection.macro == :has_many && reflection.options[:as]
@@ -1462,12 +1495,16 @@ module ActiveRecord
1462
1495
  aliased_table_name, "#{reflection.options[:as]}_type",
1463
1496
  klass.quote(parent.active_record.base_class.name)
1464
1497
  ]
1465
-
1498
+ when reflection.macro == :has_one && reflection.options[:as]
1499
+ " LEFT OUTER JOIN %s ON %s.%s = %s.%s AND %s.%s = %s " % [
1500
+ table_name_and_alias,
1501
+ aliased_table_name, "#{reflection.options[:as]}_id",
1502
+ parent.aliased_table_name, parent.primary_key,
1503
+ aliased_table_name, "#{reflection.options[:as]}_type",
1504
+ klass.quote(reflection.active_record.base_class.name)
1505
+ ]
1466
1506
  else
1467
- foreign_key = options[:foreign_key] || case reflection.macro
1468
- when :has_many then reflection.active_record.to_s.classify
1469
- when :has_one then reflection.active_record.to_s
1470
- end.foreign_key
1507
+ foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
1471
1508
  " LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [
1472
1509
  table_name_and_alias,
1473
1510
  aliased_table_name, foreign_key,
@@ -37,6 +37,7 @@ module ActiveRecord
37
37
 
38
38
  # Remove all records from this association
39
39
  def delete_all
40
+ load_target
40
41
  delete(@target)
41
42
  @target = []
42
43
  end
@@ -40,8 +40,8 @@ module ActiveRecord
40
40
  end
41
41
 
42
42
  options[:conditions] = conditions
43
- options[:joins] = @join_sql
44
- options[:readonly] ||= false
43
+ options[:joins] = @join_sql
44
+ options[:readonly] = finding_with_ambigious_select?(options[:select])
45
45
 
46
46
  if options[:order] && @reflection.options[:order]
47
47
  options[:order] = "#{options[:order]}, #{@reflection.options[:order]}"
@@ -157,7 +157,13 @@ module ActiveRecord
157
157
 
158
158
  @join_sql = "INNER JOIN #{@reflection.options[:join_table]} ON #{@reflection.klass.table_name}.#{@reflection.klass.primary_key} = #{@reflection.options[:join_table]}.#{@reflection.association_foreign_key}"
159
159
  end
160
-
160
+
161
+ # Join tables with additional columns on top of the two foreign keys must be considered ambigious unless a select
162
+ # clause has been explicitly defined. Otherwise you can get broken records back, if, say, the join column also has
163
+ # and id column, which will then overwrite the id column of the records coming back.
164
+ def finding_with_ambigious_select?(select_clause)
165
+ !select_clause && @owner.connection.columns(@reflection.options[:join_table], "Join Table Columns").size != 2
166
+ end
161
167
  end
162
168
  end
163
169
  end
@@ -59,7 +59,7 @@ module ActiveRecord
59
59
  if ids.size == 1
60
60
  id = ids.first
61
61
  record = load_target.detect { |record| id == record.id }
62
- expects_array? ? [record] : record
62
+ expects_array ? [ record ] : record
63
63
  else
64
64
  load_target.select { |record| ids.include?(record.id) }
65
65
  end
@@ -63,17 +63,12 @@ module ActiveRecord
63
63
  )
64
64
  end
65
65
 
66
- def construct_conditions
66
+ def construct_conditions
67
67
  conditions = if @reflection.through_reflection.options[:as]
68
68
  "#{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.options[:as]}_id = #{@owner.quoted_id} " +
69
69
  "AND #{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.options[:as]}_type = #{@owner.class.quote @owner.class.base_class.name.to_s}"
70
70
  else
71
- case @reflection.source_reflection.macro
72
- when :belongs_to, :has_many
73
- "#{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.primary_key_name} = #{@owner.quoted_id}"
74
- else
75
- raise ActiveRecordError, "Invalid source reflection macro :#{@reflection.source_reflection.macro} for has_many #{@reflection.name}, :through => #{@reflection.through_reflection.name}. Use :source to specify the source reflection."
76
- end
71
+ "#{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.primary_key_name} = #{@owner.quoted_id}"
77
72
  end
78
73
  conditions << " AND (#{sql_conditions})" if sql_conditions
79
74
 
@@ -88,19 +83,27 @@ module ActiveRecord
88
83
  selected = custom_select || @reflection.options[:select] || "#{@reflection.table_name}.*"
89
84
  end
90
85
 
91
- def construct_joins(custom_joins = nil)
86
+ def construct_joins(custom_joins = nil)
87
+ polymorphic_join = nil
92
88
  if @reflection.through_reflection.options[:as] || @reflection.source_reflection.macro == :belongs_to
93
89
  reflection_primary_key = @reflection.klass.primary_key
94
90
  source_primary_key = @reflection.source_reflection.primary_key_name
95
91
  else
96
92
  reflection_primary_key = @reflection.source_reflection.primary_key_name
97
93
  source_primary_key = @reflection.klass.primary_key
94
+ if @reflection.source_reflection.options[:as]
95
+ polymorphic_join = "AND %s.%s = %s" % [
96
+ @reflection.table_name, "#{@reflection.source_reflection.options[:as]}_type",
97
+ @owner.class.quote(@reflection.through_reflection.klass.name)
98
+ ]
99
+ end
98
100
  end
99
101
 
100
- "INNER JOIN %s ON %s.%s = %s.%s #{@reflection.options[:joins]} #{custom_joins}" % [
102
+ "INNER JOIN %s ON %s.%s = %s.%s %s #{@reflection.options[:joins]} #{custom_joins}" % [
101
103
  @reflection.through_reflection.table_name,
102
104
  @reflection.table_name, reflection_primary_key,
103
- @reflection.through_reflection.table_name, source_primary_key
105
+ @reflection.through_reflection.table_name, source_primary_key,
106
+ polymorphic_join
104
107
  ]
105
108
  end
106
109
 
@@ -850,7 +850,7 @@ module ActiveRecord #:nodoc:
850
850
  (hash[method].keys + params.keys).uniq.each do |key|
851
851
  merge = hash[method][key] && params[key] # merge if both scopes have the same key
852
852
  if key == :conditions && merge
853
- hash[method][key] = [params[key], hash[method][key]].collect{|sql| "( %s )" % sanitize_sql(sql)}.join(" AND ")
853
+ hash[method][key] = [params[key], hash[method][key]].collect{ |sql| "( %s )" % sanitize_sql(sql) }.join(" AND ")
854
854
  elsif key == :include && merge
855
855
  hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
856
856
  else
@@ -871,7 +871,7 @@ module ActiveRecord #:nodoc:
871
871
 
872
872
  begin
873
873
  yield
874
- ensure
874
+ ensure
875
875
  self.scoped_methods.pop
876
876
  end
877
877
  end
@@ -947,7 +947,7 @@ module ActiveRecord #:nodoc:
947
947
 
948
948
  def find_one(id, options)
949
949
  conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
950
- options = options.merge :conditions => "#{table_name}.#{primary_key} = #{sanitize(id)}#{conditions}"
950
+ options.update :conditions => "#{table_name}.#{primary_key} = #{sanitize(id)}#{conditions}"
951
951
 
952
952
  if result = find_initial(options)
953
953
  result
@@ -959,7 +959,7 @@ module ActiveRecord #:nodoc:
959
959
  def find_some(ids, options)
960
960
  conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
961
961
  ids_list = ids.map { |id| sanitize(id) }.join(',')
962
- options = options.merge :conditions => "#{table_name}.#{primary_key} IN (#{ids_list})#{conditions}"
962
+ options.update :conditions => "#{table_name}.#{primary_key} IN (#{ids_list})#{conditions}"
963
963
 
964
964
  result = find_every(options)
965
965
 
@@ -1000,7 +1000,7 @@ module ActiveRecord #:nodoc:
1000
1000
  # Nest the type name in the same module as this class.
1001
1001
  # Bar is "MyApp::Business::Bar" relative to MyApp::Business::Foo
1002
1002
  def type_name_with_module(type_name)
1003
- "#{self.name.sub(/(::)?[^:]+$/, '')}#{$1}#{type_name}"
1003
+ (/^::/ =~ type_name) ? type_name : "#{parent.name}::#{type_name}"
1004
1004
  end
1005
1005
 
1006
1006
  def construct_finder_sql(options)
@@ -1036,7 +1036,9 @@ module ActiveRecord #:nodoc:
1036
1036
  end
1037
1037
  end
1038
1038
 
1039
- def add_limit!(sql, options, scope)
1039
+ # The optional scope argument is for the current :find scope.
1040
+ def add_limit!(sql, options, scope = :auto)
1041
+ scope = scope(:find) if :auto == scope
1040
1042
  if scope
1041
1043
  options[:limit] ||= scope[:limit]
1042
1044
  options[:offset] ||= scope[:offset]
@@ -1044,13 +1046,17 @@ module ActiveRecord #:nodoc:
1044
1046
  connection.add_limit_offset!(sql, options)
1045
1047
  end
1046
1048
 
1047
- def add_joins!(sql, options, scope)
1049
+ # The optional scope argument is for the current :find scope.
1050
+ def add_joins!(sql, options, scope = :auto)
1051
+ scope = scope(:find) if :auto == scope
1048
1052
  join = (scope && scope[:joins]) || options[:joins]
1049
1053
  sql << " #{join} " if join
1050
1054
  end
1051
1055
 
1052
1056
  # Adds a sanitized version of +conditions+ to the +sql+ string. Note that the passed-in +sql+ string is changed.
1053
- def add_conditions!(sql, conditions, scope)
1057
+ # The optional scope argument is for the current :find scope.
1058
+ def add_conditions!(sql, conditions, scope = :auto)
1059
+ scope = scope(:find) if :auto == scope
1054
1060
  segments = []
1055
1061
  segments << sanitize_sql(scope[:conditions]) if scope && scope[:conditions]
1056
1062
  segments << sanitize_sql(conditions) unless conditions.nil?
@@ -1237,8 +1243,6 @@ module ActiveRecord #:nodoc:
1237
1243
  begin
1238
1244
  instance_eval(modularized_name)
1239
1245
  rescue NameError => e
1240
- first_module = modularized_name.split("::").first
1241
- raise unless e.to_s.include? first_module
1242
1246
  instance_eval(type_name)
1243
1247
  end
1244
1248
  end
@@ -1325,7 +1329,7 @@ module ActiveRecord #:nodoc:
1325
1329
  unless options.has_key?(:readonly)
1326
1330
  if scoped?(:find, :readonly)
1327
1331
  options[:readonly] = scope(:find, :readonly)
1328
- elsif !options[:joins].blank?
1332
+ elsif !options[:joins].blank? && !options[:select]
1329
1333
  options[:readonly] = true
1330
1334
  end
1331
1335
  end
@@ -1583,7 +1587,9 @@ module ActiveRecord #:nodoc:
1583
1587
  # A Person object with a name attribute can ask person.respond_to?("name"), person.respond_to?("name="), and
1584
1588
  # person.respond_to?("name?") which will all return true.
1585
1589
  def respond_to?(method, include_priv = false)
1586
- if attr_name = self.class.column_methods_hash[method.to_sym]
1590
+ if @attributes.nil?
1591
+ return super
1592
+ elsif attr_name = self.class.column_methods_hash[method.to_sym]
1587
1593
  return true if @attributes.include?(attr_name) || attr_name == self.class.primary_key
1588
1594
  return false if self.class.read_methods.include?(attr_name)
1589
1595
  elsif @attributes.include?(method_name = method.to_s)
@@ -1636,10 +1642,10 @@ module ActiveRecord #:nodoc:
1636
1642
  # <last-read type="date">2004-04-15</last-read>
1637
1643
  # </topic>
1638
1644
  #
1639
- # This behaviour can be controlled with :skip_attributes and :skip_instruct
1645
+ # This behaviour can be controlled with :only, :except, and :skip_instruct
1640
1646
  # for instance:
1641
1647
  #
1642
- # topic.to_xml(:skip_instruct => true, :skip_attributes => [ :id, bonus_time, :written_on, replies_count ])
1648
+ # topic.to_xml(:skip_instruct => true, :except => [ :id, bonus_time, :written_on, replies_count ])
1643
1649
  #
1644
1650
  # <topic>
1645
1651
  # <title>The First Topic</title>
@@ -1982,7 +1988,7 @@ module ActiveRecord #:nodoc:
1982
1988
  def execute_callstack_for_multiparameter_attributes(callstack)
1983
1989
  errors = []
1984
1990
  callstack.each do |name, values|
1985
- klass = (self.class.reflect_on_aggregation(name) || column_for_attribute(name)).klass
1991
+ klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
1986
1992
  if values.empty?
1987
1993
  send(name + "=", nil)
1988
1994
  else
@@ -2064,4 +2070,4 @@ module ActiveRecord #:nodoc:
2064
2070
  value
2065
2071
  end
2066
2072
  end
2067
- end
2073
+ end
@@ -1,6 +1,6 @@
1
1
  module ActiveRecord
2
2
  module Calculations #:nodoc:
3
- CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct]
3
+ CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset]
4
4
  def self.included(base)
5
5
  base.extend(ClassMethods)
6
6
  end
@@ -153,6 +153,7 @@ module ActiveRecord
153
153
  sql << " GROUP BY #{options[:group_field]}" if options[:group]
154
154
  sql << " HAVING #{options[:having]}" if options[:group] && options[:having]
155
155
  sql << " ORDER BY #{options[:order]}" if options[:order]
156
+ add_limit!(sql, options)
156
157
  sql.join
157
158
  end
158
159
 
@@ -44,15 +44,11 @@ begin
44
44
  # Enable the id column to be bound into the sql later, by the adapter's insert method.
45
45
  # This is preferable to inserting the hard-coded value here, because the insert method
46
46
  # needs to know the id value explicitly.
47
- def attributes_with_quotes_pre_oracle #:nodoc:
48
- attributes_with_quotes
49
- end
50
-
51
-
52
- def attributes_with_quotes(creating = true) #:nodoc:
53
- aq = attributes_with_quotes_pre_oracle creating
47
+ alias :attributes_with_quotes_pre_oracle :attributes_with_quotes
48
+ def attributes_with_quotes(include_primary_key = true) #:nodoc:
49
+ aq = attributes_with_quotes_pre_oracle(include_primary_key)
54
50
  if connection.class == ConnectionAdapters::OracleAdapter
55
- aq[self.class.primary_key] = ":id" if creating && aq[self.class.primary_key].nil?
51
+ aq[self.class.primary_key] = ":id" if include_primary_key && aq[self.class.primary_key].nil?
56
52
  end
57
53
  aq
58
54
  end
@@ -214,15 +210,17 @@ begin
214
210
  end
215
211
 
216
212
  def quote(value, column = nil) #:nodoc:
217
- if column && column.type == :binary then %Q{empty_#{ column.sql_type }()}
218
- else case value
219
- when String then %Q{'#{quote_string(value)}'}
220
- when NilClass then 'null'
221
- when TrueClass then '1'
222
- when FalseClass then '0'
223
- when Numeric then value.to_s
224
- when Date, Time then %Q{'#{value.strftime("%Y-%m-%d %H:%M:%S")}'}
225
- else %Q{'#{quote_string(value.to_yaml)}'}
213
+ if column && column.type == :binary
214
+ %Q{empty_#{ column.sql_type rescue 'blob' }()}
215
+ else
216
+ case value
217
+ when String : %Q{'#{quote_string(value)}'}
218
+ when NilClass : 'null'
219
+ when TrueClass : '1'
220
+ when FalseClass : '0'
221
+ when Numeric : value.to_s
222
+ when Date, Time : %Q{'#{value.strftime("%Y-%m-%d %H:%M:%S")}'}
223
+ else %Q{'#{quote_string(value.to_yaml)}'}
226
224
  end
227
225
  end
228
226
  end
@@ -360,7 +358,7 @@ begin
360
358
  end
361
359
 
362
360
  def columns(table_name, name = nil) #:nodoc:
363
- table_info = @connection.object_info(table_name)
361
+ (owner, table_name) = @connection.describe(table_name)
364
362
 
365
363
  table_cols = %Q{
366
364
  select column_name, data_type, data_default, nullable,
@@ -369,13 +367,16 @@ begin
369
367
  null) as length,
370
368
  decode(data_type, 'NUMBER', data_scale, null) as scale
371
369
  from all_tab_columns
372
- where owner = '#{table_info.schema}'
373
- and table_name = '#{table_info.name}'
370
+ where owner = '#{owner}'
371
+ and table_name = '#{table_name}'
374
372
  order by column_id
375
373
  }
376
374
 
377
375
  select_all(table_cols, name).map do |row|
378
- row['data_default'].sub!(/^'(.*)'\s*$/, '\1') if row['data_default']
376
+ if row['data_default']
377
+ row['data_default'].sub!(/^(.*?)\s*$/, '\1')
378
+ row['data_default'].sub!(/^'(.*)'$/, '\1')
379
+ end
379
380
  OracleColumn.new(
380
381
  oracle_downcase(row['column_name']),
381
382
  row['data_default'],
@@ -389,7 +390,7 @@ begin
389
390
 
390
391
  def create_table(name, options = {}) #:nodoc:
391
392
  super(name, options)
392
- execute "CREATE SEQUENCE #{name}_seq" unless options[:id] == false
393
+ execute "CREATE SEQUENCE #{name}_seq START WITH 10000" unless options[:id] == false
393
394
  end
394
395
 
395
396
  def rename_table(name, new_name) #:nodoc:
@@ -469,7 +470,7 @@ begin
469
470
  private
470
471
 
471
472
  def select(sql, name = nil)
472
- cursor = log(sql, name) { @connection.exec sql }
473
+ cursor = execute(sql, name)
473
474
  cols = cursor.get_col_names.map { |x| oracle_downcase(x) }
474
475
  rows = []
475
476
 
@@ -535,30 +536,27 @@ begin
535
536
  # missing constant from oci8 < 0.1.14
536
537
  OCI_PTYPE_UNK = 0 unless defined?(OCI_PTYPE_UNK)
537
538
 
538
- def object_info(name)
539
- OraObject.new describe(name.to_s, OCI_PTYPE_UNK)
540
- end
541
-
542
- def describe(name, type)
539
+ # Uses the describeAny OCI call to find the target owner and table_name
540
+ # indicated by +name+, parsing through synonynms as necessary. Returns
541
+ # an array of [owner, table_name].
542
+ def describe(name)
543
543
  @desc ||= @@env.alloc(OCIDescribe)
544
544
  @desc.attrSet(OCI_ATTR_DESC_PUBLIC, -1) if VERSION >= '0.1.14'
545
- @desc.describeAny(@svc, name, type)
546
- @desc.attrGet(OCI_ATTR_PARAM)
547
- end
548
-
549
- class OraObject #:nodoc:
550
- attr_reader :schema, :name
551
- def initialize(info)
552
- case info.attrGet(OCI_ATTR_PTYPE)
553
- when OCI_PTYPE_TABLE, OCI_PTYPE_VIEW
554
- @schema = info.attrGet(OCI_ATTR_OBJ_SCHEMA)
555
- @name = info.attrGet(OCI_ATTR_OBJ_NAME)
556
- when OCI_PTYPE_SYN
557
- @schema = info.attrGet(OCI_ATTR_SCHEMA_NAME)
558
- @name = info.attrGet(OCI_ATTR_NAME)
559
- end
545
+ @desc.describeAny(@svc, name.to_s, OCI_PTYPE_UNK)
546
+ info = @desc.attrGet(OCI_ATTR_PARAM)
547
+
548
+ case info.attrGet(OCI_ATTR_PTYPE)
549
+ when OCI_PTYPE_TABLE, OCI_PTYPE_VIEW
550
+ owner = info.attrGet(OCI_ATTR_OBJ_SCHEMA)
551
+ table_name = info.attrGet(OCI_ATTR_OBJ_NAME)
552
+ [owner, table_name]
553
+ when OCI_PTYPE_SYN
554
+ schema = info.attrGet(OCI_ATTR_SCHEMA_NAME)
555
+ name = info.attrGet(OCI_ATTR_NAME)
556
+ describe(schema + '.' + name)
560
557
  end
561
558
  end
559
+
562
560
  end
563
561
 
564
562
 
@@ -24,6 +24,8 @@ module ActiveRecord
24
24
  PGconn.connect(host, port, "", "", database, username, password), logger, config
25
25
  )
26
26
 
27
+ PGconn.translate_results = false if PGconn.respond_to? :translate_results=
28
+
27
29
  pga.schema_search_path = config[:schema_search_path] || config[:schema_order]
28
30
 
29
31
  pga
@@ -63,8 +65,9 @@ module ActiveRecord
63
65
  @connection.query 'SELECT 1'
64
66
  true
65
67
  end
66
- rescue PGError
67
- false
68
+ # postgres-pr raises a NoMethodError when querying if no conn is available
69
+ rescue PGError, NoMethodError
70
+ false
68
71
  end
69
72
 
70
73
  # Close then reopen the connection.
@@ -339,6 +342,8 @@ module ActiveRecord
339
342
 
340
343
  private
341
344
  BYTEA_COLUMN_TYPE_OID = 17
345
+ TIMESTAMPOID = 1114
346
+ TIMESTAMPTZOID = 1184
342
347
 
343
348
  def configure_connection
344
349
  if @config[:encoding]
@@ -355,7 +360,7 @@ module ActiveRecord
355
360
 
356
361
  def select(sql, name = nil)
357
362
  res = execute(sql, name)
358
- results = res.result
363
+ results = res.result
359
364
  rows = []
360
365
  if results.length > 0
361
366
  fields = res.fields
@@ -363,9 +368,14 @@ module ActiveRecord
363
368
  hashed_row = {}
364
369
  row.each_index do |cel_index|
365
370
  column = row[cel_index]
366
- if res.type(cel_index) == BYTEA_COLUMN_TYPE_OID
367
- column = unescape_bytea(column)
371
+
372
+ case res.type(cel_index)
373
+ when BYTEA_COLUMN_TYPE_OID
374
+ column = unescape_bytea(column)
375
+ when TIMESTAMPTZOID, TIMESTAMPOID
376
+ column = cast_to_time(column)
368
377
  end
378
+
369
379
  hashed_row[fields[cel_index]] = column
370
380
  end
371
381
  rows << hashed_row
@@ -472,8 +482,8 @@ module ActiveRecord
472
482
  return "t" if value =~ /true/i
473
483
  return "f" if value =~ /false/i
474
484
 
475
- # Char/String type values
476
- return $1 if value =~ /^'(.*)'::(bpchar|text|character varying)$/
485
+ # Char/String/Bytea type values
486
+ return $1 if value =~ /^'(.*)'::(bpchar|text|character varying|bytea)$/
477
487
 
478
488
  # Numeric values
479
489
  return value if value =~ /^-?[0-9]+(\.[0-9]*)?/
@@ -485,6 +495,14 @@ module ActiveRecord
485
495
  # and we can't know the value of that, so return nil.
486
496
  return nil
487
497
  end
498
+
499
+ # Only needed for DateTime instances
500
+ def cast_to_time(value)
501
+ return value unless value.class == DateTime
502
+ v = value
503
+ time_array = [v.year, v.month, v.day, v.hour, v.min, v.sec]
504
+ Time.send(Base.default_timezone, *time_array) rescue nil
505
+ end
488
506
  end
489
507
  end
490
508
  end
@@ -61,6 +61,7 @@ module ActiveRecord
61
61
  # that can then add columns to it, following the same format as add_column. See example above. The options hash is for
62
62
  # fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create table definition.
63
63
  # * <tt>drop_table(name)</tt>: Drops the table called +name+.
64
+ # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+ to +new_name+.
64
65
  # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column to the table called +table_name+
65
66
  # named +column_name+ specified to be one of the following types:
66
67
  # :string, :text, :integer, :float, :datetime, :timestamp, :time, :date, :binary, :boolean. A default value can be specified
@@ -97,7 +97,7 @@ module ActiveRecord
97
97
  # Holds all the meta-data about an aggregation as it was specified in the Active Record class.
98
98
  class AggregateReflection < MacroReflection #:nodoc:
99
99
  def klass
100
- @klass ||= Object.const_get(class_name)
100
+ @klass ||= Object.const_get(options[:class_name] || class_name)
101
101
  end
102
102
 
103
103
  private
@@ -174,7 +174,11 @@ module ActiveRecord
174
174
  end
175
175
 
176
176
  if source_reflection.options[:polymorphic]
177
- raise HasManyThroughAssociationPolymorphicError.new(class_name, @reflection, source_reflection)
177
+ raise HasManyThroughAssociationPolymorphicError.new(class_name, self, source_reflection)
178
+ end
179
+
180
+ unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
181
+ raise HasManyThroughSourceAssociationMacroError.new(self)
178
182
  end
179
183
  end
180
184
  end
@@ -277,8 +277,8 @@ module ActiveRecord
277
277
  # Validates each attribute against a block.
278
278
  #
279
279
  # class Person < ActiveRecord::Base
280
- # validates_each :first_name, :last_name do |record, attr|
281
- # record.errors.add attr, 'starts with z.' if attr[0] == ?z
280
+ # validates_each :first_name, :last_name do |record, attr, value|
281
+ # record.errors.add attr, 'starts with z.' if value[0] == ?z
282
282
  # end
283
283
  # end
284
284
  #
@@ -2,7 +2,7 @@ module ActiveRecord
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
4
  MINOR = 14
5
- TINY = 0
5
+ TINY = 1
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
data/rakefile CHANGED
@@ -106,7 +106,7 @@ spec = Gem::Specification.new do |s|
106
106
  s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
107
107
  end
108
108
 
109
- s.add_dependency('activesupport', '= 1.3.0' + PKG_BUILD)
109
+ s.add_dependency('activesupport', '= 1.3.1' + PKG_BUILD)
110
110
 
111
111
  s.files.delete "test/fixtures/fixture_database.sqlite"
112
112
  s.files.delete "test/fixtures/fixture_database_2.sqlite"
@@ -42,7 +42,7 @@ class AdapterTest < Test::Unit::TestCase
42
42
 
43
43
  def test_current_database
44
44
  if @connection.respond_to?(:current_database)
45
- assert_equal "activerecord_unittest", @connection.current_database
45
+ assert_equal ENV['ARUNIT_DB_NAME'] || "activerecord_unittest", @connection.current_database
46
46
  end
47
47
  end
48
48
 
@@ -138,6 +138,16 @@ class EagerAssociationTest < Test::Unit::TestCase
138
138
  assert_equal count, posts.size
139
139
  end
140
140
 
141
+ def test_eager_with_has_many_and_limit_ond_high_offset
142
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
143
+ assert_equal 0, posts.size
144
+ end
145
+
146
+ def test_count_eager_with_has_many_and_limit_ond_high_offset
147
+ posts = Post.count(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
148
+ assert_equal 0, posts
149
+ end
150
+
141
151
  def test_eager_with_has_many_and_limit_with_no_results
142
152
  posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.title = 'magic forest'")
143
153
  assert_equal 0, posts.size
@@ -208,6 +208,14 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
208
208
  end
209
209
  end
210
210
 
211
+ def test_include_polymorphic_has_one
212
+ post = Post.find_by_id(posts(:welcome).id, :include => :tagging)
213
+ tagging = taggings(:welcome_general)
214
+ assert_no_queries do
215
+ assert_equal tagging, post.tagging
216
+ end
217
+ end
218
+
211
219
  def test_include_polymorphic_has_many_through
212
220
  posts = Post.find(:all, :order => 'posts.id')
213
221
  posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id')
@@ -295,6 +303,30 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
295
303
  assert_equal comments(:more_greetings), authors(:david).comments.find(2)
296
304
  end
297
305
 
306
+ def test_has_many_through_polymorphic_has_one
307
+ assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tagging }
308
+ end
309
+
310
+ def test_has_many_through_polymorphic_has_many
311
+ assert_equal [taggings(:welcome_general), taggings(:thinking_general)], authors(:david).taggings.uniq.sort_by { |t| t.id }
312
+ end
313
+
314
+ def test_include_has_many_through_polymorphic_has_many
315
+ author = Author.find_by_id(authors(:david).id, :include => :taggings)
316
+ expected_taggings = [taggings(:welcome_general), taggings(:thinking_general)]
317
+ assert_no_queries do
318
+ assert_equal expected_taggings, author.taggings.uniq.sort_by { |t| t.id }
319
+ end
320
+ end
321
+
322
+ def test_has_many_through_has_many_through
323
+ assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tags }
324
+ end
325
+
326
+ def test_has_many_through_habtm
327
+ assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).post_categories }
328
+ end
329
+
298
330
  def test_eager_load_has_many_through_has_many
299
331
  author = Author.find :first, :conditions => ['name = ?', 'David'], :include => :comments, :order => 'comments.id'
300
332
  SpecialComment.new; VerySpecialComment.new
@@ -7,6 +7,7 @@ require 'fixtures/reply'
7
7
  require 'fixtures/computer'
8
8
  require 'fixtures/customer'
9
9
  require 'fixtures/order'
10
+ require 'fixtures/category'
10
11
  require 'fixtures/post'
11
12
  require 'fixtures/author'
12
13
 
@@ -549,6 +550,25 @@ class HasManyAssociationsTest < Test::Unit::TestCase
549
550
  assert_equal 0, companies(:first_firm).clients_of_firm.size
550
551
  assert_equal 0, companies(:first_firm).clients_of_firm(true).size
551
552
  end
553
+
554
+ def test_delete_all
555
+ force_signal37_to_load_all_clients_of_firm
556
+ companies(:first_firm).clients_of_firm.create("name" => "Another Client")
557
+ assert_equal 2, companies(:first_firm).clients_of_firm.size
558
+ companies(:first_firm).clients_of_firm.delete_all
559
+ assert_equal 0, companies(:first_firm).clients_of_firm.size
560
+ assert_equal 0, companies(:first_firm).clients_of_firm(true).size
561
+ end
562
+
563
+ def test_delete_all_with_not_yet_loaded_association_collection
564
+ force_signal37_to_load_all_clients_of_firm
565
+ companies(:first_firm).clients_of_firm.create("name" => "Another Client")
566
+ assert_equal 2, companies(:first_firm).clients_of_firm.size
567
+ companies(:first_firm).clients_of_firm.reset
568
+ companies(:first_firm).clients_of_firm.delete_all
569
+ assert_equal 0, companies(:first_firm).clients_of_firm.size
570
+ assert_equal 0, companies(:first_firm).clients_of_firm(true).size
571
+ end
552
572
 
553
573
  def test_clearing_an_association_collection
554
574
  firm = companies(:first_firm)
@@ -1080,7 +1100,7 @@ end
1080
1100
 
1081
1101
 
1082
1102
  class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1083
- fixtures :accounts, :companies, :developers, :projects, :developers_projects
1103
+ fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects
1084
1104
 
1085
1105
  def test_has_and_belongs_to_many
1086
1106
  david = Developer.find(1)
@@ -1469,6 +1489,25 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1469
1489
  AND developer_id = #{developer.id}
1470
1490
  end_sql
1471
1491
  end
1492
+
1493
+ def test_updating_attributes_on_non_rich_associations
1494
+ welcome = categories(:technology).posts.first
1495
+ welcome.title = "Something else"
1496
+ assert welcome.save!
1497
+ end
1498
+
1499
+ def test_updating_attributes_on_rich_associations
1500
+ david = projects(:action_controller).developers.first
1501
+ david.name = "DHH"
1502
+ assert_raises(ActiveRecord::ReadOnlyRecord) { david.save! }
1503
+ end
1504
+
1505
+
1506
+ def test_updating_attributes_on_rich_associations_with_limited_find
1507
+ david = projects(:action_controller).developers.find(:all, :select => "developers.*").first
1508
+ david.name = "DHH"
1509
+ assert david.save!
1510
+ end
1472
1511
 
1473
1512
  def test_join_table_alias
1474
1513
  assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL').size
@@ -2,6 +2,7 @@ require 'abstract_unit'
2
2
  require 'fixtures/topic'
3
3
  require 'fixtures/reply'
4
4
  require 'fixtures/company'
5
+ require 'fixtures/customer'
5
6
  require 'fixtures/developer'
6
7
  require 'fixtures/project'
7
8
  require 'fixtures/default'
@@ -721,6 +722,14 @@ class BasicsTest < Test::Unit::TestCase
721
722
  task.attributes = attributes
722
723
  assert_equal time, task.starting
723
724
  end
725
+
726
+ def test_multiparameter_assignment_of_aggregation
727
+ customer = Customer.new
728
+ address = Address.new("The Street", "The City", "The Country")
729
+ attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
730
+ customer.attributes = attributes
731
+ assert_equal address, customer.address
732
+ end
724
733
 
725
734
  def test_attributes_on_dummy_time
726
735
  # Oracle and SQL Server do not have a TIME datatype.
@@ -1257,6 +1266,11 @@ class BasicsTest < Test::Unit::TestCase
1257
1266
  assert_equal(%w( title ), topics(:first).attributes(:only => :title).keys)
1258
1267
  assert_equal(%w( title author_name type id approved ), topics(:first).attributes(:only => [ :title, :id, :type, :approved, :author_name ]).keys)
1259
1268
  end
1269
+
1270
+ def test_type_name_with_module_should_handle_beginning
1271
+ assert_equal 'ActiveRecord::Person', ActiveRecord::Base.send(:type_name_with_module, 'Person')
1272
+ assert_equal '::Person', ActiveRecord::Base.send(:type_name_with_module, '::Person')
1273
+ end
1260
1274
 
1261
1275
  # FIXME: this test ought to run, but it needs to run sandboxed so that it
1262
1276
  # doesn't b0rk the current test environment by undefing everything.
@@ -48,6 +48,18 @@ class CalculationsTest < Test::Unit::TestCase
48
48
  assert_equal [6, 2, 1], c.keys.compact
49
49
  end
50
50
 
51
+ def test_should_limit_calculation
52
+ c = Account.sum(:credit_limit, :conditions => "firm_id IS NOT NULL",
53
+ :group => :firm_id, :order => "firm_id", :limit => 2)
54
+ assert_equal [1, 2], c.keys.compact
55
+ end
56
+
57
+ def test_should_limit_calculation_with_offset
58
+ c = Account.sum(:credit_limit, :conditions => "firm_id IS NOT NULL",
59
+ :group => :firm_id, :order => "firm_id", :limit => 2, :offset => 1)
60
+ assert_equal [2, 6], c.keys.compact
61
+ end
62
+
51
63
  def test_should_group_by_summed_field_having_condition
52
64
  c = Account.sum(:credit_limit, :group => :firm_id,
53
65
  :having => 'sum(credit_limit) > 50')
@@ -6,7 +6,7 @@ ActiveRecord::Base.logger = Logger.new STDOUT
6
6
  ActiveRecord::Base.logger.level = Logger::WARN
7
7
 
8
8
  # Set these to your database connection strings
9
- db = 'activerecord_unit_tests'
9
+ db = ENV['ARUNIT_DB'] || 'activerecord_unittest'
10
10
 
11
11
  ActiveRecord::Base.establish_connection(
12
12
  :adapter => 'oracle',
@@ -31,6 +31,11 @@ class Author < ActiveRecord::Base
31
31
  has_many :author_favorites
32
32
  has_many :favorite_authors, :through => :author_favorites, :order => 'name'
33
33
 
34
+ has_many :tagging, :through => :posts # through polymorphic has_one
35
+ has_many :taggings, :through => :posts, :source => :taggings # through polymorphic has_many
36
+ has_many :tags, :through => :posts # through has_many :through
37
+ has_many :post_categories, :through => :posts, :source => :categories
38
+
34
39
  belongs_to :author_address
35
40
 
36
41
  attr_accessor :post_log
@@ -14,6 +14,10 @@ class Address
14
14
  def close_to?(other_address)
15
15
  city == other_address.city && country == other_address.country
16
16
  end
17
+
18
+ def ==(other)
19
+ other.is_a?(self.class) && other.street == street && other.city == city && other.country == country
20
+ end
17
21
  end
18
22
 
19
23
  class Money
@@ -1,5 +1,5 @@
1
1
  class Tag < ActiveRecord::Base
2
- has_many :taggings, :as => :taggable
2
+ has_many :taggings
3
3
  has_many :taggables, :through => :taggings
4
- has_one :tagging, :as => :taggable
4
+ has_one :tagging
5
5
  end
@@ -257,8 +257,8 @@ if ActiveRecord::Base.connection.supports_migrations?
257
257
 
258
258
  assert_nothing_raised do
259
259
  if current_adapter?(:OracleAdapter)
260
- # Oracle requires the explicit sequence for the pk
261
- ActiveRecord::Base.connection.execute "INSERT INTO octopi (id, url) VALUES (octopi_seq.nextval, 'http://www.foreverflying.com/octopus-black7.jpg')"
260
+ # Oracle requires the explicit sequence value for the pk
261
+ ActiveRecord::Base.connection.execute "INSERT INTO octopi (id, url) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"
262
262
  else
263
263
  ActiveRecord::Base.connection.execute "INSERT INTO octopi (url) VALUES ('http://www.foreverflying.com/octopus-black7.jpg')"
264
264
  end
@@ -466,9 +466,13 @@ if ActiveRecord::Base.connection.supports_migrations?
466
466
 
467
467
  columns = Person.connection.columns(:binary_testings)
468
468
  data_column = columns.detect { |c| c.name == "data" }
469
-
470
- assert_equal "", data_column.default
471
-
469
+
470
+ if current_adapter?(:OracleAdapter)
471
+ assert_equal "empty_blob()", data_column.default
472
+ else
473
+ assert_equal "", data_column.default
474
+ end
475
+
472
476
  Person.connection.drop_table :binary_testings rescue nil
473
477
  end
474
478
 
@@ -3,6 +3,8 @@ require 'fixtures/post'
3
3
  require 'fixtures/comment'
4
4
  require 'fixtures/developer'
5
5
  require 'fixtures/project'
6
+ require 'fixtures/reader'
7
+ require 'fixtures/person'
6
8
 
7
9
  # Dummy class methods to test implicit association scoping.
8
10
  def Comment.foo() find :first end
@@ -50,19 +52,23 @@ class ReadOnlyTest < Test::Unit::TestCase
50
52
  def test_habtm_find_readonly
51
53
  dev = Developer.find(1)
52
54
  assert !dev.projects.empty?
53
- dev.projects.each { |p| assert !p.readonly? }
54
- dev.projects.find(:all) { |p| assert !p.readonly? }
55
- dev.projects.find(:all, :readonly => true) { |p| assert p.readonly? }
55
+ assert dev.projects.all?(&:readonly?)
56
+ assert dev.projects.find(:all).all?(&:readonly?)
57
+ assert dev.projects.find(:all, :readonly => true).all?(&:readonly?)
56
58
  end
57
59
 
58
60
  def test_has_many_find_readonly
59
61
  post = Post.find(1)
60
62
  assert !post.comments.empty?
61
- post.comments.each { |r| assert !r.readonly? }
62
- post.comments.find(:all) { |r| assert !r.readonly? }
63
- post.comments.find(:all, :readonly => true) { |r| assert r.readonly? }
63
+ assert !post.comments.any?(&:readonly?)
64
+ assert !post.comments.find(:all).any?(&:readonly?)
65
+ assert post.comments.find(:all, :readonly => true).all?(&:readonly?)
64
66
  end
65
67
 
68
+ def test_has_many_with_through_is_not_implicitly_marked_readonly
69
+ assert people = Post.find(1).people
70
+ assert !people.any?(&:readonly?)
71
+ end
66
72
 
67
73
  def test_readonly_scoping
68
74
  Post.with_scope(:find => { :conditions => '1=1' }) do
@@ -74,6 +74,8 @@ class ReflectionTest < Test::Unit::TestCase
74
74
  assert_equal reflection_for_address, Customer.reflect_on_aggregation(:address)
75
75
 
76
76
  assert_equal Address, Customer.reflect_on_aggregation(:address).klass
77
+
78
+ assert_equal Money, Customer.reflect_on_aggregation(:balance).klass
77
79
  end
78
80
 
79
81
  def test_has_many_reflection
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: activerecord
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.14.0
7
- date: 2006-03-27 00:00:00 -06:00
6
+ version: 1.14.1
7
+ date: 2006-04-06 00:00:00 -05:00
8
8
  summary: Implements the ActiveRecord pattern for ORM.
9
9
  require_paths:
10
10
  - lib
@@ -196,6 +196,9 @@ files:
196
196
  - test/fixtures/developers_projects.yml
197
197
  - test/fixtures/entrant.rb
198
198
  - test/fixtures/entrants.yml
199
+ - test/fixtures/fixture_database.sqlite
200
+ - test/fixtures/fixture_database.sqlite3
201
+ - test/fixtures/fixture_database_2.sqlite
199
202
  - test/fixtures/fk_test_has_fk.yml
200
203
  - test/fixtures/fk_test_has_pk.yml
201
204
  - test/fixtures/flowers.jpg
@@ -320,5 +323,5 @@ dependencies:
320
323
  requirements:
321
324
  - - "="
322
325
  - !ruby/object:Gem::Version
323
- version: 1.3.0
326
+ version: 1.3.1
324
327
  version: