activerecord 4.1.0.beta2 → 4.1.0.rc1

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +622 -9
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_record.rb +1 -1
  5. data/lib/active_record/associations.rb +10 -7
  6. data/lib/active_record/associations/alias_tracker.rb +39 -29
  7. data/lib/active_record/associations/association.rb +1 -1
  8. data/lib/active_record/associations/association_scope.rb +56 -31
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -0
  10. data/lib/active_record/associations/builder/association.rb +6 -0
  11. data/lib/active_record/associations/builder/belongs_to.rb +1 -1
  12. data/lib/active_record/associations/collection_association.rb +33 -9
  13. data/lib/active_record/associations/collection_proxy.rb +53 -5
  14. data/lib/active_record/associations/has_many_association.rb +1 -1
  15. data/lib/active_record/associations/join_dependency.rb +5 -5
  16. data/lib/active_record/associations/join_dependency/join_association.rb +8 -8
  17. data/lib/active_record/associations/preloader.rb +1 -1
  18. data/lib/active_record/associations/singular_association.rb +1 -1
  19. data/lib/active_record/attribute_methods.rb +28 -5
  20. data/lib/active_record/attribute_methods/dirty.rb +27 -4
  21. data/lib/active_record/attribute_methods/read.rb +1 -1
  22. data/lib/active_record/attribute_methods/serialization.rb +18 -0
  23. data/lib/active_record/autosave_association.rb +1 -1
  24. data/lib/active_record/base.rb +1 -1
  25. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -2
  26. data/lib/active_record/connection_adapters/abstract/database_statements.rb +16 -9
  27. data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -1
  28. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +8 -8
  29. data/lib/active_record/connection_adapters/abstract/transaction.rb +4 -0
  30. data/lib/active_record/connection_adapters/abstract_adapter.rb +15 -5
  31. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +2 -6
  32. data/lib/active_record/connection_adapters/connection_specification.rb +200 -43
  33. data/lib/active_record/connection_adapters/mysql2_adapter.rb +7 -1
  34. data/lib/active_record/connection_adapters/mysql_adapter.rb +8 -2
  35. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +3 -2
  36. data/lib/active_record/connection_adapters/postgresql/cast.rb +7 -7
  37. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +2 -2
  38. data/lib/active_record/connection_adapters/postgresql/quoting.rb +1 -1
  39. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +13 -0
  40. data/lib/active_record/connection_adapters/postgresql_adapter.rb +32 -17
  41. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +25 -3
  42. data/lib/active_record/connection_handling.rb +64 -3
  43. data/lib/active_record/core.rb +28 -24
  44. data/lib/active_record/dynamic_matchers.rb +6 -2
  45. data/lib/active_record/enum.rb +111 -17
  46. data/lib/active_record/errors.rb +12 -0
  47. data/lib/active_record/fixtures.rb +13 -15
  48. data/lib/active_record/inheritance.rb +29 -9
  49. data/lib/active_record/integration.rb +4 -2
  50. data/lib/active_record/migration.rb +20 -7
  51. data/lib/active_record/migration/command_recorder.rb +18 -6
  52. data/lib/active_record/persistence.rb +10 -5
  53. data/lib/active_record/querying.rb +1 -0
  54. data/lib/active_record/railtie.rb +11 -8
  55. data/lib/active_record/railties/databases.rake +24 -38
  56. data/lib/active_record/relation.rb +3 -2
  57. data/lib/active_record/relation/batches.rb +24 -9
  58. data/lib/active_record/relation/finder_methods.rb +100 -11
  59. data/lib/active_record/relation/query_methods.rb +39 -27
  60. data/lib/active_record/result.rb +1 -1
  61. data/lib/active_record/sanitization.rb +7 -5
  62. data/lib/active_record/scoping.rb +5 -0
  63. data/lib/active_record/scoping/named.rb +6 -0
  64. data/lib/active_record/store.rb +1 -1
  65. data/lib/active_record/tasks/database_tasks.rb +45 -23
  66. data/lib/active_record/timestamp.rb +2 -2
  67. data/lib/active_record/transactions.rb +7 -7
  68. data/lib/active_record/validations/presence.rb +1 -1
  69. data/lib/active_record/version.rb +1 -1
  70. metadata +5 -6
  71. data/lib/active_record/associations/join_helper.rb +0 -36
@@ -75,7 +75,7 @@ module ActiveRecord
75
75
  # # #<Pet id: nil, name: "Choo-Choo">
76
76
  # # ]
77
77
  #
78
- # person.pets.select([:id, :name])
78
+ # person.pets.select(:id, :name )
79
79
  # # => [
80
80
  # # #<Pet id: 1, name: "Fancy-Fancy">,
81
81
  # # #<Pet id: 2, name: "Spook">,
@@ -84,7 +84,7 @@ module ActiveRecord
84
84
  #
85
85
  # Be careful because this also means you're initializing a model
86
86
  # object with only the fields that you've selected. If you attempt
87
- # to access a field that is not in the initialized record you'll
87
+ # to access a field except +id+ that is not in the initialized record you'll
88
88
  # receive:
89
89
  #
90
90
  # person.pets.select(:name).first.person_id
@@ -106,13 +106,13 @@ module ActiveRecord
106
106
  # # #<Pet id: 2, name: "Spook">,
107
107
  # # #<Pet id: 3, name: "Choo-Choo">
108
108
  # # ]
109
- def select(select = nil, &block)
110
- @association.select(select, &block)
109
+ def select(*fields, &block)
110
+ @association.select(*fields, &block)
111
111
  end
112
112
 
113
113
  # Finds an object in the collection responding to the +id+. Uses the same
114
114
  # rules as <tt>ActiveRecord::Base.find</tt>. Returns <tt>ActiveRecord::RecordNotFound</tt>
115
- # error if the object can not be found.
115
+ # error if the object cannot be found.
116
116
  #
117
117
  # class Person < ActiveRecord::Base
118
118
  # has_many :pets
@@ -170,6 +170,32 @@ module ActiveRecord
170
170
  @association.first(*args)
171
171
  end
172
172
 
173
+ # Same as +first+ except returns only the second record.
174
+ def second(*args)
175
+ @association.second(*args)
176
+ end
177
+
178
+ # Same as +first+ except returns only the third record.
179
+ def third(*args)
180
+ @association.third(*args)
181
+ end
182
+
183
+ # Same as +first+ except returns only the fourth record.
184
+ def fourth(*args)
185
+ @association.fourth(*args)
186
+ end
187
+
188
+ # Same as +first+ except returns only the fifth record.
189
+ def fifth(*args)
190
+ @association.fifth(*args)
191
+ end
192
+
193
+ # Same as +first+ except returns only the forty second record.
194
+ # Also known as accessing "the reddit".
195
+ def forty_two(*args)
196
+ @association.forty_two(*args)
197
+ end
198
+
173
199
  # Returns the last record, or the last +n+ records, from the collection.
174
200
  # If the collection is empty, the first form returns +nil+, and the second
175
201
  # form returns an empty array.
@@ -978,6 +1004,28 @@ module ActiveRecord
978
1004
  proxy_association.reload
979
1005
  self
980
1006
  end
1007
+
1008
+ # Unloads the association. Returns +self+.
1009
+ #
1010
+ # class Person < ActiveRecord::Base
1011
+ # has_many :pets
1012
+ # end
1013
+ #
1014
+ # person.pets # fetches pets from the database
1015
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1016
+ #
1017
+ # person.pets # uses the pets cache
1018
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1019
+ #
1020
+ # person.pets.reset # clears the pets cache
1021
+ #
1022
+ # person.pets # fetches pets from the database
1023
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1024
+ def reset
1025
+ proxy_association.reset
1026
+ proxy_association.reset_scope
1027
+ self
1028
+ end
981
1029
  end
982
1030
  end
983
1031
  end
@@ -111,7 +111,7 @@ module ActiveRecord
111
111
  records.each(&:destroy!)
112
112
  update_counter(-records.length) unless inverse_updates_counter_cache?
113
113
  else
114
- if records == :all
114
+ if records == :all || !reflection.klass.primary_key
115
115
  scope = self.scope
116
116
  else
117
117
  scope = self.scope.where(reflection.klass.primary_key => records)
@@ -93,8 +93,8 @@ module ActiveRecord
93
93
  # joins # => []
94
94
  #
95
95
  def initialize(base, associations, joins)
96
- @alias_tracker = AliasTracker.new(base.connection, joins)
97
- @alias_tracker.aliased_name_for(base.table_name) # Updates the count for base.table_name to 1
96
+ @alias_tracker = AliasTracker.create(base.connection, joins)
97
+ @alias_tracker.aliased_name_for(base.table_name, base.table_name) # Updates the count for base.table_name to 1
98
98
  tree = self.class.make_tree associations
99
99
  @join_root = JoinBase.new base, build(tree, base)
100
100
  @join_root.children.each { |child| construct_tables! @join_root, child }
@@ -114,7 +114,7 @@ module ActiveRecord
114
114
  walk join_root, oj.join_root
115
115
  else
116
116
  oj.join_root.children.flat_map { |child|
117
- make_outer_joins join_root, child
117
+ make_outer_joins oj.join_root, child
118
118
  }
119
119
  end
120
120
  }
@@ -163,7 +163,7 @@ module ActiveRecord
163
163
 
164
164
  def make_outer_joins(parent, child)
165
165
  tables = table_aliases_for(parent, child)
166
- join_type = Arel::OuterJoin
166
+ join_type = Arel::Nodes::OuterJoin
167
167
  joins = make_constraints parent, child, tables, join_type
168
168
 
169
169
  joins.concat child.children.flat_map { |c| make_outer_joins(child, c) }
@@ -171,7 +171,7 @@ module ActiveRecord
171
171
 
172
172
  def make_inner_joins(parent, child)
173
173
  tables = child.tables
174
- join_type = Arel::InnerJoin
174
+ join_type = Arel::Nodes::InnerJoin
175
175
  joins = make_constraints parent, child, tables, join_type
176
176
 
177
177
  joins.concat child.children.flat_map { |c| make_inner_joins(child, c) }
@@ -25,7 +25,8 @@ module ActiveRecord
25
25
  joins = []
26
26
  tables = tables.reverse
27
27
 
28
- scope_chain_iter = scope_chain.reverse_each
28
+ scope_chain_index = 0
29
+ scope_chain = scope_chain.reverse
29
30
 
30
31
  # The chain starts with the target table, but we want to end with it here (makes
31
32
  # more sense in this context), so we reverse
@@ -44,19 +45,14 @@ module ActiveRecord
44
45
 
45
46
  constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
46
47
 
47
- scope_chain_items = scope_chain_iter.next.map do |item|
48
+ scope_chain_items = scope_chain[scope_chain_index].map do |item|
48
49
  if item.is_a?(Relation)
49
50
  item
50
51
  else
51
52
  ActiveRecord::Relation.create(klass, table).instance_exec(node, &item)
52
53
  end
53
54
  end
54
-
55
- if reflection.type
56
- scope_chain_items <<
57
- ActiveRecord::Relation.create(klass, table)
58
- .where(reflection.type => foreign_klass.base_class.name)
59
- end
55
+ scope_chain_index += 1
60
56
 
61
57
  scope_chain_items.concat [klass.send(:build_default_scope)].compact
62
58
 
@@ -64,6 +60,10 @@ module ActiveRecord
64
60
  left.merge right
65
61
  end
66
62
 
63
+ if reflection.type
64
+ constraint = constraint.and table[reflection.type].eq foreign_klass.base_class.name
65
+ end
66
+
67
67
  if rel && !rel.arel.constraints.empty?
68
68
  constraint = constraint.and rel.arel.constraints
69
69
  end
@@ -183,7 +183,7 @@ module ActiveRecord
183
183
  def run(preloader); end
184
184
 
185
185
  def preloaded_records
186
- owners.flat_map { |owner| owner.read_attribute reflection.name }
186
+ owners.flat_map { |owner| owner.association(reflection.name).target }
187
187
  end
188
188
  end
189
189
 
@@ -39,7 +39,7 @@ module ActiveRecord
39
39
  end
40
40
 
41
41
  def find_target
42
- if record = scope.first
42
+ if record = scope.take
43
43
  set_inverse_instance record
44
44
  end
45
45
  end
@@ -110,16 +110,34 @@ module ActiveRecord
110
110
  end
111
111
  end
112
112
 
113
- # A method name is 'dangerous' if it is already defined by Active Record, but
113
+ # A method name is 'dangerous' if it is already (re)defined by Active Record, but
114
114
  # not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
115
115
  def dangerous_attribute_method?(name) # :nodoc:
116
116
  method_defined_within?(name, Base)
117
117
  end
118
118
 
119
- def method_defined_within?(name, klass, sup = klass.superclass) # :nodoc:
119
+ def method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
120
120
  if klass.method_defined?(name) || klass.private_method_defined?(name)
121
- if sup.method_defined?(name) || sup.private_method_defined?(name)
122
- klass.instance_method(name).owner != sup.instance_method(name).owner
121
+ if superklass.method_defined?(name) || superklass.private_method_defined?(name)
122
+ klass.instance_method(name).owner != superklass.instance_method(name).owner
123
+ else
124
+ true
125
+ end
126
+ else
127
+ false
128
+ end
129
+ end
130
+
131
+ # A class method is 'dangerous' if it is already (re)defined by Active Record, but
132
+ # not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
133
+ def dangerous_class_method?(method_name)
134
+ class_method_defined_within?(method_name, Base)
135
+ end
136
+
137
+ def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc
138
+ if klass.respond_to?(name, true)
139
+ if superklass.respond_to?(name, true)
140
+ klass.method(name).owner != superklass.method(name).owner
123
141
  else
124
142
  true
125
143
  end
@@ -260,6 +278,11 @@ module ActiveRecord
260
278
  }
261
279
  end
262
280
 
281
+ # Placeholder so it can be overriden when needed by serialization
282
+ def attributes_for_coder # :nodoc:
283
+ attributes
284
+ end
285
+
263
286
  # Returns an <tt>#inspect</tt>-like string for the value of the
264
287
  # attribute +attr_name+. String attributes are truncated upto 50
265
288
  # characters, Date and Time attributes are returned in the
@@ -330,7 +353,7 @@ module ActiveRecord
330
353
  end
331
354
 
332
355
  # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
333
- # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)). It raises
356
+ # "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
334
357
  # <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
335
358
  #
336
359
  # Alias for the <tt>read_attribute</tt> method.
@@ -38,11 +38,37 @@ module ActiveRecord
38
38
  end
39
39
  end
40
40
 
41
+ def initialize_dup(other) # :nodoc:
42
+ super
43
+ init_changed_attributes
44
+ end
45
+
41
46
  private
47
+ def initialize_internals_callback
48
+ super
49
+ init_changed_attributes
50
+ end
51
+
52
+ def init_changed_attributes
53
+ @changed_attributes = nil
54
+ # Intentionally avoid using #column_defaults since overridden defaults (as is done in
55
+ # optimistic locking) won't get written unless they get marked as changed
56
+ self.class.columns.each do |c|
57
+ attr, orig_value = c.name, c.default
58
+ changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @attributes[attr])
59
+ end
60
+ end
61
+
42
62
  # Wrap write_attribute to remember original attribute value.
43
63
  def write_attribute(attr, value)
44
64
  attr = attr.to_s
45
65
 
66
+ save_changed_attribute(attr, value)
67
+
68
+ super(attr, value)
69
+ end
70
+
71
+ def save_changed_attribute(attr, value)
46
72
  # The attribute already has an unsaved change.
47
73
  if attribute_changed?(attr)
48
74
  old = changed_attributes[attr]
@@ -51,9 +77,6 @@ module ActiveRecord
51
77
  old = clone_attribute_value(:read_attribute, attr)
52
78
  changed_attributes[attr] = old if _field_changed?(attr, old, value)
53
79
  end
54
-
55
- # Carry on.
56
- super(attr, value)
57
80
  end
58
81
 
59
82
  def update_record(*)
@@ -67,7 +90,7 @@ module ActiveRecord
67
90
  # Serialized attributes should always be written in case they've been
68
91
  # changed in place.
69
92
  def keys_for_partial_write
70
- changed | (attributes.keys & self.class.serialized_attributes.keys)
93
+ changed
71
94
  end
72
95
 
73
96
  def _field_changed?(attr, old, value)
@@ -102,7 +102,7 @@ module ActiveRecord
102
102
  end
103
103
 
104
104
  # Returns the value of the attribute identified by <tt>attr_name</tt> after
105
- # it has been typecast (for example, "2004-12-12" in a data column is cast
105
+ # it has been typecast (for example, "2004-12-12" in a date column is cast
106
106
  # to a date object, like Date.new(2004, 12, 12)).
107
107
  def read_attribute(attr_name)
108
108
  # If it's cached, just return it
@@ -115,6 +115,14 @@ module ActiveRecord
115
115
  end
116
116
  end
117
117
 
118
+ def should_record_timestamps?
119
+ super || (self.record_timestamps && (attributes.keys & self.class.serialized_attributes.keys).present?)
120
+ end
121
+
122
+ def keys_for_partial_write
123
+ super | (attributes.keys & self.class.serialized_attributes.keys)
124
+ end
125
+
118
126
  def type_cast_attribute_for_write(column, value)
119
127
  if column && coder = self.class.serialized_attributes[column.name]
120
128
  Attribute.new(coder, value, :unserialized)
@@ -156,6 +164,16 @@ module ActiveRecord
156
164
  super
157
165
  end
158
166
  end
167
+
168
+ def attributes_for_coder
169
+ attribute_names.each_with_object({}) do |name, attrs|
170
+ attrs[name] = if self.class.serialized_attributes.include?(name)
171
+ @attributes[name].serialized_value
172
+ else
173
+ read_attribute(name)
174
+ end
175
+ end
176
+ end
159
177
  end
160
178
  end
161
179
  end
@@ -301,7 +301,7 @@ module ActiveRecord
301
301
  def association_valid?(reflection, record)
302
302
  return true if record.destroyed? || record.marked_for_destruction?
303
303
 
304
- unless valid = record.valid?
304
+ unless valid = record.valid?(self.validation_context)
305
305
  if reflection.options[:autosave]
306
306
  record.errors.each do |attribute, message|
307
307
  attribute = "#{reflection.name}.#{attribute}"
@@ -294,6 +294,7 @@ module ActiveRecord #:nodoc:
294
294
  extend Enum
295
295
  extend Delegation::DelegateCache
296
296
 
297
+ include Core
297
298
  include Persistence
298
299
  include NoTouching
299
300
  include ReadonlyAttributes
@@ -320,7 +321,6 @@ module ActiveRecord #:nodoc:
320
321
  include Reflection
321
322
  include Serialization
322
323
  include Store
323
- include Core
324
324
  end
325
325
 
326
326
  ActiveSupport.run_load_hooks(:active_record, Base)
@@ -86,7 +86,7 @@ module ActiveRecord
86
86
  end
87
87
  end
88
88
 
89
- # Return the number of threads currently waiting on this
89
+ # Returns the number of threads currently waiting on this
90
90
  # queue.
91
91
  def num_waiting
92
92
  synchronize do
@@ -393,7 +393,7 @@ module ActiveRecord
393
393
  synchronize do
394
394
  stale = Time.now - @dead_connection_timeout
395
395
  connections.dup.each do |conn|
396
- if conn.in_use? && stale > conn.last_use && !conn.active?
396
+ if conn.in_use? && stale > conn.last_use && !conn.active_threadsafe?
397
397
  remove conn
398
398
  end
399
399
  end
@@ -20,6 +20,14 @@ module ActiveRecord
20
20
 
21
21
  # Returns an ActiveRecord::Result instance.
22
22
  def select_all(arel, name = nil, binds = [])
23
+ if arel.is_a?(Relation)
24
+ relation = arel
25
+ arel = relation.arel
26
+ if !binds || binds.empty?
27
+ binds = relation.bind_values
28
+ end
29
+ end
30
+
23
31
  select(to_sql(arel, binds), name, binds)
24
32
  end
25
33
 
@@ -39,13 +47,16 @@ module ActiveRecord
39
47
  # Returns an array of the values of the first column in a select:
40
48
  # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
41
49
  def select_values(arel, name = nil)
42
- select_rows(to_sql(arel, []), name)
43
- .map { |v| v[0] }
50
+ binds = []
51
+ if arel.is_a?(Relation)
52
+ arel, binds = arel.arel, arel.bind_values
53
+ end
54
+ select_rows(to_sql(arel, binds), name, binds).map(&:first)
44
55
  end
45
56
 
46
57
  # Returns an array of arrays containing the field values.
47
58
  # Order is the same as that returned by +columns+.
48
- def select_rows(sql, name = nil)
59
+ def select_rows(sql, name = nil, binds = [])
49
60
  end
50
61
  undef_method :select_rows
51
62
 
@@ -286,10 +297,6 @@ module ActiveRecord
286
297
  # Inserts the given fixture into the table. Overridden in adapters that require
287
298
  # something beyond a simple insert (eg. Oracle).
288
299
  def insert_fixture(fixture, table_name)
289
- execute fixture_sql(fixture, table_name), 'Fixture Insert'
290
- end
291
-
292
- def fixture_sql(fixture, table_name)
293
300
  columns = schema_cache.columns_hash(table_name)
294
301
 
295
302
  key_list = []
@@ -298,7 +305,7 @@ module ActiveRecord
298
305
  quote(value, columns[name])
299
306
  end
300
307
 
301
- "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})"
308
+ execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
302
309
  end
303
310
 
304
311
  def empty_insert_statement_value
@@ -346,7 +353,7 @@ module ActiveRecord
346
353
 
347
354
  protected
348
355
 
349
- # Return a subquery for the given key using the join information.
356
+ # Returns a subquery for the given key using the join information.
350
357
  def subquery_for(key, select)
351
358
  subselect = select.clone
352
359
  subselect.projections = [key]