activerecord 3.1.11 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +6294 -97
- data/README.rdoc +2 -2
- data/examples/performance.rb +55 -31
- data/lib/active_record/aggregations.rb +2 -2
- data/lib/active_record/associations/association.rb +2 -42
- data/lib/active_record/associations/association_scope.rb +3 -30
- data/lib/active_record/associations/builder/association.rb +6 -4
- data/lib/active_record/associations/builder/belongs_to.rb +3 -3
- data/lib/active_record/associations/builder/collection_association.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +5 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -16
- data/lib/active_record/associations/collection_association.rb +55 -28
- data/lib/active_record/associations/collection_proxy.rb +1 -35
- data/lib/active_record/associations/has_many_association.rb +5 -1
- data/lib/active_record/associations/has_many_through_association.rb +11 -8
- data/lib/active_record/associations/join_dependency.rb +1 -1
- data/lib/active_record/associations/preloader/association.rb +3 -1
- data/lib/active_record/associations.rb +82 -69
- data/lib/active_record/attribute_assignment.rb +221 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
- data/lib/active_record/attribute_methods/dirty.rb +3 -3
- data/lib/active_record/attribute_methods/primary_key.rb +62 -25
- data/lib/active_record/attribute_methods/read.rb +72 -83
- data/lib/active_record/attribute_methods/serialization.rb +93 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -14
- data/lib/active_record/attribute_methods/write.rb +27 -5
- data/lib/active_record/attribute_methods.rb +209 -30
- data/lib/active_record/autosave_association.rb +23 -8
- data/lib/active_record/base.rb +217 -1709
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +98 -132
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +82 -29
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +13 -42
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +9 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +36 -25
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +78 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
- data/lib/active_record/connection_adapters/column.rb +2 -2
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +138 -578
- data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -658
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +144 -94
- data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +43 -22
- data/lib/active_record/counter_cache.rb +4 -3
- data/lib/active_record/dynamic_matchers.rb +79 -0
- data/lib/active_record/errors.rb +11 -1
- data/lib/active_record/explain.rb +83 -0
- data/lib/active_record/explain_subscriber.rb +21 -0
- data/lib/active_record/fixtures/file.rb +65 -0
- data/lib/active_record/fixtures.rb +31 -76
- data/lib/active_record/identity_map.rb +4 -11
- data/lib/active_record/inheritance.rb +167 -0
- data/lib/active_record/integration.rb +49 -0
- data/lib/active_record/locking/optimistic.rb +30 -25
- data/lib/active_record/locking/pessimistic.rb +23 -1
- data/lib/active_record/log_subscriber.rb +3 -3
- data/lib/active_record/migration/command_recorder.rb +8 -8
- data/lib/active_record/migration.rb +47 -30
- data/lib/active_record/model_schema.rb +366 -0
- data/lib/active_record/nested_attributes.rb +3 -2
- data/lib/active_record/persistence.rb +51 -9
- data/lib/active_record/querying.rb +58 -0
- data/lib/active_record/railtie.rb +24 -28
- data/lib/active_record/railties/controller_runtime.rb +3 -1
- data/lib/active_record/railties/databases.rake +134 -77
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +26 -0
- data/lib/active_record/reflection.rb +7 -15
- data/lib/active_record/relation/batches.rb +5 -2
- data/lib/active_record/relation/calculations.rb +27 -6
- data/lib/active_record/relation/delegation.rb +49 -0
- data/lib/active_record/relation/finder_methods.rb +6 -5
- data/lib/active_record/relation/predicate_builder.rb +12 -19
- data/lib/active_record/relation/query_methods.rb +76 -10
- data/lib/active_record/relation/spawn_methods.rb +11 -2
- data/lib/active_record/relation.rb +77 -34
- data/lib/active_record/result.rb +1 -1
- data/lib/active_record/sanitization.rb +194 -0
- data/lib/active_record/schema_dumper.rb +5 -2
- data/lib/active_record/scoping/default.rb +142 -0
- data/lib/active_record/scoping/named.rb +202 -0
- data/lib/active_record/scoping.rb +152 -0
- data/lib/active_record/serialization.rb +1 -43
- data/lib/active_record/serializers/xml_serializer.rb +2 -44
- data/lib/active_record/session_store.rb +15 -15
- data/lib/active_record/store.rb +50 -0
- data/lib/active_record/test_case.rb +11 -7
- data/lib/active_record/timestamp.rb +16 -3
- data/lib/active_record/transactions.rb +5 -5
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations/associated.rb +5 -4
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/version.rb +2 -2
- data/lib/active_record.rb +28 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +9 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +5 -1
- data/lib/rails/generators/active_record/model/templates/migration.rb +3 -5
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
- metadata +50 -40
- checksums.yaml +0 -7
- data/lib/active_record/named_scope.rb +0 -200
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module ActiveRecord
|
|
2
|
+
module Integration
|
|
3
|
+
# Returns a String, which Action Pack uses for constructing an URL to this
|
|
4
|
+
# object. The default implementation returns this record's id as a String,
|
|
5
|
+
# or nil if this record's unsaved.
|
|
6
|
+
#
|
|
7
|
+
# For example, suppose that you have a User model, and that you have a
|
|
8
|
+
# <tt>resources :users</tt> route. Normally, +user_path+ will
|
|
9
|
+
# construct a path with the user object's 'id' in it:
|
|
10
|
+
#
|
|
11
|
+
# user = User.find_by_name('Phusion')
|
|
12
|
+
# user_path(user) # => "/users/1"
|
|
13
|
+
#
|
|
14
|
+
# You can override +to_param+ in your model to make +user_path+ construct
|
|
15
|
+
# a path using the user's name instead of the user's id:
|
|
16
|
+
#
|
|
17
|
+
# class User < ActiveRecord::Base
|
|
18
|
+
# def to_param # overridden
|
|
19
|
+
# name
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# user = User.find_by_name('Phusion')
|
|
24
|
+
# user_path(user) # => "/users/Phusion"
|
|
25
|
+
def to_param
|
|
26
|
+
# We can't use alias_method here, because method 'id' optimizes itself on the fly.
|
|
27
|
+
id && id.to_s # Be sure to stringify the id for routes
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns a cache key that can be used to identify this record.
|
|
31
|
+
#
|
|
32
|
+
# ==== Examples
|
|
33
|
+
#
|
|
34
|
+
# Product.new.cache_key # => "products/new"
|
|
35
|
+
# Product.find(5).cache_key # => "products/5" (updated_at not available)
|
|
36
|
+
# Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
|
|
37
|
+
def cache_key
|
|
38
|
+
case
|
|
39
|
+
when new_record?
|
|
40
|
+
"#{self.class.model_name.cache_key}/new"
|
|
41
|
+
when timestamp = self[:updated_at]
|
|
42
|
+
timestamp = timestamp.utc.to_s(:number)
|
|
43
|
+
"#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
|
|
44
|
+
else
|
|
45
|
+
"#{self.class.model_name.cache_key}/#{id}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -37,6 +37,9 @@ module ActiveRecord
|
|
|
37
37
|
# You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
|
|
38
38
|
# or otherwise apply the business logic needed to resolve the conflict.
|
|
39
39
|
#
|
|
40
|
+
# This locking mechanism will function inside a single Ruby process. To make it work across all
|
|
41
|
+
# web requests, the recommended approach is to add +lock_version+ as a hidden field to your form.
|
|
42
|
+
#
|
|
40
43
|
# You must ensure that your database schema defaults the +lock_version+ column to 0.
|
|
41
44
|
#
|
|
42
45
|
# This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
|
|
@@ -48,10 +51,6 @@ module ActiveRecord
|
|
|
48
51
|
included do
|
|
49
52
|
cattr_accessor :lock_optimistically, :instance_writer => false
|
|
50
53
|
self.lock_optimistically = true
|
|
51
|
-
|
|
52
|
-
class << self
|
|
53
|
-
alias_method :locking_column=, :set_locking_column
|
|
54
|
-
end
|
|
55
54
|
end
|
|
56
55
|
|
|
57
56
|
def locking_enabled? #:nodoc:
|
|
@@ -65,21 +64,6 @@ module ActiveRecord
|
|
|
65
64
|
send(lock_col + '=', previous_lock_value + 1)
|
|
66
65
|
end
|
|
67
66
|
|
|
68
|
-
def attributes_from_column_definition
|
|
69
|
-
result = super
|
|
70
|
-
|
|
71
|
-
# If the locking column has no default value set,
|
|
72
|
-
# start the lock version at zero. Note we can't use
|
|
73
|
-
# <tt>locking_enabled?</tt> at this point as
|
|
74
|
-
# <tt>@attributes</tt> may not have been initialized yet.
|
|
75
|
-
|
|
76
|
-
if result.key?(self.class.locking_column) && lock_optimistically
|
|
77
|
-
result[self.class.locking_column] ||= 0
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
result
|
|
81
|
-
end
|
|
82
|
-
|
|
83
67
|
def update(attribute_names = @attributes.keys) #:nodoc:
|
|
84
68
|
return super unless locking_enabled?
|
|
85
69
|
return 0 if attribute_names.empty?
|
|
@@ -103,7 +87,7 @@ module ActiveRecord
|
|
|
103
87
|
affected_rows = connection.update stmt
|
|
104
88
|
|
|
105
89
|
unless affected_rows == 1
|
|
106
|
-
raise ActiveRecord::StaleObjectError, "
|
|
90
|
+
raise ActiveRecord::StaleObjectError.new(self, "update")
|
|
107
91
|
end
|
|
108
92
|
|
|
109
93
|
affected_rows
|
|
@@ -127,7 +111,7 @@ module ActiveRecord
|
|
|
127
111
|
affected_rows = self.class.unscoped.where(predicate).delete_all
|
|
128
112
|
|
|
129
113
|
unless affected_rows == 1
|
|
130
|
-
raise ActiveRecord::StaleObjectError, "
|
|
114
|
+
raise ActiveRecord::StaleObjectError.new(self, "destroy")
|
|
131
115
|
end
|
|
132
116
|
end
|
|
133
117
|
|
|
@@ -145,15 +129,24 @@ module ActiveRecord
|
|
|
145
129
|
lock_optimistically && columns_hash[locking_column]
|
|
146
130
|
end
|
|
147
131
|
|
|
132
|
+
def locking_column=(value)
|
|
133
|
+
@original_locking_column = @locking_column if defined?(@locking_column)
|
|
134
|
+
@locking_column = value.to_s
|
|
135
|
+
end
|
|
136
|
+
|
|
148
137
|
# Set the column to use for optimistic locking. Defaults to +lock_version+.
|
|
149
138
|
def set_locking_column(value = nil, &block)
|
|
150
|
-
|
|
151
|
-
value
|
|
139
|
+
deprecated_property_setter :locking_column, value, block
|
|
152
140
|
end
|
|
153
141
|
|
|
154
142
|
# The version column used for optimistic locking. Defaults to +lock_version+.
|
|
155
143
|
def locking_column
|
|
156
|
-
reset_locking_column
|
|
144
|
+
reset_locking_column unless defined?(@locking_column)
|
|
145
|
+
@locking_column
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def original_locking_column #:nodoc:
|
|
149
|
+
deprecated_original_property_getter :locking_column
|
|
157
150
|
end
|
|
158
151
|
|
|
159
152
|
# Quote the column name used for optimistic locking.
|
|
@@ -163,7 +156,7 @@ module ActiveRecord
|
|
|
163
156
|
|
|
164
157
|
# Reset the column used for optimistic locking back to the +lock_version+ default.
|
|
165
158
|
def reset_locking_column
|
|
166
|
-
|
|
159
|
+
self.locking_column = DEFAULT_LOCKING_COLUMN
|
|
167
160
|
end
|
|
168
161
|
|
|
169
162
|
# Make sure the lock version column gets updated when counters are
|
|
@@ -172,6 +165,18 @@ module ActiveRecord
|
|
|
172
165
|
counters = counters.merge(locking_column => 1) if locking_enabled?
|
|
173
166
|
super
|
|
174
167
|
end
|
|
168
|
+
|
|
169
|
+
# If the locking column has no default value set,
|
|
170
|
+
# start the lock version at zero. Note we can't use
|
|
171
|
+
# <tt>locking_enabled?</tt> at this point as
|
|
172
|
+
# <tt>@attributes</tt> may not have been initialized yet.
|
|
173
|
+
def initialize_attributes(attributes) #:nodoc:
|
|
174
|
+
if attributes.key?(locking_column) && lock_optimistically
|
|
175
|
+
attributes[locking_column] ||= 0
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
attributes
|
|
179
|
+
end
|
|
175
180
|
end
|
|
176
181
|
end
|
|
177
182
|
end
|
|
@@ -14,7 +14,7 @@ module ActiveRecord
|
|
|
14
14
|
# Account.transaction do
|
|
15
15
|
# # select * from accounts where name = 'shugo' limit 1 for update
|
|
16
16
|
# shugo = Account.where("name = 'shugo'").lock(true).first
|
|
17
|
-
# yuko = Account.where("name = '
|
|
17
|
+
# yuko = Account.where("name = 'yuko'").lock(true).first
|
|
18
18
|
# shugo.balance -= 100
|
|
19
19
|
# shugo.save!
|
|
20
20
|
# yuko.balance += 100
|
|
@@ -38,6 +38,18 @@ module ActiveRecord
|
|
|
38
38
|
# account2.save!
|
|
39
39
|
# end
|
|
40
40
|
#
|
|
41
|
+
# You can start a transaction and acquire the lock in one go by calling
|
|
42
|
+
# <tt>with_lock</tt> with a block. The block is called from within
|
|
43
|
+
# a transaction, the object is already locked. Example:
|
|
44
|
+
#
|
|
45
|
+
# account = Account.first
|
|
46
|
+
# account.with_lock do
|
|
47
|
+
# # This block is called within a transaction,
|
|
48
|
+
# # account is already locked.
|
|
49
|
+
# account.balance -= 100
|
|
50
|
+
# account.save!
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
41
53
|
# Database-specific information on row locking:
|
|
42
54
|
# MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
|
|
43
55
|
# PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
|
|
@@ -50,6 +62,16 @@ module ActiveRecord
|
|
|
50
62
|
reload(:lock => lock) if persisted?
|
|
51
63
|
self
|
|
52
64
|
end
|
|
65
|
+
|
|
66
|
+
# Wraps the passed block in a transaction, locking the object
|
|
67
|
+
# before yielding. You pass can the SQL locking clause
|
|
68
|
+
# as argument (see <tt>lock!</tt>).
|
|
69
|
+
def with_lock(lock = true)
|
|
70
|
+
transaction do
|
|
71
|
+
lock!(lock)
|
|
72
|
+
yield
|
|
73
|
+
end
|
|
74
|
+
end
|
|
53
75
|
end
|
|
54
76
|
end
|
|
55
77
|
end
|
|
@@ -26,9 +26,9 @@ module ActiveRecord
|
|
|
26
26
|
|
|
27
27
|
return if 'SCHEMA' == payload[:name]
|
|
28
28
|
|
|
29
|
-
name
|
|
30
|
-
sql
|
|
31
|
-
binds
|
|
29
|
+
name = '%s (%.1fms)' % [payload[:name], event.duration]
|
|
30
|
+
sql = payload[:sql].squeeze(' ')
|
|
31
|
+
binds = nil
|
|
32
32
|
|
|
33
33
|
unless (payload[:binds] || []).empty?
|
|
34
34
|
binds = " " + payload[:binds].map { |col,v|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module ActiveRecord
|
|
2
2
|
class Migration
|
|
3
|
-
# ActiveRecord::Migration::CommandRecorder records commands done during
|
|
4
|
-
# a migration and knows how to reverse those commands.
|
|
3
|
+
# <tt>ActiveRecord::Migration::CommandRecorder</tt> records commands done during
|
|
4
|
+
# a migration and knows how to reverse those commands. The CommandRecorder
|
|
5
5
|
# knows how to invert the following commands:
|
|
6
6
|
#
|
|
7
7
|
# * add_column
|
|
8
8
|
# * add_index
|
|
9
|
-
# *
|
|
9
|
+
# * add_timestamps
|
|
10
10
|
# * create_table
|
|
11
11
|
# * remove_timestamps
|
|
12
12
|
# * rename_column
|
|
@@ -20,21 +20,21 @@ module ActiveRecord
|
|
|
20
20
|
@delegate = delegate
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
# record +command+.
|
|
23
|
+
# record +command+. +command+ should be a method name and arguments.
|
|
24
24
|
# For example:
|
|
25
25
|
#
|
|
26
|
-
# recorder.record(:method_name, [:arg1, arg2])
|
|
26
|
+
# recorder.record(:method_name, [:arg1, :arg2])
|
|
27
27
|
def record(*command)
|
|
28
28
|
@commands << command
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
# Returns a list that represents commands that are the inverse of the
|
|
32
|
-
# commands stored in +commands+.
|
|
32
|
+
# commands stored in +commands+. For example:
|
|
33
33
|
#
|
|
34
34
|
# recorder.record(:rename_table, [:old, :new])
|
|
35
35
|
# recorder.inverse # => [:rename_table, [:new, :old]]
|
|
36
36
|
#
|
|
37
|
-
# This method will raise an IrreversibleMigration exception if it cannot
|
|
37
|
+
# This method will raise an +IrreversibleMigration+ exception if it cannot
|
|
38
38
|
# invert the +commands+.
|
|
39
39
|
def inverse
|
|
40
40
|
@commands.reverse.map { |name, args|
|
|
@@ -59,7 +59,7 @@ module ActiveRecord
|
|
|
59
59
|
private
|
|
60
60
|
|
|
61
61
|
def invert_create_table(args)
|
|
62
|
-
[:drop_table, args]
|
|
62
|
+
[:drop_table, [args.first]]
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def invert_rename_table(args)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require "active_support/core_ext/module/delegation"
|
|
2
2
|
require "active_support/core_ext/class/attribute_accessors"
|
|
3
3
|
require "active_support/core_ext/array/wrap"
|
|
4
|
+
require 'active_support/deprecation'
|
|
4
5
|
|
|
5
6
|
module ActiveRecord
|
|
6
7
|
# Exception that can be raised to stop migrations from going backwards.
|
|
@@ -68,9 +69,9 @@ module ActiveRecord
|
|
|
68
69
|
# create_table :system_settings do |t|
|
|
69
70
|
# t.string :name
|
|
70
71
|
# t.string :label
|
|
71
|
-
# t.text
|
|
72
|
+
# t.text :value
|
|
72
73
|
# t.string :type
|
|
73
|
-
# t.integer
|
|
74
|
+
# t.integer :position
|
|
74
75
|
# end
|
|
75
76
|
#
|
|
76
77
|
# SystemSetting.create :name => "notice",
|
|
@@ -116,8 +117,9 @@ module ActiveRecord
|
|
|
116
117
|
# +column_names+ from the table called +table_name+.
|
|
117
118
|
# * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
|
|
118
119
|
# with the name of the column. Other options include
|
|
119
|
-
# <tt>:name</tt
|
|
120
|
-
# <tt>{ :name => "users_name_index", :unique => true }</tt>)
|
|
120
|
+
# <tt>:name</tt>, <tt>:unique</tt> (e.g.
|
|
121
|
+
# <tt>{ :name => "users_name_index", :unique => true }</tt>) and <tt>:order</tt>
|
|
122
|
+
# (e.g. { :order => {:name => :desc} }</tt>).
|
|
121
123
|
# * <tt>remove_index(table_name, :column => column_name)</tt>: Removes the index
|
|
122
124
|
# specified by +column_name+.
|
|
123
125
|
# * <tt>remove_index(table_name, :name => index_name)</tt>: Removes the index
|
|
@@ -183,7 +185,7 @@ module ActiveRecord
|
|
|
183
185
|
#
|
|
184
186
|
# class RemoveEmptyTags < ActiveRecord::Migration
|
|
185
187
|
# def up
|
|
186
|
-
# Tag.
|
|
188
|
+
# Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
|
|
187
189
|
# end
|
|
188
190
|
#
|
|
189
191
|
# def down
|
|
@@ -229,7 +231,7 @@ module ActiveRecord
|
|
|
229
231
|
# def up
|
|
230
232
|
# add_column :people, :salary, :integer
|
|
231
233
|
# Person.reset_column_information
|
|
232
|
-
# Person.
|
|
234
|
+
# Person.all.each do |p|
|
|
233
235
|
# p.update_attribute :salary, SalaryCalculator.compute(p)
|
|
234
236
|
# end
|
|
235
237
|
# end
|
|
@@ -249,7 +251,7 @@ module ActiveRecord
|
|
|
249
251
|
# def up
|
|
250
252
|
# ...
|
|
251
253
|
# say_with_time "Updating salaries..." do
|
|
252
|
-
# Person.
|
|
254
|
+
# Person.all.each do |p|
|
|
253
255
|
# p.update_attribute :salary, SalaryCalculator.compute(p)
|
|
254
256
|
# end
|
|
255
257
|
# end
|
|
@@ -301,7 +303,7 @@ module ActiveRecord
|
|
|
301
303
|
#
|
|
302
304
|
# class TenderloveMigration < ActiveRecord::Migration
|
|
303
305
|
# def change
|
|
304
|
-
# create_table(:horses) do
|
|
306
|
+
# create_table(:horses) do |t|
|
|
305
307
|
# t.column :content, :text
|
|
306
308
|
# t.column :remind_at, :datetime
|
|
307
309
|
# end
|
|
@@ -442,6 +444,7 @@ module ActiveRecord
|
|
|
442
444
|
say_with_time "#{method}(#{arg_list})" do
|
|
443
445
|
unless arguments.empty? || method == :execute
|
|
444
446
|
arguments[0] = Migrator.proper_table_name(arguments.first)
|
|
447
|
+
arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
|
|
445
448
|
end
|
|
446
449
|
return super unless connection.respond_to?(method)
|
|
447
450
|
connection.send(method, *arguments, &block)
|
|
@@ -455,26 +458,28 @@ module ActiveRecord
|
|
|
455
458
|
|
|
456
459
|
destination_migrations = ActiveRecord::Migrator.migrations(destination)
|
|
457
460
|
last = destination_migrations.last
|
|
458
|
-
sources.each do |
|
|
461
|
+
sources.each do |scope, path|
|
|
459
462
|
source_migrations = ActiveRecord::Migrator.migrations(path)
|
|
460
463
|
|
|
461
464
|
source_migrations.each do |migration|
|
|
462
465
|
source = File.read(migration.filename)
|
|
463
|
-
source = "# This migration comes from #{
|
|
466
|
+
source = "# This migration comes from #{scope} (originally #{migration.version})\n#{source}"
|
|
464
467
|
|
|
465
468
|
if duplicate = destination_migrations.detect { |m| m.name == migration.name }
|
|
466
|
-
options[:on_skip]
|
|
469
|
+
if options[:on_skip] && duplicate.scope != scope.to_s
|
|
470
|
+
options[:on_skip].call(scope, migration)
|
|
471
|
+
end
|
|
467
472
|
next
|
|
468
473
|
end
|
|
469
474
|
|
|
470
475
|
migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
|
|
471
|
-
new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.rb")
|
|
476
|
+
new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
|
|
472
477
|
old_path, migration.filename = migration.filename, new_path
|
|
473
478
|
last = migration
|
|
474
479
|
|
|
475
|
-
|
|
480
|
+
File.open(migration.filename, "w") { |f| f.write source }
|
|
476
481
|
copied << migration
|
|
477
|
-
options[:on_copy].call(
|
|
482
|
+
options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
|
|
478
483
|
destination_migrations << migration
|
|
479
484
|
end
|
|
480
485
|
end
|
|
@@ -493,9 +498,9 @@ module ActiveRecord
|
|
|
493
498
|
|
|
494
499
|
# MigrationProxy is used to defer loading of the actual migration classes
|
|
495
500
|
# until they are needed
|
|
496
|
-
class MigrationProxy < Struct.new(:name, :version, :filename)
|
|
501
|
+
class MigrationProxy < Struct.new(:name, :version, :filename, :scope)
|
|
497
502
|
|
|
498
|
-
def initialize(name, version, filename)
|
|
503
|
+
def initialize(name, version, filename, scope)
|
|
499
504
|
super
|
|
500
505
|
@migration = nil
|
|
501
506
|
end
|
|
@@ -504,7 +509,7 @@ module ActiveRecord
|
|
|
504
509
|
File.basename(filename)
|
|
505
510
|
end
|
|
506
511
|
|
|
507
|
-
delegate :migrate, :announce, :write, :to
|
|
512
|
+
delegate :migrate, :announce, :write, :to => :migration
|
|
508
513
|
|
|
509
514
|
private
|
|
510
515
|
|
|
@@ -524,16 +529,16 @@ module ActiveRecord
|
|
|
524
529
|
attr_writer :migrations_paths
|
|
525
530
|
alias :migrations_path= :migrations_paths=
|
|
526
531
|
|
|
527
|
-
def migrate(migrations_paths, target_version = nil)
|
|
532
|
+
def migrate(migrations_paths, target_version = nil, &block)
|
|
528
533
|
case
|
|
529
534
|
when target_version.nil?
|
|
530
|
-
up(migrations_paths, target_version)
|
|
535
|
+
up(migrations_paths, target_version, &block)
|
|
531
536
|
when current_version == 0 && target_version == 0
|
|
532
537
|
[]
|
|
533
538
|
when current_version > target_version
|
|
534
|
-
down(migrations_paths, target_version)
|
|
539
|
+
down(migrations_paths, target_version, &block)
|
|
535
540
|
else
|
|
536
|
-
up(migrations_paths, target_version)
|
|
541
|
+
up(migrations_paths, target_version, &block)
|
|
537
542
|
end
|
|
538
543
|
end
|
|
539
544
|
|
|
@@ -545,12 +550,12 @@ module ActiveRecord
|
|
|
545
550
|
move(:up, migrations_paths, steps)
|
|
546
551
|
end
|
|
547
552
|
|
|
548
|
-
def up(migrations_paths, target_version = nil)
|
|
549
|
-
self.new(:up, migrations_paths, target_version).migrate
|
|
553
|
+
def up(migrations_paths, target_version = nil, &block)
|
|
554
|
+
self.new(:up, migrations_paths, target_version).migrate(&block)
|
|
550
555
|
end
|
|
551
556
|
|
|
552
|
-
def down(migrations_paths, target_version = nil)
|
|
553
|
-
self.new(:down, migrations_paths, target_version).migrate
|
|
557
|
+
def down(migrations_paths, target_version = nil, &block)
|
|
558
|
+
self.new(:down, migrations_paths, target_version).migrate(&block)
|
|
554
559
|
end
|
|
555
560
|
|
|
556
561
|
def run(direction, migrations_paths, target_version)
|
|
@@ -590,15 +595,23 @@ module ActiveRecord
|
|
|
590
595
|
migrations_paths.first
|
|
591
596
|
end
|
|
592
597
|
|
|
593
|
-
def migrations(paths)
|
|
598
|
+
def migrations(paths, *args)
|
|
599
|
+
if args.empty?
|
|
600
|
+
subdirectories = true
|
|
601
|
+
else
|
|
602
|
+
subdirectories = args.first
|
|
603
|
+
ActiveSupport::Deprecation.warn "The `subdirectories` argument to `migrations` is deprecated"
|
|
604
|
+
end
|
|
605
|
+
|
|
594
606
|
paths = Array.wrap(paths)
|
|
595
607
|
|
|
596
|
-
|
|
608
|
+
glob = subdirectories ? "**/" : ""
|
|
609
|
+
files = Dir[*paths.map { |p| "#{p}/#{glob}[0-9]*_*.rb" }]
|
|
597
610
|
|
|
598
611
|
seen = Hash.new false
|
|
599
612
|
|
|
600
613
|
migrations = files.map do |file|
|
|
601
|
-
version, name = file.scan(/([0-9]+)_([_a-z0-9]*)
|
|
614
|
+
version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first
|
|
602
615
|
|
|
603
616
|
raise IllegalMigrationNameError.new(file) unless version
|
|
604
617
|
version = version.to_i
|
|
@@ -609,7 +622,7 @@ module ActiveRecord
|
|
|
609
622
|
|
|
610
623
|
seen[version] = seen[name] = true
|
|
611
624
|
|
|
612
|
-
MigrationProxy.new(name, version, file)
|
|
625
|
+
MigrationProxy.new(name, version, file, scope)
|
|
613
626
|
end
|
|
614
627
|
|
|
615
628
|
migrations.sort_by(&:version)
|
|
@@ -652,7 +665,7 @@ module ActiveRecord
|
|
|
652
665
|
end
|
|
653
666
|
end
|
|
654
667
|
|
|
655
|
-
def migrate
|
|
668
|
+
def migrate(&block)
|
|
656
669
|
current = migrations.detect { |m| m.version == current_version }
|
|
657
670
|
target = migrations.detect { |m| m.version == @target_version }
|
|
658
671
|
|
|
@@ -669,6 +682,10 @@ module ActiveRecord
|
|
|
669
682
|
|
|
670
683
|
ran = []
|
|
671
684
|
runnable.each do |migration|
|
|
685
|
+
if block && !block.call(migration)
|
|
686
|
+
next
|
|
687
|
+
end
|
|
688
|
+
|
|
672
689
|
Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
|
|
673
690
|
|
|
674
691
|
seen = migrated.include?(migration.version.to_i)
|