activerecord 1.11.0 → 1.11.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 +20 -1
- data/lib/active_record/acts/list.rb +1 -1
- data/lib/active_record/associations.rb +33 -6
- data/lib/active_record/base.rb +9 -2
- data/lib/active_record/connection_adapters/abstract_adapter.rb +13 -7
- data/lib/active_record/connection_adapters/mysql_adapter.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +3 -3
- data/lib/active_record/migration.rb +28 -3
- data/lib/active_record/query_cache.rb +1 -2
- data/lib/active_record/reflection.rb +32 -30
- data/lib/active_record/vendor/mysql411.rb +9 -0
- data/rakefile +2 -2
- data/test/associations_go_eager_test.rb +60 -9
- data/test/migration_test.rb +15 -0
- data/test/reflection_test.rb +21 -12
- data/test/threaded_connections_test.rb +34 -0
- metadata +4 -3
data/CHANGELOG
CHANGED
@@ -1,4 +1,23 @@
|
|
1
|
-
*1.11.
|
1
|
+
*1.11.1* (11 July, 2005)
|
2
|
+
|
3
|
+
* Added support for limit and offset with eager loading of has_one and belongs_to associations. Using the options with has_many and has_and_belongs_to_many associations will now raise an ActiveRecord::ConfigurationError #1692 [Rick Olsen]
|
4
|
+
|
5
|
+
* Fixed that assume_bottom_position (in acts_as_list) could be called on items already last in the list and they would move one position away from the list #1648 [tyler@kianta.com]
|
6
|
+
|
7
|
+
* Added ActiveRecord::Base.threaded_connections flag to turn off 1-connection per thread (required for thread safety). By default it's on, but WEBrick in Rails need it off #1685 [Sam Stephenson]
|
8
|
+
|
9
|
+
* Correct reflected table name for singular associations. #1688 [court3nay@gmail.com]
|
10
|
+
|
11
|
+
* Fixed optimistic locking with SQL Server #1660 [tom@popdog.net]
|
12
|
+
|
13
|
+
* Added ActiveRecord::Migrator.migrate that can figure out whether to go up or down based on the target version and the current
|
14
|
+
|
15
|
+
* Added better error message for "packets out of order" #1630 [courtenay]
|
16
|
+
|
17
|
+
* Fixed first run of "rake migrate" on PostgreSQL by not expecting a return value on the id #1640
|
18
|
+
|
19
|
+
|
20
|
+
*1.11.0* (6 July, 2005)
|
2
21
|
|
3
22
|
* Fixed that Yaml error message in fixtures hid the real error #1623 [Nicholas Seckar]
|
4
23
|
|
@@ -173,7 +173,7 @@ module ActiveRecord
|
|
173
173
|
end
|
174
174
|
|
175
175
|
def assume_bottom_position
|
176
|
-
update_attribute(position_column, bottom_position_in_list.to_i + 1)
|
176
|
+
update_attribute(position_column, bottom_position_in_list.to_i + 1) unless last?
|
177
177
|
end
|
178
178
|
|
179
179
|
def assume_top_position
|
@@ -168,7 +168,8 @@ module ActiveRecord
|
|
168
168
|
# catch-all for performance problems, but its a great way to cut down on the number of queries in a situation as the one described above.
|
169
169
|
#
|
170
170
|
# Please note that because eager loading is fetching both models and associations in the same grab, it doesn't make sense to use the
|
171
|
-
# :limit
|
171
|
+
# :limit and :offset options on has_many and has_and_belongs_to_many associations and an ConfigurationError exception will be raised
|
172
|
+
# if attempted. It does, however, work just fine with has_one and belongs_to associations.
|
172
173
|
#
|
173
174
|
# Also have in mind that since the eager loading is pulling from multiple tables, you'll have to disambiguate any column references
|
174
175
|
# in both conditions and orders. So :order => "posts.id DESC" will work while :order => "id DESC" will not. This may require that
|
@@ -739,13 +740,17 @@ module ActiveRecord
|
|
739
740
|
end
|
740
741
|
|
741
742
|
def find_with_associations(options = {})
|
742
|
-
reflections
|
743
|
+
reflections = reflect_on_included_associations(options[:include])
|
744
|
+
|
745
|
+
guard_against_missing_reflections(reflections, options)
|
746
|
+
guard_against_unlimitable_reflections(reflections, options)
|
747
|
+
|
743
748
|
schema_abbreviations = generate_schema_abbreviations(reflections)
|
744
749
|
primary_key_table = generate_primary_key_table(reflections, schema_abbreviations)
|
745
750
|
|
746
|
-
rows
|
747
|
-
records, records_in_order
|
748
|
-
primary_key
|
751
|
+
rows = select_all_rows(options, schema_abbreviations, reflections)
|
752
|
+
records, records_in_order = { }, []
|
753
|
+
primary_key = primary_key_table[table_name]
|
749
754
|
|
750
755
|
for row in rows
|
751
756
|
id = row[primary_key]
|
@@ -781,6 +786,24 @@ module ActiveRecord
|
|
781
786
|
[ associations ].flatten.collect { |association| reflect_on_association(association) }
|
782
787
|
end
|
783
788
|
|
789
|
+
def guard_against_missing_reflections(reflections, options)
|
790
|
+
reflections.each do |r|
|
791
|
+
raise(
|
792
|
+
ConfigurationError,
|
793
|
+
"Association was not found; perhaps you misspelled it? You specified :include => :#{options[:include].join(', :')}"
|
794
|
+
) if r.nil?
|
795
|
+
end
|
796
|
+
end
|
797
|
+
|
798
|
+
def guard_against_unlimitable_reflections(reflections, options)
|
799
|
+
if (options[:offset] || options[:limit]) && !using_limitable_reflections?(reflections)
|
800
|
+
raise(
|
801
|
+
ConfigurationError,
|
802
|
+
"You can not use offset and limit together with has_many or has_and_belongs_to_many associations"
|
803
|
+
)
|
804
|
+
end
|
805
|
+
end
|
806
|
+
|
784
807
|
def generate_schema_abbreviations(reflections)
|
785
808
|
schema = [ [ table_name, column_names ] ]
|
786
809
|
schema += reflections.collect { |r| [ r.table_name, r.klass.column_names ] }
|
@@ -823,10 +846,14 @@ module ActiveRecord
|
|
823
846
|
add_conditions!(sql, options[:conditions])
|
824
847
|
add_sti_conditions!(sql, reflections)
|
825
848
|
sql << "ORDER BY #{options[:order]} " if options[:order]
|
826
|
-
|
849
|
+
add_limit!(sql, options) if using_limitable_reflections?(reflections)
|
827
850
|
return sanitize_sql(sql)
|
828
851
|
end
|
829
852
|
|
853
|
+
def using_limitable_reflections?(reflections)
|
854
|
+
reflections.reject { |r| [ :belongs_to, :has_one ].include?(r.macro) }.length.zero?
|
855
|
+
end
|
856
|
+
|
830
857
|
def add_sti_conditions!(sql, reflections)
|
831
858
|
sti_sql = ""
|
832
859
|
reflections.each do |reflection|
|
data/lib/active_record/base.rb
CHANGED
@@ -26,6 +26,8 @@ module ActiveRecord #:nodoc:
|
|
26
26
|
end
|
27
27
|
class StaleObjectError < ActiveRecordError #:nodoc:
|
28
28
|
end
|
29
|
+
class ConfigurationError < StandardError #:nodoc:
|
30
|
+
end
|
29
31
|
|
30
32
|
class AttributeAssignmentError < ActiveRecordError #:nodoc:
|
31
33
|
attr_reader :exception, :attribute
|
@@ -289,6 +291,11 @@ module ActiveRecord #:nodoc:
|
|
289
291
|
# This is set to :local by default.
|
290
292
|
cattr_accessor :default_timezone
|
291
293
|
@@default_timezone = :local
|
294
|
+
|
295
|
+
# Determines whether or not to use a connection for each thread, or a single shared connection for all threads.
|
296
|
+
# Defaults to true; Railties' WEBrick server sets this to false.
|
297
|
+
cattr_accessor :threaded_connections
|
298
|
+
@@threaded_connections = true
|
292
299
|
|
293
300
|
class << self # Class methods
|
294
301
|
# Find operates with three different retreval approaches:
|
@@ -331,7 +338,7 @@ module ActiveRecord #:nodoc:
|
|
331
338
|
|
332
339
|
case args.first
|
333
340
|
when :first
|
334
|
-
find(:all, options.merge({ :limit => 1 })).first
|
341
|
+
find(:all, options.merge(options[:include] ? { } : { :limit => 1 })).first
|
335
342
|
when :all
|
336
343
|
options[:include] ? find_with_associations(options) : find_by_sql(construct_finder_sql(options))
|
337
344
|
else
|
@@ -836,7 +843,7 @@ module ActiveRecord #:nodoc:
|
|
836
843
|
# MyApp::Business::Account would be appear as MyApp::Business::AccountSubclass.
|
837
844
|
def compute_type(type_name)
|
838
845
|
type_name_with_module(type_name).split("::").inject(Object) do |final_type, part|
|
839
|
-
final_type
|
846
|
+
final_type.const_get(part)
|
840
847
|
end
|
841
848
|
end
|
842
849
|
|
@@ -86,6 +86,14 @@ module ActiveRecord
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
+
def self.active_connections #:nodoc:
|
90
|
+
if threaded_connections
|
91
|
+
Thread.current['active_connections'] ||= {}
|
92
|
+
else
|
93
|
+
@@active_connections ||= {}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
89
97
|
# Locate the connection of the nearest super class. This can be an
|
90
98
|
# active or defined connections: if it is the latter, it will be
|
91
99
|
# opened and set as the active connection for the class it was defined
|
@@ -94,7 +102,7 @@ module ActiveRecord
|
|
94
102
|
klass = self
|
95
103
|
ar_super = ActiveRecord::Base.superclass
|
96
104
|
until klass == ar_super
|
97
|
-
if conn =
|
105
|
+
if conn = active_connections[klass]
|
98
106
|
return conn
|
99
107
|
elsif conn = @@defined_connections[klass]
|
100
108
|
klass.connection = conn
|
@@ -109,7 +117,7 @@ module ActiveRecord
|
|
109
117
|
def self.connected?
|
110
118
|
klass = self
|
111
119
|
until klass == ActiveRecord::Base.superclass
|
112
|
-
if
|
120
|
+
if active_connections[klass]
|
113
121
|
return true
|
114
122
|
else
|
115
123
|
klass = klass.superclass
|
@@ -125,8 +133,7 @@ module ActiveRecord
|
|
125
133
|
def self.remove_connection(klass=self)
|
126
134
|
conn = @@defined_connections[klass]
|
127
135
|
@@defined_connections.delete(klass)
|
128
|
-
|
129
|
-
Thread.current['active_connections'][klass] = nil
|
136
|
+
active_connections[klass] = nil
|
130
137
|
conn.config if conn
|
131
138
|
end
|
132
139
|
|
@@ -134,8 +141,7 @@ module ActiveRecord
|
|
134
141
|
def self.connection=(spec)
|
135
142
|
raise ConnectionNotEstablished unless spec
|
136
143
|
conn = self.send(spec.adapter_method, spec.config)
|
137
|
-
|
138
|
-
Thread.current['active_connections'][self] = conn
|
144
|
+
active_connections[self] = conn
|
139
145
|
end
|
140
146
|
|
141
147
|
# Converts all strings in a hash to symbols.
|
@@ -361,7 +367,7 @@ module ActiveRecord
|
|
361
367
|
def initialize_schema_information
|
362
368
|
begin
|
363
369
|
execute "CREATE TABLE schema_info (version #{type_to_sql(:integer)})"
|
364
|
-
|
370
|
+
execute "INSERT INTO schema_info (version) VALUES(0)"
|
365
371
|
rescue ActiveRecord::StatementInvalid
|
366
372
|
# Schema has been intialized
|
367
373
|
end
|
@@ -129,6 +129,8 @@ module ActiveRecord
|
|
129
129
|
@logger.info "Retrying invalid statement with reopened connection"
|
130
130
|
log(sql, name) { @connection.query(sql) }
|
131
131
|
end
|
132
|
+
elsif exception.message.split(":").first =~ /Packets out of order/
|
133
|
+
raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem update mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information."
|
132
134
|
else
|
133
135
|
raise
|
134
136
|
end
|
@@ -66,7 +66,7 @@ module ActiveRecord
|
|
66
66
|
return nil if value.nil? || value =~ /^\s*null\s*$/i
|
67
67
|
case type
|
68
68
|
when :string then value
|
69
|
-
when :integer then value == true || value == false ? value == true ?
|
69
|
+
when :integer then value == true || value == false ? value == true ? 1 : 0 : value.to_i
|
70
70
|
when :float then value.to_f
|
71
71
|
when :datetime then cast_to_datetime(value)
|
72
72
|
when :timestamp then cast_to_time(value)
|
@@ -236,9 +236,9 @@ module ActiveRecord
|
|
236
236
|
end
|
237
237
|
|
238
238
|
def execute(sql, name = nil)
|
239
|
-
if sql =~
|
239
|
+
if sql =~ /^\s*INSERT/i
|
240
240
|
insert(sql, name)
|
241
|
-
elsif sql =~
|
241
|
+
elsif sql =~ /^\s*UPDATE|^\s*DELETE/i
|
242
242
|
log(sql, name) do
|
243
243
|
@connection.execute(sql)
|
244
244
|
retVal = select_one("SELECT @@ROWCOUNT AS AffectedRows")["AffectedRows"]
|
@@ -108,6 +108,18 @@ module ActiveRecord
|
|
108
108
|
# add_column :items, :completed_items_count
|
109
109
|
# end
|
110
110
|
# end
|
111
|
+
#
|
112
|
+
# And some times you need to do something in SQL not abstracted directly by migrations:
|
113
|
+
#
|
114
|
+
# class MakeJoinUnique < ActiveRecord::Migration
|
115
|
+
# def self.up
|
116
|
+
# execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
|
117
|
+
# end
|
118
|
+
#
|
119
|
+
# def self.down
|
120
|
+
# execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
|
121
|
+
# end
|
122
|
+
# end
|
111
123
|
class Migration
|
112
124
|
class << self
|
113
125
|
def up() end
|
@@ -122,6 +134,17 @@ module ActiveRecord
|
|
122
134
|
|
123
135
|
class Migrator#:nodoc:
|
124
136
|
class << self
|
137
|
+
def migrate(migrations_path, target_version = nil)
|
138
|
+
case
|
139
|
+
when target_version.nil?, current_version < target_version
|
140
|
+
up(migrations_path, target_version)
|
141
|
+
when current_version > target_version
|
142
|
+
down(migrations_path, target_version)
|
143
|
+
when current_version == target_version
|
144
|
+
return # You're on the right version
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
125
148
|
def up(migrations_path, target_version = nil)
|
126
149
|
self.new(:up, migrations_path, target_version).migrate
|
127
150
|
end
|
@@ -146,7 +169,7 @@ module ActiveRecord
|
|
146
169
|
end
|
147
170
|
|
148
171
|
def migrate
|
149
|
-
migration_classes do |version, migration_class|
|
172
|
+
migration_classes.each do |(version, migration_class)|
|
150
173
|
Base.logger.info("Reached target version: #{@target_version}") and break if reached_target_version?(version)
|
151
174
|
next if irrelevant_migration?(version)
|
152
175
|
|
@@ -158,11 +181,13 @@ module ActiveRecord
|
|
158
181
|
|
159
182
|
private
|
160
183
|
def migration_classes
|
161
|
-
|
184
|
+
migrations = migration_files.collect do |migration_file|
|
162
185
|
load(migration_file)
|
163
186
|
version, name = migration_version_and_name(migration_file)
|
164
|
-
|
187
|
+
[ version.to_i, migration_class(name) ]
|
165
188
|
end
|
189
|
+
|
190
|
+
down? ? migrations.sort.reverse : migrations.sort
|
166
191
|
end
|
167
192
|
|
168
193
|
def migration_files
|
@@ -51,8 +51,7 @@ module ActiveRecord
|
|
51
51
|
QueryCache.new(self.send(spec.adapter_method, spec.config)) :
|
52
52
|
self.send(spec.adapter_method, spec.config)
|
53
53
|
|
54
|
-
|
55
|
-
Thread.current['active_connections'][self] = conn
|
54
|
+
active_connections[self] = conn
|
56
55
|
end
|
57
56
|
end
|
58
57
|
|
@@ -10,13 +10,13 @@ module ActiveRecord
|
|
10
10
|
|
11
11
|
def composed_of_with_reflection(part_id, options = {})
|
12
12
|
composed_of_without_reflection(part_id, options)
|
13
|
-
|
13
|
+
reflect_on_all_aggregations << AggregateReflection.new(:composed_of, part_id, options, self)
|
14
14
|
end
|
15
15
|
|
16
|
-
alias_method :composed_of, :composed_of_with_reflection
|
16
|
+
alias_method :composed_of, :composed_of_with_reflection
|
17
17
|
end
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
for association_type in %w( belongs_to has_one has_many has_and_belongs_to_many )
|
21
21
|
base.module_eval <<-"end_eval"
|
22
22
|
class << self
|
@@ -24,16 +24,16 @@ module ActiveRecord
|
|
24
24
|
|
25
25
|
def #{association_type}_with_reflection(association_id, options = {})
|
26
26
|
#{association_type}_without_reflection(association_id, options)
|
27
|
-
|
27
|
+
reflect_on_all_associations << AssociationReflection.new(:#{association_type}, association_id, options, self)
|
28
28
|
end
|
29
29
|
|
30
|
-
alias_method :#{association_type}, :#{association_type}_with_reflection
|
30
|
+
alias_method :#{association_type}, :#{association_type}_with_reflection
|
31
31
|
end
|
32
32
|
end_eval
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
# Reflection allows you to interrogate Active Record classes and objects about their associations and aggregations.
|
36
|
+
# Reflection allows you to interrogate Active Record classes and objects about their associations and aggregations.
|
37
37
|
# This information can for example be used in a form builder that took an Active Record object and created input
|
38
38
|
# fields for all of the attributes depending on their type and displayed the associations to other objects.
|
39
39
|
#
|
@@ -41,9 +41,9 @@ module ActiveRecord
|
|
41
41
|
module ClassMethods
|
42
42
|
# Returns an array of AggregateReflection objects for all the aggregations in the class.
|
43
43
|
def reflect_on_all_aggregations
|
44
|
-
read_inheritable_attribute
|
44
|
+
read_inheritable_attribute(:aggregations) or write_inheritable_attribute(:aggregations, [])
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
# Returns the AggregateReflection object for the named +aggregation+ (use the symbol). Example:
|
48
48
|
# Account.reflect_on_aggregation(:balance) # returns the balance AggregateReflection
|
49
49
|
def reflect_on_aggregation(aggregation)
|
@@ -52,47 +52,47 @@ module ActiveRecord
|
|
52
52
|
|
53
53
|
# Returns an array of AssociationReflection objects for all the aggregations in the class.
|
54
54
|
def reflect_on_all_associations
|
55
|
-
read_inheritable_attribute
|
55
|
+
read_inheritable_attribute(:associations) or write_inheritable_attribute(:associations, [])
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
# Returns the AssociationReflection object for the named +aggregation+ (use the symbol). Example:
|
59
59
|
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
|
60
60
|
def reflect_on_association(association)
|
61
|
-
reflect_on_all_associations.find { |reflection| reflection.name == association }
|
61
|
+
reflect_on_all_associations.find { |reflection| reflection.name == association }
|
62
62
|
end
|
63
63
|
end
|
64
|
-
|
65
64
|
|
66
|
-
|
65
|
+
|
66
|
+
# Abstract base class for AggregateReflection and AssociationReflection that describes the interface available for both of
|
67
67
|
# those classes. Objects of AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
|
68
68
|
class MacroReflection
|
69
69
|
attr_reader :active_record
|
70
70
|
def initialize(macro, name, options, active_record)
|
71
71
|
@macro, @name, @options, @active_record = macro, name, options, active_record
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
# Returns the name of the macro, so it would return :balance for "composed_of :balance, :class_name => 'Money'" or
|
75
75
|
# :clients for "has_many :clients".
|
76
76
|
def name
|
77
77
|
@name
|
78
78
|
end
|
79
|
-
|
80
|
-
# Returns the name of the macro, so it would return :composed_of for
|
79
|
+
|
80
|
+
# Returns the name of the macro, so it would return :composed_of for
|
81
81
|
# "composed_of :balance, :class_name => 'Money'" or :has_many for "has_many :clients".
|
82
82
|
def macro
|
83
83
|
@macro
|
84
84
|
end
|
85
|
-
|
86
|
-
# Returns the hash of options used for the macro, so it would return { :class_name => "Money" } for
|
85
|
+
|
86
|
+
# Returns the hash of options used for the macro, so it would return { :class_name => "Money" } for
|
87
87
|
# "composed_of :balance, :class_name => 'Money'" or {} for "has_many :clients".
|
88
88
|
def options
|
89
89
|
@options
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
# Returns the class for the macro, so "composed_of :balance, :class_name => 'Money'" would return the Money class and
|
93
93
|
# "has_many :clients" would return the Client class.
|
94
94
|
def klass() end
|
95
|
-
|
95
|
+
|
96
96
|
def ==(other_aggregation)
|
97
97
|
name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record
|
98
98
|
end
|
@@ -102,9 +102,9 @@ module ActiveRecord
|
|
102
102
|
# Holds all the meta-data about an aggregation as it was specified in the Active Record class.
|
103
103
|
class AggregateReflection < MacroReflection #:nodoc:
|
104
104
|
def klass
|
105
|
-
Object.const_get(options[:class_name] || name_to_class_name(name.id2name))
|
105
|
+
Object.const_get(options[:class_name] || name_to_class_name(name.id2name))
|
106
106
|
end
|
107
|
-
|
107
|
+
|
108
108
|
private
|
109
109
|
def name_to_class_name(name)
|
110
110
|
name.capitalize.gsub(/_(.)/) { |s| $1.capitalize }
|
@@ -116,21 +116,23 @@ module ActiveRecord
|
|
116
116
|
def klass
|
117
117
|
@klass ||= active_record.send(:compute_type, (name_to_class_name(name.id2name)))
|
118
118
|
end
|
119
|
-
|
119
|
+
|
120
120
|
def table_name
|
121
121
|
@table_name ||= klass.table_name
|
122
122
|
end
|
123
123
|
|
124
124
|
private
|
125
125
|
def name_to_class_name(name)
|
126
|
-
if name
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
126
|
+
if name =~ /::/
|
127
|
+
name
|
128
|
+
else
|
129
|
+
unless class_name = options[:class_name]
|
130
|
+
class_name = name.to_s.camelize
|
131
|
+
class_name = class_name.singularize if [:has_many, :has_and_belongs_to_many].include?(macro)
|
132
|
+
end
|
133
|
+
active_record.send(:type_name_with_module, class_name)
|
131
134
|
end
|
132
|
-
return class_name || name
|
133
135
|
end
|
134
136
|
end
|
135
137
|
end
|
136
|
-
end
|
138
|
+
end
|
data/rakefile
CHANGED
@@ -8,7 +8,7 @@ require 'rake/contrib/rubyforgepublisher'
|
|
8
8
|
|
9
9
|
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
10
10
|
PKG_NAME = 'activerecord'
|
11
|
-
PKG_VERSION = '1.11.
|
11
|
+
PKG_VERSION = '1.11.1' + PKG_BUILD
|
12
12
|
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
13
13
|
|
14
14
|
RELEASE_NAME = "REL #{PKG_VERSION}"
|
@@ -82,7 +82,7 @@ spec = Gem::Specification.new do |s|
|
|
82
82
|
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
83
83
|
end
|
84
84
|
|
85
|
-
s.add_dependency('activesupport', '= 1.1.
|
85
|
+
s.add_dependency('activesupport', '= 1.1.1' + PKG_BUILD)
|
86
86
|
|
87
87
|
s.files.delete "test/fixtures/fixture_database.sqlite"
|
88
88
|
s.files.delete "test/fixtures/fixture_database_2.sqlite"
|
@@ -29,7 +29,7 @@ class EagerAssociationTest < Test::Unit::TestCase
|
|
29
29
|
assert_equal posts(:thinking), posts[4]
|
30
30
|
assert_equal posts(:welcome), posts[5]
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def test_loading_with_multiple_associations
|
34
34
|
posts = Post.find(:all, :include => [ :comments, :author, :categories ], :order => "posts.id")
|
35
35
|
assert_equal 2, posts.first.comments.size
|
@@ -48,10 +48,51 @@ class EagerAssociationTest < Test::Unit::TestCase
|
|
48
48
|
|
49
49
|
def test_eager_association_loading_with_belongs_to
|
50
50
|
comments = Comment.find(:all, :include => :post)
|
51
|
+
assert_equal 9, comments.length
|
51
52
|
titles = comments.map { |c| c.post.title }
|
52
53
|
assert titles.include?(posts(:welcome).title)
|
53
54
|
assert titles.include?(posts(:sti_post_and_comments).title)
|
54
55
|
end
|
56
|
+
|
57
|
+
def test_eager_association_loading_with_belongs_to_and_limit
|
58
|
+
comments = Comment.find(:all, :include => :post, :limit => 5)
|
59
|
+
assert_equal 5, comments.length
|
60
|
+
assert_equal [1,2,3,5,6], comments.collect { |c| c.id }
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_eager_association_loading_with_belongs_to_and_limit_and_conditions
|
64
|
+
comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 4', :limit => 3)
|
65
|
+
assert_equal 3, comments.length
|
66
|
+
assert_equal [5,6,7], comments.collect { |c| c.id }
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_eager_association_loading_with_belongs_to_and_limit_and_offset
|
70
|
+
comments = Comment.find(:all, :include => :post, :limit => 3, :offset => 2)
|
71
|
+
assert_equal 3, comments.length
|
72
|
+
assert_equal [3,5,6], comments.collect { |c| c.id }
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions
|
76
|
+
comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 4', :limit => 3, :offset => 1)
|
77
|
+
assert_equal 3, comments.length
|
78
|
+
assert_equal [6,7,8], comments.collect { |c| c.id }
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations
|
82
|
+
posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1)
|
83
|
+
assert_equal 1, posts.length
|
84
|
+
assert_equal [4], posts.collect { |p| p.id }
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations
|
88
|
+
posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1, :offset => 1)
|
89
|
+
assert_equal 0, posts.length
|
90
|
+
assert_equal [], posts
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_eager_association_raise_on_limit
|
94
|
+
assert_raises(ActiveRecord::ConfigurationError) { Post.find(:all, :include => [:author, :comments], :limit => 1) }
|
95
|
+
end
|
55
96
|
|
56
97
|
def test_eager_association_loading_with_habtm
|
57
98
|
posts = Post.find(:all, :include => :categories, :order => "posts.id")
|
@@ -61,23 +102,23 @@ class EagerAssociationTest < Test::Unit::TestCase
|
|
61
102
|
assert posts[0].categories.include?(categories(:technology))
|
62
103
|
assert posts[1].categories.include?(categories(:general))
|
63
104
|
end
|
64
|
-
|
105
|
+
|
65
106
|
def test_eager_with_inheritance
|
66
107
|
posts = SpecialPost.find(:all, :include => [ :comments ])
|
67
|
-
end
|
108
|
+
end
|
68
109
|
|
69
110
|
def test_eager_has_one_with_association_inheritance
|
70
111
|
post = Post.find(4, :include => [ :very_special_comment ])
|
71
112
|
assert_equal "VerySpecialComment", post.very_special_comment.class.to_s
|
72
|
-
end
|
73
|
-
|
113
|
+
end
|
114
|
+
|
74
115
|
def test_eager_has_many_with_association_inheritance
|
75
116
|
post = Post.find(4, :include => [ :special_comments ])
|
76
117
|
post.special_comments.each do |special_comment|
|
77
118
|
assert_equal "SpecialComment", special_comment.class.to_s
|
78
119
|
end
|
79
|
-
end
|
80
|
-
|
120
|
+
end
|
121
|
+
|
81
122
|
def test_eager_habtm_with_association_inheritance
|
82
123
|
post = Post.find(6, :include => [ :special_categories ])
|
83
124
|
assert_equal 1, post.special_categories.size
|
@@ -90,7 +131,17 @@ class EagerAssociationTest < Test::Unit::TestCase
|
|
90
131
|
assert_not_nil companies(:first_firm).account
|
91
132
|
f = Firm.find(:first, :include => :account,
|
92
133
|
:conditions => ["companies.name = ?", "37signals"])
|
93
|
-
assert_not_nil
|
134
|
+
assert_not_nil f.account
|
135
|
+
assert_equal companies(:first_firm, :reload).account, f.account
|
94
136
|
end
|
95
|
-
end
|
96
137
|
|
138
|
+
def test_eager_with_invalid_association_reference
|
139
|
+
assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
140
|
+
post = Post.find(6, :include=>[ :monkeys ])
|
141
|
+
}
|
142
|
+
assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys, :elephants") {
|
143
|
+
post = Post.find(6, :include=>[ :monkeys, :elephants ])
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
data/test/migration_test.rb
CHANGED
@@ -182,5 +182,20 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
182
182
|
assert !Person.column_methods_hash.include?(:last_name)
|
183
183
|
assert_raises(ActiveRecord::StatementInvalid) { Reminder.column_methods_hash }
|
184
184
|
end
|
185
|
+
|
186
|
+
def test_migrator_going_down_due_to_version_target
|
187
|
+
ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
|
188
|
+
ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
|
189
|
+
|
190
|
+
assert !Person.column_methods_hash.include?(:last_name)
|
191
|
+
assert_raises(ActiveRecord::StatementInvalid) { Reminder.column_methods_hash }
|
192
|
+
|
193
|
+
ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations/')
|
194
|
+
|
195
|
+
Person.reset_column_information
|
196
|
+
assert Person.column_methods_hash.include?(:last_name)
|
197
|
+
assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
|
198
|
+
assert_equal "hello world", Reminder.find(:first).content
|
199
|
+
end
|
185
200
|
end
|
186
201
|
end
|
data/test/reflection_test.rb
CHANGED
@@ -17,7 +17,7 @@ class ReflectionTest < Test::Unit::TestCase
|
|
17
17
|
@first.attribute_names
|
18
18
|
)
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def test_columns
|
22
22
|
assert_equal 12, Topic.columns.length
|
23
23
|
end
|
@@ -30,7 +30,7 @@ class ReflectionTest < Test::Unit::TestCase
|
|
30
30
|
def test_content_columns
|
31
31
|
assert_equal 8, Topic.content_columns.length
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
def test_column_string_type_and_limit
|
35
35
|
assert_equal :string, @first.column_for_attribute("title").type
|
36
36
|
assert_equal 255, @first.column_for_attribute("title").limit
|
@@ -39,10 +39,10 @@ class ReflectionTest < Test::Unit::TestCase
|
|
39
39
|
def test_human_name_for_column
|
40
40
|
assert_equal "Author name", @first.column_for_attribute("author_name").human_name
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
def test_integer_columns
|
44
44
|
assert_equal :integer, @first.column_for_attribute("id").type
|
45
|
-
end
|
45
|
+
end
|
46
46
|
|
47
47
|
def test_aggregation_reflection
|
48
48
|
reflection_for_address = ActiveRecord::Reflection::AggregateReflection.new(
|
@@ -61,23 +61,32 @@ class ReflectionTest < Test::Unit::TestCase
|
|
61
61
|
[ reflection_for_address, reflection_for_balance, reflection_for_gps_location ],
|
62
62
|
Customer.reflect_on_all_aggregations
|
63
63
|
)
|
64
|
-
|
64
|
+
|
65
65
|
assert_equal reflection_for_address, Customer.reflect_on_aggregation(:address)
|
66
|
-
|
66
|
+
|
67
67
|
assert_equal Address, Customer.reflect_on_aggregation(:address).klass
|
68
68
|
end
|
69
|
-
|
70
|
-
def
|
71
|
-
reflection_for_clients = ActiveRecord::Reflection::AssociationReflection.new(
|
72
|
-
:has_many, :clients, { :order => "id", :dependent => true }, Firm
|
73
|
-
)
|
69
|
+
|
70
|
+
def test_has_many_reflection
|
71
|
+
reflection_for_clients = ActiveRecord::Reflection::AssociationReflection.new(:has_many, :clients, { :order => "id", :dependent => true }, Firm)
|
74
72
|
|
75
73
|
assert_equal reflection_for_clients, Firm.reflect_on_association(:clients)
|
76
74
|
|
77
75
|
assert_equal Client, Firm.reflect_on_association(:clients).klass
|
76
|
+
assert_equal 'companies', Firm.reflect_on_association(:clients).table_name
|
77
|
+
|
78
78
|
assert_equal Client, Firm.reflect_on_association(:clients_of_firm).klass
|
79
|
+
assert_equal 'companies', Firm.reflect_on_association(:clients_of_firm).table_name
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_has_one_reflection
|
83
|
+
reflection_for_account = ActiveRecord::Reflection::AssociationReflection.new(:has_one, :account, { :foreign_key => "firm_id", :dependent => true }, Firm)
|
84
|
+
assert_equal reflection_for_account, Firm.reflect_on_association(:account)
|
85
|
+
|
86
|
+
assert_equal Account, Firm.reflect_on_association(:account).klass
|
87
|
+
assert_equal 'accounts', Firm.reflect_on_association(:account).table_name
|
79
88
|
end
|
80
|
-
|
89
|
+
|
81
90
|
def test_association_reflection_in_modules
|
82
91
|
assert_equal MyApplication::Business::Client, MyApplication::Business::Firm.reflect_on_association(:clients_of_firm).klass
|
83
92
|
assert_equal MyApplication::Business::Firm, MyApplication::Billing::Account.reflect_on_association(:firm).klass
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class ThreadedConnectionsTest < Test::Unit::TestCase
|
4
|
+
self.use_transactional_fixtures = false
|
5
|
+
|
6
|
+
fixtures :topics
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@connection = ActiveRecord::Base.remove_connection
|
10
|
+
@connections = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def gather_connections(use_threaded_connections)
|
14
|
+
ActiveRecord::Base.threaded_connections = use_threaded_connections
|
15
|
+
ActiveRecord::Base.establish_connection(@connection)
|
16
|
+
|
17
|
+
5.times do
|
18
|
+
Thread.new do
|
19
|
+
Topic.find :first
|
20
|
+
@connections << ActiveRecord::Base.active_connections.values.first
|
21
|
+
end.join
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_threaded_connections
|
26
|
+
gather_connections(true)
|
27
|
+
assert_equal @connections.uniq.length, 5
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_unthreaded_connections
|
31
|
+
gather_connections(false)
|
32
|
+
assert_equal @connections.uniq.length, 1
|
33
|
+
end
|
34
|
+
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.10
|
|
3
3
|
specification_version: 1
|
4
4
|
name: activerecord
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.11.
|
7
|
-
date: 2005-07-
|
6
|
+
version: 1.11.1
|
7
|
+
date: 2005-07-11
|
8
8
|
summary: Implements the ActiveRecord pattern for ORM.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -110,6 +110,7 @@ files:
|
|
110
110
|
- test/reflection_test.rb
|
111
111
|
- test/schema_test_postgresql.rb
|
112
112
|
- test/thread_safety_test.rb
|
113
|
+
- test/threaded_connections_test.rb
|
113
114
|
- test/transactions_test.rb
|
114
115
|
- test/unconnected_test.rb
|
115
116
|
- test/validations_test.rb
|
@@ -247,5 +248,5 @@ dependencies:
|
|
247
248
|
-
|
248
249
|
- "="
|
249
250
|
- !ruby/object:Gem::Version
|
250
|
-
version: 1.1.
|
251
|
+
version: 1.1.1
|
251
252
|
version:
|