activerecord 4.0.5 → 4.0.6.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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +198 -0
  3. data/lib/active_record/association_relation.rb +4 -0
  4. data/lib/active_record/associations.rb +7 -1
  5. data/lib/active_record/associations/builder/belongs_to.rb +3 -4
  6. data/lib/active_record/associations/collection_association.rb +5 -5
  7. data/lib/active_record/associations/collection_proxy.rb +4 -2
  8. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +1 -1
  9. data/lib/active_record/associations/has_many_association.rb +4 -4
  10. data/lib/active_record/associations/has_many_through_association.rb +5 -1
  11. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  12. data/lib/active_record/associations/preloader.rb +6 -27
  13. data/lib/active_record/associations/preloader/association.rb +1 -1
  14. data/lib/active_record/associations/singular_association.rb +3 -3
  15. data/lib/active_record/attribute_methods.rb +1 -0
  16. data/lib/active_record/attribute_methods/dirty.rb +2 -2
  17. data/lib/active_record/attribute_methods/serialization.rb +16 -5
  18. data/lib/active_record/base.rb +1 -1
  19. data/lib/active_record/callbacks.rb +2 -2
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +9 -12
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
  22. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  23. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +6 -0
  24. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +5 -5
  25. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -4
  26. data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -0
  27. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +10 -5
  28. data/lib/active_record/connection_adapters/postgresql_adapter.rb +7 -0
  29. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +6 -3
  30. data/lib/active_record/connection_handling.rb +2 -2
  31. data/lib/active_record/core.rb +1 -0
  32. data/lib/active_record/locking/optimistic.rb +1 -1
  33. data/lib/active_record/migration.rb +1 -1
  34. data/lib/active_record/null_relation.rb +9 -3
  35. data/lib/active_record/persistence.rb +9 -7
  36. data/lib/active_record/railties/databases.rake +2 -1
  37. data/lib/active_record/reflection.rb +3 -3
  38. data/lib/active_record/relation.rb +4 -2
  39. data/lib/active_record/relation/merger.rb +10 -2
  40. data/lib/active_record/relation/query_methods.rb +2 -2
  41. data/lib/active_record/scoping/default.rb +3 -3
  42. data/lib/active_record/store.rb +16 -4
  43. data/lib/active_record/test_case.rb +6 -0
  44. data/lib/active_record/timestamp.rb +2 -2
  45. data/lib/active_record/transactions.rb +1 -1
  46. data/lib/active_record/version.rb +1 -1
  47. metadata +27 -37
@@ -18,11 +18,11 @@ module ActiveRecord
18
18
  end
19
19
 
20
20
  def create(attributes = {}, &block)
21
- create_record(attributes, &block)
21
+ _create_record(attributes, &block)
22
22
  end
23
23
 
24
24
  def create!(attributes = {}, &block)
25
- create_record(attributes, true, &block)
25
+ _create_record(attributes, true, &block)
26
26
  end
27
27
 
28
28
  def build(attributes = {})
@@ -51,7 +51,7 @@ module ActiveRecord
51
51
  replace(record)
52
52
  end
53
53
 
54
- def create_record(attributes, raise_error = false)
54
+ def _create_record(attributes, raise_error = false)
55
55
  record = build_record(attributes)
56
56
  yield(record) if block_given?
57
57
  saved = record.save
@@ -161,6 +161,7 @@ module ActiveRecord
161
161
  # this is probably horribly slow, but should only happen at most once for a given AR class
162
162
  attribute_method.bind(self).call(*args, &block)
163
163
  else
164
+ return super unless respond_to_missing?(method, true)
164
165
  send(method, *args, &block)
165
166
  end
166
167
  else
@@ -70,11 +70,11 @@ module ActiveRecord
70
70
  super(attr, value)
71
71
  end
72
72
 
73
- def update_record(*)
73
+ def _update_record(*)
74
74
  partial_writes? ? super(keys_for_partial_write) : super
75
75
  end
76
76
 
77
- def create_record(*)
77
+ def _create_record(*)
78
78
  partial_writes? ? super(keys_for_partial_write) : super
79
79
  end
80
80
 
@@ -27,7 +27,8 @@ module ActiveRecord
27
27
  # ==== Parameters
28
28
  #
29
29
  # * +attr_name+ - The field name that should be serialized.
30
- # * +class_name+ - Optional, class name that the object type should be equal to.
30
+ # * +class_name_or_coder+ - Optional, a coder object, which responds to `.load` / `.dump`
31
+ # or a class name that the object type should be equal to.
31
32
  #
32
33
  # ==== Example
33
34
  #
@@ -35,13 +36,23 @@ module ActiveRecord
35
36
  # class User < ActiveRecord::Base
36
37
  # serialize :preferences
37
38
  # end
38
- def serialize(attr_name, class_name = Object)
39
+ #
40
+ # # Serialize preferences using JSON as coder.
41
+ # class User < ActiveRecord::Base
42
+ # serialize :preferences, JSON
43
+ # end
44
+ #
45
+ # # Serialize preferences as Hash using YAML coder.
46
+ # class User < ActiveRecord::Base
47
+ # serialize :preferences, Hash
48
+ # end
49
+ def serialize(attr_name, class_name_or_coder = Object)
39
50
  include Behavior
40
51
 
41
- coder = if [:load, :dump].all? { |x| class_name.respond_to?(x) }
42
- class_name
52
+ coder = if [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
53
+ class_name_or_coder
43
54
  else
44
- Coders::YAMLColumn.new(class_name)
55
+ Coders::YAMLColumn.new(class_name_or_coder)
45
56
  end
46
57
 
47
58
  # merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy
@@ -305,8 +305,8 @@ module ActiveRecord #:nodoc:
305
305
  include Locking::Optimistic
306
306
  include Locking::Pessimistic
307
307
  include AttributeMethods
308
- include Callbacks
309
308
  include Timestamp
309
+ include Callbacks
310
310
  include Associations
311
311
  include ActiveModel::SecurePassword
312
312
  include AutosaveAssociation
@@ -302,11 +302,11 @@ module ActiveRecord
302
302
  run_callbacks(:save) { super }
303
303
  end
304
304
 
305
- def create_record #:nodoc:
305
+ def _create_record #:nodoc:
306
306
  run_callbacks(:create) { super }
307
307
  end
308
308
 
309
- def update_record(*) #:nodoc:
309
+ def _update_record(*) #:nodoc:
310
310
  run_callbacks(:update) { super }
311
311
  end
312
312
  end
@@ -21,14 +21,7 @@ module ActiveRecord
21
21
  # Returns an array of record hashes with the column names as keys and
22
22
  # column values as values.
23
23
  def select_all(arel, name = nil, binds = [])
24
- if arel.is_a?(Relation)
25
- relation = arel
26
- arel = relation.arel
27
- if !binds || binds.empty?
28
- binds = relation.bind_values
29
- end
30
- end
31
-
24
+ arel, binds = binds_from_relation arel, binds
32
25
  select(to_sql(arel, binds), name, binds)
33
26
  end
34
27
 
@@ -49,10 +42,7 @@ module ActiveRecord
49
42
  # Returns an array of the values of the first column in a select:
50
43
  # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
51
44
  def select_values(arel, name = nil)
52
- binds = []
53
- if arel.is_a?(Relation)
54
- arel, binds = arel.arel, arel.bind_values
55
- end
45
+ arel, binds = binds_from_relation arel, []
56
46
  select_rows(to_sql(arel, binds), name, binds).map(&:first)
57
47
  end
58
48
 
@@ -396,6 +386,13 @@ module ActiveRecord
396
386
  row = result.rows.first
397
387
  row && row.first
398
388
  end
389
+
390
+ def binds_from_relation(relation, binds)
391
+ if relation.is_a?(Relation) && binds.blank?
392
+ relation, binds = relation.arel, relation.bind_values
393
+ end
394
+ [relation, binds]
395
+ end
399
396
  end
400
397
  end
401
398
  end
@@ -57,6 +57,7 @@ module ActiveRecord
57
57
 
58
58
  def select_all(arel, name = nil, binds = [])
59
59
  if @query_cache_enabled && !locked?(arel)
60
+ arel, binds = binds_from_relation arel, binds
60
61
  sql = to_sql(arel, binds)
61
62
  cache_sql(sql, binds) { super(sql, name, binds) }
62
63
  else
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  # are typically created by methods in TableDefinition, and added to the
16
16
  # +columns+ attribute of said TableDefinition object, in order to be used
17
17
  # for generating a number of table creation or table changing SQL statements.
18
- class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key) #:nodoc:
18
+ class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key, :sql_type) #:nodoc:
19
19
  def string_to_binary(value)
20
20
  value
21
21
  end
@@ -869,6 +869,12 @@ module ActiveRecord
869
869
  end
870
870
  end
871
871
 
872
+ def quote_value(value, column)
873
+ column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale)
874
+
875
+ quote(value, column)
876
+ end
877
+
872
878
  private
873
879
  def create_table_definition(name, temporary, options)
874
880
  TableDefinition.new native_database_types, name, temporary, options
@@ -743,22 +743,22 @@ module ActiveRecord
743
743
  end
744
744
 
745
745
  def configure_connection
746
- variables = @config[:variables] || {}
746
+ variables = @config.fetch(:variables, {}).stringify_keys
747
747
 
748
748
  # By default, MySQL 'where id is null' selects the last inserted id.
749
749
  # Turn this off. http://dev.rubyonrails.org/ticket/6778
750
- variables[:sql_auto_is_null] = 0
750
+ variables['sql_auto_is_null'] = 0
751
751
 
752
752
  # Increase timeout so the server doesn't disconnect us.
753
753
  wait_timeout = @config[:wait_timeout]
754
754
  wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
755
- variables[:wait_timeout] = self.class.type_cast_config_to_integer(wait_timeout)
755
+ variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout)
756
756
 
757
757
  # Make MySQL reject illegal values rather than truncating or blanking them, see
758
758
  # http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
759
759
  # If the user has provided another value for sql_mode, don't replace it.
760
- if strict_mode? && !variables.has_key?(:sql_mode)
761
- variables[:sql_mode] = 'STRICT_ALL_TABLES'
760
+ if strict_mode? && !variables.has_key?('sql_mode')
761
+ variables['sql_mode'] = 'STRICT_ALL_TABLES'
762
762
  end
763
763
 
764
764
  # NAMES does not have an equals sign, see
@@ -143,10 +143,7 @@ module ActiveRecord
143
143
  fields.each_with_index do |fname, i|
144
144
  ftype = result.ftype i
145
145
  fmod = result.fmod i
146
- types[fname] = OID::TYPE_MAP.fetch(ftype, fmod) { |oid, mod|
147
- warn "unknown OID: #{fname}(#{oid}) (#{sql})"
148
- OID::Identity.new
149
- }
146
+ types[fname] = get_oid_type(ftype, fmod, fname)
150
147
  end
151
148
 
152
149
  ret = ActiveRecord::Result.new(fields, result.values, types)
@@ -167,6 +167,15 @@ module ActiveRecord
167
167
  end
168
168
  result
169
169
  end
170
+
171
+ # Does not quote function default values for UUID columns
172
+ def quote_default_value(value, column) #:nodoc:
173
+ if column.type == :uuid && value =~ /\(\)/
174
+ value
175
+ else
176
+ quote(value, column)
177
+ end
178
+ end
170
179
  end
171
180
  end
172
181
  end
@@ -185,13 +185,15 @@ module ActiveRecord
185
185
  def columns(table_name)
186
186
  # Limit, precision, and scale are all handled by the superclass.
187
187
  column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
188
- oid = OID::TYPE_MAP.fetch(oid.to_i, fmod.to_i) {
189
- OID::Identity.new
190
- }
188
+ oid = get_oid_type(oid.to_i, fmod.to_i, column_name)
191
189
  PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
192
190
  end
193
191
  end
194
192
 
193
+ def column_for(table_name, column_name) #:nodoc:
194
+ columns(table_name).detect { |c| c.name == column_name.to_s }
195
+ end
196
+
195
197
  # Returns the current database name.
196
198
  def current_database
197
199
  query('select current_database()', 'SCHEMA')[0][0]
@@ -408,13 +410,16 @@ module ActiveRecord
408
410
  # Changes the default value of a table column.
409
411
  def change_column_default(table_name, column_name, default)
410
412
  clear_cache!
411
- execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
413
+ column = column_for(table_name, column_name)
414
+
415
+ execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote_default_value(default, column)}" if column
412
416
  end
413
417
 
414
418
  def change_column_null(table_name, column_name, null, default = nil)
415
419
  clear_cache!
416
420
  unless null || default.nil?
417
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
421
+ column = column_for(table_name, column_name)
422
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
418
423
  end
419
424
  execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
420
425
  end
@@ -749,6 +749,13 @@ module ActiveRecord
749
749
 
750
750
  private
751
751
 
752
+ def get_oid_type(oid, fmod, column_name)
753
+ OID::TYPE_MAP.fetch(oid, fmod) {
754
+ warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
755
+ OID::TYPE_MAP[oid] = OID::Identity.new
756
+ }
757
+ end
758
+
752
759
  def reload_type_map
753
760
  OID::TYPE_MAP.clear
754
761
  initialize_type_map
@@ -297,9 +297,12 @@ module ActiveRecord
297
297
  # Don't cache statements if they are not prepared
298
298
  if without_prepared_statement?(binds)
299
299
  stmt = @connection.prepare(sql)
300
- cols = stmt.columns
301
- records = stmt.to_a
302
- stmt.close
300
+ begin
301
+ cols = stmt.columns
302
+ records = stmt.to_a
303
+ ensure
304
+ stmt.close
305
+ end
303
306
  stmt = records
304
307
  else
305
308
  cache = @statements[sql] ||= {
@@ -15,14 +15,14 @@ module ActiveRecord
15
15
  # Example for SQLite database:
16
16
  #
17
17
  # ActiveRecord::Base.establish_connection(
18
- # adapter: "sqlite",
18
+ # adapter: "sqlite3",
19
19
  # database: "path/to/dbfile"
20
20
  # )
21
21
  #
22
22
  # Also accepts keys as strings (for parsing from YAML for example):
23
23
  #
24
24
  # ActiveRecord::Base.establish_connection(
25
- # "adapter" => "sqlite",
25
+ # "adapter" => "sqlite3",
26
26
  # "database" => "path/to/dbfile"
27
27
  # )
28
28
  #
@@ -257,6 +257,7 @@ module ActiveRecord
257
257
  @attributes_cache = {}
258
258
 
259
259
  @new_record = true
260
+ @destroyed = false
260
261
 
261
262
  ensure_proper_type
262
263
  super
@@ -66,7 +66,7 @@ module ActiveRecord
66
66
  send(lock_col + '=', previous_lock_value + 1)
67
67
  end
68
68
 
69
- def update_record(attribute_names = @attributes.keys) #:nodoc:
69
+ def _update_record(attribute_names = @attributes.keys) #:nodoc:
70
70
  return super unless locking_enabled?
71
71
  return 0 if attribute_names.empty?
72
72
 
@@ -620,7 +620,7 @@ module ActiveRecord
620
620
 
621
621
  say_with_time "#{method}(#{arg_list})" do
622
622
  unless @connection.respond_to? :revert
623
- unless arguments.empty? || method == :execute
623
+ unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
624
624
  arguments[0] = Migrator.proper_table_name(arguments.first)
625
625
  arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
626
626
  end
@@ -43,15 +43,21 @@ module ActiveRecord
43
43
  end
44
44
 
45
45
  def count(*)
46
- 0
46
+ calculate :count, nil
47
47
  end
48
48
 
49
49
  def sum(*)
50
50
  0
51
51
  end
52
52
 
53
- def calculate(_operation, _column_name, _options = {})
54
- nil
53
+ def calculate(operation, _column_name, _options = {})
54
+ # TODO: Remove _options argument as soon we remove support to
55
+ # activerecord-deprecated_finders.
56
+ if operation == :count
57
+ group_values.any? ? Hash.new : 0
58
+ else
59
+ nil
60
+ end
55
61
  end
56
62
 
57
63
  def exists?(_id = false)
@@ -37,7 +37,7 @@ module ActiveRecord
37
37
  end
38
38
 
39
39
  # Given an attributes hash, +instantiate+ returns a new instance of
40
- # the appropriate class.
40
+ # the appropriate class. Accepts only keys as strings.
41
41
  #
42
42
  # For example, +Post.all+ may return Comments, Messages, and Emails
43
43
  # by storing the record's subclass in a +type+ attribute. By calling
@@ -46,10 +46,10 @@ module ActiveRecord
46
46
  #
47
47
  # See +ActiveRecord::Inheritance#discriminate_class_for_record+ to see
48
48
  # how this "single-table" inheritance mapping is implemented.
49
- def instantiate(record, column_types = {})
50
- klass = discriminate_class_for_record(record)
49
+ def instantiate(attributes, column_types = {})
50
+ klass = discriminate_class_for_record(attributes)
51
51
  column_types = klass.decorate_columns(column_types.dup)
52
- klass.allocate.init_with('attributes' => record, 'column_types' => column_types)
52
+ klass.allocate.init_with('attributes' => attributes, 'column_types' => column_types)
53
53
  end
54
54
 
55
55
  private
@@ -448,6 +448,8 @@ module ActiveRecord
448
448
  @changed_attributes.except!(*changes.keys)
449
449
  primary_key = self.class.primary_key
450
450
  self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
451
+ else
452
+ true
451
453
  end
452
454
  end
453
455
 
@@ -475,13 +477,13 @@ module ActiveRecord
475
477
 
476
478
  def create_or_update
477
479
  raise ReadOnlyRecord if readonly?
478
- result = new_record? ? create_record : update_record
480
+ result = new_record? ? _create_record : _update_record
479
481
  result != false
480
482
  end
481
483
 
482
484
  # Updates the associated record with values matching those of the instance attributes.
483
485
  # Returns the number of affected rows.
484
- def update_record(attribute_names = @attributes.keys)
486
+ def _update_record(attribute_names = @attributes.keys)
485
487
  attributes_with_values = arel_attributes_with_values_for_update(attribute_names)
486
488
  if attributes_with_values.empty?
487
489
  0
@@ -504,7 +506,7 @@ module ActiveRecord
504
506
 
505
507
  # Creates a record with values matching those of the instance attributes
506
508
  # and returns its id.
507
- def create_record(attribute_names = @attributes.keys)
509
+ def _create_record(attribute_names = @attributes.keys)
508
510
  attributes_values = arel_attributes_with_values_for_create(attribute_names)
509
511
 
510
512
  new_id = self.class.unscoped.insert attributes_values
@@ -287,7 +287,8 @@ db_namespace = namespace :db do
287
287
  current_config = ActiveRecord::Tasks::DatabaseTasks.current_config
288
288
  ActiveRecord::Tasks::DatabaseTasks.structure_dump(current_config, filename)
289
289
 
290
- if ActiveRecord::Base.connection.supports_migrations?
290
+ if ActiveRecord::Base.connection.supports_migrations? &&
291
+ ActiveRecord::SchemaMigration.table_exists?
291
292
  File.open(filename, "a") do |f|
292
293
  f.puts ActiveRecord::Base.connection.dump_schema_information
293
294
  end