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 +56 -1
- data/lib/active_record/associations.rb +72 -35
- data/lib/active_record/associations/association_collection.rb +1 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +9 -3
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +13 -10
- data/lib/active_record/base.rb +22 -16
- data/lib/active_record/calculations.rb +2 -1
- data/lib/active_record/connection_adapters/oracle_adapter.rb +41 -43
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +25 -7
- data/lib/active_record/migration.rb +1 -0
- data/lib/active_record/reflection.rb +6 -2
- data/lib/active_record/validations.rb +2 -2
- data/lib/active_record/version.rb +1 -1
- data/rakefile +1 -1
- data/test/adapter_test.rb +1 -1
- data/test/associations_go_eager_test.rb +10 -0
- data/test/associations_join_model_test.rb +32 -0
- data/test/associations_test.rb +40 -1
- data/test/base_test.rb +14 -0
- data/test/calculations_test.rb +12 -0
- data/test/connections/native_oracle/connection.rb +1 -1
- data/test/fixtures/author.rb +5 -0
- data/test/fixtures/customer.rb +4 -0
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/tag.rb +2 -2
- data/test/migration_test.rb +9 -5
- data/test/readonly_test.rb +12 -6
- data/test/reflection_test.rb +2 -0
- metadata +6 -3
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, :
|
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
|
-
#
|
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.
|
703
|
-
#
|
704
|
-
#
|
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
|
-
|
955
|
-
|
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
|
-
|
960
|
-
|
961
|
-
|
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
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
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] ||
|
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,
|
@@ -40,8 +40,8 @@ module ActiveRecord
|
|
40
40
|
end
|
41
41
|
|
42
42
|
options[:conditions] = conditions
|
43
|
-
options[:joins]
|
44
|
-
options[:readonly]
|
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
|
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
|
-
|
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
|
|
data/lib/active_record/base.rb
CHANGED
@@ -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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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 :
|
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, :
|
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
|
-
|
48
|
-
|
49
|
-
|
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
|
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
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
when
|
222
|
-
when
|
223
|
-
when
|
224
|
-
when
|
225
|
-
|
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
|
-
|
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 = '#{
|
373
|
-
and table_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
|
-
|
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 =
|
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
|
-
|
539
|
-
|
540
|
-
|
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,
|
546
|
-
@desc.attrGet(OCI_ATTR_PARAM)
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
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
|
-
|
67
|
-
|
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
|
-
|
367
|
-
|
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,
|
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
|
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
|
#
|
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.
|
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"
|
data/test/adapter_test.rb
CHANGED
@@ -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
|
data/test/associations_test.rb
CHANGED
@@ -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
|
data/test/base_test.rb
CHANGED
@@ -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.
|
data/test/calculations_test.rb
CHANGED
@@ -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 = '
|
9
|
+
db = ENV['ARUNIT_DB'] || 'activerecord_unittest'
|
10
10
|
|
11
11
|
ActiveRecord::Base.establish_connection(
|
12
12
|
:adapter => 'oracle',
|
data/test/fixtures/author.rb
CHANGED
@@ -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
|
data/test/fixtures/customer.rb
CHANGED
@@ -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
|
Binary file
|
Binary file
|
Binary file
|
data/test/fixtures/tag.rb
CHANGED
data/test/migration_test.rb
CHANGED
@@ -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 (
|
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
|
-
|
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
|
|
data/test/readonly_test.rb
CHANGED
@@ -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.
|
54
|
-
dev.projects.find(:all)
|
55
|
-
dev.projects.find(:all, :readonly => true)
|
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.
|
62
|
-
post.comments.find(:all)
|
63
|
-
post.comments.find(:all, :readonly => true)
|
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
|
data/test/reflection_test.rb
CHANGED
@@ -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.
|
7
|
-
date: 2006-
|
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.
|
326
|
+
version: 1.3.1
|
324
327
|
version:
|