activerecord 4.2.0.beta1 → 4.2.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. checksums.yaml +4 -4
  2. data/CHANGELOG.md +137 -38
  3. data/lib/active_record/associations.rb +78 -13
  4. data/lib/active_record/associations/association_scope.rb +53 -40
  5. data/lib/active_record/associations/collection_association.rb +1 -1
  6. data/lib/active_record/associations/collection_proxy.rb +4 -4
  7. data/lib/active_record/associations/has_many_through_association.rb +6 -6
  8. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  9. data/lib/active_record/associations/preloader.rb +32 -23
  10. data/lib/active_record/associations/singular_association.rb +1 -1
  11. data/lib/active_record/associations/through_association.rb +5 -1
  12. data/lib/active_record/attribute_methods.rb +7 -7
  13. data/lib/active_record/attribute_methods/dirty.rb +20 -9
  14. data/lib/active_record/attribute_methods/query.rb +1 -1
  15. data/lib/active_record/attribute_methods/read.rb +1 -3
  16. data/lib/active_record/attribute_methods/serialization.rb +3 -4
  17. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -0
  18. data/lib/active_record/autosave_association.rb +3 -3
  19. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -1
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +5 -0
  21. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +2 -0
  22. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +5 -8
  23. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +8 -4
  24. data/lib/active_record/connection_adapters/abstract/transaction.rb +11 -5
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +7 -1
  26. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +23 -15
  27. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -1
  28. data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -1
  29. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -4
  30. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -0
  31. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +3 -5
  32. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +8 -6
  33. data/lib/active_record/connection_adapters/postgresql_adapter.rb +9 -6
  34. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +6 -5
  35. data/lib/active_record/core.rb +11 -3
  36. data/lib/active_record/counter_cache.rb +1 -1
  37. data/lib/active_record/fixtures.rb +15 -8
  38. data/lib/active_record/gem_version.rb +2 -2
  39. data/lib/active_record/migration.rb +8 -12
  40. data/lib/active_record/reflection.rb +20 -18
  41. data/lib/active_record/relation/calculations.rb +7 -7
  42. data/lib/active_record/relation/finder_methods.rb +10 -9
  43. data/lib/active_record/relation/predicate_builder.rb +2 -2
  44. data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -4
  45. data/lib/active_record/relation/query_methods.rb +8 -12
  46. data/lib/active_record/schema_dumper.rb +20 -28
  47. data/lib/active_record/tasks/database_tasks.rb +9 -5
  48. data/lib/active_record/transactions.rb +9 -9
  49. data/lib/active_record/type.rb +1 -0
  50. data/lib/active_record/type/binary.rb +10 -0
  51. data/lib/active_record/type/decorator.rb +14 -0
  52. data/lib/active_record/type/serialized.rb +8 -3
  53. metadata +7 -6
@@ -640,7 +640,7 @@ module ActiveRecord
640
640
  end
641
641
 
642
642
  def call(env)
643
- testing = env.key?('rack.test')
643
+ testing = env['rack.test']
644
644
 
645
645
  response = @app.call(env)
646
646
  response[2] = ::Rack::BodyProxy.new(response[2]) do
@@ -83,6 +83,11 @@ module ActiveRecord
83
83
  exec_query(sql, name, binds)
84
84
  end
85
85
 
86
+ # Executes the truncate statement.
87
+ def truncate(table_name, name = nil)
88
+ raise NotImplementedError
89
+ end
90
+
86
91
  # Executes update +sql+ statement in the context of this connection using
87
92
  # +binds+ as the bind substitutes. +name+ is logged along with
88
93
  # the executed +sql+ statement.
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/string/strip'
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  class AbstractAdapter
@@ -2,7 +2,6 @@ require 'date'
2
2
  require 'set'
3
3
  require 'bigdecimal'
4
4
  require 'bigdecimal/util'
5
- require 'active_support/core_ext/string/strip'
6
5
 
7
6
  module ActiveRecord
8
7
  module ConnectionAdapters #:nodoc:
@@ -61,12 +60,11 @@ module ActiveRecord
61
60
  def emit_warning_if_null_unspecified(options)
62
61
  return if options.key?(:null)
63
62
 
64
- ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
65
- `timestamp` was called without specifying an option for `null`. In Rails
66
- 5.0, this behavior will change to `null: false`. You should manually
67
- specify `null: true` to prevent the behavior of your existing migrations
68
- from changing.
69
- MESSAGE
63
+ ActiveSupport::Deprecation.warn \
64
+ "`timestamp` was called without specifying an option for `null`. In Rails " \
65
+ "5.0, this behavior will change to `null: false`. You should manually " \
66
+ "specify `null: true` to prevent the behavior of your existing migrations " \
67
+ "from changing."
70
68
  end
71
69
  end
72
70
 
@@ -327,7 +325,6 @@ module ActiveRecord
327
325
  end
328
326
 
329
327
  column.limit = limit
330
- column.array = options[:array] if column.respond_to?(:array)
331
328
  column.precision = options[:precision]
332
329
  column.scale = options[:scale]
333
330
  column.default = options[:default]
@@ -19,12 +19,16 @@ module ActiveRecord
19
19
  spec = {}
20
20
  spec[:name] = column.name.inspect
21
21
  spec[:type] = column.type.to_s
22
- spec[:limit] = column.limit.inspect if column.limit != types[column.type][:limit]
22
+ spec[:null] = 'false' unless column.null
23
+
24
+ limit = column.limit || types[column.type][:limit]
25
+ spec[:limit] = limit.inspect if limit
23
26
  spec[:precision] = column.precision.inspect if column.precision
24
27
  spec[:scale] = column.scale.inspect if column.scale
25
- spec[:null] = 'false' unless column.null
26
- spec[:default] = schema_default(column) if column.has_default?
27
- spec.delete(:default) if spec[:default].nil?
28
+
29
+ default = schema_default(column) if column.has_default?
30
+ spec[:default] = default unless default.nil?
31
+
28
32
  spec
29
33
  end
30
34
 
@@ -190,11 +190,17 @@ module ActiveRecord
190
190
  rollback_transaction if transaction
191
191
  raise
192
192
  ensure
193
- begin
194
- commit_transaction unless error
195
- rescue Exception
196
- transaction.rollback unless transaction.state.completed?
197
- raise
193
+ unless error
194
+ if Thread.current.status == 'aborting'
195
+ rollback_transaction
196
+ else
197
+ begin
198
+ commit_transaction
199
+ rescue Exception
200
+ transaction.rollback unless transaction.state.completed?
201
+ raise
202
+ end
203
+ end
198
204
  end
199
205
  end
200
206
 
@@ -66,6 +66,7 @@ module ActiveRecord
66
66
  # Most of the methods in the adapter are useful during migrations. Most
67
67
  # notably, the instance methods provided by SchemaStatement are very useful.
68
68
  class AbstractAdapter
69
+ ADAPTER_NAME = 'Abstract'.freeze
69
70
  include Quoting, DatabaseStatements, SchemaStatements
70
71
  include DatabaseLimits
71
72
  include QueryCache
@@ -167,7 +168,7 @@ module ActiveRecord
167
168
  # Returns the human-readable name of the adapter. Use mixed case - one
168
169
  # can always use downcase if needed.
169
170
  def adapter_name
170
- 'Abstract'
171
+ self.class::ADAPTER_NAME
171
172
  end
172
173
 
173
174
  # Does this adapter support migrations?
@@ -239,6 +240,11 @@ module ActiveRecord
239
240
  false
240
241
  end
241
242
 
243
+ # Does this adapter support views?
244
+ def supports_views?
245
+ false
246
+ end
247
+
242
248
  # This is meant to be implemented by the adapters that support extensions
243
249
  def disable_extension(name)
244
250
  end
@@ -1,4 +1,5 @@
1
1
  require 'arel/visitors/bind_visitor'
2
+ require 'active_support/core_ext/string/strip'
2
3
 
3
4
  module ActiveRecord
4
5
  module ConnectionAdapters
@@ -161,10 +162,6 @@ module ActiveRecord
161
162
  end
162
163
  end
163
164
 
164
- def adapter_name #:nodoc:
165
- self.class::ADAPTER_NAME
166
- end
167
-
168
165
  # Returns true, since this connection adapter supports migrations.
169
166
  def supports_migrations?
170
167
  true
@@ -200,6 +197,10 @@ module ActiveRecord
200
197
  true
201
198
  end
202
199
 
200
+ def supports_views?
201
+ version[0] >= 5
202
+ end
203
+
203
204
  def native_database_types
204
205
  NATIVE_DATABASE_TYPES
205
206
  end
@@ -388,6 +389,10 @@ module ActiveRecord
388
389
  end
389
390
  end
390
391
 
392
+ def truncate(table_name, name = nil)
393
+ execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
394
+ end
395
+
391
396
  def table_exists?(name)
392
397
  return false unless name.present?
393
398
  return true if tables(nil, nil, name).any?
@@ -639,18 +644,21 @@ module ActiveRecord
639
644
 
640
645
  def initialize_type_map(m) # :nodoc:
641
646
  super
647
+
642
648
  m.register_type(%r(enum)i) do |sql_type|
643
649
  limit = sql_type[/^enum\((.+)\)/i, 1]
644
650
  .split(',').map{|enum| enum.strip.length - 2}.max
645
651
  Type::String.new(limit: limit)
646
652
  end
647
653
 
648
- m.register_type %r(tinytext)i, Type::Text.new(limit: 255)
649
- m.register_type %r(tinyblob)i, Type::Binary.new(limit: 255)
650
- m.register_type %r(mediumtext)i, Type::Text.new(limit: 16777215)
651
- m.register_type %r(mediumblob)i, Type::Binary.new(limit: 16777215)
652
- m.register_type %r(longtext)i, Type::Text.new(limit: 2147483647)
653
- m.register_type %r(longblob)i, Type::Binary.new(limit: 2147483647)
654
+ m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
655
+ m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
656
+ m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
657
+ m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
658
+ m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
659
+ m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
660
+ m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
661
+ m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
654
662
  m.register_type %r(^bigint)i, Type::Integer.new(limit: 8)
655
663
  m.register_type %r(^int)i, Type::Integer.new(limit: 4)
656
664
  m.register_type %r(^mediumint)i, Type::Integer.new(limit: 3)
@@ -782,10 +790,6 @@ module ActiveRecord
782
790
  full_version =~ /mariadb/i
783
791
  end
784
792
 
785
- def supports_views?
786
- version[0] >= 5
787
- end
788
-
789
793
  def supports_rename_index?
790
794
  mariadb? ? false : (version[0] == 5 && version[1] >= 7) || version[0] >= 6
791
795
  end
@@ -812,7 +816,11 @@ module ActiveRecord
812
816
  # NAMES does not have an equals sign, see
813
817
  # http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
814
818
  # (trailing comma because variable_assignments will always have content)
815
- encoding = "NAMES #{@config[:encoding]}, " if @config[:encoding]
819
+ if @config[:encoding]
820
+ encoding = "NAMES #{@config[:encoding]}"
821
+ encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
822
+ encoding << ", "
823
+ end
816
824
 
817
825
  # Gather up all of the SET variables...
818
826
  variable_assignments = variables.map do |k, v|
@@ -29,7 +29,7 @@ module ActiveRecord
29
29
 
30
30
  module ConnectionAdapters
31
31
  class Mysql2Adapter < AbstractMysqlAdapter
32
- ADAPTER_NAME = 'Mysql2'
32
+ ADAPTER_NAME = 'Mysql2'.freeze
33
33
 
34
34
  def initialize(connection, logger, connection_options, config)
35
35
  super
@@ -66,7 +66,7 @@ module ActiveRecord
66
66
  # * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
67
67
  #
68
68
  class MysqlAdapter < AbstractMysqlAdapter
69
- ADAPTER_NAME = 'MySQL'
69
+ ADAPTER_NAME = 'MySQL'.freeze
70
70
 
71
71
  class StatementPool < ConnectionAdapters::StatementPool
72
72
  def initialize(connection, max = 1000)
@@ -25,10 +25,10 @@ module ActiveRecord
25
25
  if !infinity?(from) && extracted[:exclude_start]
26
26
  if from.respond_to?(:succ)
27
27
  from = from.succ
28
- ActiveSupport::Deprecation.warn <<-MESSAGE
29
- Excluding the beginning of a Range is only partialy supported through `#succ`.
30
- This is not reliable and will be removed in the future.
31
- MESSAGE
28
+ ActiveSupport::Deprecation.warn \
29
+ "Excluding the beginning of a Range is only partialy supported " \
30
+ "through `#succ`. This is not reliable and will be removed in " \
31
+ "the future."
32
32
  else
33
33
  raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')"
34
34
  end
@@ -12,6 +12,8 @@ module ActiveRecord
12
12
  [a-fA-F0-9]{4}-?
13
13
  [a-fA-F0-9]{4}-?\}?\z}x
14
14
 
15
+ alias_method :type_cast_for_database, :type_cast_from_database
16
+
15
17
  def type
16
18
  :uuid
17
19
  end
@@ -131,12 +131,10 @@ module ActiveRecord
131
131
  column name, type, options
132
132
  end
133
133
 
134
- def column(name, type = nil, options = {})
135
- super
136
- column = self[name]
134
+ def new_column_definition(name, type, options) # :nodoc:
135
+ column = super
137
136
  column.array = options[:array]
138
-
139
- self
137
+ column
140
138
  end
141
139
 
142
140
  private
@@ -60,8 +60,8 @@ module ActiveRecord
60
60
  def create_database(name, options = {})
61
61
  options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
62
62
 
63
- option_string = options.sum do |key, value|
64
- case key
63
+ option_string = options.inject("") do |memo, (key, value)|
64
+ memo += case key
65
65
  when :owner
66
66
  " OWNER = \"#{value}\""
67
67
  when :template
@@ -281,9 +281,9 @@ module ActiveRecord
281
281
  def default_sequence_name(table_name, pk = nil) #:nodoc:
282
282
  result = serial_sequence(table_name, pk || 'id')
283
283
  return nil unless result
284
- Utils.extract_schema_qualified_name(result)
284
+ Utils.extract_schema_qualified_name(result).to_s
285
285
  rescue ActiveRecord::StatementInvalid
286
- PostgreSQL::Name.new(nil, "#{table_name}_#{pk || 'id'}_seq")
286
+ PostgreSQL::Name.new(nil, "#{table_name}_#{pk || 'id'}_seq").to_s
287
287
  end
288
288
 
289
289
  def serial_sequence(table, column)
@@ -466,7 +466,7 @@ module ActiveRecord
466
466
 
467
467
  def foreign_keys(table_name)
468
468
  fk_info = select_all <<-SQL.strip_heredoc
469
- SELECT t2.relname AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
469
+ SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
470
470
  FROM pg_constraint c
471
471
  JOIN pg_class t1 ON c.conrelid = t1.oid
472
472
  JOIN pg_class t2 ON c.confrelid = t2.oid
@@ -488,6 +488,7 @@ module ActiveRecord
488
488
 
489
489
  options[:on_delete] = extract_foreign_key_action(row['on_delete'])
490
490
  options[:on_update] = extract_foreign_key_action(row['on_update'])
491
+
491
492
  ForeignKeyDefinition.new(table_name, row['to_table'], options)
492
493
  end
493
494
  end
@@ -549,7 +550,8 @@ module ActiveRecord
549
550
  # Convert Arel node to string
550
551
  s = s.to_sql unless s.is_a?(String)
551
552
  # Remove any ASC/DESC modifiers
552
- s.gsub(/\s+(?:ASC|DESC)?\s*(?:NULLS\s+(?:FIRST|LAST)\s*)?/i, '')
553
+ s.gsub(/\s+(?:ASC|DESC)\b/i, '')
554
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
553
555
  }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
554
556
 
555
557
  [super, *order_columns].join(', ')
@@ -74,7 +74,7 @@ module ActiveRecord
74
74
  # In addition, default connection parameters of libpq can be set per environment variables.
75
75
  # See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
76
76
  class PostgreSQLAdapter < AbstractAdapter
77
- ADAPTER_NAME = 'PostgreSQL'
77
+ ADAPTER_NAME = 'PostgreSQL'.freeze
78
78
 
79
79
  NATIVE_DATABASE_TYPES = {
80
80
  primary_key: "serial primary key",
@@ -118,11 +118,6 @@ module ActiveRecord
118
118
  include PostgreSQL::DatabaseStatements
119
119
  include Savepoints
120
120
 
121
- # Returns 'PostgreSQL' as adapter name for identification purposes.
122
- def adapter_name
123
- ADAPTER_NAME
124
- end
125
-
126
121
  def schema_creation # :nodoc:
127
122
  PostgreSQL::SchemaCreation.new self
128
123
  end
@@ -163,6 +158,10 @@ module ActiveRecord
163
158
  true
164
159
  end
165
160
 
161
+ def supports_views?
162
+ true
163
+ end
164
+
166
165
  def index_algorithms
167
166
  { concurrently: 'CONCURRENTLY' }
168
167
  end
@@ -256,6 +255,10 @@ module ActiveRecord
256
255
  @statements.clear
257
256
  end
258
257
 
258
+ def truncate(table_name, name = nil)
259
+ exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
260
+ end
261
+
259
262
  # Is this connection alive and ready for queries?
260
263
  def active?
261
264
  @connection.query 'SELECT 1'
@@ -67,6 +67,7 @@ module ActiveRecord
67
67
  #
68
68
  # * <tt>:database</tt> - Path to the database file.
69
69
  class SQLite3Adapter < AbstractAdapter
70
+ ADAPTER_NAME = 'SQLite'.freeze
70
71
  include Savepoints
71
72
 
72
73
  NATIVE_DATABASE_TYPES = {
@@ -147,10 +148,6 @@ module ActiveRecord
147
148
  end
148
149
  end
149
150
 
150
- def adapter_name #:nodoc:
151
- 'SQLite'
152
- end
153
-
154
151
  def supports_ddl_transactions?
155
152
  true
156
153
  end
@@ -186,6 +183,10 @@ module ActiveRecord
186
183
  true
187
184
  end
188
185
 
186
+ def supports_views?
187
+ true
188
+ end
189
+
189
190
  def active?
190
191
  @active != false
191
192
  end
@@ -372,7 +373,7 @@ module ActiveRecord
372
373
  sql = <<-SQL
373
374
  SELECT name
374
375
  FROM sqlite_master
375
- WHERE type = 'table' AND NOT name = 'sqlite_sequence'
376
+ WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
376
377
  SQL
377
378
  sql << " AND name = #{quote_table_name(table_name)}" if table_name
378
379
 
@@ -124,6 +124,8 @@ module ActiveRecord
124
124
  def find(*ids)
125
125
  # We don't have cache keys for this stuff yet
126
126
  return super unless ids.length == 1
127
+ # Allow symbols to super to maintain compatibility for deprecated finders until Rails 5
128
+ return super if ids.first.kind_of?(Symbol)
127
129
  return super if block_given? ||
128
130
  primary_key.nil? ||
129
131
  default_scopes.any? ||
@@ -151,7 +153,8 @@ module ActiveRecord
151
153
  end
152
154
 
153
155
  def find_by(*args)
154
- return super if current_scope || args.length > 1 || reflect_on_all_aggregations.any?
156
+ return super if current_scope || !(Hash === args.first) || reflect_on_all_aggregations.any?
157
+ return super if default_scopes.any?
155
158
 
156
159
  hash = args.first
157
160
 
@@ -159,6 +162,9 @@ module ActiveRecord
159
162
  v.nil? || Array === v || Hash === v
160
163
  }
161
164
 
165
+ # We can't cache Post.find_by(author: david) ...yet
166
+ return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) }
167
+
162
168
  key = hash.keys
163
169
 
164
170
  klass = self
@@ -177,9 +183,11 @@ module ActiveRecord
177
183
  end
178
184
  end
179
185
 
180
- def initialize_generated_modules
181
- super
186
+ def find_by!(*args)
187
+ find_by(*args) or raise RecordNotFound.new("Couldn't find #{name}")
188
+ end
182
189
 
190
+ def initialize_generated_modules
183
191
  generated_association_methods
184
192
  end
185
193