activerecord 3.0.0.beta → 3.0.0.beta2

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.

Files changed (53) hide show
  1. data/CHANGELOG +8 -1
  2. data/lib/active_record.rb +9 -6
  3. data/lib/active_record/aggregations.rb +5 -0
  4. data/lib/active_record/association_preload.rb +7 -2
  5. data/lib/active_record/associations.rb +74 -54
  6. data/lib/active_record/associations/association_collection.rb +1 -0
  7. data/lib/active_record/associations/association_proxy.rb +2 -1
  8. data/lib/active_record/associations/has_many_association.rb +4 -0
  9. data/lib/active_record/associations/has_many_through_association.rb +1 -0
  10. data/lib/active_record/attribute_methods/dirty.rb +11 -9
  11. data/lib/active_record/attribute_methods/primary_key.rb +6 -0
  12. data/lib/active_record/attribute_methods/query.rb +2 -0
  13. data/lib/active_record/base.rb +57 -212
  14. data/lib/active_record/callbacks.rb +10 -0
  15. data/lib/active_record/connection_adapters/abstract/database_statements.rb +24 -1
  16. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -0
  17. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +10 -5
  18. data/lib/active_record/connection_adapters/abstract_adapter.rb +1 -0
  19. data/lib/active_record/connection_adapters/mysql_adapter.rb +22 -5
  20. data/lib/active_record/connection_adapters/postgresql_adapter.rb +34 -8
  21. data/lib/active_record/dynamic_finder_match.rb +3 -0
  22. data/lib/active_record/errors.rb +165 -0
  23. data/lib/active_record/fixtures.rb +1 -0
  24. data/lib/active_record/migration.rb +8 -6
  25. data/lib/active_record/named_scope.rb +14 -5
  26. data/lib/active_record/nested_attributes.rb +6 -2
  27. data/lib/active_record/query_cache.rb +2 -0
  28. data/lib/active_record/railtie.rb +30 -19
  29. data/lib/active_record/railties/databases.rake +13 -7
  30. data/lib/active_record/railties/{subscriber.rb → log_subscriber.rb} +7 -2
  31. data/lib/active_record/reflection.rb +5 -3
  32. data/lib/active_record/relation.rb +13 -2
  33. data/lib/active_record/relation/batches.rb +84 -0
  34. data/lib/active_record/relation/calculations.rb +2 -0
  35. data/lib/active_record/relation/finder_methods.rb +13 -2
  36. data/lib/active_record/relation/predicate_builder.rb +2 -7
  37. data/lib/active_record/relation/query_methods.rb +20 -27
  38. data/lib/active_record/relation/spawn_methods.rb +18 -28
  39. data/lib/active_record/schema.rb +2 -0
  40. data/lib/active_record/validations/uniqueness.rb +2 -4
  41. data/lib/active_record/version.rb +3 -2
  42. data/lib/{generators → rails/generators}/active_record.rb +0 -0
  43. data/lib/{generators → rails/generators}/active_record/migration/migration_generator.rb +1 -1
  44. data/lib/{generators → rails/generators}/active_record/migration/templates/migration.rb +0 -0
  45. data/lib/{generators → rails/generators}/active_record/model/model_generator.rb +1 -1
  46. data/lib/{generators → rails/generators}/active_record/model/templates/migration.rb +0 -0
  47. data/lib/{generators → rails/generators}/active_record/model/templates/model.rb +0 -0
  48. data/lib/{generators → rails/generators}/active_record/observer/observer_generator.rb +1 -1
  49. data/lib/{generators → rails/generators}/active_record/observer/templates/observer.rb +0 -0
  50. data/lib/{generators → rails/generators}/active_record/session_migration/session_migration_generator.rb +1 -1
  51. data/lib/{generators → rails/generators}/active_record/session_migration/templates/migration.rb +0 -0
  52. metadata +61 -34
  53. data/lib/active_record/batches.rb +0 -79
data/CHANGELOG CHANGED
@@ -1,4 +1,11 @@
1
- *Edge*
1
+ *Rails 3.0.0 [Beta 2] (pending)*
2
+
3
+ * To prefix the table names of all models in a module, define self.table_name_prefix on the module. #4032 [Andrew White]
4
+
5
+ * Silenced "SHOW FIELDS" and "SET SQL_AUTO_IS_NULL=0" statements from the MySQL driver to improve log signal to noise ration in development [DHH]
6
+
7
+
8
+ *Rails 3.0.0 [Beta 1] (February 4th, 2010)*
2
9
 
3
10
  * PostgreSQLAdapter: set time_zone to UTC when Base.default_timezone == :utc so that Postgres doesn't incorrectly offset-adjust values inserted into TIMESTAMP WITH TIME ZONE columns. #3777 [Jack Christensen]
4
11
 
@@ -30,7 +30,6 @@ $:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include
30
30
 
31
31
  require 'active_support'
32
32
  require 'active_model'
33
- require 'arel'
34
33
 
35
34
  module ActiveRecord
36
35
  extend ActiveSupport::Autoload
@@ -38,8 +37,8 @@ module ActiveRecord
38
37
  eager_autoload do
39
38
  autoload :VERSION
40
39
 
41
- autoload :ActiveRecordError, 'active_record/base'
42
- autoload :ConnectionNotEstablished, 'active_record/base'
40
+ autoload :ActiveRecordError, 'active_record/errors'
41
+ autoload :ConnectionNotEstablished, 'active_record/errors'
43
42
 
44
43
  autoload :Aggregations
45
44
  autoload :AssociationPreload
@@ -55,10 +54,10 @@ module ActiveRecord
55
54
  autoload :Calculations
56
55
  autoload :PredicateBuilder
57
56
  autoload :SpawnMethods
57
+ autoload :Batches
58
58
  end
59
59
 
60
60
  autoload :Base
61
- autoload :Batches
62
61
  autoload :Callbacks
63
62
  autoload :DynamicFinderMatch
64
63
  autoload :DynamicScopeMatch
@@ -106,6 +105,7 @@ module ActiveRecord
106
105
 
107
106
  eager_autoload do
108
107
  autoload :AbstractAdapter
108
+ autoload :ConnectionManagement, "active_record/connection_adapters/abstract/connection_pool"
109
109
  end
110
110
  end
111
111
 
@@ -113,5 +113,8 @@ module ActiveRecord
113
113
  autoload :TestFixtures, 'active_record/fixtures'
114
114
  end
115
115
 
116
- Arel::Table.engine = Arel::Sql::Engine.new(ActiveRecord::Base)
117
- I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
116
+ ActiveSupport.on_load(:active_record) do
117
+ Arel::Table.engine = Arel::Sql::Engine.new(self)
118
+ end
119
+
120
+ I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
@@ -213,6 +213,11 @@ module ActiveRecord
213
213
  module_eval do
214
214
  define_method(name) do |*args|
215
215
  force_reload = args.first || false
216
+
217
+ unless instance_variable_defined?("@#{name}")
218
+ instance_variable_set("@#{name}", nil)
219
+ end
220
+
216
221
  if (instance_variable_get("@#{name}").nil? || force_reload) && (!allow_nil || mapping.any? {|pair| !read_attribute(pair.first).nil? })
217
222
  attrs = mapping.collect {|pair| read_attribute(pair.first)}
218
223
  object = case constructor
@@ -126,7 +126,7 @@ module ActiveRecord
126
126
  parent_records.each do |parent_record|
127
127
  association_proxy = parent_record.send(reflection_name)
128
128
  association_proxy.loaded
129
- association_proxy.target.push *Array.wrap(associated_record)
129
+ association_proxy.target.push(*Array.wrap(associated_record))
130
130
 
131
131
  association_proxy.__send__(:set_inverse_instance, associated_record, parent_record)
132
132
  end
@@ -159,6 +159,11 @@ module ActiveRecord
159
159
  association_proxy.__send__(:set_inverse_instance, associated_record, mapped_record)
160
160
  end
161
161
  end
162
+
163
+ id_to_record_map.each do |id, records|
164
+ next if seen_keys.include?(id.to_s)
165
+ records.each {|record| record.send("set_#{reflection_name}_target", nil) }
166
+ end
162
167
  end
163
168
 
164
169
  # Given a collection of ActiveRecord objects, constructs a Hash which maps
@@ -324,7 +329,7 @@ module ActiveRecord
324
329
  klass = klass_name.constantize
325
330
 
326
331
  table_name = klass.quoted_table_name
327
- primary_key = klass.primary_key
332
+ primary_key = reflection.options[:primary_key] || klass.primary_key
328
333
  column_type = klass.columns.detect{|c| c.name == primary_key}.type
329
334
 
330
335
  ids = id_map.keys.map do |id|
@@ -1,5 +1,6 @@
1
1
  require 'active_support/core_ext/module/delegation'
2
2
  require 'active_support/core_ext/enumerable'
3
+ require 'active_support/core_ext/object/blank'
3
4
 
4
5
  module ActiveRecord
5
6
  class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
@@ -85,6 +86,15 @@ module ActiveRecord
85
86
  end
86
87
  end
87
88
 
89
+ # This error is raised when trying to destroy a parent instance in a N:1, 1:1 assosications
90
+ # (has_many, has_one) when there is at least 1 child assosociated instance.
91
+ # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
92
+ class DeleteRestrictionError < ActiveRecordError #:nodoc:
93
+ def initialize(reflection)
94
+ super("Cannot delete record because of dependent #{reflection.name}")
95
+ end
96
+ end
97
+
88
98
  # See ActiveRecord::Associations::ClassMethods for documentation.
89
99
  module Associations # :nodoc:
90
100
  extend ActiveSupport::Concern
@@ -111,8 +121,11 @@ module ActiveRecord
111
121
  private
112
122
  # Gets the specified association instance if it responds to :loaded?, nil otherwise.
113
123
  def association_instance_get(name)
114
- association = instance_variable_get("@#{name}")
115
- association if association.respond_to?(:loaded?)
124
+ ivar = "@#{name}"
125
+ if instance_variable_defined?(ivar)
126
+ association = instance_variable_get(ivar)
127
+ association if association.respond_to?(:loaded?)
128
+ end
116
129
  end
117
130
 
118
131
  # Set the specified association instance.
@@ -827,6 +840,8 @@ module ActiveRecord
827
840
  # objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
828
841
  # objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. *Warning:* This option is ignored when also using
829
842
  # the <tt>:through</tt> option.
843
+ # the <tt>:through</tt> option. If set to <tt>:restrict</tt>
844
+ # this object cannot be deleted if it has any associated object.
830
845
  # [:finder_sql]
831
846
  # Specify a complete SQL statement to fetch the association. This is a good way to go for complex
832
847
  # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
@@ -1207,7 +1222,7 @@ module ActiveRecord
1207
1222
  # * <tt>Developer#projects.empty?</tt>
1208
1223
  # * <tt>Developer#projects.size</tt>
1209
1224
  # * <tt>Developer#projects.find(id)</tt>
1210
- # * <tt>Developer#clients.exists?(...)</tt>
1225
+ # * <tt>Developer#projects.exists?(...)</tt>
1211
1226
  # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("project_id" => id)</tt>)
1212
1227
  # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("project_id" => id); c.save; c</tt>)
1213
1228
  # The declaration may include an options hash to specialize the behavior of the association.
@@ -1465,9 +1480,15 @@ module ActiveRecord
1465
1480
 
1466
1481
  # Creates before_destroy callback methods that nullify, delete or destroy
1467
1482
  # has_many associated objects, according to the defined :dependent rule.
1483
+ # If the association is marked as :dependent => :restrict, create a callback
1484
+ # that prevents deleting entirely.
1468
1485
  #
1469
1486
  # See HasManyAssociation#delete_records. Dependent associations
1470
1487
  # delete children, otherwise foreign key is set to NULL.
1488
+ # See HasManyAssociation#delete_records. Dependent associations
1489
+ # delete children if the option is set to :destroy or :delete_all, set the
1490
+ # foreign key to NULL if the option is set to :nullify, and do not touch the
1491
+ # child records if the option is set to :restrict.
1471
1492
  #
1472
1493
  # The +extra_conditions+ parameter, which is not used within the main
1473
1494
  # Active Record codebase, is meant to allow plugins to define extra
@@ -1527,68 +1548,76 @@ module ActiveRecord
1527
1548
  %@#{dependent_conditions}@)
1528
1549
  end
1529
1550
  CALLBACK
1551
+ when :restrict
1552
+ method_name = "has_many_dependent_restrict_for_#{reflection.name}".to_sym
1553
+ define_method(method_name) do
1554
+ unless send(reflection.name).empty?
1555
+ raise DeleteRestrictionError.new(reflection)
1556
+ end
1557
+ end
1558
+ before_destroy method_name
1530
1559
  else
1531
- raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
1560
+ raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, :nullify or :restrict (#{reflection.options[:dependent].inspect})"
1532
1561
  end
1533
1562
  end
1534
1563
  end
1535
1564
 
1536
1565
  # Creates before_destroy callback methods that nullify, delete or destroy
1537
1566
  # has_one associated objects, according to the defined :dependent rule.
1567
+ # If the association is marked as :dependent => :restrict, create a callback
1568
+ # that prevents deleting entirely.
1538
1569
  def configure_dependency_for_has_one(reflection)
1539
1570
  if reflection.options.include?(:dependent)
1540
- case reflection.options[:dependent]
1541
- when :destroy
1542
- method_name = "has_one_dependent_destroy_for_#{reflection.name}".to_sym
1543
- define_method(method_name) do
1544
- association = send(reflection.name)
1545
- association.destroy unless association.nil?
1546
- end
1547
- before_destroy method_name
1548
- when :delete
1549
- method_name = "has_one_dependent_delete_for_#{reflection.name}".to_sym
1550
- define_method(method_name) do
1551
- # Retrieve the associated object and delete it. The retrieval
1552
- # is necessary because there may be multiple associated objects
1553
- # with foreign keys pointing to this object, and we only want
1554
- # to delete the correct one, not all of them.
1555
- association = send(reflection.name)
1556
- association.delete unless association.nil?
1557
- end
1558
- before_destroy method_name
1571
+ name = reflection.options[:dependent]
1572
+ method_name = :"has_one_dependent_#{name}_for_#{reflection.name}"
1573
+
1574
+ case name
1575
+ when :destroy, :delete
1576
+ class_eval <<-eoruby, __FILE__, __LINE__ + 1
1577
+ def #{method_name}
1578
+ association = #{reflection.name}
1579
+ association.#{name} if association
1580
+ end
1581
+ eoruby
1559
1582
  when :nullify
1560
- method_name = "has_one_dependent_nullify_for_#{reflection.name}".to_sym
1583
+ class_eval <<-eoruby, __FILE__, __LINE__ + 1
1584
+ def #{method_name}
1585
+ association = #{reflection.name}
1586
+ association.update_attribute(#{reflection.primary_key_name.inspect}, nil) if association
1587
+ end
1588
+ eoruby
1589
+ when :restrict
1590
+ method_name = "has_one_dependent_restrict_for_#{reflection.name}".to_sym
1561
1591
  define_method(method_name) do
1562
- association = send(reflection.name)
1563
- association.update_attribute(reflection.primary_key_name, nil) unless association.nil?
1592
+ unless send(reflection.name).nil?
1593
+ raise DeleteRestrictionError.new(reflection)
1594
+ end
1564
1595
  end
1565
1596
  before_destroy method_name
1566
1597
  else
1567
- raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify (#{reflection.options[:dependent].inspect})"
1598
+ raise ArgumentError, "The :dependent option expects either :destroy, :delete, :nullify or :restrict (#{reflection.options[:dependent].inspect})"
1568
1599
  end
1600
+
1601
+ before_destroy method_name
1569
1602
  end
1570
1603
  end
1571
1604
 
1572
1605
  def configure_dependency_for_belongs_to(reflection)
1573
1606
  if reflection.options.include?(:dependent)
1574
- case reflection.options[:dependent]
1575
- when :destroy
1576
- method_name = "belongs_to_dependent_destroy_for_#{reflection.name}".to_sym
1577
- define_method(method_name) do
1578
- association = send(reflection.name)
1579
- association.destroy unless association.nil?
1580
- end
1581
- after_destroy method_name
1582
- when :delete
1583
- method_name = "belongs_to_dependent_delete_for_#{reflection.name}".to_sym
1584
- define_method(method_name) do
1585
- association = send(reflection.name)
1586
- association.delete unless association.nil?
1587
- end
1588
- after_destroy method_name
1589
- else
1590
- raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{reflection.options[:dependent].inspect})"
1607
+ name = reflection.options[:dependent]
1608
+
1609
+ unless [:destroy, :delete].include?(name)
1610
+ raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{reflection.options[:dependent].inspect})"
1591
1611
  end
1612
+
1613
+ method_name = :"belongs_to_dependent_#{name}_for_#{reflection.name}"
1614
+ class_eval <<-eoruby, __FILE__, __LINE__ + 1
1615
+ def #{method_name}
1616
+ association = #{reflection.name}
1617
+ association.#{name} if association
1618
+ end
1619
+ eoruby
1620
+ after_destroy method_name
1592
1621
  end
1593
1622
  end
1594
1623
 
@@ -1686,15 +1715,6 @@ module ActiveRecord
1686
1715
  reflection
1687
1716
  end
1688
1717
 
1689
- def using_limitable_reflections?(reflections)
1690
- reflections.collect(&:collection?).length.zero?
1691
- end
1692
-
1693
- def column_aliases(join_dependency)
1694
- join_dependency.joins.collect{|join| join.column_names_with_alias.collect{|column_name, aliased_name|
1695
- "#{connection.quote_table_name join.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}"}}.flatten.join(", ")
1696
- end
1697
-
1698
1718
  def add_association_callbacks(association_name, options)
1699
1719
  callbacks = %w(before_add after_add before_remove after_remove)
1700
1720
  callbacks.each do |callback_name|
@@ -2011,7 +2031,7 @@ module ActiveRecord
2011
2031
  [aliased_table[foreign_key].eq(parent_table[reflection.options[:primary_key] || parent.primary_key])]
2012
2032
  end
2013
2033
  when :belongs_to
2014
- [aliased_table[reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name])]
2034
+ [aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name])]
2015
2035
  end
2016
2036
 
2017
2037
  unless klass.descends_from_active_record?
@@ -101,6 +101,7 @@ module ActiveRecord
101
101
  Array(@target)
102
102
  end
103
103
  end
104
+ alias_method :to_a, :to_ary
104
105
 
105
106
  def reset
106
107
  reset_target!
@@ -49,10 +49,11 @@ module ActiveRecord
49
49
  alias_method :proxy_respond_to?, :respond_to?
50
50
  alias_method :proxy_extend, :extend
51
51
  delegate :to_param, :to => :proxy_target
52
- instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
52
+ instance_methods.each { |m| undef_method m unless m =~ /^(?:nil\?|send|object_id|to_a)$|^__|proxy_/ }
53
53
 
54
54
  def initialize(owner, reflection)
55
55
  @owner, @reflection = owner, reflection
56
+ @updated = false
56
57
  reflection.check_validity!
57
58
  Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
58
59
  reset
@@ -5,6 +5,10 @@ module ActiveRecord
5
5
  # If the association has a <tt>:through</tt> option further specialization
6
6
  # is provided by its child HasManyThroughAssociation.
7
7
  class HasManyAssociation < AssociationCollection #:nodoc:
8
+ def initialize(owner, reflection)
9
+ @finder_sql = nil
10
+ super
11
+ end
8
12
  protected
9
13
  def owner_quoted_id
10
14
  if @reflection.options[:primary_key]
@@ -1,4 +1,5 @@
1
1
  require "active_record/associations/through_association_scope"
2
+ require 'active_support/core_ext/object/blank'
2
3
 
3
4
  module ActiveRecord
4
5
  module Associations
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/object/blank'
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  module Dirty
@@ -18,7 +20,7 @@ module ActiveRecord
18
20
  def save_with_dirty(*args) #:nodoc:
19
21
  if status = save_without_dirty(*args)
20
22
  @previously_changed = changes
21
- changed_attributes.clear
23
+ @changed_attributes.clear
22
24
  end
23
25
  status
24
26
  end
@@ -26,16 +28,16 @@ module ActiveRecord
26
28
  # Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
27
29
  def save_with_dirty!(*args) #:nodoc:
28
30
  save_without_dirty!(*args).tap do
29
- @previously_changed = changes
30
- changed_attributes.clear
31
+ @previously_changed = changes
32
+ @changed_attributes.clear
31
33
  end
32
34
  end
33
35
 
34
36
  # <tt>reload</tt> the record and clears changed attributes.
35
37
  def reload_with_dirty(*args) #:nodoc:
36
38
  reload_without_dirty(*args).tap do
37
- previously_changed_attributes.clear
38
- changed_attributes.clear
39
+ @previously_changed.clear
40
+ @changed_attributes.clear
39
41
  end
40
42
  end
41
43
 
@@ -45,12 +47,12 @@ module ActiveRecord
45
47
  attr = attr.to_s
46
48
 
47
49
  # The attribute already has an unsaved change.
48
- if changed_attributes.include?(attr)
49
- old = changed_attributes[attr]
50
- changed_attributes.delete(attr) unless field_changed?(attr, old, value)
50
+ if attribute_changed?(attr)
51
+ old = @changed_attributes[attr]
52
+ @changed_attributes.delete(attr) unless field_changed?(attr, old, value)
51
53
  else
52
54
  old = clone_attribute_value(:read_attribute, attr)
53
- changed_attributes[attr] = old if field_changed?(attr, old, value)
55
+ @changed_attributes[attr] = old if field_changed?(attr, old, value)
54
56
  end
55
57
 
56
58
  # Carry on.
@@ -3,6 +3,12 @@ module ActiveRecord
3
3
  module PrimaryKey
4
4
  extend ActiveSupport::Concern
5
5
 
6
+ # Returns this record's primary key value wrapped in an Array
7
+ # or nil if the record is a new_record?
8
+ def to_key
9
+ new_record? ? nil : [ send(self.class.primary_key) ]
10
+ end
11
+
6
12
  module ClassMethods
7
13
  # Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
8
14
  # primary_key_prefix_type setting, though.
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/object/blank'
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  module Query
@@ -11,174 +11,14 @@ require 'active_support/core_ext/hash/deep_merge'
11
11
  require 'active_support/core_ext/hash/indifferent_access'
12
12
  require 'active_support/core_ext/hash/slice'
13
13
  require 'active_support/core_ext/string/behavior'
14
- require 'active_support/core_ext/object/metaclass'
14
+ require 'active_support/core_ext/object/singleton_class'
15
15
  require 'active_support/core_ext/module/delegation'
16
+ require 'active_support/core_ext/object/duplicable'
17
+ require 'active_support/core_ext/object/blank'
18
+ require 'arel'
19
+ require 'active_record/errors'
16
20
 
17
21
  module ActiveRecord #:nodoc:
18
- # Generic Active Record exception class.
19
- class ActiveRecordError < StandardError
20
- end
21
-
22
- # Raised when the single-table inheritance mechanism fails to locate the subclass
23
- # (for example due to improper usage of column that +inheritance_column+ points to).
24
- class SubclassNotFound < ActiveRecordError #:nodoc:
25
- end
26
-
27
- # Raised when an object assigned to an association has an incorrect type.
28
- #
29
- # class Ticket < ActiveRecord::Base
30
- # has_many :patches
31
- # end
32
- #
33
- # class Patch < ActiveRecord::Base
34
- # belongs_to :ticket
35
- # end
36
- #
37
- # # Comments are not patches, this assignment raises AssociationTypeMismatch.
38
- # @ticket.patches << Comment.new(:content => "Please attach tests to your patch.")
39
- class AssociationTypeMismatch < ActiveRecordError
40
- end
41
-
42
- # Raised when unserialized object's type mismatches one specified for serializable field.
43
- class SerializationTypeMismatch < ActiveRecordError
44
- end
45
-
46
- # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt> misses adapter field).
47
- class AdapterNotSpecified < ActiveRecordError
48
- end
49
-
50
- # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
51
- class AdapterNotFound < ActiveRecordError
52
- end
53
-
54
- # Raised when connection to the database could not been established (for example when <tt>connection=</tt> is given a nil object).
55
- class ConnectionNotEstablished < ActiveRecordError
56
- end
57
-
58
- # Raised when Active Record cannot find record by given id or set of ids.
59
- class RecordNotFound < ActiveRecordError
60
- end
61
-
62
- # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
63
- # saved because record is invalid.
64
- class RecordNotSaved < ActiveRecordError
65
- end
66
-
67
- # Raised when SQL statement cannot be executed by the database (for example, it's often the case for MySQL when Ruby driver used is too old).
68
- class StatementInvalid < ActiveRecordError
69
- end
70
-
71
- # Raised when SQL statement is invalid and the application gets a blank result.
72
- class ThrowResult < ActiveRecordError
73
- end
74
-
75
- # Parent class for all specific exceptions which wrap database driver exceptions
76
- # provides access to the original exception also.
77
- class WrappedDatabaseException < StatementInvalid
78
- attr_reader :original_exception
79
-
80
- def initialize(message, original_exception)
81
- super(message)
82
- @original_exception = original_exception
83
- end
84
- end
85
-
86
- # Raised when a record cannot be inserted because it would violate a uniqueness constraint.
87
- class RecordNotUnique < WrappedDatabaseException
88
- end
89
-
90
- # Raised when a record cannot be inserted or updated because it references a non-existent record.
91
- class InvalidForeignKey < WrappedDatabaseException
92
- end
93
-
94
- # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example, when using +find+ method)
95
- # does not match number of expected variables.
96
- #
97
- # For example, in
98
- #
99
- # Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362]
100
- #
101
- # two placeholders are given but only one variable to fill them.
102
- class PreparedStatementInvalid < ActiveRecordError
103
- end
104
-
105
- # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
106
- # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
107
- # the page before the other.
108
- #
109
- # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
110
- class StaleObjectError < ActiveRecordError
111
- end
112
-
113
- # Raised when association is being configured improperly or
114
- # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
115
- class ConfigurationError < ActiveRecordError
116
- end
117
-
118
- # Raised on attempt to update record that is instantiated as read only.
119
- class ReadOnlyRecord < ActiveRecordError
120
- end
121
-
122
- # ActiveRecord::Transactions::ClassMethods.transaction uses this exception
123
- # to distinguish a deliberate rollback from other exceptional situations.
124
- # Normally, raising an exception will cause the +transaction+ method to rollback
125
- # the database transaction *and* pass on the exception. But if you raise an
126
- # ActiveRecord::Rollback exception, then the database transaction will be rolled back,
127
- # without passing on the exception.
128
- #
129
- # For example, you could do this in your controller to rollback a transaction:
130
- #
131
- # class BooksController < ActionController::Base
132
- # def create
133
- # Book.transaction do
134
- # book = Book.new(params[:book])
135
- # book.save!
136
- # if today_is_friday?
137
- # # The system must fail on Friday so that our support department
138
- # # won't be out of job. We silently rollback this transaction
139
- # # without telling the user.
140
- # raise ActiveRecord::Rollback, "Call tech support!"
141
- # end
142
- # end
143
- # # ActiveRecord::Rollback is the only exception that won't be passed on
144
- # # by ActiveRecord::Base.transaction, so this line will still be reached
145
- # # even on Friday.
146
- # redirect_to root_url
147
- # end
148
- # end
149
- class Rollback < ActiveRecordError
150
- end
151
-
152
- # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
153
- class DangerousAttributeError < ActiveRecordError
154
- end
155
-
156
- # Raised when unknown attributes are supplied via mass assignment.
157
- class UnknownAttributeError < NoMethodError
158
- end
159
-
160
- # Raised when an error occurred while doing a mass assignment to an attribute through the
161
- # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
162
- # offending attribute.
163
- class AttributeAssignmentError < ActiveRecordError
164
- attr_reader :exception, :attribute
165
- def initialize(message, exception, attribute)
166
- @exception = exception
167
- @attribute = attribute
168
- @message = message
169
- end
170
- end
171
-
172
- # Raised when there are multiple errors while doing a mass assignment through the +attributes+
173
- # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
174
- # objects, each corresponding to the error while assigning to an attribute.
175
- class MultiparameterAssignmentErrors < ActiveRecordError
176
- attr_reader :errors
177
- def initialize(errors)
178
- @errors = errors
179
- end
180
- end
181
-
182
22
  # Active Record objects don't specify their attributes directly, but rather infer them from the table definition with
183
23
  # which they're linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change
184
24
  # is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain
@@ -498,6 +338,9 @@ module ActiveRecord #:nodoc:
498
338
  # Accessor for the name of the prefix string to prepend to every table name. So if set to "basecamp_", all
499
339
  # table names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient way of creating a namespace
500
340
  # for tables in a shared database. By default, the prefix is the empty string.
341
+ #
342
+ # If you are organising your models within modules you can add a prefix to the models within a namespace by defining
343
+ # a singleton method in the parent module called table_name_prefix which returns your chosen prefix.
501
344
  cattr_accessor :table_name_prefix, :instance_writer => false
502
345
  @@table_name_prefix = ""
503
346
 
@@ -551,12 +394,13 @@ module ActiveRecord #:nodoc:
551
394
  class << self # Class methods
552
395
  def colorize_logging(*args)
553
396
  ActiveSupport::Deprecation.warn "ActiveRecord::Base.colorize_logging and " <<
554
- "config.active_record.colorize_logging are deprecated. Please use " <<
555
- "Rails::Subscriber.colorize_logging or config.colorize_logging instead", caller
397
+ "config.active_record.colorize_logging are deprecated. Please use " <<
398
+ "Rails::LogSubscriber.colorize_logging or config.colorize_logging instead", caller
556
399
  end
557
400
  alias :colorize_logging= :colorize_logging
558
401
 
559
402
  delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped
403
+ delegate :find_each, :find_in_batches, :to => :scoped
560
404
  delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped
561
405
  delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped
562
406
 
@@ -924,7 +768,7 @@ module ActiveRecord #:nodoc:
924
768
  contained = contained.singularize if parent.pluralize_table_names
925
769
  contained << '_'
926
770
  end
927
- name = "#{table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}"
771
+ name = "#{full_table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}"
928
772
  end
929
773
 
930
774
  @quoted_table_name = nil
@@ -932,6 +776,10 @@ module ActiveRecord #:nodoc:
932
776
  name
933
777
  end
934
778
 
779
+ def full_table_name_prefix #:nodoc:
780
+ (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
781
+ end
782
+
935
783
  # Defines the column name for use with single table inheritance
936
784
  # -- can be set in subclasses like so: self.inheritance_column = "type_id"
937
785
  def inheritance_column
@@ -1202,6 +1050,12 @@ module ActiveRecord #:nodoc:
1202
1050
 
1203
1051
  object.instance_variable_set(:'@attributes', record)
1204
1052
  object.instance_variable_set(:'@attributes_cache', {})
1053
+ object.instance_variable_set(:@new_record, false)
1054
+ object.instance_variable_set(:@readonly, false)
1055
+ object.instance_variable_set(:@destroyed, false)
1056
+ object.instance_variable_set(:@marked_for_destruction, false)
1057
+ object.instance_variable_set(:@previously_changed, {})
1058
+ object.instance_variable_set(:@changed_attributes, {})
1205
1059
 
1206
1060
  object.send(:_run_find_callbacks)
1207
1061
  object.send(:_run_initialize_callbacks)
@@ -1443,7 +1297,7 @@ module ActiveRecord #:nodoc:
1443
1297
  # <tt>options</tt> argument is the same as in find.
1444
1298
  #
1445
1299
  # class Person < ActiveRecord::Base
1446
- # default_scope :order => 'last_name, first_name'
1300
+ # default_scope order('last_name, first_name')
1447
1301
  # end
1448
1302
  def default_scope(options = {})
1449
1303
  self.default_scoping << construct_finder_arel(options)
@@ -1667,13 +1521,19 @@ module ActiveRecord #:nodoc:
1667
1521
  @attributes = attributes_from_column_definition
1668
1522
  @attributes_cache = {}
1669
1523
  @new_record = true
1524
+ @readonly = false
1525
+ @destroyed = false
1526
+ @marked_for_destruction = false
1527
+ @previously_changed = {}
1528
+ @changed_attributes = {}
1529
+
1670
1530
  ensure_proper_type
1671
- self.attributes = attributes unless attributes.nil?
1672
1531
 
1673
1532
  if scope = self.class.send(:current_scoped_methods)
1674
1533
  create_with = scope.scope_for_create
1675
1534
  create_with.each { |att,value| self.send("#{att}=", value) } if create_with
1676
1535
  end
1536
+ self.attributes = attributes unless attributes.nil?
1677
1537
 
1678
1538
  result = yield self if block_given?
1679
1539
  _run_initialize_callbacks
@@ -1731,7 +1591,7 @@ module ActiveRecord #:nodoc:
1731
1591
  # user_path(user) # => "/users/Phusion"
1732
1592
  def to_param
1733
1593
  # We can't use alias_method here, because method 'id' optimizes itself on the fly.
1734
- (id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes
1594
+ id && id.to_s # Be sure to stringify the id for routes
1735
1595
  end
1736
1596
 
1737
1597
  # Returns a cache key that can be used to identify this record.
@@ -1758,12 +1618,17 @@ module ActiveRecord #:nodoc:
1758
1618
 
1759
1619
  # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet; otherwise, returns false.
1760
1620
  def new_record?
1761
- @new_record || false
1621
+ @new_record
1762
1622
  end
1763
1623
 
1764
1624
  # Returns true if this object has been destroyed, otherwise returns false.
1765
1625
  def destroyed?
1766
- @destroyed || false
1626
+ @destroyed
1627
+ end
1628
+
1629
+ # Returns if the record is persisted, i.e. it's not a new record and it was not destroyed.
1630
+ def persisted?
1631
+ !(new_record? || destroyed?)
1767
1632
  end
1768
1633
 
1769
1634
  # :call-seq:
@@ -1815,7 +1680,7 @@ module ActiveRecord #:nodoc:
1815
1680
  # callbacks, Observer methods, or any <tt>:dependent</tt> association
1816
1681
  # options, use <tt>#destroy</tt>.
1817
1682
  def delete
1818
- self.class.delete(id) unless new_record?
1683
+ self.class.delete(id) if persisted?
1819
1684
  @destroyed = true
1820
1685
  freeze
1821
1686
  end
@@ -1823,7 +1688,7 @@ module ActiveRecord #:nodoc:
1823
1688
  # Deletes the record in the database and freezes this instance to reflect that no changes should
1824
1689
  # be made (since they can't be persisted).
1825
1690
  def destroy
1826
- unless new_record?
1691
+ if persisted?
1827
1692
  self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).delete_all
1828
1693
  end
1829
1694
 
@@ -1843,6 +1708,7 @@ module ActiveRecord #:nodoc:
1843
1708
  became.instance_variable_set("@attributes", @attributes)
1844
1709
  became.instance_variable_set("@attributes_cache", @attributes_cache)
1845
1710
  became.instance_variable_set("@new_record", new_record?)
1711
+ became.instance_variable_set("@destroyed", destroyed?)
1846
1712
  became
1847
1713
  end
1848
1714
 
@@ -1850,7 +1716,7 @@ module ActiveRecord #:nodoc:
1850
1716
  # This is especially useful for boolean flags on existing records. The regular +update_attribute+ method
1851
1717
  # in Base is replaced with this when the validations module is mixed in, which it is by default.
1852
1718
  def update_attribute(name, value)
1853
- send(name.to_s + '=', value)
1719
+ send("#{name}=", value)
1854
1720
  save(:validate => false)
1855
1721
  end
1856
1722
 
@@ -1925,7 +1791,7 @@ module ActiveRecord #:nodoc:
1925
1791
  def reload(options = nil)
1926
1792
  clear_aggregation_cache
1927
1793
  clear_association_cache
1928
- @attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes'))
1794
+ @attributes.update(self.class.send(:with_exclusive_scope) { self.class.find(self.id, options) }.instance_variable_get('@attributes'))
1929
1795
  @attributes_cache = {}
1930
1796
  self
1931
1797
  end
@@ -1994,10 +1860,9 @@ module ActiveRecord #:nodoc:
1994
1860
 
1995
1861
  # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
1996
1862
  def attributes
1997
- self.attribute_names.inject({}) do |attrs, name|
1998
- attrs[name] = read_attribute(name)
1999
- attrs
2000
- end
1863
+ attrs = {}
1864
+ attribute_names.each { |name| attrs[name] = read_attribute(name) }
1865
+ attrs
2001
1866
  end
2002
1867
 
2003
1868
  # Returns an <tt>#inspect</tt>-like string for the value of the
@@ -2041,8 +1906,7 @@ module ActiveRecord #:nodoc:
2041
1906
  def ==(comparison_object)
2042
1907
  comparison_object.equal?(self) ||
2043
1908
  (comparison_object.instance_of?(self.class) &&
2044
- comparison_object.id == id &&
2045
- !comparison_object.new_record?)
1909
+ comparison_object.id == id && !comparison_object.new_record?)
2046
1910
  end
2047
1911
 
2048
1912
  # Delegates to ==
@@ -2069,14 +1933,14 @@ module ActiveRecord #:nodoc:
2069
1933
  # Returns duplicated record with unfreezed attributes.
2070
1934
  def dup
2071
1935
  obj = super
2072
- obj.instance_variable_set('@attributes', instance_variable_get('@attributes').dup)
1936
+ obj.instance_variable_set('@attributes', @attributes.dup)
2073
1937
  obj
2074
1938
  end
2075
1939
 
2076
1940
  # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
2077
1941
  # attributes will be marked as read only since they cannot be saved.
2078
1942
  def readonly?
2079
- defined?(@readonly) && @readonly == true
1943
+ @readonly
2080
1944
  end
2081
1945
 
2082
1946
  # Marks this record as read only.
@@ -2096,10 +1960,10 @@ module ActiveRecord #:nodoc:
2096
1960
 
2097
1961
  protected
2098
1962
  def clone_attributes(reader_method = :read_attribute, attributes = {})
2099
- self.attribute_names.inject(attributes) do |attrs, name|
2100
- attrs[name] = clone_attribute_value(reader_method, name)
2101
- attrs
1963
+ attribute_names.each do |name|
1964
+ attributes[name] = clone_attribute_value(reader_method, name)
2102
1965
  end
1966
+ attributes
2103
1967
  end
2104
1968
 
2105
1969
  def clone_attribute_value(reader_method, attribute_name)
@@ -2198,26 +2062,6 @@ module ActiveRecord #:nodoc:
2198
2062
  default
2199
2063
  end
2200
2064
 
2201
- # Returns a copy of the attributes hash where all the values have been safely quoted for use in
2202
- # an SQL statement.
2203
- def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
2204
- quoted = {}
2205
- connection = self.class.connection
2206
- attribute_names.each do |name|
2207
- if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
2208
- value = read_attribute(name)
2209
-
2210
- # We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML.
2211
- if value && self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))
2212
- value = value.to_yaml
2213
- end
2214
-
2215
- quoted[name] = connection.quote(value, column)
2216
- end
2217
- end
2218
- include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
2219
- end
2220
-
2221
2065
  # Returns a copy of the attributes hash where all the values have been safely quoted for use in
2222
2066
  # an Arel insert/update method.
2223
2067
  def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
@@ -2342,7 +2186,7 @@ module ActiveRecord #:nodoc:
2342
2186
 
2343
2187
  # Returns a comma-separated pair list, like "key1 = val1, key2 = val2".
2344
2188
  def comma_pair_list(hash)
2345
- hash.inject([]) { |list, pair| list << "#{pair.first} = #{pair.last}" }.join(", ")
2189
+ hash.map { |k,v| "#{k} = #{v}" }.join(", ")
2346
2190
  end
2347
2191
 
2348
2192
  def quote_columns(quoter, hash)
@@ -2379,6 +2223,7 @@ module ActiveRecord #:nodoc:
2379
2223
  extend QueryCache::ClassMethods
2380
2224
  extend ActiveSupport::Benchmarkable
2381
2225
 
2226
+ include ActiveModel::Conversion
2382
2227
  include Validations
2383
2228
  include Locking::Optimistic, Locking::Pessimistic
2384
2229
  include AttributeMethods
@@ -2388,16 +2233,16 @@ module ActiveRecord #:nodoc:
2388
2233
  include AttributeMethods::Dirty
2389
2234
  include Callbacks, ActiveModel::Observing, Timestamp
2390
2235
  include Associations, AssociationPreload, NamedScope
2391
- include ActiveModel::Conversion
2392
2236
 
2393
2237
  # AutosaveAssociation needs to be included before Transactions, because we want
2394
2238
  # #save_with_autosave_associations to be wrapped inside a transaction.
2395
2239
  include AutosaveAssociation, NestedAttributes
2240
+ include Aggregations, Transactions, Reflection, Serialization
2396
2241
 
2397
- include Aggregations, Transactions, Reflection, Batches, Serialization
2398
-
2242
+ NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner)
2399
2243
  end
2400
2244
  end
2401
2245
 
2402
2246
  # TODO: Remove this and make it work with LAZY flag
2403
2247
  require 'active_record/connection_adapters/abstract_adapter'
2248
+ ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)