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
@@ -10,9 +10,6 @@ module ActiveRecord
10
10
  # The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the
11
11
  # attributes on the objects that are to be created.
12
12
  #
13
- # +create+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
14
- # in the +options+ parameter.
15
- #
16
13
  # ==== Examples
17
14
  # # Create a single new object
18
15
  # User.create(first_name: 'Jamie')
@@ -67,7 +64,7 @@ module ActiveRecord
67
64
  end
68
65
 
69
66
  # Returns true if this object hasn't been saved yet -- that is, a record
70
- # for the object doesn't exist in the data store yet; otherwise, returns false.
67
+ # for the object doesn't exist in the database yet; otherwise, returns false.
71
68
  def new_record?
72
69
  sync_with_transaction_state
73
70
  @new_record
@@ -198,7 +195,11 @@ module ActiveRecord
198
195
  # share the same set of attributes.
199
196
  def becomes!(klass)
200
197
  became = becomes(klass)
201
- became.public_send("#{klass.inheritance_column}=", klass.sti_name) unless self.class.descends_from_active_record?
198
+ sti_type = nil
199
+ if !klass.descends_from_active_record?
200
+ sti_type = klass.sti_name
201
+ end
202
+ became.public_send("#{klass.inheritance_column}=", sti_type)
202
203
  became
203
204
  end
204
205
 
@@ -386,7 +387,7 @@ module ActiveRecord
386
387
 
387
388
  fresh_object =
388
389
  if options && options[:lock]
389
- self.class.unscoped { self.class.lock.find(id) }
390
+ self.class.unscoped { self.class.lock(options[:lock]).find(id) }
390
391
  else
391
392
  self.class.unscoped { self.class.find(id) }
392
393
  end
@@ -249,7 +249,7 @@ db_namespace = namespace :db do
249
249
  desc 'Load a schema.rb file into the database'
250
250
  task :load => [:environment, :load_config] do
251
251
  file = ENV['SCHEMA'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, 'schema.rb')
252
- if File.exists?(file)
252
+ if File.exist?(file)
253
253
  load(file)
254
254
  else
255
255
  abort %{#{file} doesn't exist yet. Run `rake db:migrate` to create it, then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.}
@@ -274,7 +274,7 @@ db_namespace = namespace :db do
274
274
  desc 'Clear a db/schema_cache.dump file.'
275
275
  task :clear => [:environment, :load_config] do
276
276
  filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.dump")
277
- FileUtils.rm(filename) if File.exists?(filename)
277
+ FileUtils.rm(filename) if File.exist?(filename)
278
278
  end
279
279
  end
280
280
 
@@ -344,7 +344,7 @@ db_namespace = namespace :db do
344
344
  end
345
345
 
346
346
  # desc "Recreate the test database from a fresh schema"
347
- task :clone do
347
+ task :clone => :environment do
348
348
  case ActiveRecord::Base.schema_format
349
349
  when :ruby
350
350
  db_namespace["test:clone_schema"].invoke
@@ -365,7 +365,7 @@ db_namespace = namespace :db do
365
365
  end
366
366
 
367
367
  # desc 'Check for pending migrations and load the test schema'
368
- task :prepare => :load_config do
368
+ task :prepare => [:environment, :load_config] do
369
369
  unless ActiveRecord::Base.configurations.blank?
370
370
  db_namespace['test:load'].invoke
371
371
  end
@@ -402,4 +402,3 @@ namespace :railties do
402
402
  end
403
403
 
404
404
  task 'test:prepare' => ['db:test:prepare', 'db:test:load', 'db:abort_if_pending_migrations']
405
-
@@ -71,16 +71,13 @@ module ActiveRecord
71
71
  while records.any?
72
72
  records_size = records.size
73
73
  primary_key_offset = records.last.id
74
+ raise "Primary key not included in the custom select clause" unless primary_key_offset
74
75
 
75
76
  yield records
76
77
 
77
78
  break if records_size < batch_size
78
79
 
79
- if primary_key_offset
80
- records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
81
- else
82
- raise "Primary key not included in the custom select clause"
83
- end
80
+ records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
84
81
  end
85
82
  end
86
83
 
@@ -332,7 +332,9 @@ module ActiveRecord
332
332
  }
333
333
  key = key.first if key.size == 1
334
334
  key = key_records[key] if associated
335
- [key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
335
+
336
+ column_type = calculated_data.column_types.fetch(aggregate_alias) { column_for(column_name) }
337
+ [key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)]
336
338
  end]
337
339
  end
338
340
 
@@ -4,11 +4,12 @@ module ActiveRecord
4
4
  # If no record can be found for all of the listed ids, then RecordNotFound will be raised. If the primary key
5
5
  # is an integer, find by id coerces its arguments using +to_i+.
6
6
  #
7
- # Person.find(1) # returns the object for ID = 1
8
- # Person.find("1") # returns the object for ID = 1
9
- # Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
10
- # Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
11
- # Person.find([1]) # returns an array for the object with ID = 1
7
+ # Person.find(1) # returns the object for ID = 1
8
+ # Person.find("1") # returns the object for ID = 1
9
+ # Person.find("31-sarah") # returns the object for ID = 31
10
+ # Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
11
+ # Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
12
+ # Person.find([1]) # returns an array for the object with ID = 1
12
13
  # Person.where("administrator = 1").order("created_on DESC").find(1)
13
14
  #
14
15
  # Note that returned records may not be in the same order as the ids you
@@ -107,13 +107,29 @@ module ActiveRecord
107
107
  def merge_multi_values
108
108
  lhs_wheres = relation.where_values
109
109
  rhs_wheres = values[:where] || []
110
+
110
111
  lhs_binds = relation.bind_values
111
112
  rhs_binds = values[:bind] || []
112
113
 
113
114
  removed, kept = partition_overwrites(lhs_wheres, rhs_wheres)
114
115
 
115
- relation.where_values = kept + rhs_wheres
116
- relation.bind_values = filter_binds(lhs_binds, removed) + rhs_binds
116
+ where_values = kept + rhs_wheres
117
+ bind_values = filter_binds(lhs_binds, removed) + rhs_binds
118
+
119
+ conn = relation.klass.connection
120
+ bv_index = 0
121
+ where_values.map! do |node|
122
+ if Arel::Nodes::Equality === node && Arel::Nodes::BindParam === node.right
123
+ substitute = conn.substitute_at(bind_values[bv_index].first, bv_index)
124
+ bv_index += 1
125
+ Arel::Nodes::Equality.new(node.left, substitute)
126
+ else
127
+ node
128
+ end
129
+ end
130
+
131
+ relation.where_values = where_values
132
+ relation.bind_values = bind_values
117
133
 
118
134
  if values[:reordering]
119
135
  # override any order specified in the original relation
@@ -609,12 +609,11 @@ module ActiveRecord
609
609
  self
610
610
  end
611
611
 
612
- # Returns a chainable relation with zero records, specifically an
613
- # instance of the <tt>ActiveRecord::NullRelation</tt> class.
612
+ # Returns a chainable relation with zero records.
614
613
  #
615
- # The returned <tt>ActiveRecord::NullRelation</tt> inherits from Relation and implements the
616
- # Null Object pattern. It is an object with defined null behavior and always returns an empty
617
- # array of records without querying the database.
614
+ # The returned relation implements the Null Object pattern. It is an
615
+ # object with defined null behavior and always returns an empty array of
616
+ # records without querying the database.
618
617
  #
619
618
  # Any subsequent condition chained to the returned relation will continue
620
619
  # generating an empty relation and will not fire any query to the database.
@@ -849,7 +848,7 @@ module ActiveRecord
849
848
 
850
849
  where_values.reject! do |rel|
851
850
  case rel
852
- when Arel::Nodes::In, Arel::Nodes::Equality
851
+ when Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual
853
852
  subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right)
854
853
  subrelation.name.to_sym == target_value_sym
855
854
  else
@@ -99,8 +99,9 @@ module ActiveRecord
99
99
  # { status: nil, group_id: 1 }
100
100
  # # => "status = NULL , group_id = 1"
101
101
  def sanitize_sql_hash_for_assignment(attrs, table)
102
+ c = connection
102
103
  attrs.map do |attr, value|
103
- "#{connection.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value)}"
104
+ "#{c.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value, c, columns_hash[attr.to_s])}"
104
105
  end.join(', ')
105
106
  end
106
107
 
@@ -151,8 +152,10 @@ module ActiveRecord
151
152
  end
152
153
  end
153
154
 
154
- def quote_bound_value(value, c = connection) #:nodoc:
155
- if value.respond_to?(:map) && !value.acts_like?(:string)
155
+ def quote_bound_value(value, c = connection, column = nil) #:nodoc:
156
+ if column
157
+ c.quote(value, column)
158
+ elsif value.respond_to?(:map) && !value.acts_like?(:string)
156
159
  if value.respond_to?(:empty?) && value.empty?
157
160
  c.quote(nil)
158
161
  else
@@ -138,7 +138,7 @@ module ActiveRecord
138
138
  end
139
139
 
140
140
  def load(yaml)
141
- self.class.as_indifferent_hash @coder.load(yaml)
141
+ self.class.as_indifferent_hash @coder.load(yaml || '')
142
142
  end
143
143
 
144
144
  def self.as_indifferent_hash(obj)
@@ -134,8 +134,9 @@ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
134
134
  args << "--password=#{configuration['password']}" if configuration['password']
135
135
  args.concat(['--default-character-set', configuration['encoding']]) if configuration['encoding']
136
136
  configuration.slice('host', 'port', 'socket').each do |k, v|
137
- args.concat([ "--#{k}", v ]) if v
137
+ args.concat([ "--#{k}", v.to_s ]) if v
138
138
  end
139
+
139
140
  args
140
141
  end
141
142
  end
@@ -59,7 +59,7 @@ module ActiveRecord
59
59
 
60
60
  def structure_load(filename)
61
61
  set_psql_env
62
- Kernel.system("psql -q -f #{filename} #{configuration['database']}")
62
+ Kernel.system("psql -q -f #{Shellwords.escape(filename)} #{configuration['database']}")
63
63
  end
64
64
 
65
65
  private
@@ -10,9 +10,9 @@ module ActiveRecord
10
10
  #
11
11
  # config.active_record.record_timestamps = false
12
12
  #
13
- # Timestamps are in the local timezone by default but you can use UTC by setting:
13
+ # Timestamps are in UTC by default but you can use the local timezone by setting:
14
14
  #
15
- # config.active_record.default_timezone = :utc
15
+ # config.active_record.default_timezone = :local
16
16
  #
17
17
  # == Time Zone aware attributes
18
18
  #
@@ -218,8 +218,8 @@ module ActiveRecord
218
218
  # after_commit :do_bar, on: :update
219
219
  # after_commit :do_baz, on: :destroy
220
220
  #
221
- # after_commit :do_foo_bar, :on [:create, :update]
222
- # after_commit :do_bar_baz, :on [:update, :destroy]
221
+ # after_commit :do_foo_bar, on: [:create, :update]
222
+ # after_commit :do_bar_baz, on: [:update, :destroy]
223
223
  #
224
224
  # Note that transactional fixtures do not play well with this feature. Please
225
225
  # use the +test_after_commit+ gem to have these hooks fired in tests.
@@ -275,6 +275,10 @@ module ActiveRecord
275
275
  with_transaction_returning_status { super }
276
276
  end
277
277
 
278
+ def touch(*) #:nodoc:
279
+ with_transaction_returning_status { super }
280
+ end
281
+
278
282
  # Reset id and @new_record if the transaction rolls back.
279
283
  def rollback_active_record_state!
280
284
  remember_transaction_record_state
@@ -293,7 +297,7 @@ module ActiveRecord
293
297
  def committed! #:nodoc:
294
298
  run_callbacks :commit if destroyed? || persisted?
295
299
  ensure
296
- clear_transaction_record_state
300
+ @_start_transaction_state.clear
297
301
  end
298
302
 
299
303
  # Call the +after_rollback+ callbacks. The +force_restore_state+ argument indicates if the record
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  super
6
6
  attributes.each do |attribute|
7
7
  next unless record.class.reflect_on_association(attribute)
8
- associated_records = Array(record.send(attribute))
8
+ associated_records = Array.wrap(record.send(attribute))
9
9
 
10
10
  # Superclass validates presence. Ensure present records aren't about to be destroyed.
11
11
  if associated_records.present? && associated_records.all? { |r| r.marked_for_destruction? }
@@ -53,10 +53,18 @@ module ActiveRecord
53
53
  def build_relation(klass, table, attribute, value) #:nodoc:
54
54
  if reflection = klass.reflect_on_association(attribute)
55
55
  attribute = reflection.foreign_key
56
- value = value.attributes[reflection.primary_key_column.name]
56
+ value = value.attributes[reflection.primary_key_column.name] unless value.nil?
57
57
  end
58
58
 
59
- column = klass.columns_hash[attribute.to_s]
59
+ attribute_name = attribute.to_s
60
+
61
+ # the attribute may be an aliased attribute
62
+ if klass.attribute_aliases[attribute_name]
63
+ attribute = klass.attribute_aliases[attribute_name]
64
+ attribute_name = attribute.to_s
65
+ end
66
+
67
+ column = klass.columns_hash[attribute_name]
60
68
  value = klass.connection.type_cast(value, column)
61
69
  value = value.to_s[0, column.limit] if value && column.limit && column.text?
62
70
 
@@ -171,11 +179,11 @@ module ActiveRecord
171
179
  # WHERE title = 'My Post' |
172
180
  # |
173
181
  # | # User 2 does the same thing and also
174
- # | # infers that his title is unique.
182
+ # | # infers that their title is unique.
175
183
  # | SELECT * FROM comments
176
184
  # | WHERE title = 'My Post'
177
185
  # |
178
- # # User 1 inserts his comment. |
186
+ # # User 1 inserts their comment. |
179
187
  # INSERT INTO comments |
180
188
  # (title, content) VALUES |
181
189
  # ('My Post', 'hi!') |
@@ -201,7 +209,7 @@ module ActiveRecord
201
209
  # exception. You can either choose to let this error propagate (which
202
210
  # will result in the default Rails exception page being shown), or you
203
211
  # can catch it and restart the transaction (e.g. by telling the user
204
- # that the title already exists, and asking him to re-enter the title).
212
+ # that the title already exists, and asking them to re-enter the title).
205
213
  # This technique is also known as
206
214
  # {optimistic concurrency control}[http://en.wikipedia.org/wiki/Optimistic_concurrency_control].
207
215
  #
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  # Returns the version of the currently loaded ActiveRecord as a Gem::Version
3
3
  def self.version
4
- Gem::Version.new "4.0.3"
4
+ Gem::Version.new "4.0.4.rc1"
5
5
  end
6
6
 
7
7
  module VERSION #:nodoc:
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.3
4
+ version: 4.0.4.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-18 00:00:00.000000000 Z
11
+ date: 2014-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 4.0.3
19
+ version: 4.0.4.rc1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 4.0.3
26
+ version: 4.0.4.rc1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 4.0.3
33
+ version: 4.0.4.rc1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 4.0.3
40
+ version: 4.0.4.rc1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: arel
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -255,12 +255,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
255
255
  version: 1.9.3
256
256
  required_rubygems_version: !ruby/object:Gem::Requirement
257
257
  requirements:
258
- - - ">="
258
+ - - ">"
259
259
  - !ruby/object:Gem::Version
260
- version: '0'
260
+ version: 1.3.1
261
261
  requirements: []
262
262
  rubyforge_project:
263
- rubygems_version: 2.2.0
263
+ rubygems_version: 2.2.2
264
264
  signing_key:
265
265
  specification_version: 4
266
266
  summary: Object-relational mapper framework (part of Rails).