activerecord 3.1.12 → 3.2.0.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.
- data/CHANGELOG.md +6263 -103
- data/README.rdoc +2 -2
- data/examples/performance.rb +55 -31
- data/lib/active_record.rb +28 -2
- data/lib/active_record/aggregations.rb +2 -2
- data/lib/active_record/associations.rb +82 -69
- data/lib/active_record/associations/association.rb +2 -37
- 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/attribute_assignment.rb +221 -0
- data/lib/active_record/attribute_methods.rb +212 -32
- 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 +69 -80
- data/lib/active_record/attribute_methods/serialization.rb +89 -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/autosave_association.rb +23 -8
- data/lib/active_record/base.rb +223 -1712
- 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 +7 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +36 -25
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -13
- 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/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 +1 -1
- 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.rb +31 -76
- data/lib/active_record/fixtures/file.rb +65 -0
- data/lib/active_record/identity_map.rb +1 -7
- data/lib/active_record/inheritance.rb +167 -0
- data/lib/active_record/integration.rb +49 -0
- data/lib/active_record/locking/optimistic.rb +19 -11
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +3 -3
- data/lib/active_record/migration.rb +38 -29
- data/lib/active_record/migration/command_recorder.rb +7 -7
- data/lib/active_record/model_schema.rb +362 -0
- data/lib/active_record/nested_attributes.rb +3 -2
- data/lib/active_record/persistence.rb +51 -1
- 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 +133 -77
- data/lib/active_record/readonly_attributes.rb +26 -0
- data/lib/active_record/reflection.rb +7 -15
- data/lib/active_record/relation.rb +78 -35
- 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 +5 -4
- data/lib/active_record/relation/predicate_builder.rb +13 -16
- data/lib/active_record/relation/query_methods.rb +59 -4
- 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.rb +152 -0
- data/lib/active_record/scoping/default.rb +140 -0
- data/lib/active_record/scoping/named.rb +202 -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 +11 -11
- 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.rb +1 -1
- data/lib/active_record/validations/associated.rb +5 -4
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/version.rb +3 -3
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
- metadata +48 -38
- checksums.yaml +0 -7
- data/lib/active_record/named_scope.rb +0 -200
@@ -0,0 +1,65 @@
|
|
1
|
+
begin
|
2
|
+
require 'psych'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'erb'
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
module ActiveRecord
|
10
|
+
class Fixtures
|
11
|
+
class File
|
12
|
+
include Enumerable
|
13
|
+
|
14
|
+
##
|
15
|
+
# Open a fixture file named +file+. When called with a block, the block
|
16
|
+
# is called with the filehandle and the filehandle is automatically closed
|
17
|
+
# when the block finishes.
|
18
|
+
def self.open(file)
|
19
|
+
x = new file
|
20
|
+
block_given? ? yield(x) : x
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(file)
|
24
|
+
@file = file
|
25
|
+
@rows = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def each(&block)
|
29
|
+
rows.each(&block)
|
30
|
+
end
|
31
|
+
|
32
|
+
RESCUE_ERRORS = [ ArgumentError ] # :nodoc:
|
33
|
+
|
34
|
+
private
|
35
|
+
if defined?(Psych) && defined?(Psych::SyntaxError)
|
36
|
+
RESCUE_ERRORS << Psych::SyntaxError
|
37
|
+
end
|
38
|
+
|
39
|
+
def rows
|
40
|
+
return @rows if @rows
|
41
|
+
|
42
|
+
begin
|
43
|
+
data = YAML.load(render(IO.read(@file)))
|
44
|
+
rescue *RESCUE_ERRORS => error
|
45
|
+
raise Fixture::FormatError, "a YAML error occurred parsing #{@file}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}", error.backtrace
|
46
|
+
end
|
47
|
+
@rows = data ? validate(data).to_a : []
|
48
|
+
end
|
49
|
+
|
50
|
+
def render(content)
|
51
|
+
ERB.new(content).result
|
52
|
+
end
|
53
|
+
|
54
|
+
# Validate our unmarshalled data.
|
55
|
+
def validate(data)
|
56
|
+
unless Hash === data || YAML::Omap === data
|
57
|
+
raise Fixture::FormatError, 'fixture is not a hash'
|
58
|
+
end
|
59
|
+
|
60
|
+
raise Fixture::FormatError unless data.all? { |name, row| Hash === row }
|
61
|
+
data
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -90,7 +90,7 @@ module ActiveRecord
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def add(record)
|
93
|
-
repository[record.class.symbolized_sti_name][record.id] = record
|
93
|
+
repository[record.class.symbolized_sti_name][record.id] = record
|
94
94
|
end
|
95
95
|
|
96
96
|
def remove(record)
|
@@ -104,12 +104,6 @@ module ActiveRecord
|
|
104
104
|
def clear
|
105
105
|
repository.clear
|
106
106
|
end
|
107
|
-
|
108
|
-
private
|
109
|
-
|
110
|
-
def contain_all_columns?(record)
|
111
|
-
(record.class.column_names - record.attribute_names).empty?
|
112
|
-
end
|
113
107
|
end
|
114
108
|
|
115
109
|
# Reinitialize an Identity Map model object from +coder+.
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Inheritance
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
# Determine whether to store the full constant name including namespace when using STI
|
9
|
+
class_attribute :store_full_sti_class
|
10
|
+
self.store_full_sti_class = true
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
# True if this isn't a concrete subclass needing a STI type condition.
|
15
|
+
def descends_from_active_record?
|
16
|
+
if superclass.abstract_class?
|
17
|
+
superclass.descends_from_active_record?
|
18
|
+
else
|
19
|
+
superclass == Base || !columns_hash.include?(inheritance_column)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def finder_needs_type_condition? #:nodoc:
|
24
|
+
# This is like this because benchmarking justifies the strange :false stuff
|
25
|
+
:true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
|
26
|
+
end
|
27
|
+
|
28
|
+
def symbolized_base_class
|
29
|
+
@symbolized_base_class ||= base_class.to_s.to_sym
|
30
|
+
end
|
31
|
+
|
32
|
+
def symbolized_sti_name
|
33
|
+
@symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the base AR subclass that this class descends from. If A
|
37
|
+
# extends AR::Base, A.base_class will return A. If B descends from A
|
38
|
+
# through some arbitrarily deep hierarchy, B.base_class will return A.
|
39
|
+
#
|
40
|
+
# If B < A and C < B and if A is an abstract_class then both B.base_class
|
41
|
+
# and C.base_class would return B as the answer since A is an abstract_class.
|
42
|
+
def base_class
|
43
|
+
class_of_active_record_descendant(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
|
47
|
+
attr_accessor :abstract_class
|
48
|
+
|
49
|
+
# Returns whether this class is an abstract class or not.
|
50
|
+
def abstract_class?
|
51
|
+
defined?(@abstract_class) && @abstract_class == true
|
52
|
+
end
|
53
|
+
|
54
|
+
def sti_name
|
55
|
+
store_full_sti_class ? name : name.demodulize
|
56
|
+
end
|
57
|
+
|
58
|
+
# Finder methods must instantiate through this method to work with the
|
59
|
+
# single-table inheritance model that makes it possible to create
|
60
|
+
# objects of different types from the same table.
|
61
|
+
def instantiate(record)
|
62
|
+
sti_class = find_sti_class(record[inheritance_column])
|
63
|
+
record_id = sti_class.primary_key && record[sti_class.primary_key]
|
64
|
+
|
65
|
+
if ActiveRecord::IdentityMap.enabled? && record_id
|
66
|
+
if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number?
|
67
|
+
record_id = record_id.to_i
|
68
|
+
end
|
69
|
+
if instance = IdentityMap.get(sti_class, record_id)
|
70
|
+
instance.reinit_with('attributes' => record)
|
71
|
+
else
|
72
|
+
instance = sti_class.allocate.init_with('attributes' => record)
|
73
|
+
IdentityMap.add(instance)
|
74
|
+
end
|
75
|
+
else
|
76
|
+
instance = sti_class.allocate.init_with('attributes' => record)
|
77
|
+
end
|
78
|
+
|
79
|
+
instance
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
|
84
|
+
# Returns the class descending directly from ActiveRecord::Base or an
|
85
|
+
# abstract class, if any, in the inheritance hierarchy.
|
86
|
+
def class_of_active_record_descendant(klass)
|
87
|
+
if klass == Base || klass.superclass == Base || klass.superclass.abstract_class?
|
88
|
+
klass
|
89
|
+
elsif klass.superclass.nil?
|
90
|
+
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
|
91
|
+
else
|
92
|
+
class_of_active_record_descendant(klass.superclass)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns the class type of the record using the current module as a prefix. So descendants of
|
97
|
+
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
|
98
|
+
def compute_type(type_name)
|
99
|
+
if type_name.match(/^::/)
|
100
|
+
# If the type is prefixed with a scope operator then we assume that
|
101
|
+
# the type_name is an absolute reference.
|
102
|
+
ActiveSupport::Dependencies.constantize(type_name)
|
103
|
+
else
|
104
|
+
# Build a list of candidates to search for
|
105
|
+
candidates = []
|
106
|
+
name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
|
107
|
+
candidates << type_name
|
108
|
+
|
109
|
+
candidates.each do |candidate|
|
110
|
+
begin
|
111
|
+
constant = ActiveSupport::Dependencies.constantize(candidate)
|
112
|
+
return constant if candidate == constant.to_s
|
113
|
+
rescue NameError => e
|
114
|
+
# We don't want to swallow NoMethodError < NameError errors
|
115
|
+
raise e unless e.instance_of?(NameError)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
raise NameError, "uninitialized constant #{candidates.first}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def find_sti_class(type_name)
|
126
|
+
if type_name.blank? || !columns_hash.include?(inheritance_column)
|
127
|
+
self
|
128
|
+
else
|
129
|
+
begin
|
130
|
+
if store_full_sti_class
|
131
|
+
ActiveSupport::Dependencies.constantize(type_name)
|
132
|
+
else
|
133
|
+
compute_type(type_name)
|
134
|
+
end
|
135
|
+
rescue NameError
|
136
|
+
raise SubclassNotFound,
|
137
|
+
"The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
|
138
|
+
"This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
|
139
|
+
"Please rename this column if you didn't intend it to be used for storing the inheritance class " +
|
140
|
+
"or overwrite #{name}.inheritance_column to use another column for that information."
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def type_condition(table = arel_table)
|
146
|
+
sti_column = table[inheritance_column.to_sym]
|
147
|
+
sti_names = ([self] + descendants).map { |model| model.sti_name }
|
148
|
+
|
149
|
+
sti_column.in(sti_names)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
# Sets the attribute used for single table inheritance to this class name if this is not the
|
156
|
+
# ActiveRecord::Base descendant.
|
157
|
+
# Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
|
158
|
+
# do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
|
159
|
+
# No such attribute would be set for objects of the Message class in that example.
|
160
|
+
def ensure_proper_type
|
161
|
+
klass = self.class
|
162
|
+
if klass.finder_needs_type_condition?
|
163
|
+
write_attribute(klass.inheritance_column, klass.sti_name)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -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:
|
@@ -66,7 +65,7 @@ module ActiveRecord
|
|
66
65
|
end
|
67
66
|
|
68
67
|
def attributes_from_column_definition
|
69
|
-
result =
|
68
|
+
result = self.class.column_defaults.dup
|
70
69
|
|
71
70
|
# If the locking column has no default value set,
|
72
71
|
# start the lock version at zero. Note we can't use
|
@@ -103,7 +102,7 @@ module ActiveRecord
|
|
103
102
|
affected_rows = connection.update stmt
|
104
103
|
|
105
104
|
unless affected_rows == 1
|
106
|
-
raise ActiveRecord::StaleObjectError, "
|
105
|
+
raise ActiveRecord::StaleObjectError.new(self, "update")
|
107
106
|
end
|
108
107
|
|
109
108
|
affected_rows
|
@@ -127,7 +126,7 @@ module ActiveRecord
|
|
127
126
|
affected_rows = self.class.unscoped.where(predicate).delete_all
|
128
127
|
|
129
128
|
unless affected_rows == 1
|
130
|
-
raise ActiveRecord::StaleObjectError, "
|
129
|
+
raise ActiveRecord::StaleObjectError.new(self, "destroy")
|
131
130
|
end
|
132
131
|
end
|
133
132
|
|
@@ -145,15 +144,24 @@ module ActiveRecord
|
|
145
144
|
lock_optimistically && columns_hash[locking_column]
|
146
145
|
end
|
147
146
|
|
147
|
+
def locking_column=(value)
|
148
|
+
@original_locking_column = @locking_column if defined?(@locking_column)
|
149
|
+
@locking_column = value.to_s
|
150
|
+
end
|
151
|
+
|
148
152
|
# Set the column to use for optimistic locking. Defaults to +lock_version+.
|
149
153
|
def set_locking_column(value = nil, &block)
|
150
|
-
|
151
|
-
value
|
154
|
+
deprecated_property_setter :locking_column, value, block
|
152
155
|
end
|
153
156
|
|
154
157
|
# The version column used for optimistic locking. Defaults to +lock_version+.
|
155
158
|
def locking_column
|
156
|
-
reset_locking_column
|
159
|
+
reset_locking_column unless defined?(@locking_column)
|
160
|
+
@locking_column
|
161
|
+
end
|
162
|
+
|
163
|
+
def original_locking_column #:nodoc:
|
164
|
+
deprecated_original_property_getter :locking_column
|
157
165
|
end
|
158
166
|
|
159
167
|
# Quote the column name used for optimistic locking.
|
@@ -163,7 +171,7 @@ module ActiveRecord
|
|
163
171
|
|
164
172
|
# Reset the column used for optimistic locking back to the +lock_version+ default.
|
165
173
|
def reset_locking_column
|
166
|
-
|
174
|
+
self.locking_column = DEFAULT_LOCKING_COLUMN
|
167
175
|
end
|
168
176
|
|
169
177
|
# Make sure the lock version column gets updated when counters are
|
@@ -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
|
@@ -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|
|
@@ -68,9 +68,9 @@ module ActiveRecord
|
|
68
68
|
# create_table :system_settings do |t|
|
69
69
|
# t.string :name
|
70
70
|
# t.string :label
|
71
|
-
# t.text
|
71
|
+
# t.text :value
|
72
72
|
# t.string :type
|
73
|
-
# t.integer
|
73
|
+
# t.integer :position
|
74
74
|
# end
|
75
75
|
#
|
76
76
|
# SystemSetting.create :name => "notice",
|
@@ -116,8 +116,9 @@ module ActiveRecord
|
|
116
116
|
# +column_names+ from the table called +table_name+.
|
117
117
|
# * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
|
118
118
|
# with the name of the column. Other options include
|
119
|
-
# <tt>:name</tt
|
120
|
-
# <tt>{ :name => "users_name_index", :unique => true }</tt>)
|
119
|
+
# <tt>:name</tt>, <tt>:unique</tt> (e.g.
|
120
|
+
# <tt>{ :name => "users_name_index", :unique => true }</tt>) and <tt>:order</tt>
|
121
|
+
# (e.g. { :order => {:name => :desc} }</tt>).
|
121
122
|
# * <tt>remove_index(table_name, :column => column_name)</tt>: Removes the index
|
122
123
|
# specified by +column_name+.
|
123
124
|
# * <tt>remove_index(table_name, :name => index_name)</tt>: Removes the index
|
@@ -183,7 +184,7 @@ module ActiveRecord
|
|
183
184
|
#
|
184
185
|
# class RemoveEmptyTags < ActiveRecord::Migration
|
185
186
|
# def up
|
186
|
-
# Tag.
|
187
|
+
# Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
|
187
188
|
# end
|
188
189
|
#
|
189
190
|
# def down
|
@@ -229,7 +230,7 @@ module ActiveRecord
|
|
229
230
|
# def up
|
230
231
|
# add_column :people, :salary, :integer
|
231
232
|
# Person.reset_column_information
|
232
|
-
# Person.
|
233
|
+
# Person.all.each do |p|
|
233
234
|
# p.update_attribute :salary, SalaryCalculator.compute(p)
|
234
235
|
# end
|
235
236
|
# end
|
@@ -249,7 +250,7 @@ module ActiveRecord
|
|
249
250
|
# def up
|
250
251
|
# ...
|
251
252
|
# say_with_time "Updating salaries..." do
|
252
|
-
# Person.
|
253
|
+
# Person.all.each do |p|
|
253
254
|
# p.update_attribute :salary, SalaryCalculator.compute(p)
|
254
255
|
# end
|
255
256
|
# end
|
@@ -301,7 +302,7 @@ module ActiveRecord
|
|
301
302
|
#
|
302
303
|
# class TenderloveMigration < ActiveRecord::Migration
|
303
304
|
# def change
|
304
|
-
# create_table(:horses) do
|
305
|
+
# create_table(:horses) do |t|
|
305
306
|
# t.column :content, :text
|
306
307
|
# t.column :remind_at, :datetime
|
307
308
|
# end
|
@@ -442,6 +443,7 @@ module ActiveRecord
|
|
442
443
|
say_with_time "#{method}(#{arg_list})" do
|
443
444
|
unless arguments.empty? || method == :execute
|
444
445
|
arguments[0] = Migrator.proper_table_name(arguments.first)
|
446
|
+
arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
|
445
447
|
end
|
446
448
|
return super unless connection.respond_to?(method)
|
447
449
|
connection.send(method, *arguments, &block)
|
@@ -455,26 +457,28 @@ module ActiveRecord
|
|
455
457
|
|
456
458
|
destination_migrations = ActiveRecord::Migrator.migrations(destination)
|
457
459
|
last = destination_migrations.last
|
458
|
-
sources.each do |
|
460
|
+
sources.each do |scope, path|
|
459
461
|
source_migrations = ActiveRecord::Migrator.migrations(path)
|
460
462
|
|
461
463
|
source_migrations.each do |migration|
|
462
464
|
source = File.read(migration.filename)
|
463
|
-
source = "# This migration comes from #{
|
465
|
+
source = "# This migration comes from #{scope} (originally #{migration.version})\n#{source}"
|
464
466
|
|
465
467
|
if duplicate = destination_migrations.detect { |m| m.name == migration.name }
|
466
|
-
options[:on_skip]
|
468
|
+
if options[:on_skip] && duplicate.scope != scope.to_s
|
469
|
+
options[:on_skip].call(scope, migration)
|
470
|
+
end
|
467
471
|
next
|
468
472
|
end
|
469
473
|
|
470
474
|
migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
|
471
|
-
new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.rb")
|
475
|
+
new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
|
472
476
|
old_path, migration.filename = migration.filename, new_path
|
473
477
|
last = migration
|
474
478
|
|
475
|
-
|
479
|
+
File.open(migration.filename, "w") { |f| f.write source }
|
476
480
|
copied << migration
|
477
|
-
options[:on_copy].call(
|
481
|
+
options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
|
478
482
|
destination_migrations << migration
|
479
483
|
end
|
480
484
|
end
|
@@ -493,9 +497,9 @@ module ActiveRecord
|
|
493
497
|
|
494
498
|
# MigrationProxy is used to defer loading of the actual migration classes
|
495
499
|
# until they are needed
|
496
|
-
class MigrationProxy < Struct.new(:name, :version, :filename)
|
500
|
+
class MigrationProxy < Struct.new(:name, :version, :filename, :scope)
|
497
501
|
|
498
|
-
def initialize(name, version, filename)
|
502
|
+
def initialize(name, version, filename, scope)
|
499
503
|
super
|
500
504
|
@migration = nil
|
501
505
|
end
|
@@ -524,16 +528,16 @@ module ActiveRecord
|
|
524
528
|
attr_writer :migrations_paths
|
525
529
|
alias :migrations_path= :migrations_paths=
|
526
530
|
|
527
|
-
def migrate(migrations_paths, target_version = nil)
|
531
|
+
def migrate(migrations_paths, target_version = nil, &block)
|
528
532
|
case
|
529
533
|
when target_version.nil?
|
530
|
-
up(migrations_paths, target_version)
|
534
|
+
up(migrations_paths, target_version, &block)
|
531
535
|
when current_version == 0 && target_version == 0
|
532
536
|
[]
|
533
537
|
when current_version > target_version
|
534
|
-
down(migrations_paths, target_version)
|
538
|
+
down(migrations_paths, target_version, &block)
|
535
539
|
else
|
536
|
-
up(migrations_paths, target_version)
|
540
|
+
up(migrations_paths, target_version, &block)
|
537
541
|
end
|
538
542
|
end
|
539
543
|
|
@@ -545,12 +549,12 @@ module ActiveRecord
|
|
545
549
|
move(:up, migrations_paths, steps)
|
546
550
|
end
|
547
551
|
|
548
|
-
def up(migrations_paths, target_version = nil)
|
549
|
-
self.new(:up, migrations_paths, target_version).migrate
|
552
|
+
def up(migrations_paths, target_version = nil, &block)
|
553
|
+
self.new(:up, migrations_paths, target_version).migrate(&block)
|
550
554
|
end
|
551
555
|
|
552
|
-
def down(migrations_paths, target_version = nil)
|
553
|
-
self.new(:down, migrations_paths, target_version).migrate
|
556
|
+
def down(migrations_paths, target_version = nil, &block)
|
557
|
+
self.new(:down, migrations_paths, target_version).migrate(&block)
|
554
558
|
end
|
555
559
|
|
556
560
|
def run(direction, migrations_paths, target_version)
|
@@ -590,15 +594,16 @@ module ActiveRecord
|
|
590
594
|
migrations_paths.first
|
591
595
|
end
|
592
596
|
|
593
|
-
def migrations(paths)
|
597
|
+
def migrations(paths, subdirectories = true)
|
594
598
|
paths = Array.wrap(paths)
|
595
599
|
|
596
|
-
|
600
|
+
glob = subdirectories ? "**/" : ""
|
601
|
+
files = Dir[*paths.map { |p| "#{p}/#{glob}[0-9]*_*.rb" }]
|
597
602
|
|
598
603
|
seen = Hash.new false
|
599
604
|
|
600
605
|
migrations = files.map do |file|
|
601
|
-
version, name = file.scan(/([0-9]+)_([_a-z0-9]*)
|
606
|
+
version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first
|
602
607
|
|
603
608
|
raise IllegalMigrationNameError.new(file) unless version
|
604
609
|
version = version.to_i
|
@@ -609,7 +614,7 @@ module ActiveRecord
|
|
609
614
|
|
610
615
|
seen[version] = seen[name] = true
|
611
616
|
|
612
|
-
MigrationProxy.new(name, version, file)
|
617
|
+
MigrationProxy.new(name, version, file, scope)
|
613
618
|
end
|
614
619
|
|
615
620
|
migrations.sort_by(&:version)
|
@@ -652,7 +657,7 @@ module ActiveRecord
|
|
652
657
|
end
|
653
658
|
end
|
654
659
|
|
655
|
-
def migrate
|
660
|
+
def migrate(&block)
|
656
661
|
current = migrations.detect { |m| m.version == current_version }
|
657
662
|
target = migrations.detect { |m| m.version == @target_version }
|
658
663
|
|
@@ -669,6 +674,10 @@ module ActiveRecord
|
|
669
674
|
|
670
675
|
ran = []
|
671
676
|
runnable.each do |migration|
|
677
|
+
if block && !block.call(migration)
|
678
|
+
next
|
679
|
+
end
|
680
|
+
|
672
681
|
Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
|
673
682
|
|
674
683
|
seen = migrated.include?(migration.version.to_i)
|