activerecord 3.2.22.4 → 4.0.13

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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2799 -617
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +23 -32
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +40 -34
  7. data/lib/active_record/association_relation.rb +22 -0
  8. data/lib/active_record/associations/alias_tracker.rb +4 -2
  9. data/lib/active_record/associations/association.rb +60 -46
  10. data/lib/active_record/associations/association_scope.rb +46 -40
  11. data/lib/active_record/associations/belongs_to_association.rb +17 -4
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +73 -56
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +130 -96
  21. data/lib/active_record/associations/collection_proxy.rb +916 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
  23. data/lib/active_record/associations/has_many_association.rb +35 -8
  24. data/lib/active_record/associations/has_many_through_association.rb +37 -17
  25. data/lib/active_record/associations/has_one_association.rb +42 -19
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
  28. data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
  29. data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
  30. data/lib/active_record/associations/join_dependency.rb +30 -9
  31. data/lib/active_record/associations/join_helper.rb +1 -11
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/preloader.rb +20 -43
  39. data/lib/active_record/associations/singular_association.rb +11 -11
  40. data/lib/active_record/associations/through_association.rb +3 -3
  41. data/lib/active_record/associations.rb +223 -282
  42. data/lib/active_record/attribute_assignment.rb +134 -154
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  44. data/lib/active_record/attribute_methods/dirty.rb +36 -29
  45. data/lib/active_record/attribute_methods/primary_key.rb +45 -31
  46. data/lib/active_record/attribute_methods/query.rb +5 -4
  47. data/lib/active_record/attribute_methods/read.rb +67 -90
  48. data/lib/active_record/attribute_methods/serialization.rb +133 -70
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
  50. data/lib/active_record/attribute_methods/write.rb +34 -39
  51. data/lib/active_record/attribute_methods.rb +268 -108
  52. data/lib/active_record/autosave_association.rb +80 -73
  53. data/lib/active_record/base.rb +54 -451
  54. data/lib/active_record/callbacks.rb +60 -22
  55. data/lib/active_record/coders/yaml_column.rb +18 -21
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
  67. data/lib/active_record/connection_adapters/column.rb +67 -36
  68. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
  70. data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
  71. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
  72. data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
  75. data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
  76. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
  79. data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
  80. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
  81. data/lib/active_record/connection_handling.rb +98 -0
  82. data/lib/active_record/core.rb +472 -0
  83. data/lib/active_record/counter_cache.rb +107 -108
  84. data/lib/active_record/dynamic_matchers.rb +115 -63
  85. data/lib/active_record/errors.rb +36 -18
  86. data/lib/active_record/explain.rb +15 -63
  87. data/lib/active_record/explain_registry.rb +30 -0
  88. data/lib/active_record/explain_subscriber.rb +8 -4
  89. data/lib/active_record/fixture_set/file.rb +55 -0
  90. data/lib/active_record/fixtures.rb +159 -155
  91. data/lib/active_record/inheritance.rb +93 -59
  92. data/lib/active_record/integration.rb +8 -8
  93. data/lib/active_record/locale/en.yml +8 -1
  94. data/lib/active_record/locking/optimistic.rb +39 -43
  95. data/lib/active_record/locking/pessimistic.rb +4 -4
  96. data/lib/active_record/log_subscriber.rb +19 -9
  97. data/lib/active_record/migration/command_recorder.rb +102 -33
  98. data/lib/active_record/migration/join_table.rb +15 -0
  99. data/lib/active_record/migration.rb +411 -173
  100. data/lib/active_record/model_schema.rb +81 -94
  101. data/lib/active_record/nested_attributes.rb +173 -131
  102. data/lib/active_record/null_relation.rb +67 -0
  103. data/lib/active_record/persistence.rb +254 -106
  104. data/lib/active_record/query_cache.rb +18 -36
  105. data/lib/active_record/querying.rb +19 -15
  106. data/lib/active_record/railtie.rb +113 -38
  107. data/lib/active_record/railties/console_sandbox.rb +3 -4
  108. data/lib/active_record/railties/controller_runtime.rb +4 -3
  109. data/lib/active_record/railties/databases.rake +115 -368
  110. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  111. data/lib/active_record/readonly_attributes.rb +7 -3
  112. data/lib/active_record/reflection.rb +110 -61
  113. data/lib/active_record/relation/batches.rb +29 -29
  114. data/lib/active_record/relation/calculations.rb +155 -125
  115. data/lib/active_record/relation/delegation.rb +94 -18
  116. data/lib/active_record/relation/finder_methods.rb +151 -203
  117. data/lib/active_record/relation/merger.rb +188 -0
  118. data/lib/active_record/relation/predicate_builder.rb +85 -42
  119. data/lib/active_record/relation/query_methods.rb +793 -146
  120. data/lib/active_record/relation/spawn_methods.rb +43 -150
  121. data/lib/active_record/relation.rb +293 -173
  122. data/lib/active_record/result.rb +48 -7
  123. data/lib/active_record/runtime_registry.rb +17 -0
  124. data/lib/active_record/sanitization.rb +41 -54
  125. data/lib/active_record/schema.rb +19 -12
  126. data/lib/active_record/schema_dumper.rb +41 -41
  127. data/lib/active_record/schema_migration.rb +46 -0
  128. data/lib/active_record/scoping/default.rb +56 -52
  129. data/lib/active_record/scoping/named.rb +78 -103
  130. data/lib/active_record/scoping.rb +54 -124
  131. data/lib/active_record/serialization.rb +6 -2
  132. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  133. data/lib/active_record/statement_cache.rb +26 -0
  134. data/lib/active_record/store.rb +131 -15
  135. data/lib/active_record/tasks/database_tasks.rb +204 -0
  136. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
  138. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  140. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  141. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  142. data/lib/active_record/test_case.rb +67 -38
  143. data/lib/active_record/timestamp.rb +16 -11
  144. data/lib/active_record/transactions.rb +73 -51
  145. data/lib/active_record/validations/associated.rb +19 -13
  146. data/lib/active_record/validations/presence.rb +65 -0
  147. data/lib/active_record/validations/uniqueness.rb +110 -57
  148. data/lib/active_record/validations.rb +18 -17
  149. data/lib/active_record/version.rb +7 -6
  150. data/lib/active_record.rb +63 -45
  151. data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
  152. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
  153. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  154. data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
  155. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  156. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  157. data/lib/rails/generators/active_record.rb +3 -5
  158. metadata +43 -29
  159. data/examples/associations.png +0 -0
  160. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  161. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  162. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  163. data/lib/active_record/dynamic_finder_match.rb +0 -68
  164. data/lib/active_record/dynamic_scope_match.rb +0 -23
  165. data/lib/active_record/fixtures/file.rb +0 -65
  166. data/lib/active_record/identity_map.rb +0 -162
  167. data/lib/active_record/observer.rb +0 -121
  168. data/lib/active_record/session_store.rb +0 -360
  169. data/lib/rails/generators/active_record/migration.rb +0 -15
  170. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  171. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  172. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  173. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -8,12 +8,13 @@ module ActiveRecord
8
8
  class Result
9
9
  include Enumerable
10
10
 
11
- attr_reader :columns, :rows
11
+ attr_reader :columns, :rows, :column_types
12
12
 
13
- def initialize(columns, rows)
14
- @columns = columns
15
- @rows = rows
16
- @hash_rows = nil
13
+ def initialize(columns, rows, column_types = {})
14
+ @columns = columns
15
+ @rows = rows
16
+ @hash_rows = nil
17
+ @column_types = column_types
17
18
  end
18
19
 
19
20
  def each
@@ -24,15 +25,55 @@ module ActiveRecord
24
25
  hash_rows
25
26
  end
26
27
 
28
+ alias :map! :map
29
+ alias :collect! :map
30
+
31
+ # Returns true if there are no records.
32
+ def empty?
33
+ rows.empty?
34
+ end
35
+
36
+ def to_ary
37
+ hash_rows
38
+ end
39
+
40
+ def [](idx)
41
+ hash_rows[idx]
42
+ end
43
+
44
+ def last
45
+ hash_rows.last
46
+ end
47
+
48
+ def initialize_copy(other)
49
+ @columns = columns.dup
50
+ @rows = rows.dup
51
+ @hash_rows = nil
52
+ end
53
+
27
54
  private
28
55
  def hash_rows
29
56
  @hash_rows ||=
30
57
  begin
31
58
  # We freeze the strings to prevent them getting duped when
32
- # used as keys in ActiveRecord::Model's @attributes hash
59
+ # used as keys in ActiveRecord::Base's @attributes hash
33
60
  columns = @columns.map { |c| c.dup.freeze }
34
61
  @rows.map { |row|
35
- Hash[columns.zip(row)]
62
+ # In the past we used Hash[columns.zip(row)]
63
+ # though elegant, the verbose way is much more efficient
64
+ # both time and memory wise cause it avoids a big array allocation
65
+ # this method is called a lot and needs to be micro optimised
66
+ hash = {}
67
+
68
+ index = 0
69
+ length = columns.length
70
+
71
+ while index < length
72
+ hash[columns[index]] = row[index]
73
+ index += 1
74
+ end
75
+
76
+ hash
36
77
  }
37
78
  end
38
79
  end
@@ -0,0 +1,17 @@
1
+ require 'active_support/per_thread_registry'
2
+
3
+ module ActiveRecord
4
+ # This is a thread locals registry for Active Record. For example:
5
+ #
6
+ # ActiveRecord::RuntimeRegistry.connection_handler
7
+ #
8
+ # returns the connection handler local to the current thread.
9
+ #
10
+ # See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
11
+ # for further details.
12
+ class RuntimeRegistry # :nodoc:
13
+ extend ActiveSupport::PerThreadRegistry
14
+
15
+ attr_accessor :connection_handler, :sql_runtime, :connection_id
16
+ end
17
+ end
@@ -1,5 +1,3 @@
1
- require 'active_support/concern'
2
-
3
1
  module ActiveRecord
4
2
  module Sanitization
5
3
  extend ActiveSupport::Concern
@@ -19,7 +17,7 @@ module ActiveRecord
19
17
  # Accepts an array, hash, or string of SQL conditions and sanitizes
20
18
  # them into a valid SQL fragment for a WHERE clause.
21
19
  # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
22
- # { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
20
+ # { name: "foo'bar", group_id: 4 } returns "name='foo''bar' and group_id='4'"
23
21
  # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
24
22
  def sanitize_sql_for_conditions(condition, table_name = self.table_name)
25
23
  return nil if condition.blank?
@@ -34,12 +32,12 @@ module ActiveRecord
34
32
 
35
33
  # Accepts an array, hash, or string of SQL conditions and sanitizes
36
34
  # them into a valid SQL fragment for a SET clause.
37
- # { :name => nil, :group_id => 4 } returns "name = NULL , group_id='4'"
38
- def sanitize_sql_for_assignment(assignments)
35
+ # { name: nil, group_id: 4 } returns "name = NULL , group_id='4'"
36
+ def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
39
37
  case assignments
40
- when Array; sanitize_sql_array(assignments)
41
- when Hash; sanitize_sql_hash_for_assignment(assignments)
42
- else assignments
38
+ when Array; sanitize_sql_array(assignments)
39
+ when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
40
+ else assignments
43
41
  end
44
42
  end
45
43
 
@@ -48,17 +46,17 @@ module ActiveRecord
48
46
  # aggregate attribute values.
49
47
  # Given:
50
48
  # class Person < ActiveRecord::Base
51
- # composed_of :address, :class_name => "Address",
52
- # :mapping => [%w(address_street street), %w(address_city city)]
49
+ # composed_of :address, class_name: "Address",
50
+ # mapping: [%w(address_street street), %w(address_city city)]
53
51
  # end
54
52
  # Then:
55
- # { :address => Address.new("813 abc st.", "chicago") }
56
- # # => { :address_street => "813 abc st.", :address_city => "chicago" }
53
+ # { address: Address.new("813 abc st.", "chicago") }
54
+ # # => { address_street: "813 abc st.", address_city: "chicago" }
57
55
  def expand_hash_conditions_for_aggregates(attrs)
58
56
  expanded_attrs = {}
59
57
  attrs.each do |attr, value|
60
- unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
61
- mapping = aggregate_mapping(aggregation)
58
+ if aggregation = reflect_on_aggregation(attr.to_sym)
59
+ mapping = aggregation.mapping
62
60
  mapping.each do |field_attr, aggregate_attr|
63
61
  if mapping.size == 1 && !value.respond_to?(aggregate_attr)
64
62
  expanded_attrs[field_attr] = value
@@ -74,35 +72,36 @@ module ActiveRecord
74
72
  end
75
73
 
76
74
  # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
77
- # { :name => "foo'bar", :group_id => 4 }
75
+ # { name: "foo'bar", group_id: 4 }
78
76
  # # => "name='foo''bar' and group_id= 4"
79
- # { :status => nil, :group_id => [1,2,3] }
77
+ # { status: nil, group_id: [1,2,3] }
80
78
  # # => "status IS NULL and group_id IN (1,2,3)"
81
- # { :age => 13..18 }
79
+ # { age: 13..18 }
82
80
  # # => "age BETWEEN 13 AND 18"
83
81
  # { 'other_records.id' => 7 }
84
82
  # # => "`other_records`.`id` = 7"
85
- # { :other_records => { :id => 7 } }
83
+ # { other_records: { id: 7 } }
86
84
  # # => "`other_records`.`id` = 7"
87
85
  # And for value objects on a composed_of relationship:
88
- # { :address => Address.new("123 abc st.", "chicago") }
86
+ # { address: Address.new("123 abc st.", "chicago") }
89
87
  # # => "address_street='123 abc st.' and address_city='chicago'"
90
88
  def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
91
89
  attrs = expand_hash_conditions_for_aggregates(attrs)
92
90
 
93
- table = Arel::Table.new(table_name).alias(default_table_name)
94
- PredicateBuilder.build_from_hash(arel_engine, attrs, table).map { |b|
91
+ table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
92
+ PredicateBuilder.build_from_hash(self, attrs, table).map { |b|
95
93
  connection.visitor.accept b
96
94
  }.join(' AND ')
97
95
  end
98
96
  alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
99
97
 
100
98
  # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
101
- # { :status => nil, :group_id => 1 }
99
+ # { status: nil, group_id: 1 }
102
100
  # # => "status = NULL , group_id = 1"
103
- def sanitize_sql_hash_for_assignment(attrs)
101
+ def sanitize_sql_hash_for_assignment(attrs, table)
102
+ c = connection
104
103
  attrs.map do |attr, value|
105
- "#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}"
104
+ "#{c.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value, c, columns_hash[attr.to_s])}"
106
105
  end.join(', ')
107
106
  end
108
107
 
@@ -128,7 +127,17 @@ module ActiveRecord
128
127
  raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
129
128
  bound = values.dup
130
129
  c = connection
131
- statement.gsub('?') { quote_bound_value(bound.shift, c) }
130
+ statement.gsub('?') do
131
+ replace_bind_variable(bound.shift, c)
132
+ end
133
+ end
134
+
135
+ def replace_bind_variable(value, c = connection) #:nodoc:
136
+ if ActiveRecord::Relation === value
137
+ value.to_sql
138
+ else
139
+ quote_bound_value(value, c)
140
+ end
132
141
  end
133
142
 
134
143
  def replace_named_bind_variables(statement, bind_vars) #:nodoc:
@@ -136,32 +145,17 @@ module ActiveRecord
136
145
  if $1 == ':' # skip postgresql casts
137
146
  $& # return the whole match
138
147
  elsif bind_vars.include?(match = $2.to_sym)
139
- quote_bound_value(bind_vars[match])
148
+ replace_bind_variable(bind_vars[match])
140
149
  else
141
150
  raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
142
151
  end
143
152
  end
144
153
  end
145
154
 
146
- def expand_range_bind_variables(bind_vars) #:nodoc:
147
- expanded = []
148
-
149
- bind_vars.each do |var|
150
- next if var.is_a?(Hash)
151
-
152
- if var.is_a?(Range)
153
- expanded << var.first
154
- expanded << var.last
155
- else
156
- expanded << var
157
- end
158
- end
159
-
160
- expanded
161
- end
162
-
163
- def quote_bound_value(value, c = connection) #:nodoc:
164
- 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)
165
159
  if value.respond_to?(:empty?) && value.empty?
166
160
  c.quote(nil)
167
161
  else
@@ -180,15 +174,8 @@ module ActiveRecord
180
174
  end
181
175
 
182
176
  # TODO: Deprecate this
183
- def quoted_id #:nodoc:
184
- quote_value(id, column_for_attribute(self.class.primary_key))
185
- end
186
-
187
- private
188
-
189
- # Quote strings appropriately for SQL statements.
190
- def quote_value(value, column = nil)
191
- self.class.connection.quote(value, column)
177
+ def quoted_id
178
+ self.class.quote_value(id, column_for_attribute(self.class.primary_key))
192
179
  end
193
180
  end
194
181
  end
@@ -1,4 +1,3 @@
1
- require 'active_support/core_ext/object/blank'
2
1
 
3
2
  module ActiveRecord
4
3
  # = Active Record Schema
@@ -12,16 +11,16 @@ module ActiveRecord
12
11
  #
13
12
  # ActiveRecord::Schema.define do
14
13
  # create_table :authors do |t|
15
- # t.string :name, :null => false
14
+ # t.string :name, null: false
16
15
  # end
17
16
  #
18
17
  # add_index :authors, :name, :unique
19
18
  #
20
19
  # create_table :posts do |t|
21
- # t.integer :author_id, :null => false
20
+ # t.integer :author_id, null: false
22
21
  # t.string :subject
23
22
  # t.text :body
24
- # t.boolean :private, :default => false
23
+ # t.boolean :private, default: false
25
24
  # end
26
25
  #
27
26
  # add_index :posts, :author_id
@@ -30,10 +29,24 @@ module ActiveRecord
30
29
  # ActiveRecord::Schema is only supported by database adapters that also
31
30
  # support migrations, the two features being very similar.
32
31
  class Schema < Migration
32
+
33
+ # Returns the migrations paths.
34
+ #
35
+ # ActiveRecord::Schema.new.migrations_paths
36
+ # # => ["db/migrate"] # Rails migration path by default.
33
37
  def migrations_paths
34
38
  ActiveRecord::Migrator.migrations_paths
35
39
  end
36
40
 
41
+ def define(info, &block) # :nodoc:
42
+ instance_eval(&block)
43
+
44
+ unless info[:version].blank?
45
+ initialize_schema_migrations_table
46
+ connection.assume_migrated_upto_version(info[:version], migrations_paths)
47
+ end
48
+ end
49
+
37
50
  # Eval the given block. All methods available to the current connection
38
51
  # adapter are available within the block, so you can easily use the
39
52
  # database definition DSL to build up your schema (+create_table+,
@@ -42,17 +55,11 @@ module ActiveRecord
42
55
  # The +info+ hash is optional, and if given is used to define metadata
43
56
  # about the current schema (currently, only the schema's version):
44
57
  #
45
- # ActiveRecord::Schema.define(:version => 20380119000001) do
58
+ # ActiveRecord::Schema.define(version: 20380119000001) do
46
59
  # ...
47
60
  # end
48
61
  def self.define(info={}, &block)
49
- schema = new
50
- schema.instance_eval(&block)
51
-
52
- unless info[:version].blank?
53
- initialize_schema_migrations_table
54
- assume_migrated_upto_version(info[:version], schema.migrations_paths)
55
- end
62
+ new.define(info, &block)
56
63
  end
57
64
  end
58
65
  end
@@ -24,6 +24,7 @@ module ActiveRecord
24
24
 
25
25
  def dump(stream)
26
26
  header(stream)
27
+ extensions(stream)
27
28
  tables(stream)
28
29
  trailer(stream)
29
30
  stream
@@ -38,7 +39,7 @@ module ActiveRecord
38
39
  end
39
40
 
40
41
  def header(stream)
41
- define_params = @version ? ":version => #{@version}" : ""
42
+ define_params = @version ? "version: #{@version}" : ""
42
43
 
43
44
  if stream.respond_to?(:external_encoding) && stream.external_encoding
44
45
  stream.puts "# encoding: #{stream.external_encoding.name}"
@@ -55,7 +56,7 @@ module ActiveRecord
55
56
  # from scratch. The latter is a flawed and unsustainable approach (the more migrations
56
57
  # you'll amass, the slower it'll run and the greater likelihood for issues).
57
58
  #
58
- # It's strongly recommended to check this file into your version control system.
59
+ # It's strongly recommended that you check this file into your version control system.
59
60
 
60
61
  ActiveRecord::Schema.define(#{define_params}) do
61
62
 
@@ -66,6 +67,18 @@ HEADER
66
67
  stream.puts "end"
67
68
  end
68
69
 
70
+ def extensions(stream)
71
+ return unless @connection.supports_extensions?
72
+ extensions = @connection.extensions
73
+ if extensions.any?
74
+ stream.puts " # These are extensions that must be enabled in order to support this database"
75
+ extensions.each do |extension|
76
+ stream.puts " enable_extension #{extension.inspect}"
77
+ end
78
+ stream.puts
79
+ end
80
+ end
81
+
69
82
  def tables(stream)
70
83
  @connection.tables.sort.each do |tbl|
71
84
  next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
@@ -93,44 +106,36 @@ HEADER
93
106
  end
94
107
 
95
108
  tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
96
- if columns.detect { |c| c.name == pk }
109
+ pkcol = columns.detect { |c| c.name == pk }
110
+ if pkcol
97
111
  if pk != 'id'
98
- tbl.print %Q(, :primary_key => "#{pk}")
112
+ tbl.print %Q(, primary_key: "#{pk}")
113
+ elsif pkcol.sql_type == 'uuid'
114
+ tbl.print ", id: :uuid"
115
+ tbl.print %Q(, default: "#{pkcol.default_function}") if pkcol.default_function
99
116
  end
100
117
  else
101
- tbl.print ", :id => false"
118
+ tbl.print ", id: false"
102
119
  end
103
- tbl.print ", :force => true"
120
+ tbl.print ", force: true"
104
121
  tbl.puts " do |t|"
105
122
 
106
123
  # then dump all non-primary key columns
107
124
  column_specs = columns.map do |column|
108
- raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
125
+ raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
109
126
  next if column.name == pk
110
- spec = {}
111
- spec[:name] = column.name.inspect
112
-
113
- # AR has an optimization which handles zero-scale decimals as integers. This
114
- # code ensures that the dumper still dumps the column as a decimal.
115
- spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) }
116
- 'decimal'
117
- else
118
- column.type.to_s
119
- end
120
- spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && spec[:type] != 'decimal'
121
- spec[:precision] = column.precision.inspect if column.precision
122
- spec[:scale] = column.scale.inspect if column.scale
123
- spec[:null] = 'false' unless column.null
124
- spec[:default] = default_string(column.default) if column.has_default?
125
- (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
126
- spec
127
+ @connection.column_spec(column, @types)
127
128
  end.compact
128
129
 
129
130
  # find all migration keys used in this table
130
- keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map{ |k| k.keys }.flatten
131
+ keys = @connection.migration_keys
131
132
 
132
133
  # figure out the lengths for each column based on above keys
133
- lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
134
+ lengths = keys.map { |key|
135
+ column_specs.map { |spec|
136
+ spec[key] ? spec[key].length + 2 : 0
137
+ }.max
138
+ }
134
139
 
135
140
  # the string we're going to sprintf our values against, with standardized column widths
136
141
  format_string = lengths.map{ |len| "%-#{len}s" }
@@ -166,32 +171,27 @@ HEADER
166
171
  stream
167
172
  end
168
173
 
169
- def default_string(value)
170
- case value
171
- when BigDecimal
172
- value.to_s
173
- when Date, DateTime, Time
174
- "'" + value.to_s(:db) + "'"
175
- else
176
- value.inspect
177
- end
178
- end
179
-
180
174
  def indexes(table, stream)
181
175
  if (indexes = @connection.indexes(table)).any?
182
176
  add_index_statements = indexes.map do |index|
183
177
  statement_parts = [
184
178
  ('add_index ' + remove_prefix_and_suffix(index.table).inspect),
185
179
  index.columns.inspect,
186
- (':name => ' + index.name.inspect),
180
+ ('name: ' + index.name.inspect),
187
181
  ]
188
- statement_parts << ':unique => true' if index.unique
182
+ statement_parts << 'unique: true' if index.unique
189
183
 
190
184
  index_lengths = (index.lengths || []).compact
191
- statement_parts << (':length => ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
185
+ statement_parts << ('length: ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
192
186
 
193
187
  index_orders = (index.orders || {})
194
- statement_parts << (':order => ' + index.orders.inspect) unless index_orders.empty?
188
+ statement_parts << ('order: ' + index.orders.inspect) unless index_orders.empty?
189
+
190
+ statement_parts << ('where: ' + index.where.inspect) if index.where
191
+
192
+ statement_parts << ('using: ' + index.using.inspect) if index.using
193
+
194
+ statement_parts << ('type: ' + index.type.inspect) if index.type
195
195
 
196
196
  ' ' + statement_parts.join(', ')
197
197
  end
@@ -0,0 +1,46 @@
1
+ require 'active_record/scoping/default'
2
+ require 'active_record/scoping/named'
3
+ require 'active_record/base'
4
+
5
+ module ActiveRecord
6
+ class SchemaMigration < ActiveRecord::Base
7
+ def self.primary_key
8
+ nil
9
+ end
10
+
11
+ def self.table_name
12
+ "#{Base.table_name_prefix}schema_migrations#{Base.table_name_suffix}"
13
+ end
14
+
15
+ def self.index_name
16
+ "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
17
+ end
18
+
19
+ def self.create_table(limit=nil)
20
+ unless connection.table_exists?(table_name)
21
+ version_options = {null: false}
22
+ version_options[:limit] = limit if limit
23
+
24
+ connection.create_table(table_name, id: false) do |t|
25
+ t.column :version, :string, version_options
26
+ end
27
+ connection.add_index table_name, :version, unique: true, name: index_name
28
+ end
29
+ end
30
+
31
+ def self.drop_table
32
+ if connection.table_exists?(table_name)
33
+ connection.remove_index table_name, name: index_name
34
+ connection.drop_table(table_name)
35
+ end
36
+ end
37
+
38
+ def self.normalize_migration_number(number)
39
+ "%.3d" % number.to_i
40
+ end
41
+
42
+ def version
43
+ super.to_i
44
+ end
45
+ end
46
+ end