activerecord 4.0.0.rc1 → 4.0.0.rc2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +65 -1
- data/lib/active_record/associations/association.rb +8 -4
- data/lib/active_record/associations/has_one_association.rb +2 -3
- data/lib/active_record/attribute_methods/serialization.rb +5 -5
- data/lib/active_record/autosave_association.rb +3 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +30 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/migration.rb +23 -2
- data/lib/active_record/nested_attributes.rb +32 -7
- data/lib/active_record/persistence.rb +46 -4
- data/lib/active_record/railtie.rb +1 -1
- data/lib/active_record/railties/databases.rake +8 -4
- data/lib/active_record/relation.rb +38 -14
- data/lib/active_record/relation/delegation.rb +3 -3
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/validations/associated.rb +3 -3
- data/lib/active_record/version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 357a6e6e214ac8716f87d6dc1b7444ec9fac7fb8
|
4
|
+
data.tar.gz: 2a02076afbcfa4267ee735a3ae8fe3a9ee9df311
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d7d4a1faf7d356f0b290884cb1120e18cca36a4cb92db695595a3b9d5e468348f9d4ec3d54810003cde8397c4772b88ec9fefa06e3dda29600d01169ebd76b3
|
7
|
+
data.tar.gz: 1e5d6ce02b031d8c61ad22d7da9482f96975a2662e61ec8e3fe1c4e09472f0822d60bee1e4ef721bab22c6ae07f21ffdc705d6665c0f4a7bee25443c44ace5a7
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,63 @@
|
|
1
|
-
## Rails 4.0.0 (
|
1
|
+
## Rails 4.0.0.rc2 (June 11, 2013) ##
|
2
|
+
|
3
|
+
* Fix `add_column` with `array` option when using PostgreSQL. Fixes #10432
|
4
|
+
|
5
|
+
* Do not overwrite manually built records during one-to-one nested attribute assignment
|
6
|
+
|
7
|
+
For one-to-one nested associations, if you build the new (in-memory)
|
8
|
+
child object yourself before assignment, then the NestedAttributes
|
9
|
+
module will not overwrite it, e.g.:
|
10
|
+
|
11
|
+
class Member < ActiveRecord::Base
|
12
|
+
has_one :avatar
|
13
|
+
accepts_nested_attributes_for :avatar
|
14
|
+
|
15
|
+
def avatar
|
16
|
+
super || build_avatar(width: 200)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
member = Member.new
|
21
|
+
member.avatar_attributes = {icon: 'sad'}
|
22
|
+
member.avatar.width # => 200
|
23
|
+
|
24
|
+
*Olek Janiszewski*
|
25
|
+
|
26
|
+
* fixes bug introduced by #3329. Now, when autosaving associations,
|
27
|
+
deletions happen before inserts and saves. This prevents a 'duplicate
|
28
|
+
unique value' database error that would occur if a record being created had
|
29
|
+
the same value on a unique indexed field as that of a record being destroyed.
|
30
|
+
|
31
|
+
*Adam Anderson*
|
32
|
+
|
33
|
+
|
34
|
+
* Fix pending migrations error when loading schema and `ActiveRecord::Base.table_name_prefix`
|
35
|
+
is not blank.
|
36
|
+
|
37
|
+
Call `assume_migrated_upto_version` on connection to prevent it from first
|
38
|
+
being picked up in `method_missing`.
|
39
|
+
|
40
|
+
In the base class, `Migration`, `method_missing` expects the argument to be a
|
41
|
+
table name, and calls `proper_table_name` on the arguments before sending to
|
42
|
+
`connection`. If `table_name_prefix` or `table_name_suffix` is used, the schema
|
43
|
+
version changes to `prefix_version_suffix`, breaking `rake test:prepare`.
|
44
|
+
|
45
|
+
Fixes #10411.
|
46
|
+
|
47
|
+
*Kyle Stevens*
|
48
|
+
|
49
|
+
* Mute `psql` output when running rake db:schema:load.
|
50
|
+
|
51
|
+
*Godfrey Chan*
|
52
|
+
|
53
|
+
|
54
|
+
## Rails 4.0.0.rc1 (April 29, 2013) ##
|
55
|
+
|
56
|
+
* Trigger a save on `has_one association=(associate)` when the associate contents have changed.
|
57
|
+
|
58
|
+
Fix #8856.
|
59
|
+
|
60
|
+
*Chris Thompson*
|
2
61
|
|
3
62
|
* Allow to use databases.rake tasks without having `Rails.application`.
|
4
63
|
|
@@ -928,6 +987,11 @@
|
|
928
987
|
|
929
988
|
*Aaron Stone*
|
930
989
|
|
990
|
+
* Allow setting of all libpq connection parameters through the PostgreSQL adapter. See also:
|
991
|
+
http://www.postgresql.org/docs/9.2/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS
|
992
|
+
|
993
|
+
*Lars Kanis*
|
994
|
+
|
931
995
|
* Allow `Relation#where` with no arguments to be chained with new `not` query method.
|
932
996
|
|
933
997
|
Example:
|
@@ -164,6 +164,13 @@ module ActiveRecord
|
|
164
164
|
@reflection = @owner.class.reflect_on_association(reflection_name)
|
165
165
|
end
|
166
166
|
|
167
|
+
def initialize_attributes(record) #:nodoc:
|
168
|
+
skip_assign = [reflection.foreign_key, reflection.type].compact
|
169
|
+
attributes = create_scope.except(*(record.changed - skip_assign))
|
170
|
+
record.assign_attributes(attributes)
|
171
|
+
set_inverse_instance(record)
|
172
|
+
end
|
173
|
+
|
167
174
|
private
|
168
175
|
|
169
176
|
def find_target?
|
@@ -233,10 +240,7 @@ module ActiveRecord
|
|
233
240
|
|
234
241
|
def build_record(attributes)
|
235
242
|
reflection.build_association(attributes) do |record|
|
236
|
-
|
237
|
-
attributes = create_scope.except(*(record.changed - skip_assign))
|
238
|
-
record.assign_attributes(attributes)
|
239
|
-
set_inverse_instance(record)
|
243
|
+
initialize_attributes(record)
|
240
244
|
end
|
241
245
|
end
|
242
246
|
end
|
@@ -25,9 +25,8 @@ module ActiveRecord
|
|
25
25
|
raise_on_type_mismatch!(record) if record
|
26
26
|
load_target
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
if (target || record) && target != record
|
28
|
+
return self.target if !(target || record)
|
29
|
+
if (target != record) || record.changed?
|
31
30
|
transaction_if(save) do
|
32
31
|
remove_target!(options[:dependent]) if target && !target.destroyed?
|
33
32
|
|
@@ -63,7 +63,7 @@ module ActiveRecord
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def type_cast(value)
|
66
|
-
value.unserialized_value
|
66
|
+
value.unserialized_value @column.type_cast value.value
|
67
67
|
end
|
68
68
|
|
69
69
|
def type
|
@@ -72,17 +72,17 @@ module ActiveRecord
|
|
72
72
|
end
|
73
73
|
|
74
74
|
class Attribute < Struct.new(:coder, :value, :state) # :nodoc:
|
75
|
-
def unserialized_value
|
76
|
-
state == :serialized ? unserialize : value
|
75
|
+
def unserialized_value(v = value)
|
76
|
+
state == :serialized ? unserialize(v) : value
|
77
77
|
end
|
78
78
|
|
79
79
|
def serialized_value
|
80
80
|
state == :unserialized ? serialize : value
|
81
81
|
end
|
82
82
|
|
83
|
-
def unserialize
|
83
|
+
def unserialize(v)
|
84
84
|
self.state = :unserialized
|
85
|
-
self.value = coder.load(
|
85
|
+
self.value = coder.load(v)
|
86
86
|
end
|
87
87
|
|
88
88
|
def serialize
|
@@ -17,7 +17,8 @@ module ActiveRecord
|
|
17
17
|
# be destroyed directly. They will however still be marked for destruction.
|
18
18
|
#
|
19
19
|
# Note that <tt>autosave: false</tt> is not same as not declaring <tt>:autosave</tt>.
|
20
|
-
# When the <tt>:autosave</tt> option is not present new
|
20
|
+
# When the <tt>:autosave</tt> option is not present then new association records are
|
21
|
+
# saved but the updated association records are not saved.
|
21
22
|
#
|
22
23
|
# == Validation
|
23
24
|
#
|
@@ -300,7 +301,7 @@ module ActiveRecord
|
|
300
301
|
def association_valid?(reflection, record)
|
301
302
|
return true if record.destroyed? || record.marked_for_destruction?
|
302
303
|
|
303
|
-
unless valid = record.valid?
|
304
|
+
unless valid = record.valid?
|
304
305
|
if reflection.options[:autosave]
|
305
306
|
record.errors.each do |attribute, message|
|
306
307
|
attribute = "#{reflection.name}.#{attribute}"
|
@@ -364,7 +364,9 @@ module ActiveRecord
|
|
364
364
|
# and creates it again using the provided +options+.
|
365
365
|
def recreate_database(name, options = {})
|
366
366
|
drop_database(name)
|
367
|
-
create_database(name, options)
|
367
|
+
sql = create_database(name, options)
|
368
|
+
reconnect!
|
369
|
+
sql
|
368
370
|
end
|
369
371
|
|
370
372
|
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
|
@@ -330,9 +330,38 @@ module ActiveRecord
|
|
330
330
|
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
331
331
|
include ColumnMethods
|
332
332
|
|
333
|
+
# Defines the primary key field.
|
334
|
+
# Use of the native PostgreSQL UUID type is supported, and can be used
|
335
|
+
# by defining your tables as such:
|
336
|
+
#
|
337
|
+
# create_table :stuffs, id: :uuid do |t|
|
338
|
+
# t.string :content
|
339
|
+
# t.timestamps
|
340
|
+
# end
|
341
|
+
#
|
342
|
+
# By default, this will use the +uuid_generate_v4()+ function from the
|
343
|
+
# +uuid-ossp+ extension, which MUST be enabled on your databse. To enable
|
344
|
+
# the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
|
345
|
+
# migrations To use a UUID primary key without +uuid-ossp+ enabled, you can
|
346
|
+
# set the +:default+ option to nil:
|
347
|
+
#
|
348
|
+
# create_table :stuffs, id: false do |t|
|
349
|
+
# t.primary_key :id, :uuid, default: nil
|
350
|
+
# t.uuid :foo_id
|
351
|
+
# t.timestamps
|
352
|
+
# end
|
353
|
+
#
|
354
|
+
# You may also pass a different UUID generation function from +uuid-ossp+
|
355
|
+
# or another library.
|
356
|
+
#
|
357
|
+
# Note that setting the UUID primary key default value to +nil+
|
358
|
+
# will require you to assure that you always provide a UUID value
|
359
|
+
# before saving a record (as primary keys cannot be nil). This might be
|
360
|
+
# done via the SecureRandom.uuid method and a +before_save+ callback,
|
361
|
+
# for instance.
|
333
362
|
def primary_key(name, type = :primary_key, options = {})
|
334
363
|
return super unless type == :uuid
|
335
|
-
options[:default]
|
364
|
+
options[:default] = options.fetch(:default, 'uuid_generate_v4()')
|
336
365
|
options[:primary_key] = true
|
337
366
|
column name, type, options
|
338
367
|
end
|
@@ -19,7 +19,7 @@ module ActiveRecord
|
|
19
19
|
# On the other hand, we want to monitor the performance of our real database
|
20
20
|
# queries, not the performance of the access to the query cache.
|
21
21
|
IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
|
22
|
-
EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)/i
|
22
|
+
EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)\b/i
|
23
23
|
def ignore_payload?(payload)
|
24
24
|
payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
|
25
25
|
end
|
@@ -357,11 +357,14 @@ module ActiveRecord
|
|
357
357
|
class CheckPending
|
358
358
|
def initialize(app)
|
359
359
|
@app = app
|
360
|
+
@last_check = 0
|
360
361
|
end
|
361
362
|
|
362
363
|
def call(env)
|
363
|
-
ActiveRecord::
|
364
|
+
mtime = ActiveRecord::Migrator.last_migration.mtime.to_i
|
365
|
+
if @last_check < mtime
|
364
366
|
ActiveRecord::Migration.check_pending!
|
367
|
+
@last_check = mtime
|
365
368
|
end
|
366
369
|
@app.call(env)
|
367
370
|
end
|
@@ -699,6 +702,10 @@ module ActiveRecord
|
|
699
702
|
File.basename(filename)
|
700
703
|
end
|
701
704
|
|
705
|
+
def mtime
|
706
|
+
File.mtime filename
|
707
|
+
end
|
708
|
+
|
702
709
|
delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
|
703
710
|
|
704
711
|
private
|
@@ -714,6 +721,16 @@ module ActiveRecord
|
|
714
721
|
|
715
722
|
end
|
716
723
|
|
724
|
+
class NullMigration < MigrationProxy #:nodoc:
|
725
|
+
def initialize
|
726
|
+
super(nil, 0, nil, nil)
|
727
|
+
end
|
728
|
+
|
729
|
+
def mtime
|
730
|
+
0
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
717
734
|
class Migrator#:nodoc:
|
718
735
|
class << self
|
719
736
|
attr_writer :migrations_paths
|
@@ -784,7 +801,11 @@ module ActiveRecord
|
|
784
801
|
end
|
785
802
|
|
786
803
|
def last_version
|
787
|
-
|
804
|
+
last_migration.version
|
805
|
+
end
|
806
|
+
|
807
|
+
def last_migration #:nodoc:
|
808
|
+
migrations(migrations_paths).last || NullMigration.new
|
788
809
|
end
|
789
810
|
|
790
811
|
def proper_table_name(name)
|
@@ -229,6 +229,23 @@ module ActiveRecord
|
|
229
229
|
# belongs_to :member, inverse_of: :posts
|
230
230
|
# validates_presence_of :member
|
231
231
|
# end
|
232
|
+
#
|
233
|
+
# For one-to-one nested associations, if you build the new (in-memory)
|
234
|
+
# child object yourself before assignment, then this module will not
|
235
|
+
# overwrite it, e.g.:
|
236
|
+
#
|
237
|
+
# class Member < ActiveRecord::Base
|
238
|
+
# has_one :avatar
|
239
|
+
# accepts_nested_attributes_for :avatar
|
240
|
+
#
|
241
|
+
# def avatar
|
242
|
+
# super || build_avatar(width: 200)
|
243
|
+
# end
|
244
|
+
# end
|
245
|
+
#
|
246
|
+
# member = Member.new
|
247
|
+
# member.avatar_attributes = {icon: 'sad'}
|
248
|
+
# member.avatar.width # => 200
|
232
249
|
module ClassMethods
|
233
250
|
REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.blank? } }
|
234
251
|
|
@@ -356,20 +373,28 @@ module ActiveRecord
|
|
356
373
|
def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
|
357
374
|
options = self.nested_attributes_options[association_name]
|
358
375
|
attributes = attributes.with_indifferent_access
|
376
|
+
existing_record = send(association_name)
|
359
377
|
|
360
|
-
if (options[:update_only] || !attributes['id'].blank?) &&
|
361
|
-
(options[:update_only] ||
|
362
|
-
assign_to_or_mark_for_destruction(
|
378
|
+
if (options[:update_only] || !attributes['id'].blank?) && existing_record &&
|
379
|
+
(options[:update_only] || existing_record.id.to_s == attributes['id'].to_s)
|
380
|
+
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes)
|
363
381
|
|
364
382
|
elsif attributes['id'].present?
|
365
383
|
raise_nested_attributes_record_not_found!(association_name, attributes['id'])
|
366
384
|
|
367
385
|
elsif !reject_new_record?(association_name, attributes)
|
368
|
-
|
369
|
-
|
370
|
-
|
386
|
+
assignable_attributes = attributes.except(*UNASSIGNABLE_KEYS)
|
387
|
+
|
388
|
+
if existing_record && existing_record.new_record?
|
389
|
+
existing_record.assign_attributes(assignable_attributes)
|
390
|
+
association(association_name).initialize_attributes(existing_record)
|
371
391
|
else
|
372
|
-
|
392
|
+
method = "build_#{association_name}"
|
393
|
+
if respond_to?(method)
|
394
|
+
send(method, assignable_attributes)
|
395
|
+
else
|
396
|
+
raise ArgumentError, "Cannot build association `#{association_name}'. Are you trying to build a polymorphic one-to-one association?"
|
397
|
+
end
|
373
398
|
end
|
374
399
|
end
|
375
400
|
end
|
@@ -99,6 +99,9 @@ module ActiveRecord
|
|
99
99
|
# <tt>before_*</tt> callbacks return +false+ the action is cancelled and
|
100
100
|
# +save+ returns +false+. See ActiveRecord::Callbacks for further
|
101
101
|
# details.
|
102
|
+
#
|
103
|
+
# Attributes marked as readonly are silently ignored if the record is
|
104
|
+
# being updated.
|
102
105
|
def save(*)
|
103
106
|
create_or_update
|
104
107
|
rescue ActiveRecord::RecordInvalid
|
@@ -118,6 +121,9 @@ module ActiveRecord
|
|
118
121
|
# the <tt>before_*</tt> callbacks return +false+ the action is cancelled
|
119
122
|
# and <tt>save!</tt> raises ActiveRecord::RecordNotSaved. See
|
120
123
|
# ActiveRecord::Callbacks for further details.
|
124
|
+
#
|
125
|
+
# Attributes marked as readonly are silently ignored if the record is
|
126
|
+
# being updated.
|
121
127
|
def save!(*)
|
122
128
|
create_or_update || raise(RecordNotSaved)
|
123
129
|
end
|
@@ -204,6 +210,8 @@ module ActiveRecord
|
|
204
210
|
# * updated_at/updated_on column is updated if that column is available.
|
205
211
|
# * Updates all the attributes that are dirty in this object.
|
206
212
|
#
|
213
|
+
# This method raises an +ActiveRecord::ActiveRecordError+ if the
|
214
|
+
# attribute is marked as readonly.
|
207
215
|
def update_attribute(name, value)
|
208
216
|
name = name.to_s
|
209
217
|
verify_readonly_attribute(name)
|
@@ -325,10 +333,44 @@ module ActiveRecord
|
|
325
333
|
toggle(attribute).update_attribute(attribute, self[attribute])
|
326
334
|
end
|
327
335
|
|
328
|
-
# Reloads the
|
329
|
-
#
|
330
|
-
#
|
331
|
-
#
|
336
|
+
# Reloads the record from the database.
|
337
|
+
#
|
338
|
+
# This method modifies the receiver in-place. Attributes are updated, and
|
339
|
+
# caches busted, in particular the associations cache.
|
340
|
+
#
|
341
|
+
# If the record no longer exists in the database <tt>ActiveRecord::RecordNotFound</tt>
|
342
|
+
# is raised. Otherwise, in addition to the in-place modification the method
|
343
|
+
# returns +self+ for convenience.
|
344
|
+
#
|
345
|
+
# The optional <tt>:lock</tt> flag option allows you to lock the reloaded record:
|
346
|
+
#
|
347
|
+
# reload(lock: true) # reload with pessimistic locking
|
348
|
+
#
|
349
|
+
# Reloading is commonly used in test suites to test something is actually
|
350
|
+
# written to the database, or when some action modifies the corresponding
|
351
|
+
# row in the database but not the object in memory:
|
352
|
+
#
|
353
|
+
# assert account.deposit!(25)
|
354
|
+
# assert_equal 25, account.credit # check it is updated in memory
|
355
|
+
# assert_equal 25, account.reload.credit # check it is also persisted
|
356
|
+
#
|
357
|
+
# Another commom use case is optimistic locking handling:
|
358
|
+
#
|
359
|
+
# def with_optimistic_retry
|
360
|
+
# begin
|
361
|
+
# yield
|
362
|
+
# rescue ActiveRecord::StaleObjectError
|
363
|
+
# begin
|
364
|
+
# # Reload lock_version in particular.
|
365
|
+
# reload
|
366
|
+
# rescue ActiveRecord::RecordNotFound
|
367
|
+
# # If the record is gone there is nothing to do.
|
368
|
+
# else
|
369
|
+
# retry
|
370
|
+
# end
|
371
|
+
# end
|
372
|
+
# end
|
373
|
+
#
|
332
374
|
def reload(options = nil)
|
333
375
|
clear_aggregation_cache
|
334
376
|
clear_association_cache
|
@@ -44,7 +44,7 @@ module ActiveRecord
|
|
44
44
|
ActiveRecord::Tasks::DatabaseTasks.migrations_paths = Rails.application.paths['db/migrate'].to_a
|
45
45
|
ActiveRecord::Tasks::DatabaseTasks.fixtures_path = File.join Rails.root, 'test', 'fixtures'
|
46
46
|
|
47
|
-
if defined?(
|
47
|
+
if defined?(APP_RAKEFILE) && engine = Rails::Engine.find(find_engine_path(APP_RAKEFILE))
|
48
48
|
if engine.paths['db/migrate'].existent
|
49
49
|
ActiveRecord::Tasks::DatabaseTasks.migrations_paths += engine.paths['db/migrate'].to_a
|
50
50
|
end
|
@@ -321,9 +321,13 @@ db_namespace = namespace :db do
|
|
321
321
|
|
322
322
|
# desc "Recreate the test database from an existent schema.rb file"
|
323
323
|
task :load_schema => 'db:test:purge' do
|
324
|
-
|
325
|
-
|
326
|
-
|
324
|
+
begin
|
325
|
+
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
|
326
|
+
ActiveRecord::Schema.verbose = false
|
327
|
+
db_namespace["schema:load"].invoke
|
328
|
+
ensure
|
329
|
+
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[Rails.env])
|
330
|
+
end
|
327
331
|
end
|
328
332
|
|
329
333
|
# desc "Recreate the test database from an existent structure.sql file"
|
@@ -358,7 +362,7 @@ db_namespace = namespace :db do
|
|
358
362
|
end
|
359
363
|
|
360
364
|
# desc 'Check for pending migrations and load the test schema'
|
361
|
-
task :prepare do
|
365
|
+
task :prepare => :load_config do
|
362
366
|
unless ActiveRecord::Base.configurations.blank?
|
363
367
|
db_namespace['test:load'].invoke
|
364
368
|
end
|
@@ -141,34 +141,58 @@ module ActiveRecord
|
|
141
141
|
first || new(attributes, &block)
|
142
142
|
end
|
143
143
|
|
144
|
-
# Finds the first record with the given attributes, or creates a record
|
145
|
-
# if one is not found
|
144
|
+
# Finds the first record with the given attributes, or creates a record
|
145
|
+
# with the attributes if one is not found:
|
146
146
|
#
|
147
|
-
#
|
148
|
-
# # Find the first user named Penélope or create a new one.
|
147
|
+
# # Find the first user named "Penélope" or create a new one.
|
149
148
|
# User.find_or_create_by(first_name: 'Penélope')
|
150
|
-
# # =>
|
149
|
+
# # => #<User id: 1, first_name: "Penélope", last_name: nil>
|
151
150
|
#
|
152
|
-
# # Find the first user named Penélope or create a new one.
|
151
|
+
# # Find the first user named "Penélope" or create a new one.
|
153
152
|
# # We already have one so the existing record will be returned.
|
154
153
|
# User.find_or_create_by(first_name: 'Penélope')
|
155
|
-
# # =>
|
154
|
+
# # => #<User id: 1, first_name: "Penélope", last_name: nil>
|
156
155
|
#
|
157
|
-
# # Find the first user named Scarlett or create a new one with
|
156
|
+
# # Find the first user named "Scarlett" or create a new one with
|
157
|
+
# # a particular last name.
|
158
158
|
# User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
|
159
|
-
# # =>
|
159
|
+
# # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
|
160
160
|
#
|
161
|
-
#
|
162
|
-
#
|
161
|
+
# This method accepts a block, which is passed down to +create+. The last example
|
162
|
+
# above can be alternatively written this way:
|
163
|
+
#
|
164
|
+
# # Find the first user named "Scarlett" or create a new one with a
|
165
|
+
# # different last name.
|
163
166
|
# User.find_or_create_by(first_name: 'Scarlett') do |user|
|
164
|
-
# user.last_name =
|
167
|
+
# user.last_name = 'Johansson'
|
165
168
|
# end
|
166
|
-
# # =>
|
169
|
+
# # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
|
170
|
+
#
|
171
|
+
# This method always returns a record, but if creation was attempted and
|
172
|
+
# failed due to validation errors it won't be persisted, you get what
|
173
|
+
# +create+ returns in such situation.
|
174
|
+
#
|
175
|
+
# Please note *this method is not atomic*, it runs first a SELECT, and if
|
176
|
+
# there are no results an INSERT is attempted. If there are other threads
|
177
|
+
# or processes there is a race condition between both calls and it could
|
178
|
+
# be the case that you end up with two similar records.
|
179
|
+
#
|
180
|
+
# Whether that is a problem or not depends on the logic of the
|
181
|
+
# application, but in the particular case in which rows have a UNIQUE
|
182
|
+
# constraint an exception may be raised, just retry:
|
183
|
+
#
|
184
|
+
# begin
|
185
|
+
# CreditAccount.find_or_create_by(user_id: user.id)
|
186
|
+
# rescue ActiveRecord::RecordNotUnique
|
187
|
+
# retry
|
188
|
+
# end
|
189
|
+
#
|
167
190
|
def find_or_create_by(attributes, &block)
|
168
191
|
find_by(attributes) || create(attributes, &block)
|
169
192
|
end
|
170
193
|
|
171
|
-
# Like <tt>find_or_create_by</tt>, but calls <tt>create!</tt> so an exception
|
194
|
+
# Like <tt>find_or_create_by</tt>, but calls <tt>create!</tt> so an exception
|
195
|
+
# is raised if the created record is invalid.
|
172
196
|
def find_or_create_by!(attributes, &block)
|
173
197
|
find_by(attributes) || create!(attributes, &block)
|
174
198
|
end
|
@@ -14,14 +14,14 @@ module ActiveRecord
|
|
14
14
|
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
|
15
15
|
:connection, :columns_hash, :to => :klass
|
16
16
|
|
17
|
-
module ClassSpecificRelation
|
17
|
+
module ClassSpecificRelation # :nodoc:
|
18
18
|
extend ActiveSupport::Concern
|
19
19
|
|
20
20
|
included do
|
21
21
|
@delegation_mutex = Mutex.new
|
22
22
|
end
|
23
23
|
|
24
|
-
module ClassMethods
|
24
|
+
module ClassMethods # :nodoc:
|
25
25
|
def name
|
26
26
|
superclass.name
|
27
27
|
end
|
@@ -70,7 +70,7 @@ module ActiveRecord
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
module ClassMethods
|
73
|
+
module ClassMethods # :nodoc:
|
74
74
|
@@subclasses = ThreadSafe::Cache.new(:initial_capacity => 2)
|
75
75
|
|
76
76
|
def new(klass, *args)
|
data/lib/active_record/schema.rb
CHANGED
@@ -2,15 +2,15 @@ module ActiveRecord
|
|
2
2
|
module Validations
|
3
3
|
class AssociatedValidator < ActiveModel::EachValidator #:nodoc:
|
4
4
|
def validate_each(record, attribute, value)
|
5
|
-
if Array.wrap(value).reject {|r| r.marked_for_destruction? || r.valid?
|
5
|
+
if Array.wrap(value).reject {|r| r.marked_for_destruction? || r.valid?}.any?
|
6
6
|
record.errors.add(attribute, :invalid, options.merge(:value => value))
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
module ClassMethods
|
12
|
-
# Validates whether the associated object or objects are all valid
|
13
|
-
#
|
12
|
+
# Validates whether the associated object or objects are all valid.
|
13
|
+
# Works with any kind of association.
|
14
14
|
#
|
15
15
|
# class Book < ActiveRecord::Base
|
16
16
|
# has_many :pages
|
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.0.
|
4
|
+
version: 4.0.0.rc2
|
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: 2013-
|
11
|
+
date: 2013-06-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.0.
|
19
|
+
version: 4.0.0.rc2
|
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.0.
|
26
|
+
version: 4.0.0.rc2
|
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.0.
|
33
|
+
version: 4.0.0.rc2
|
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.0.
|
40
|
+
version: 4.0.0.rc2
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: arel
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -259,7 +259,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
259
259
|
version: 1.3.1
|
260
260
|
requirements: []
|
261
261
|
rubyforge_project:
|
262
|
-
rubygems_version: 2.0.
|
262
|
+
rubygems_version: 2.0.2
|
263
263
|
signing_key:
|
264
264
|
specification_version: 4
|
265
265
|
summary: Object-relational mapper framework (part of Rails).
|