activerecord 4.0.3 → 4.0.4.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +412 -0
  3. data/lib/active_record/associations.rb +2 -2
  4. data/lib/active_record/associations/association.rb +7 -2
  5. data/lib/active_record/associations/association_scope.rb +7 -2
  6. data/lib/active_record/associations/builder/belongs_to.rb +9 -3
  7. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  8. data/lib/active_record/associations/collection_association.rb +1 -1
  9. data/lib/active_record/associations/collection_proxy.rb +1 -1
  10. data/lib/active_record/associations/has_many_through_association.rb +1 -1
  11. data/lib/active_record/associations/has_one_association.rb +4 -2
  12. data/lib/active_record/associations/singular_association.rb +1 -1
  13. data/lib/active_record/attribute_methods.rb +32 -15
  14. data/lib/active_record/attribute_methods/serialization.rb +10 -0
  15. data/lib/active_record/autosave_association.rb +8 -2
  16. data/lib/active_record/callbacks.rb +2 -2
  17. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -1
  18. data/lib/active_record/connection_adapters/abstract/database_statements.rb +14 -3
  19. data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -1
  20. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +9 -9
  21. data/lib/active_record/connection_adapters/abstract/transaction.rb +4 -0
  22. data/lib/active_record/connection_adapters/abstract_adapter.rb +7 -0
  23. data/lib/active_record/connection_adapters/column.rb +11 -3
  24. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -1
  25. data/lib/active_record/connection_adapters/mysql_adapter.rb +2 -2
  26. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +3 -2
  27. data/lib/active_record/connection_adapters/postgresql/cast.rb +12 -8
  28. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +2 -2
  29. data/lib/active_record/connection_adapters/postgresql/quoting.rb +1 -1
  30. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +13 -0
  31. data/lib/active_record/connection_adapters/postgresql_adapter.rb +20 -15
  32. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +7 -3
  33. data/lib/active_record/core.rb +17 -9
  34. data/lib/active_record/counter_cache.rb +1 -1
  35. data/lib/active_record/dynamic_matchers.rb +7 -2
  36. data/lib/active_record/inheritance.rb +15 -7
  37. data/lib/active_record/locking/pessimistic.rb +3 -3
  38. data/lib/active_record/migration.rb +10 -6
  39. data/lib/active_record/migration/command_recorder.rb +12 -5
  40. data/lib/active_record/persistence.rb +7 -6
  41. data/lib/active_record/railties/databases.rake +4 -5
  42. data/lib/active_record/relation/batches.rb +2 -5
  43. data/lib/active_record/relation/calculations.rb +3 -1
  44. data/lib/active_record/relation/finder_methods.rb +6 -5
  45. data/lib/active_record/relation/merger.rb +18 -2
  46. data/lib/active_record/relation/query_methods.rb +5 -6
  47. data/lib/active_record/sanitization.rb +6 -3
  48. data/lib/active_record/store.rb +1 -1
  49. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -1
  50. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  51. data/lib/active_record/timestamp.rb +2 -2
  52. data/lib/active_record/transactions.rb +7 -3
  53. data/lib/active_record/validations/presence.rb +1 -1
  54. data/lib/active_record/validations/uniqueness.rb +13 -5
  55. data/lib/active_record/version.rb +1 -1
  56. metadata +9 -9
@@ -207,7 +207,7 @@ module ActiveRecord
207
207
 
208
208
  # Returns an array of arrays containing the field values.
209
209
  # Order is the same as that returned by +columns+.
210
- def select_rows(sql, name = nil)
210
+ def select_rows(sql, name = nil, binds = [])
211
211
  execute(sql, name).to_a
212
212
  end
213
213
 
@@ -213,9 +213,9 @@ module ActiveRecord
213
213
 
214
214
  # DATABASE STATEMENTS ======================================
215
215
 
216
- def select_rows(sql, name = nil)
216
+ def select_rows(sql, name = nil, binds = [])
217
217
  @connection.query_with_result = true
218
- rows = exec_query(sql, name).rows
218
+ rows = exec_query(sql, name, binds).rows
219
219
  @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
220
220
  rows
221
221
  end
@@ -84,8 +84,9 @@ module ActiveRecord
84
84
  end
85
85
 
86
86
  def add_item_to_array(array, current_item, quoted)
87
- if current_item.length == 0
88
- elsif !quoted && current_item == 'NULL'
87
+ return if !quoted && current_item.length == 0
88
+
89
+ if !quoted && current_item == 'NULL'
89
90
  array.push nil
90
91
  else
91
92
  array.push current_item
@@ -35,11 +35,11 @@ module ActiveRecord
35
35
  end
36
36
  end
37
37
 
38
- def hstore_to_string(object)
38
+ def hstore_to_string(object, array_member = false)
39
39
  if Hash === object
40
- object.map { |k,v|
41
- "#{escape_hstore(k)}=>#{escape_hstore(v)}"
42
- }.join ','
40
+ string = object.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(',')
41
+ string = escape_hstore(string) if array_member
42
+ string
43
43
  else
44
44
  object
45
45
  end
@@ -49,10 +49,10 @@ module ActiveRecord
49
49
  if string.nil?
50
50
  nil
51
51
  elsif String === string
52
- Hash[string.scan(HstorePair).map { |k,v|
52
+ Hash[string.scan(HstorePair).map { |k, v|
53
53
  v = v.upcase == 'NULL' ? nil : v.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
54
54
  k = k.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
55
- [k,v]
55
+ [k, v]
56
56
  }]
57
57
  else
58
58
  string
@@ -100,7 +100,11 @@ module ActiveRecord
100
100
  if string.nil?
101
101
  nil
102
102
  elsif String === string
103
- IPAddr.new(string)
103
+ begin
104
+ IPAddr.new(string)
105
+ rescue ArgumentError
106
+ nil
107
+ end
104
108
  else
105
109
  string
106
110
  end
@@ -142,7 +146,7 @@ module ActiveRecord
142
146
 
143
147
  def quote_and_escape(value)
144
148
  case value
145
- when "NULL"
149
+ when "NULL", Numeric
146
150
  value
147
151
  else
148
152
  value = value.gsub(/\\/, ARRAY_ESCAPE)
@@ -46,8 +46,8 @@ module ActiveRecord
46
46
 
47
47
  # Executes a SELECT query and returns an array of rows. Each row is an
48
48
  # array of field values.
49
- def select_rows(sql, name = nil)
50
- select_raw(sql, name).last
49
+ def select_rows(sql, name = nil, binds = [])
50
+ exec_query(sql, name, binds).rows
51
51
  end
52
52
 
53
53
  # Executes an INSERT query and returns the new record's ID
@@ -109,7 +109,7 @@ module ActiveRecord
109
109
  { :value => value, :format => 1 }
110
110
  when Hash
111
111
  case column.sql_type
112
- when 'hstore' then PostgreSQLColumn.hstore_to_string(value)
112
+ when 'hstore' then PostgreSQLColumn.hstore_to_string(value, array_member)
113
113
  when 'json' then PostgreSQLColumn.json_to_string(value)
114
114
  else super(value, column)
115
115
  end
@@ -126,6 +126,19 @@ module ActiveRecord
126
126
  SQL
127
127
  end
128
128
 
129
+ def index_name_exists?(table_name, index_name, default)
130
+ exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
131
+ SELECT COUNT(*)
132
+ FROM pg_class t
133
+ INNER JOIN pg_index d ON t.oid = d.indrelid
134
+ INNER JOIN pg_class i ON d.indexrelid = i.oid
135
+ WHERE i.relkind = 'i'
136
+ AND i.relname = '#{index_name}'
137
+ AND t.relname = '#{table_name}'
138
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
139
+ SQL
140
+ end
141
+
129
142
  # Returns an array of indexes for the given table.
130
143
  def indexes(table_name, name = nil)
131
144
  result = query(<<-SQL, 'SCHEMA')
@@ -46,7 +46,7 @@ module ActiveRecord
46
46
  # PostgreSQL-specific extensions to column definitions in a table.
47
47
  class PostgreSQLColumn < Column #:nodoc:
48
48
  attr_accessor :array
49
- # Instantiates a new PostgreSQL column definition in a table.
49
+
50
50
  def initialize(name, default, oid_type, sql_type = nil, null = true)
51
51
  @oid_type = oid_type
52
52
  default_value = self.class.extract_value_from_default(default)
@@ -62,6 +62,14 @@ module ActiveRecord
62
62
  @default_function = default if has_default_function?(default_value, default)
63
63
  end
64
64
 
65
+ def number?
66
+ !array && super
67
+ end
68
+
69
+ def text?
70
+ !array && super
71
+ end
72
+
65
73
  # :stopdoc:
66
74
  class << self
67
75
  include ConnectionAdapters::PostgreSQLColumn::Cast
@@ -565,11 +573,16 @@ module ActiveRecord
565
573
 
566
574
  # Is this connection alive and ready for queries?
567
575
  def active?
568
- @connection.connect_poll != PG::PGRES_POLLING_FAILED
576
+ @connection.query 'SELECT 1'
577
+ true
569
578
  rescue PGError
570
579
  false
571
580
  end
572
581
 
582
+ def active_threadsafe?
583
+ @connection.connect_poll != PG::PGRES_POLLING_FAILED
584
+ end
585
+
573
586
  # Close then reopen the connection.
574
587
  def reconnect!
575
588
  super
@@ -706,6 +719,10 @@ module ActiveRecord
706
719
  !native_database_types[type].nil?
707
720
  end
708
721
 
722
+ def update_table_definition(table_name, base) #:nodoc:
723
+ Table.new(table_name, base)
724
+ end
725
+
709
726
  protected
710
727
 
711
728
  # Returns the version of the connected PostgreSQL server.
@@ -768,7 +785,7 @@ module ActiveRecord
768
785
  end
769
786
  end
770
787
 
771
- FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
788
+ FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
772
789
 
773
790
  def exec_no_cache(sql, binds)
774
791
  @connection.async_exec(sql)
@@ -891,14 +908,6 @@ module ActiveRecord
891
908
  exec_query(sql, name, binds)
892
909
  end
893
910
 
894
- def select_raw(sql, name = nil)
895
- res = execute(sql, name)
896
- results = result_as_array(res)
897
- fields = res.fields
898
- res.clear
899
- return fields, results
900
- end
901
-
902
911
  # Returns the list of a table's column names, data types, and default values.
903
912
  #
904
913
  # The underlying query is roughly:
@@ -947,10 +956,6 @@ module ActiveRecord
947
956
  def create_table_definition(name, temporary, options)
948
957
  TableDefinition.new native_database_types, name, temporary, options
949
958
  end
950
-
951
- def update_table_definition(table_name, base)
952
- Table.new(table_name, base)
953
- end
954
959
  end
955
960
  end
956
961
  end
@@ -347,8 +347,8 @@ module ActiveRecord
347
347
  end
348
348
  alias :create :insert_sql
349
349
 
350
- def select_rows(sql, name = nil)
351
- exec_query(sql, name).rows
350
+ def select_rows(sql, name = nil, binds = [])
351
+ exec_query(sql, name, binds).rows
352
352
  end
353
353
 
354
354
  def create_savepoint
@@ -616,7 +616,11 @@ module ActiveRecord
616
616
 
617
617
  def translate_exception(exception, message)
618
618
  case exception.message
619
- when /column(s)? .* (is|are) not unique/
619
+ # SQLite 3.8.2 returns a newly formatted error message:
620
+ # UNIQUE constraint failed: *table_name*.*column_name*
621
+ # Older versions of SQLite return:
622
+ # column *column_name* is not unique
623
+ when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
620
624
  RecordNotUnique.new(message, exception)
621
625
  else
622
626
  super
@@ -112,7 +112,7 @@ module ActiveRecord
112
112
  elsif abstract_class?
113
113
  "#{super}(abstract)"
114
114
  elsif !connected?
115
- "#{super}(no database connection)"
115
+ "#{super} (call '#{super}.connection' to establish a connection)"
116
116
  elsif table_exists?
117
117
  attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
118
118
  "#{super}(#{attr_list})"
@@ -129,7 +129,7 @@ module ActiveRecord
129
129
  # Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
130
130
  #
131
131
  # class Post < ActiveRecord::Base
132
- # scope :published_and_commented, published.and(self.arel_table[:comments_count].gt(0))
132
+ # scope :published_and_commented, -> { published.and(self.arel_table[:comments_count].gt(0)) }
133
133
  # end
134
134
  def arel_table
135
135
  @arel_table ||= Arel::Table.new(table_name, arel_engine)
@@ -275,7 +275,7 @@ module ActiveRecord
275
275
  # Post.new.encode_with(coder)
276
276
  # coder # => {"attributes" => {"id" => nil, ... }}
277
277
  def encode_with(coder)
278
- coder['attributes'] = attributes
278
+ coder['attributes'] = attributes_for_coder
279
279
  end
280
280
 
281
281
  # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
@@ -373,6 +373,17 @@ module ActiveRecord
373
373
  !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_create_callbacks.empty?
374
374
  end
375
375
 
376
+ # Required to deserialize Syck properly.
377
+ if YAML.const_defined?(:ENGINE) && YAML::ENGINE.syck?
378
+ ActiveSupport::Deprecation.warn(
379
+ "Syck is deprecated and support for serialization has been removed." \
380
+ " ActiveRecord::Core#yaml_initialize will be removed in 4.1 which will break deserialization support with Syck."
381
+ )
382
+ def yaml_initialize(tag, coder) # :nodoc:
383
+ init_with(coder)
384
+ end
385
+ end
386
+
376
387
  private
377
388
 
378
389
  # Updates the attributes on this particular ActiveRecord object so that
@@ -397,13 +408,10 @@ module ActiveRecord
397
408
  end
398
409
 
399
410
  def update_attributes_from_transaction_state(transaction_state, depth)
400
- if transaction_state && !has_transactional_callbacks?
411
+ if transaction_state && transaction_state.finalized? && !has_transactional_callbacks?
401
412
  unless @reflects_state[depth]
402
- if transaction_state.committed?
403
- committed!
404
- elsif transaction_state.rolledback?
405
- rolledback!
406
- end
413
+ restore_transaction_record_state if transaction_state.rolledback?
414
+ clear_transaction_record_state
407
415
  @reflects_state[depth] = true
408
416
  end
409
417
 
@@ -77,7 +77,7 @@ module ActiveRecord
77
77
  "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
78
78
  end
79
79
 
80
- where(primary_key => id).update_all updates.join(', ')
80
+ unscoped.where(primary_key => id).update_all updates.join(', ')
81
81
  end
82
82
 
83
83
  # Increment a numeric field by one, via a direct SQL update.
@@ -84,13 +84,18 @@ module ActiveRecord
84
84
  "#{finder}(#{attributes_hash})"
85
85
  end
86
86
 
87
+ # The parameters in the signature may have reserved Ruby words, in order
88
+ # to prevent errors, we start each param name with `_`.
89
+ #
87
90
  # Extended in activerecord-deprecated_finders
88
91
  def signature
89
- attribute_names.join(', ')
92
+ attribute_names.map { |name| "_#{name}" }.join(', ')
90
93
  end
91
94
 
95
+ # Given that the parameters starts with `_`, the finder needs to use the
96
+ # same parameter name.
92
97
  def attributes_hash
93
- "{" + attribute_names.map { |name| ":#{name} => #{name}" }.join(',') + "}"
98
+ "{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(',') + "}"
94
99
  end
95
100
 
96
101
  def finder
@@ -18,13 +18,17 @@ module ActiveRecord
18
18
  if abstract_class? || self == Base
19
19
  raise NotImplementedError, "#{self} is an abstract class and can not be instantiated."
20
20
  end
21
- if (attrs = args.first).is_a?(Hash)
22
- if subclass = subclass_from_attrs(attrs)
23
- return subclass.new(*args, &block)
24
- end
21
+
22
+ attrs = args.first
23
+ if subclass_from_attributes?(attrs)
24
+ subclass = subclass_from_attributes(attrs)
25
+ end
26
+
27
+ if subclass
28
+ subclass.new(*args, &block)
29
+ else
30
+ super
25
31
  end
26
- # Delegate to the original .new
27
- super
28
32
  end
29
33
 
30
34
  # True if this isn't a concrete subclass needing a STI type condition.
@@ -168,7 +172,11 @@ module ActiveRecord
168
172
  # is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
169
173
  # If this is a StrongParameters hash, and access to inheritance_column is not permitted,
170
174
  # this will ignore the inheritance column and return nil
171
- def subclass_from_attrs(attrs)
175
+ def subclass_from_attributes?(attrs)
176
+ columns_hash.include?(inheritance_column) && attrs.is_a?(Hash)
177
+ end
178
+
179
+ def subclass_from_attributes(attrs)
172
180
  subclass_name = attrs.with_indifferent_access[inheritance_column]
173
181
 
174
182
  if subclass_name.present? && subclass_name != self.name
@@ -3,12 +3,12 @@ module ActiveRecord
3
3
  # Locking::Pessimistic provides support for row-level locking using
4
4
  # SELECT ... FOR UPDATE and other lock types.
5
5
  #
6
- # Pass <tt>lock: true</tt> to <tt>ActiveRecord::Base.find</tt> to obtain an exclusive
6
+ # Chain <tt>ActiveRecord::Base#find</tt> to <tt>ActiveRecord::QueryMethods#lock</tt> to obtain an exclusive
7
7
  # lock on the selected rows:
8
8
  # # select * from accounts where id=1 for update
9
- # Account.find(1, lock: true)
9
+ # Account.lock.find(1)
10
10
  #
11
- # Pass <tt>lock: 'some locking clause'</tt> to give a database-specific locking clause
11
+ # Call <tt>lock('some locking clause')</tt> to use a database-specific locking clause
12
12
  # of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
13
13
  #
14
14
  # Account.transaction do
@@ -32,7 +32,11 @@ module ActiveRecord
32
32
 
33
33
  class PendingMigrationError < ActiveRecordError#:nodoc:
34
34
  def initialize
35
- super("Migrations are pending; run 'bin/rake db:migrate RAILS_ENV=#{Rails.env}' to resolve this issue.")
35
+ if defined?(Rails)
36
+ super("Migrations are pending; run 'bin/rake db:migrate RAILS_ENV=#{::Rails.env}' to resolve this issue.")
37
+ else
38
+ super("Migrations are pending; run 'bin/rake db:migrate' to resolve this issue.")
39
+ end
36
40
  end
37
41
  end
38
42
 
@@ -120,8 +124,8 @@ module ActiveRecord
120
124
  # a column but keeps the type and content.
121
125
  # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
122
126
  # the column to a different type using the same parameters as add_column.
123
- # * <tt>remove_column(table_name, column_names)</tt>: Removes the column listed in
124
- # +column_names+ from the table called +table_name+.
127
+ # * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
128
+ # named +column_name+ from the table called +table_name+.
125
129
  # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
126
130
  # with the name of the column. Other options include
127
131
  # <tt>:name</tt>, <tt>:unique</tt> (e.g.
@@ -629,7 +633,7 @@ module ActiveRecord
629
633
  def copy(destination, sources, options = {})
630
634
  copied = []
631
635
 
632
- FileUtils.mkdir_p(destination) unless File.exists?(destination)
636
+ FileUtils.mkdir_p(destination) unless File.exist?(destination)
633
637
 
634
638
  destination_migrations = ActiveRecord::Migrator.migrations(destination)
635
639
  last = destination_migrations.last
@@ -716,7 +720,7 @@ module ActiveRecord
716
720
 
717
721
  def load_migration
718
722
  require(File.expand_path(filename))
719
- name.constantize.new
723
+ name.constantize.new(name, version)
720
724
  end
721
725
 
722
726
  end
@@ -875,7 +879,7 @@ module ActiveRecord
875
879
 
876
880
  validate(@migrations)
877
881
 
878
- ActiveRecord::SchemaMigration.create_table
882
+ Base.connection.initialize_schema_migrations_table
879
883
  end
880
884
 
881
885
  def current_version
@@ -86,7 +86,7 @@ module ActiveRecord
86
86
  alias :remove_belongs_to :remove_reference
87
87
 
88
88
  def change_table(table_name, options = {})
89
- yield ConnectionAdapters::Table.new(table_name, self)
89
+ yield delegate.update_table_definition(table_name, self)
90
90
  end
91
91
 
92
92
  private
@@ -139,7 +139,12 @@ module ActiveRecord
139
139
 
140
140
  def invert_add_index(args)
141
141
  table, columns, options = *args
142
- [:remove_index, [table, (options || {}).merge(column: columns)]]
142
+ options ||= {}
143
+
144
+ index_name = options[:name]
145
+ options_hash = index_name ? { name: index_name } : { column: columns }
146
+
147
+ [:remove_index, [table, options_hash]]
143
148
  end
144
149
 
145
150
  def invert_remove_index(args)
@@ -158,9 +163,11 @@ module ActiveRecord
158
163
 
159
164
  # Forwards any missing method call to the \target.
160
165
  def method_missing(method, *args, &block)
161
- @delegate.send(method, *args, &block)
162
- rescue NoMethodError => e
163
- raise e, e.message.sub(/ for #<.*$/, " via proxy for #{@delegate}")
166
+ if @delegate.respond_to?(method)
167
+ @delegate.send(method, *args, &block)
168
+ else
169
+ super
170
+ end
164
171
  end
165
172
  end
166
173
  end