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 CHANGED
@@ -1,4 +1,23 @@
1
- *1.11.0* (5th July, 2005)
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 property and it will be ignored if attempted.
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 = reflect_on_included_associations(options[:include])
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 = select_all_rows(options, schema_abbreviations, reflections)
747
- records, records_in_order = { }, []
748
- primary_key = primary_key_table[table_name]
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|
@@ -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 = final_type.const_get(part)
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 = (Thread.current['active_connections'] ||= {})[klass]
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 Thread.current['active_connections'].is_a?(Hash) && Thread.current['active_connections'][klass]
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
- Thread.current['active_connections'] ||= {}
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
- Thread.current['active_connections'] ||= {}
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
- insert "INSERT INTO schema_info (version) VALUES(0)"
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 ? '1' : '0' : value.to_i
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 =~ /^INSERT/i
239
+ if sql =~ /^\s*INSERT/i
240
240
  insert(sql, name)
241
- elsif sql =~ /^UPDATE|DELETE/i
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
- for migration_file in migration_files
184
+ migrations = migration_files.collect do |migration_file|
162
185
  load(migration_file)
163
186
  version, name = migration_version_and_name(migration_file)
164
- yield version, migration_class(name)
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
- Thread.current['active_connections'] ||= {}
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
- write_inheritable_array "aggregations", [ AggregateReflection.new(:composed_of, part_id, options, self) ]
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
- write_inheritable_array "associations", [ AssociationReflection.new(:#{association_type}, association_id, options, self) ]
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 "aggregations"
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 "associations"
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 } unless reflect_on_all_associations.nil?
61
+ reflect_on_all_associations.find { |reflection| reflection.name == association }
62
62
  end
63
63
  end
64
-
65
64
 
66
- # Abstract base class for AggregateReflection and AssociationReflection that describes the interface available for both of
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
- class_name = active_record.send(
128
- :type_name_with_module,
129
- (options[:class_name] || active_record.class_name(active_record.table_name_prefix + name + active_record.table_name_suffix))
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
@@ -303,4 +303,13 @@ class Mysql
303
303
  end
304
304
  self
305
305
  end
306
+
307
+
308
+ # Get rid of GC.start in #free.
309
+ class Result
310
+ def free
311
+ @handle.skip_result
312
+ @handle = @fields = @data = nil
313
+ end
314
+ end
306
315
  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.0' + PKG_BUILD
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.0' + PKG_BUILD)
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 companies(:first_firm, :reload).account
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
@@ -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
@@ -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 test_association_reflection
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.0
7
- date: 2005-07-06
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.0
251
+ version: 1.1.1
251
252
  version: