activerecord 1.13.0 → 1.13.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 +91 -0
- data/lib/active_record.rb +2 -2
- data/lib/active_record/acts/list.rb +16 -12
- data/lib/active_record/acts/tree.rb +2 -2
- data/lib/active_record/aggregations.rb +6 -0
- data/lib/active_record/associations.rb +38 -16
- data/lib/active_record/associations/has_many_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/base.rb +46 -33
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +33 -9
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +11 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +3 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +41 -21
- data/lib/active_record/connection_adapters/firebird_adapter.rb +414 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +68 -29
- data/lib/active_record/connection_adapters/oci_adapter.rb +141 -21
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +82 -21
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +39 -6
- data/lib/active_record/fixtures.rb +1 -0
- data/lib/active_record/migration.rb +30 -13
- data/lib/active_record/validations.rb +18 -7
- data/lib/active_record/vendor/mysql.rb +89 -12
- data/lib/active_record/version.rb +2 -2
- data/rakefile +38 -3
- data/test/abstract_unit.rb +5 -0
- data/test/aggregations_test.rb +19 -0
- data/test/associations_go_eager_test.rb +26 -2
- data/test/associations_test.rb +29 -10
- data/test/base_test.rb +57 -6
- data/test/binary_test.rb +3 -3
- data/test/connections/native_db2/connection.rb +1 -1
- data/test/connections/native_firebird/connection.rb +24 -0
- data/test/connections/native_mysql/connection.rb +1 -1
- data/test/connections/native_oci/connection.rb +1 -1
- data/test/connections/native_postgresql/connection.rb +6 -6
- data/test/connections/native_sqlite/connection.rb +1 -1
- data/test/connections/native_sqlite3/connection.rb +1 -1
- data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
- data/test/connections/native_sqlserver/connection.rb +1 -1
- data/test/connections/native_sqlserver_odbc/connection.rb +1 -1
- data/test/default_test_firebird.rb +16 -0
- data/test/deprecated_associations_test.rb +1 -1
- data/test/finder_test.rb +11 -1
- data/test/fixtures/author.rb +30 -30
- data/test/fixtures/comment.rb +1 -1
- data/test/fixtures/company.rb +3 -1
- data/test/fixtures/customer.rb +4 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +54 -0
- data/test/fixtures/db_definitions/firebird.sql +259 -0
- data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
- data/test/fixtures/db_definitions/firebird2.sql +6 -0
- data/test/fixtures/db_definitions/oci.sql +8 -0
- data/test/fixtures/db_definitions/postgresql.sql +3 -2
- data/test/fixtures/developer.rb +10 -0
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/mixin.rb +11 -1
- data/test/fixtures/mixins.yml +20 -1
- data/test/fixtures_test.rb +65 -45
- data/test/inheritance_test.rb +1 -1
- data/test/migration_test.rb +7 -1
- data/test/mixin_test.rb +267 -98
- data/test/multiple_db_test.rb +13 -1
- data/test/pk_test.rb +1 -0
- metadata +11 -5
- data/lib/active_record/vendor/mysql411.rb +0 -311
- data/test/debug.log +0 -2857
data/CHANGELOG
CHANGED
@@ -1,3 +1,94 @@
|
|
1
|
+
*1.13.1* (December 7th, 2005)
|
2
|
+
|
3
|
+
* MySQL: more robust test for nullified result hashes. #3124 [Stefan Kaes]
|
4
|
+
|
5
|
+
* SQLite: find database file when RAILS_ROOT is a symlink. #3116 [anna@wota.jp]
|
6
|
+
|
7
|
+
* Reloading an instance refreshes its aggregations as well as its associations. #3024 [François Beausolei]
|
8
|
+
|
9
|
+
* Fixed that using :include together with :conditions array in Base.find would cause NoMethodError #2887 [Paul Hammmond]
|
10
|
+
|
11
|
+
* PostgreSQL: more robust sequence name discovery. #3087 [Rick Olson]
|
12
|
+
|
13
|
+
* Oracle: use syntax compatible with Oracle 8. #3131 [Michael Schoen]
|
14
|
+
|
15
|
+
* MySQL: work around ruby-mysql/mysql-ruby inconsistency with mysql.stat. Eliminate usage of mysql.ping because it doesn't guarantee reconnect. Explicitly close and reopen the connection instead. [Jeremy Kemper]
|
16
|
+
|
17
|
+
* When AbstractAdapter#log rescues an exception, attempt to detect and reconnect to an inactive database connection. Connection adapter must respond to the active? and reconnect! instance methods. Initial support for PostgreSQL, MySQL, and SQLite. Make certain that all statements which may need reconnection are performed within a logged block: for example, this means no avoiding log(sql, name) { } if @logger.nil? [Jeremy Kemper]
|
18
|
+
|
19
|
+
* Firebird: active? and reconnect! methods for handling stale connections. #428 [Ken Kunz <kennethkunz@gmail.com>]
|
20
|
+
|
21
|
+
* Firebird: updated for FireRuby 0.4.0. #3009 [Ken Kunz <kennethkunz@gmail.com>]
|
22
|
+
|
23
|
+
* Introducing the Firebird adapter. Quote columns and use attribute_condition more consistently. Setup guide: http://wiki.rubyonrails.com/rails/pages/Firebird+Adapter #1874 [Ken Kunz <kennethkunz@gmail.com>]
|
24
|
+
|
25
|
+
* MySQL and PostgreSQL: active? compatibility with the pure-Ruby driver. #428 [Jeremy Kemper]
|
26
|
+
|
27
|
+
* Oracle: active? check pings the database rather than testing the last command status. #428 [Michael Schoen]
|
28
|
+
|
29
|
+
* SQLServer: resolve column aliasing/quoting collision when using limit or offset in an eager find. #2974 [kajism@yahoo.com]
|
30
|
+
|
31
|
+
* Reloading a model doesn't lose track of its connection. #2996 [junk@miriamtech.com, Jeremy Kemper]
|
32
|
+
|
33
|
+
* Fixed bug where using update_attribute after pushing a record to a habtm association of the object caused duplicate rows in the join table. #2888 [colman@rominato.com, Florian Weber, Michael Schoen]
|
34
|
+
|
35
|
+
* MySQL: introduce :encoding option to specify the character set for client, connection, and results. Only available for MySQL 4.1 and later with the mysql-ruby driver. Do SHOW CHARACTER SET in mysql client to see available encodings. #2975 [Shugo Maeda]
|
36
|
+
|
37
|
+
* Add tasks to create, drop and rebuild the MySQL and PostgreSQL test databases. [Marcel Molina Jr.]
|
38
|
+
|
39
|
+
* Correct boolean handling in generated reader methods. #2945 [don.park@gmail.com, Stefan Kaes]
|
40
|
+
|
41
|
+
* Don't generate read methods for columns whose names are not valid ruby method names. #2946 [Stefan Kaes]
|
42
|
+
|
43
|
+
* Document :force option to create_table. #2921 [Blair Zajac <blair@orcaware.com>]
|
44
|
+
|
45
|
+
* Don't add the same conditions twice in has_one finder sql. #2916 [Jeremy Evans]
|
46
|
+
|
47
|
+
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
|
48
|
+
|
49
|
+
* SQLServer: insert uses given primary key value if not nil rather than SELECT @@IDENTITY. #2866 [kajism@yahoo.com, Tom Ward <tom@popdog.net>]
|
50
|
+
|
51
|
+
* Correct documentation for Base.delete_all. #1568 [Newhydra]
|
52
|
+
|
53
|
+
* Oracle: test case for column default parsing. #2788 [Michael Schoen <schoenm@earthlink.net>]
|
54
|
+
|
55
|
+
* Update documentation for Migrations. #2861 [Tom Werner <tom@cube6media.com>]
|
56
|
+
|
57
|
+
* Oracle: Much faster column reflection. #2848 [Michael Schoen <schoenm@earthlink.net>]
|
58
|
+
|
59
|
+
* Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing). Base.define_attr_method allows nil values. [Jeremy Kemper]
|
60
|
+
|
61
|
+
* PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence. [Jeremy Kemper]
|
62
|
+
|
63
|
+
* PostgreSQL: correctly discover custom primary key sequences. #2594 [Blair Zajac <blair@orcaware.com>, meadow.nnick@gmail.com, Jeremy Kemper]
|
64
|
+
|
65
|
+
* SQLServer: don't report limits for unsupported field types. #2835 [Ryan Tomayko]
|
66
|
+
|
67
|
+
* Include the Enumerable module in ActiveRecord::Errors. [Rick Bradley <rick@rickbradley.com>]
|
68
|
+
|
69
|
+
* Add :group option, correspond to GROUP BY, to the find method and to the has_many association. #2818 [rubyonrails@atyp.de]
|
70
|
+
|
71
|
+
* Don't cast nil or empty strings to a dummy date. #2789 [Rick Bradley <rick@rickbradley.com>]
|
72
|
+
|
73
|
+
* acts_as_list plays nicely with inheritance by remembering the class which declared it. #2811 [rephorm@rephorm.com]
|
74
|
+
|
75
|
+
* Fix sqlite adaptor's detection of missing dbfile or database declaration. [Nicholas Seckar]
|
76
|
+
|
77
|
+
* Fixed acts_as_list for definitions without an explicit :order #2803 [jonathan@bluewire.net.nz]
|
78
|
+
|
79
|
+
* Upgrade bundled ruby-mysql 0.2.4 with mysql411 shim (see #440) to ruby-mysql 0.2.6 with a patchset for 4.1 protocol support. Local change [301] is now a part of the main driver; reapplied local change [2182]. Removed GC.start from Result.free. [tommy@tmtm.org, akuroda@gmail.com, Doug Fales <doug.fales@gmail.com>, Jeremy Kemper]
|
80
|
+
|
81
|
+
* Correct handling of complex order clauses with SQL Server limit emulation. #2770 [Tom Ward <tom@popdog.net>, Matt B.]
|
82
|
+
|
83
|
+
* Correct whitespace problem in Oracle default column value parsing. #2788 [rick@rickbradley.com]
|
84
|
+
|
85
|
+
* Destroy associated has_and_belongs_to_many records after all before_destroy callbacks but before destroy. This allows you to act on the habtm association as you please while preserving referential integrity. #2065 [larrywilliams1@gmail.com, sam.kirchmeier@gmail.com, elliot@townx.org, Jeremy Kemper]
|
86
|
+
|
87
|
+
* Deprecate the old, confusing :exclusively_dependent option in favor of :dependent => :delete_all. [Jeremy Kemper]
|
88
|
+
|
89
|
+
* More compatible Oracle column reflection. #2771 [Ryan Davis <ryand-ruby@zenspider.com>, Michael Schoen <schoenm@earthlink.net>]
|
90
|
+
|
91
|
+
|
1
92
|
*1.13.0* (November 7th, 2005)
|
2
93
|
|
3
94
|
* Fixed faulty regex in get_table_name method (SQLServerAdapter) #2639 [Ryan Tomayko]
|
data/lib/active_record.rb
CHANGED
@@ -66,11 +66,11 @@ ActiveRecord::Base.class_eval do
|
|
66
66
|
end
|
67
67
|
|
68
68
|
unless defined?(RAILS_CONNECTION_ADAPTERS)
|
69
|
-
RAILS_CONNECTION_ADAPTERS = %w(mysql postgresql sqlite sqlserver db2 oci)
|
69
|
+
RAILS_CONNECTION_ADAPTERS = %w(mysql postgresql sqlite firebird sqlserver db2 oci)
|
70
70
|
end
|
71
71
|
|
72
72
|
RAILS_CONNECTION_ADAPTERS.each do |adapter|
|
73
|
-
require "active_record/connection_adapters
|
73
|
+
require "active_record/connection_adapters/" + adapter + "_adapter"
|
74
74
|
end
|
75
75
|
|
76
76
|
require 'active_record/query_cache'
|
@@ -54,6 +54,10 @@ module ActiveRecord
|
|
54
54
|
class_eval <<-EOV
|
55
55
|
include ActiveRecord::Acts::List::InstanceMethods
|
56
56
|
|
57
|
+
def acts_as_list_class
|
58
|
+
::#{self.name}
|
59
|
+
end
|
60
|
+
|
57
61
|
def position_column
|
58
62
|
'#{configuration[:column]}'
|
59
63
|
end
|
@@ -78,7 +82,7 @@ module ActiveRecord
|
|
78
82
|
def move_lower
|
79
83
|
return unless lower_item
|
80
84
|
|
81
|
-
|
85
|
+
acts_as_list_class.transaction do
|
82
86
|
lower_item.decrement_position
|
83
87
|
increment_position
|
84
88
|
end
|
@@ -87,7 +91,7 @@ module ActiveRecord
|
|
87
91
|
def move_higher
|
88
92
|
return unless higher_item
|
89
93
|
|
90
|
-
|
94
|
+
acts_as_list_class.transaction do
|
91
95
|
higher_item.increment_position
|
92
96
|
decrement_position
|
93
97
|
end
|
@@ -95,7 +99,7 @@ module ActiveRecord
|
|
95
99
|
|
96
100
|
def move_to_bottom
|
97
101
|
return unless in_list?
|
98
|
-
|
102
|
+
acts_as_list_class.transaction do
|
99
103
|
decrement_positions_on_lower_items
|
100
104
|
assume_bottom_position
|
101
105
|
end
|
@@ -103,7 +107,7 @@ module ActiveRecord
|
|
103
107
|
|
104
108
|
def move_to_top
|
105
109
|
return unless in_list?
|
106
|
-
|
110
|
+
acts_as_list_class.transaction do
|
107
111
|
increment_positions_on_higher_items
|
108
112
|
assume_top_position
|
109
113
|
end
|
@@ -135,14 +139,14 @@ module ActiveRecord
|
|
135
139
|
|
136
140
|
def higher_item
|
137
141
|
return nil unless in_list?
|
138
|
-
|
142
|
+
acts_as_list_class.find(:first, :conditions =>
|
139
143
|
"#{scope_condition} AND #{position_column} = #{(send(position_column).to_i - 1).to_s}"
|
140
144
|
)
|
141
145
|
end
|
142
146
|
|
143
147
|
def lower_item
|
144
148
|
return nil unless in_list?
|
145
|
-
|
149
|
+
acts_as_list_class.find(:first, :conditions =>
|
146
150
|
"#{scope_condition} AND #{position_column} = #{(send(position_column).to_i + 1).to_s}"
|
147
151
|
)
|
148
152
|
end
|
@@ -171,7 +175,7 @@ module ActiveRecord
|
|
171
175
|
def bottom_item(except = nil)
|
172
176
|
conditions = scope_condition
|
173
177
|
conditions = "#{conditions} AND id != #{except.id}" if except
|
174
|
-
|
178
|
+
acts_as_list_class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
|
175
179
|
end
|
176
180
|
|
177
181
|
def assume_bottom_position
|
@@ -184,7 +188,7 @@ module ActiveRecord
|
|
184
188
|
|
185
189
|
# This has the effect of moving all the higher items up one.
|
186
190
|
def decrement_positions_on_higher_items(position)
|
187
|
-
|
191
|
+
acts_as_list_class.update_all(
|
188
192
|
"#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} <= #{position}"
|
189
193
|
)
|
190
194
|
end
|
@@ -192,7 +196,7 @@ module ActiveRecord
|
|
192
196
|
# This has the effect of moving all the lower items up one.
|
193
197
|
def decrement_positions_on_lower_items
|
194
198
|
return unless in_list?
|
195
|
-
|
199
|
+
acts_as_list_class.update_all(
|
196
200
|
"#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{send(position_column).to_i}"
|
197
201
|
)
|
198
202
|
end
|
@@ -200,20 +204,20 @@ module ActiveRecord
|
|
200
204
|
# This has the effect of moving all the higher items down one.
|
201
205
|
def increment_positions_on_higher_items
|
202
206
|
return unless in_list?
|
203
|
-
|
207
|
+
acts_as_list_class.update_all(
|
204
208
|
"#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
|
205
209
|
)
|
206
210
|
end
|
207
211
|
|
208
212
|
# This has the effect of moving all the lower items down one.
|
209
213
|
def increment_positions_on_lower_items(position)
|
210
|
-
|
214
|
+
acts_as_list_class.update_all(
|
211
215
|
"#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{position}"
|
212
216
|
)
|
213
217
|
end
|
214
218
|
|
215
219
|
def increment_positions_on_all_items
|
216
|
-
|
220
|
+
acts_as_list_class.update_all(
|
217
221
|
"#{position_column} = (#{position_column} + 1)", "#{scope_condition}"
|
218
222
|
)
|
219
223
|
end
|
@@ -49,10 +49,10 @@ module ActiveRecord
|
|
49
49
|
|
50
50
|
module_eval <<-END
|
51
51
|
def self.roots
|
52
|
-
self.find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => "#{configuration[:order]}")
|
52
|
+
self.find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
|
53
53
|
end
|
54
54
|
def self.root
|
55
|
-
self.find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => "#{configuration[:order]}")
|
55
|
+
self.find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
|
56
56
|
end
|
57
57
|
END
|
58
58
|
|
@@ -5,6 +5,12 @@ module ActiveRecord
|
|
5
5
|
base.extend(ClassMethods)
|
6
6
|
end
|
7
7
|
|
8
|
+
def clear_aggregation_cache #:nodoc:
|
9
|
+
self.class.reflect_on_all_aggregations.to_a.each do |assoc|
|
10
|
+
instance_variable_set "@#{assoc.name}", nil
|
11
|
+
end unless self.new_record?
|
12
|
+
end
|
13
|
+
|
8
14
|
# Active Record implements aggregation through a macro-like class method called +composed_of+ for representing attributes
|
9
15
|
# as value objects. It expresses relationships like "Account [is] composed of Money [among other things]" or "Person [is]
|
10
16
|
# composed of [an] address". Each call to the macro adds a description of how the value objects are created from the
|
@@ -307,14 +307,18 @@ module ActiveRecord
|
|
307
307
|
# sql fragment, such as "price > 5 AND name LIKE 'B%'".
|
308
308
|
# * <tt>:order</tt> - specify the order in which the associated objects are returned as a "ORDER BY" sql fragment,
|
309
309
|
# such as "last_name, first_name DESC"
|
310
|
+
# * <tt>:group</tt> - specify the attribute by which the associated objects are returned as a "GROUP BY" sql fragment,
|
311
|
+
# such as "category"
|
310
312
|
# * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name
|
311
313
|
# of this class in lower-case and "_id" suffixed. So a +Person+ class that makes a has_many association will use "person_id"
|
312
314
|
# as the default foreign_key.
|
313
315
|
# * <tt>:dependent</tt> - if set to :destroy (or true) all the associated objects are destroyed
|
314
|
-
# alongside this object
|
315
|
-
#
|
316
|
+
# alongside this object by calling their destroy method. If set to :delete_all all associated
|
317
|
+
# objects are deleted *without* calling their destroy method. If set to :nullify all associated
|
318
|
+
# objects' foreign keys are set to NULL *without* calling their save callbacks.
|
316
319
|
# May not be set if :exclusively_dependent is also set.
|
317
|
-
# * <tt>:exclusively_dependent</tt> -
|
320
|
+
# * <tt>:exclusively_dependent</tt> - Deprecated; equivalent to :dependent => :delete_all. If set to true all
|
321
|
+
# the associated object are deleted in one SQL statement without having their
|
318
322
|
# before_destroy callback run. This should only be used on associations that depend solely on this class and don't need to do any
|
319
323
|
# clean-up in before_destroy. The upside is that it's much faster, especially if there's a counter_cache involved.
|
320
324
|
# May not be set if :dependent is also set.
|
@@ -339,7 +343,8 @@ module ActiveRecord
|
|
339
343
|
options.assert_valid_keys(
|
340
344
|
:foreign_key, :class_name, :exclusively_dependent, :dependent,
|
341
345
|
:conditions, :order, :include, :finder_sql, :counter_sql,
|
342
|
-
:before_add, :after_add, :before_remove, :after_remove, :extend
|
346
|
+
:before_add, :after_add, :before_remove, :after_remove, :extend,
|
347
|
+
:group
|
343
348
|
)
|
344
349
|
|
345
350
|
options[:extend] = create_extension_module(association_id, extension) if block_given?
|
@@ -350,24 +355,28 @@ module ActiveRecord
|
|
350
355
|
require_association_class(association_class_name)
|
351
356
|
|
352
357
|
raise ArgumentError, ':dependent and :exclusively_dependent are mutually exclusive options. You may specify one or the other.' if options[:dependent] and options[:exclusively_dependent]
|
353
|
-
|
358
|
+
|
359
|
+
if options[:exclusively_dependent]
|
360
|
+
options[:dependent] = :delete_all
|
361
|
+
#warn "The :exclusively_dependent option is deprecated. Please use :dependent => :delete_all instead.")
|
362
|
+
end
|
363
|
+
|
354
364
|
# See HasManyAssociation#delete_records. Dependent associations
|
355
365
|
# delete children, otherwise foreign key is set to NULL.
|
356
366
|
case options[:dependent]
|
357
367
|
when :destroy, true
|
358
368
|
module_eval "before_destroy '#{association_name}.each { |o| o.destroy }'"
|
369
|
+
when :delete_all
|
370
|
+
module_eval "before_destroy { |record| #{association_class_name}.delete_all(%(#{association_class_primary_key_name} = \#{record.quoted_id})) }"
|
359
371
|
when :nullify
|
360
372
|
module_eval "before_destroy { |record| #{association_class_name}.update_all(%(#{association_class_primary_key_name} = NULL), %(#{association_class_primary_key_name} = \#{record.quoted_id})) }"
|
361
373
|
when nil, false
|
362
374
|
# pass
|
363
375
|
else
|
364
|
-
raise ArgumentError, 'The :dependent option expects either true, :destroy or :nullify'
|
365
|
-
end
|
366
|
-
|
367
|
-
if options[:exclusively_dependent]
|
368
|
-
module_eval "before_destroy { |record| #{association_class_name}.delete_all(%(#{association_class_primary_key_name} = \#{record.quoted_id})) }"
|
376
|
+
raise ArgumentError, 'The :dependent option expects either true, :destroy, :delete_all, or :nullify'
|
369
377
|
end
|
370
378
|
|
379
|
+
|
371
380
|
add_multiple_associated_save_callbacks(association_name)
|
372
381
|
add_association_callbacks(association_name, options)
|
373
382
|
|
@@ -646,8 +655,17 @@ module ActiveRecord
|
|
646
655
|
|
647
656
|
collection_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, HasAndBelongsToManyAssociation)
|
648
657
|
|
649
|
-
|
650
|
-
|
658
|
+
# Don't use a before_destroy callback since users' before_destroy
|
659
|
+
# callbacks will be executed after the association is wiped out.
|
660
|
+
old_method = "destroy_without_habtm_shim_for_#{association_name}"
|
661
|
+
class_eval <<-end_eval
|
662
|
+
alias_method :#{old_method}, :destroy_without_callbacks
|
663
|
+
def destroy_without_callbacks
|
664
|
+
#{association_name}.clear
|
665
|
+
#{old_method}
|
666
|
+
end
|
667
|
+
end_eval
|
668
|
+
|
651
669
|
add_association_callbacks(association_name, options)
|
652
670
|
|
653
671
|
# deprecated api
|
@@ -791,6 +809,9 @@ module ActiveRecord
|
|
791
809
|
records_to_save.each { |record| association.send(:insert_record, record) }
|
792
810
|
association.send(:construct_sql) # reconstruct the SQL queries now that we know the owner's id
|
793
811
|
end
|
812
|
+
|
813
|
+
@new_record_before_save = false
|
814
|
+
true
|
794
815
|
end_eval
|
795
816
|
|
796
817
|
# Doesn't use after_save as that would save associations added in after_create/after_update twice
|
@@ -959,9 +980,10 @@ module ActiveRecord
|
|
959
980
|
end
|
960
981
|
|
961
982
|
def include_eager_conditions?(options)
|
962
|
-
|
963
|
-
|
964
|
-
|
983
|
+
conditions = options[:conditions]
|
984
|
+
return false unless conditions
|
985
|
+
conditions = conditions.first if conditions.is_a?(Array)
|
986
|
+
conditions.scan(/(\w+)\.\w+/).flatten.any? do |condition_table_name|
|
965
987
|
condition_table_name != table_name
|
966
988
|
end
|
967
989
|
end
|
@@ -981,7 +1003,7 @@ module ActiveRecord
|
|
981
1003
|
end
|
982
1004
|
|
983
1005
|
def column_aliases(schema_abbreviations)
|
984
|
-
schema_abbreviations.collect { |cn, tc| "#{tc.
|
1006
|
+
schema_abbreviations.collect { |cn, tc| "#{tc[0]}.#{connection.quote_column_name tc[1]} AS #{cn}" }.join(", ")
|
985
1007
|
end
|
986
1008
|
|
987
1009
|
def association_join(reflection)
|
@@ -65,7 +65,7 @@ module ActiveRecord
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def construct_sql
|
68
|
-
@finder_sql = "#{@association_class.table_name}.#{@association_class_primary_key_name} = #{@owner.quoted_id}
|
68
|
+
@finder_sql = "#{@association_class.table_name}.#{@association_class_primary_key_name} = #{@owner.quoted_id}"
|
69
69
|
@finder_sql << " AND (#{sanitize_sql(@options[:conditions])})" if @options[:conditions]
|
70
70
|
@finder_sql
|
71
71
|
end
|
data/lib/active_record/base.rb
CHANGED
@@ -243,24 +243,6 @@ module ActiveRecord #:nodoc:
|
|
243
243
|
# on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
|
244
244
|
cattr_accessor :logger
|
245
245
|
|
246
|
-
# Returns the connection currently associated with the class. This can
|
247
|
-
# also be used to "borrow" the connection to do database work unrelated
|
248
|
-
# to any of the specific Active Records.
|
249
|
-
def self.connection
|
250
|
-
if allow_concurrency
|
251
|
-
retrieve_connection
|
252
|
-
else
|
253
|
-
@connection ||= retrieve_connection
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
# Returns the connection currently associated with the class. This can
|
258
|
-
# also be used to "borrow" the connection to do database work that isn't
|
259
|
-
# easily done without going straight to SQL.
|
260
|
-
def connection
|
261
|
-
self.class.connection
|
262
|
-
end
|
263
|
-
|
264
246
|
def self.inherited(child) #:nodoc:
|
265
247
|
@@subclasses[self] ||= []
|
266
248
|
@@subclasses[self] << child
|
@@ -363,6 +345,7 @@ module ActiveRecord #:nodoc:
|
|
363
345
|
#
|
364
346
|
# * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro.
|
365
347
|
# * <tt>:order</tt>: An SQL fragment like "created_at DESC, name".
|
348
|
+
# * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
|
366
349
|
# * <tt>:limit</tt>: An integer determining the limit on the number of rows that should be returned.
|
367
350
|
# * <tt>:offset</tt>: An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
|
368
351
|
# * <tt>:joins</tt>: An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
|
@@ -391,6 +374,7 @@ module ActiveRecord #:nodoc:
|
|
391
374
|
# Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
|
392
375
|
# Person.find(:all, :offset => 10, :limit => 10)
|
393
376
|
# Person.find(:all, :include => [ :account, :friends ])
|
377
|
+
# Person.find(:all, :group => "category")
|
394
378
|
def find(*args)
|
395
379
|
options = extract_options_from_args!(args)
|
396
380
|
|
@@ -511,7 +495,7 @@ module ActiveRecord #:nodoc:
|
|
511
495
|
|
512
496
|
# Deletes all the records that match the +condition+ without instantiating the objects first (and hence not
|
513
497
|
# calling the destroy method). Example:
|
514
|
-
# Post.
|
498
|
+
# Post.delete_all "person_id = 5 AND (category = 'Something' OR category = 'Else')"
|
515
499
|
def delete_all(conditions = nil)
|
516
500
|
sql = "DELETE FROM #{table_name} "
|
517
501
|
add_conditions!(sql, conditions)
|
@@ -644,9 +628,16 @@ module ActiveRecord #:nodoc:
|
|
644
628
|
"type"
|
645
629
|
end
|
646
630
|
|
647
|
-
#
|
631
|
+
# Lazy-set the sequence name to the connection's default. This method
|
632
|
+
# is only ever called once since set_sequence_name overrides it.
|
648
633
|
def sequence_name
|
649
|
-
|
634
|
+
reset_sequence_name
|
635
|
+
end
|
636
|
+
|
637
|
+
def reset_sequence_name
|
638
|
+
default = connection.default_sequence_name(table_name, primary_key)
|
639
|
+
set_sequence_name(default)
|
640
|
+
default
|
650
641
|
end
|
651
642
|
|
652
643
|
# Sets the table name to use to the given value, or (if the value
|
@@ -697,9 +688,11 @@ module ActiveRecord #:nodoc:
|
|
697
688
|
# given block. This is required for Oracle and is useful for any
|
698
689
|
# database which relies on sequences for primary key generation.
|
699
690
|
#
|
700
|
-
#
|
701
|
-
#
|
702
|
-
#
|
691
|
+
# If a sequence name is not explicitly set when using Oracle or Firebird,
|
692
|
+
# it will default to the commonly used pattern of: #{table_name}_seq
|
693
|
+
#
|
694
|
+
# If a sequence name is not explicitly set when using PostgreSQL, it
|
695
|
+
# will discover the sequence corresponding to your primary key for you.
|
703
696
|
#
|
704
697
|
# Example:
|
705
698
|
#
|
@@ -926,6 +919,7 @@ module ActiveRecord #:nodoc:
|
|
926
919
|
sql = "SELECT #{options[:select] || '*'} FROM #{table_name} "
|
927
920
|
add_joins!(sql, options)
|
928
921
|
add_conditions!(sql, options[:conditions])
|
922
|
+
sql << " GROUP BY #{options[:group]} " if options[:group]
|
929
923
|
sql << " ORDER BY #{options[:order]} " if options[:order]
|
930
924
|
add_limit!(sql, options)
|
931
925
|
sql
|
@@ -952,8 +946,9 @@ module ActiveRecord #:nodoc:
|
|
952
946
|
end
|
953
947
|
|
954
948
|
def type_condition
|
955
|
-
|
956
|
-
|
949
|
+
quoted_inheritance_column = connection.quote_column_name(inheritance_column)
|
950
|
+
type_condition = subclasses.inject("#{table_name}.#{quoted_inheritance_column} = '#{name.demodulize}' ") do |condition, subclass|
|
951
|
+
condition << "OR #{table_name}.#{quoted_inheritance_column} = '#{subclass.name.demodulize}' "
|
957
952
|
end
|
958
953
|
|
959
954
|
" (#{type_condition}) "
|
@@ -1007,7 +1002,7 @@ module ActiveRecord #:nodoc:
|
|
1007
1002
|
|
1008
1003
|
def construct_conditions_from_arguments(attribute_names, arguments)
|
1009
1004
|
conditions = []
|
1010
|
-
attribute_names.each_with_index { |name, idx| conditions << "#{table_name}.#{name} #{attribute_condition(arguments[idx])} " }
|
1005
|
+
attribute_names.each_with_index { |name, idx| conditions << "#{table_name}.#{connection.quote_column_name(name)} #{attribute_condition(arguments[idx])} " }
|
1011
1006
|
[ conditions.join(" AND "), *arguments[0...attribute_names.length] ]
|
1012
1007
|
end
|
1013
1008
|
|
@@ -1050,12 +1045,12 @@ module ActiveRecord #:nodoc:
|
|
1050
1045
|
def define_attr_method(name, value=nil, &block)
|
1051
1046
|
sing = class << self; self; end
|
1052
1047
|
sing.send :alias_method, "original_#{name}", name
|
1053
|
-
if
|
1048
|
+
if block_given?
|
1049
|
+
sing.send :define_method, name, &block
|
1050
|
+
else
|
1054
1051
|
# use eval instead of a block to work around a memory leak in dev
|
1055
1052
|
# mode in fcgi
|
1056
1053
|
sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
|
1057
|
-
else
|
1058
|
-
sing.send :define_method, name, &block
|
1059
1054
|
end
|
1060
1055
|
end
|
1061
1056
|
|
@@ -1171,7 +1166,7 @@ module ActiveRecord #:nodoc:
|
|
1171
1166
|
end
|
1172
1167
|
|
1173
1168
|
def validate_find_options(options)
|
1174
|
-
options.assert_valid_keys [:conditions, :include, :joins, :limit, :offset, :order, :select, :readonly]
|
1169
|
+
options.assert_valid_keys [:conditions, :include, :joins, :limit, :offset, :order, :select, :readonly, :group]
|
1175
1170
|
end
|
1176
1171
|
|
1177
1172
|
def encode_quoted_value(value)
|
@@ -1244,7 +1239,11 @@ module ActiveRecord #:nodoc:
|
|
1244
1239
|
freeze
|
1245
1240
|
end
|
1246
1241
|
|
1247
|
-
# Returns a clone of the record that hasn't been assigned an id yet and
|
1242
|
+
# Returns a clone of the record that hasn't been assigned an id yet and
|
1243
|
+
# is treated as a new record. Note that this is a "shallow" clone:
|
1244
|
+
# it copies the object's attributes only, not its associations.
|
1245
|
+
# The extent of a "deep" clone is application-specific and is therefore
|
1246
|
+
# left to the application to implement according to its need.
|
1248
1247
|
def clone
|
1249
1248
|
attrs = self.attributes_before_type_cast
|
1250
1249
|
attrs.delete(self.class.primary_key)
|
@@ -1305,6 +1304,7 @@ module ActiveRecord #:nodoc:
|
|
1305
1304
|
|
1306
1305
|
# Reloads the attributes of this object from the database.
|
1307
1306
|
def reload
|
1307
|
+
clear_aggregation_cache
|
1308
1308
|
clear_association_cache
|
1309
1309
|
@attributes.update(self.class.find(self.id).instance_variable_get('@attributes'))
|
1310
1310
|
self
|
@@ -1443,6 +1443,10 @@ module ActiveRecord #:nodoc:
|
|
1443
1443
|
|
1444
1444
|
# Creates a new record with values matching those of the instance attributes.
|
1445
1445
|
def create
|
1446
|
+
if self.id.nil? and connection.prefetch_primary_key?(self.class.table_name)
|
1447
|
+
self.id = connection.next_sequence_value(self.class.sequence_name)
|
1448
|
+
end
|
1449
|
+
|
1446
1450
|
self.id = connection.insert(
|
1447
1451
|
"INSERT INTO #{self.class.table_name} " +
|
1448
1452
|
"(#{quoted_column_names.join(', ')}) " +
|
@@ -1542,7 +1546,16 @@ module ActiveRecord #:nodoc:
|
|
1542
1546
|
self.class.read_methods << attr_name
|
1543
1547
|
end
|
1544
1548
|
|
1545
|
-
|
1549
|
+
begin
|
1550
|
+
self.class.class_eval("def #{symbol}; #{access_code}; end")
|
1551
|
+
rescue SyntaxError => err
|
1552
|
+
self.class.read_methods.delete(attr_name)
|
1553
|
+
if logger
|
1554
|
+
logger.warn "Exception occured during reader method compilation."
|
1555
|
+
logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
|
1556
|
+
logger.warn "#{err.message}"
|
1557
|
+
end
|
1558
|
+
end
|
1546
1559
|
end
|
1547
1560
|
|
1548
1561
|
# Returns true if the attribute is of a text column and marked for serialization.
|