activerecord 3.0.0 → 4.0.0

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 (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +35 -44
  5. data/examples/performance.rb +110 -100
  6. data/lib/active_record/aggregations.rb +59 -75
  7. data/lib/active_record/associations/alias_tracker.rb +76 -0
  8. data/lib/active_record/associations/association.rb +248 -0
  9. data/lib/active_record/associations/association_scope.rb +135 -0
  10. data/lib/active_record/associations/belongs_to_association.rb +60 -59
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -59
  12. data/lib/active_record/associations/builder/association.rb +108 -0
  13. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  14. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  16. data/lib/active_record/associations/builder/has_many.rb +15 -0
  17. data/lib/active_record/associations/builder/has_one.rb +25 -0
  18. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  19. data/lib/active_record/associations/collection_association.rb +608 -0
  20. data/lib/active_record/associations/collection_proxy.rb +986 -0
  21. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +40 -112
  22. data/lib/active_record/associations/has_many_association.rb +83 -76
  23. data/lib/active_record/associations/has_many_through_association.rb +147 -66
  24. data/lib/active_record/associations/has_one_association.rb +67 -108
  25. data/lib/active_record/associations/has_one_through_association.rb +21 -25
  26. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  27. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  28. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  29. data/lib/active_record/associations/join_dependency.rb +235 -0
  30. data/lib/active_record/associations/join_helper.rb +45 -0
  31. data/lib/active_record/associations/preloader/association.rb +121 -0
  32. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  33. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  35. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  36. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  37. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  38. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  39. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  40. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  41. data/lib/active_record/associations/preloader.rb +178 -0
  42. data/lib/active_record/associations/singular_association.rb +64 -0
  43. data/lib/active_record/associations/through_association.rb +87 -0
  44. data/lib/active_record/associations.rb +512 -1224
  45. data/lib/active_record/attribute_assignment.rb +201 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +49 -12
  47. data/lib/active_record/attribute_methods/dirty.rb +51 -28
  48. data/lib/active_record/attribute_methods/primary_key.rb +94 -22
  49. data/lib/active_record/attribute_methods/query.rb +5 -4
  50. data/lib/active_record/attribute_methods/read.rb +63 -72
  51. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -41
  53. data/lib/active_record/attribute_methods/write.rb +39 -13
  54. data/lib/active_record/attribute_methods.rb +362 -29
  55. data/lib/active_record/autosave_association.rb +132 -75
  56. data/lib/active_record/base.rb +83 -1627
  57. data/lib/active_record/callbacks.rb +69 -47
  58. data/lib/active_record/coders/yaml_column.rb +38 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +411 -138
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +21 -11
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -173
  62. data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -22
  63. data/lib/active_record/connection_adapters/abstract/quoting.rb +82 -25
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +176 -414
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +562 -232
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +281 -53
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  70. data/lib/active_record/connection_adapters/column.rb +318 -0
  71. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  73. data/lib/active_record/connection_adapters/mysql_adapter.rb +365 -450
  74. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  75. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +672 -752
  82. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  83. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +588 -17
  84. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  85. data/lib/active_record/connection_handling.rb +98 -0
  86. data/lib/active_record/core.rb +463 -0
  87. data/lib/active_record/counter_cache.rb +108 -101
  88. data/lib/active_record/dynamic_matchers.rb +131 -0
  89. data/lib/active_record/errors.rb +54 -13
  90. data/lib/active_record/explain.rb +38 -0
  91. data/lib/active_record/explain_registry.rb +30 -0
  92. data/lib/active_record/explain_subscriber.rb +29 -0
  93. data/lib/active_record/fixture_set/file.rb +55 -0
  94. data/lib/active_record/fixtures.rb +703 -785
  95. data/lib/active_record/inheritance.rb +200 -0
  96. data/lib/active_record/integration.rb +60 -0
  97. data/lib/active_record/locale/en.yml +8 -1
  98. data/lib/active_record/locking/optimistic.rb +69 -60
  99. data/lib/active_record/locking/pessimistic.rb +34 -12
  100. data/lib/active_record/log_subscriber.rb +40 -6
  101. data/lib/active_record/migration/command_recorder.rb +164 -0
  102. data/lib/active_record/migration/join_table.rb +15 -0
  103. data/lib/active_record/migration.rb +614 -216
  104. data/lib/active_record/model_schema.rb +345 -0
  105. data/lib/active_record/nested_attributes.rb +248 -119
  106. data/lib/active_record/null_relation.rb +65 -0
  107. data/lib/active_record/persistence.rb +275 -57
  108. data/lib/active_record/query_cache.rb +29 -9
  109. data/lib/active_record/querying.rb +62 -0
  110. data/lib/active_record/railtie.rb +135 -21
  111. data/lib/active_record/railties/console_sandbox.rb +5 -0
  112. data/lib/active_record/railties/controller_runtime.rb +17 -5
  113. data/lib/active_record/railties/databases.rake +249 -359
  114. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  115. data/lib/active_record/readonly_attributes.rb +30 -0
  116. data/lib/active_record/reflection.rb +283 -103
  117. data/lib/active_record/relation/batches.rb +38 -34
  118. data/lib/active_record/relation/calculations.rb +252 -139
  119. data/lib/active_record/relation/delegation.rb +125 -0
  120. data/lib/active_record/relation/finder_methods.rb +182 -188
  121. data/lib/active_record/relation/merger.rb +161 -0
  122. data/lib/active_record/relation/predicate_builder.rb +86 -21
  123. data/lib/active_record/relation/query_methods.rb +917 -134
  124. data/lib/active_record/relation/spawn_methods.rb +53 -92
  125. data/lib/active_record/relation.rb +405 -143
  126. data/lib/active_record/result.rb +67 -0
  127. data/lib/active_record/runtime_registry.rb +17 -0
  128. data/lib/active_record/sanitization.rb +168 -0
  129. data/lib/active_record/schema.rb +20 -14
  130. data/lib/active_record/schema_dumper.rb +55 -46
  131. data/lib/active_record/schema_migration.rb +39 -0
  132. data/lib/active_record/scoping/default.rb +146 -0
  133. data/lib/active_record/scoping/named.rb +175 -0
  134. data/lib/active_record/scoping.rb +82 -0
  135. data/lib/active_record/serialization.rb +8 -46
  136. data/lib/active_record/serializers/xml_serializer.rb +21 -68
  137. data/lib/active_record/statement_cache.rb +26 -0
  138. data/lib/active_record/store.rb +156 -0
  139. data/lib/active_record/tasks/database_tasks.rb +203 -0
  140. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  141. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  142. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  143. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  144. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  145. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  146. data/lib/active_record/test_case.rb +57 -28
  147. data/lib/active_record/timestamp.rb +49 -18
  148. data/lib/active_record/transactions.rb +106 -63
  149. data/lib/active_record/translation.rb +22 -0
  150. data/lib/active_record/validations/associated.rb +25 -24
  151. data/lib/active_record/validations/presence.rb +65 -0
  152. data/lib/active_record/validations/uniqueness.rb +123 -83
  153. data/lib/active_record/validations.rb +29 -29
  154. data/lib/active_record/version.rb +7 -5
  155. data/lib/active_record.rb +83 -34
  156. data/lib/rails/generators/active_record/migration/migration_generator.rb +46 -9
  157. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  158. data/lib/rails/generators/active_record/migration/templates/migration.rb +30 -8
  159. data/lib/rails/generators/active_record/model/model_generator.rb +15 -5
  160. data/lib/rails/generators/active_record/model/templates/model.rb +7 -2
  161. data/lib/rails/generators/active_record/model/templates/module.rb +3 -1
  162. data/lib/rails/generators/active_record.rb +4 -8
  163. metadata +163 -121
  164. data/CHANGELOG +0 -6023
  165. data/examples/associations.png +0 -0
  166. data/lib/active_record/association_preload.rb +0 -403
  167. data/lib/active_record/associations/association_collection.rb +0 -562
  168. data/lib/active_record/associations/association_proxy.rb +0 -295
  169. data/lib/active_record/associations/through_association_scope.rb +0 -154
  170. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -113
  171. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -401
  172. data/lib/active_record/dynamic_finder_match.rb +0 -53
  173. data/lib/active_record/dynamic_scope_match.rb +0 -32
  174. data/lib/active_record/named_scope.rb +0 -138
  175. data/lib/active_record/observer.rb +0 -140
  176. data/lib/active_record/session_store.rb +0 -340
  177. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -16
  178. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  179. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -2
  180. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -24
  181. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -1,4 +1,3 @@
1
- require 'active_support/core_ext/object/blank'
2
1
  require 'date'
3
2
  require 'set'
4
3
  require 'bigdecimal'
@@ -6,302 +5,40 @@ require 'bigdecimal/util'
6
5
 
7
6
  module ActiveRecord
8
7
  module ConnectionAdapters #:nodoc:
9
- # An abstract definition of a column in a table.
10
- class Column
11
- TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
12
- FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set
13
-
14
- module Format
15
- ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
16
- ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
17
- end
18
-
19
- attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale
20
- attr_accessor :primary
21
-
22
- # Instantiates a new column in the table.
23
- #
24
- # +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>.
25
- # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
26
- # +sql_type+ is used to extract the column's length, if necessary. For example +60+ in
27
- # <tt>company_name varchar(60)</tt>.
28
- # It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
29
- # +null+ determines if this column allows +NULL+ values.
30
- def initialize(name, default, sql_type = nil, null = true)
31
- @name, @sql_type, @null = name, sql_type, null
32
- @limit, @precision, @scale = extract_limit(sql_type), extract_precision(sql_type), extract_scale(sql_type)
33
- @type = simplified_type(sql_type)
34
- @default = extract_default(default)
35
-
36
- @primary = nil
37
- end
38
-
39
- # Returns +true+ if the column is either of type string or text.
40
- def text?
41
- type == :string || type == :text
42
- end
43
-
44
- # Returns +true+ if the column is either of type integer, float or decimal.
45
- def number?
46
- type == :integer || type == :float || type == :decimal
47
- end
48
-
49
- def has_default?
50
- !default.nil?
51
- end
52
-
53
- # Returns the Ruby class that corresponds to the abstract data type.
54
- def klass
55
- case type
56
- when :integer then Fixnum
57
- when :float then Float
58
- when :decimal then BigDecimal
59
- when :datetime then Time
60
- when :date then Date
61
- when :timestamp then Time
62
- when :time then Time
63
- when :text, :string then String
64
- when :binary then String
65
- when :boolean then Object
66
- end
67
- end
68
-
69
- # Casts value (which is a String) to an appropriate instance.
70
- def type_cast(value)
71
- return nil if value.nil?
72
- case type
73
- when :string then value
74
- when :text then value
75
- when :integer then value.to_i rescue value ? 1 : 0
76
- when :float then value.to_f
77
- when :decimal then self.class.value_to_decimal(value)
78
- when :datetime then self.class.string_to_time(value)
79
- when :timestamp then self.class.string_to_time(value)
80
- when :time then self.class.string_to_dummy_time(value)
81
- when :date then self.class.string_to_date(value)
82
- when :binary then self.class.binary_to_string(value)
83
- when :boolean then self.class.value_to_boolean(value)
84
- else value
85
- end
86
- end
87
-
88
- def type_cast_code(var_name)
89
- case type
90
- when :string then nil
91
- when :text then nil
92
- when :integer then "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)"
93
- when :float then "#{var_name}.to_f"
94
- when :decimal then "#{self.class.name}.value_to_decimal(#{var_name})"
95
- when :datetime then "#{self.class.name}.string_to_time(#{var_name})"
96
- when :timestamp then "#{self.class.name}.string_to_time(#{var_name})"
97
- when :time then "#{self.class.name}.string_to_dummy_time(#{var_name})"
98
- when :date then "#{self.class.name}.string_to_date(#{var_name})"
99
- when :binary then "#{self.class.name}.binary_to_string(#{var_name})"
100
- when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})"
101
- else nil
102
- end
103
- end
104
-
105
- # Returns the human name of the column name.
106
- #
107
- # ===== Examples
108
- # Column.new('sales_stage', ...).human_name # => 'Sales stage'
109
- def human_name
110
- Base.human_attribute_name(@name)
111
- end
112
-
113
- def extract_default(default)
114
- type_cast(default)
115
- end
116
-
117
- class << self
118
- # Used to convert from Strings to BLOBs
119
- def string_to_binary(value)
120
- value
121
- end
122
-
123
- # Used to convert from BLOBs to Strings
124
- def binary_to_string(value)
125
- value
126
- end
127
-
128
- def string_to_date(string)
129
- return string unless string.is_a?(String)
130
- return nil if string.empty?
131
-
132
- fast_string_to_date(string) || fallback_string_to_date(string)
133
- end
134
-
135
- def string_to_time(string)
136
- return string unless string.is_a?(String)
137
- return nil if string.empty?
138
-
139
- fast_string_to_time(string) || fallback_string_to_time(string)
140
- end
141
-
142
- def string_to_dummy_time(string)
143
- return string unless string.is_a?(String)
144
- return nil if string.empty?
145
-
146
- string_to_time "2000-01-01 #{string}"
147
- end
148
-
149
- # convert something to a boolean
150
- def value_to_boolean(value)
151
- if value.is_a?(String) && value.blank?
152
- nil
153
- else
154
- TRUE_VALUES.include?(value)
155
- end
156
- end
157
-
158
- # convert something to a BigDecimal
159
- def value_to_decimal(value)
160
- # Using .class is faster than .is_a? and
161
- # subclasses of BigDecimal will be handled
162
- # in the else clause
163
- if value.class == BigDecimal
164
- value
165
- elsif value.respond_to?(:to_d)
166
- value.to_d
167
- else
168
- value.to_s.to_d
169
- end
170
- end
171
-
172
- protected
173
- # '0.123456' -> 123456
174
- # '1.123456' -> 123456
175
- def microseconds(time)
176
- ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
177
- end
178
-
179
- def new_date(year, mon, mday)
180
- if year && year != 0
181
- Date.new(year, mon, mday) rescue nil
182
- end
183
- end
184
-
185
- def new_time(year, mon, mday, hour, min, sec, microsec)
186
- # Treat 0000-00-00 00:00:00 as nil.
187
- return nil if year.nil? || year == 0
188
-
189
- Time.time_with_datetime_fallback(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
190
- end
191
-
192
- def fast_string_to_date(string)
193
- if string =~ Format::ISO_DATE
194
- new_date $1.to_i, $2.to_i, $3.to_i
195
- end
196
- end
197
-
198
- # Doesn't handle time zones.
199
- def fast_string_to_time(string)
200
- if string =~ Format::ISO_DATETIME
201
- microsec = ($7.to_f * 1_000_000).to_i
202
- new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
203
- end
204
- end
205
-
206
- def fallback_string_to_date(string)
207
- new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
208
- end
209
-
210
- def fallback_string_to_time(string)
211
- time_hash = Date._parse(string)
212
- time_hash[:sec_fraction] = microseconds(time_hash)
213
-
214
- new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
215
- end
216
- end
217
-
218
- private
219
- def extract_limit(sql_type)
220
- $1.to_i if sql_type =~ /\((.*)\)/
221
- end
222
-
223
- def extract_precision(sql_type)
224
- $2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i
225
- end
226
-
227
- def extract_scale(sql_type)
228
- case sql_type
229
- when /^(numeric|decimal|number)\((\d+)\)/i then 0
230
- when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i
231
- end
232
- end
233
-
234
- def simplified_type(field_type)
235
- case field_type
236
- when /int/i
237
- :integer
238
- when /float|double/i
239
- :float
240
- when /decimal|numeric|number/i
241
- extract_scale(field_type) == 0 ? :integer : :decimal
242
- when /datetime/i
243
- :datetime
244
- when /timestamp/i
245
- :timestamp
246
- when /time/i
247
- :time
248
- when /date/i
249
- :date
250
- when /clob/i, /text/i
251
- :text
252
- when /blob/i, /binary/i
253
- :binary
254
- when /char/i, /string/i
255
- :string
256
- when /boolean/i
257
- :boolean
258
- end
259
- end
260
- end
261
-
262
- class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc:
8
+ # Abstract representation of an index definition on a table. Instances of
9
+ # this type are typically created and returned by methods in database
10
+ # adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
11
+ class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using) #:nodoc:
263
12
  end
264
13
 
265
14
  # Abstract representation of a column definition. Instances of this type
266
15
  # are typically created by methods in TableDefinition, and added to the
267
16
  # +columns+ attribute of said TableDefinition object, in order to be used
268
17
  # for generating a number of table creation or table changing SQL statements.
269
- class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
270
-
271
- def sql_type
272
- base.type_to_sql(type.to_sym, limit, precision, scale) rescue type
18
+ class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key) #:nodoc:
19
+ def string_to_binary(value)
20
+ value
273
21
  end
274
22
 
275
- def to_sql
276
- column_sql = "#{base.quote_column_name(name)} #{sql_type}"
277
- column_options = {}
278
- column_options[:null] = null unless null.nil?
279
- column_options[:default] = default unless default.nil?
280
- add_column_options!(column_sql, column_options) unless type.to_sym == :primary_key
281
- column_sql
23
+ def primary_key?
24
+ primary_key || type.to_sym == :primary_key
282
25
  end
283
-
284
- private
285
-
286
- def add_column_options!(sql, options)
287
- base.add_column_options!(sql, options.merge(:column => self))
288
- end
289
26
  end
290
27
 
291
28
  # Represents the schema of an SQL table in an abstract way. This class
292
29
  # provides methods for manipulating the schema representation.
293
30
  #
294
- # Inside migration files, the +t+ object in +create_table+ and
295
- # +change_table+ is actually of this type:
31
+ # Inside migration files, the +t+ object in +create_table+
32
+ # is actually of this type:
296
33
  #
297
34
  # class SomeMigration < ActiveRecord::Migration
298
- # def self.up
35
+ # def up
299
36
  # create_table :foo do |t|
300
37
  # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
301
38
  # end
302
39
  # end
303
40
  #
304
- # def self.down
41
+ # def down
305
42
  # ...
306
43
  # end
307
44
  # end
@@ -311,39 +48,29 @@ module ActiveRecord
311
48
  class TableDefinition
312
49
  # An array of ColumnDefinition objects, representing the column changes
313
50
  # that have been defined.
314
- attr_accessor :columns
51
+ attr_accessor :indexes
52
+ attr_reader :name, :temporary, :options
315
53
 
316
- def initialize(base)
317
- @columns = []
318
- @base = base
54
+ def initialize(types, name, temporary, options)
55
+ @columns_hash = {}
56
+ @indexes = {}
57
+ @native = types
58
+ @temporary = temporary
59
+ @options = options
60
+ @name = name
319
61
  end
320
62
 
321
- #Handles non supported datatypes - e.g. XML
322
- def method_missing(symbol, *args)
323
- if symbol.to_s == 'xml'
324
- xml_column_fallback(args)
325
- else
326
- super
327
- end
328
- end
329
-
330
- def xml_column_fallback(*args)
331
- case @base.adapter_name.downcase
332
- when 'sqlite', 'mysql'
333
- options = args.extract_options!
334
- column(args[0], :text, options)
335
- end
336
- end
63
+ def columns; @columns_hash.values; end
337
64
 
338
65
  # Appends a primary key definition to the table definition.
339
66
  # Can be called multiple times, but this is probably not a good idea.
340
- def primary_key(name)
341
- column(name, :primary_key)
67
+ def primary_key(name, type = :primary_key, options = {})
68
+ column(name, type, options.merge(:primary_key => true))
342
69
  end
343
70
 
344
71
  # Returns a ColumnDefinition for the column with name +name+.
345
72
  def [](name)
346
- @columns.find {|column| column.name.to_s == name.to_s}
73
+ @columns_hash[name.to_s]
347
74
  end
348
75
 
349
76
  # Instantiates a new column for the table.
@@ -361,7 +88,7 @@ module ActiveRecord
361
88
  # Available options are (none of these exists by default):
362
89
  # * <tt>:limit</tt> -
363
90
  # Requests a maximum column length. This is number of characters for <tt>:string</tt> and
364
- # <tt>:text</tt> columns and number of bytes for :binary and :integer columns.
91
+ # <tt>:text</tt> columns and number of bytes for <tt>:binary</tt> and <tt>:integer</tt> columns.
365
92
  # * <tt>:default</tt> -
366
93
  # The column's default value. Use nil for NULL.
367
94
  # * <tt>:null</tt> -
@@ -410,25 +137,25 @@ module ActiveRecord
410
137
  # This method returns <tt>self</tt>.
411
138
  #
412
139
  # == Examples
413
- # # Assuming td is an instance of TableDefinition
140
+ # # Assuming +td+ is an instance of TableDefinition
414
141
  # td.column(:granted, :boolean)
415
142
  # # granted BOOLEAN
416
143
  #
417
- # td.column(:picture, :binary, :limit => 2.megabytes)
144
+ # td.column(:picture, :binary, limit: 2.megabytes)
418
145
  # # => picture BLOB(2097152)
419
146
  #
420
- # td.column(:sales_stage, :string, :limit => 20, :default => 'new', :null => false)
147
+ # td.column(:sales_stage, :string, limit: 20, default: 'new', null: false)
421
148
  # # => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
422
149
  #
423
- # td.column(:bill_gates_money, :decimal, :precision => 15, :scale => 2)
150
+ # td.column(:bill_gates_money, :decimal, precision: 15, scale: 2)
424
151
  # # => bill_gates_money DECIMAL(15,2)
425
152
  #
426
- # td.column(:sensor_reading, :decimal, :precision => 30, :scale => 20)
153
+ # td.column(:sensor_reading, :decimal, precision: 30, scale: 20)
427
154
  # # => sensor_reading DECIMAL(30,20)
428
155
  #
429
156
  # # While <tt>:scale</tt> defaults to zero on most databases, it
430
157
  # # probably wouldn't hurt to include it.
431
- # td.column(:huge_integer, :decimal, :precision => 30)
158
+ # td.column(:huge_integer, :decimal, precision: 30)
432
159
  # # => huge_integer DECIMAL(30)
433
160
  #
434
161
  # # Defines a column with a database-specific type.
@@ -443,67 +170,76 @@ module ActiveRecord
443
170
  #
444
171
  # What can be written like this with the regular calls to column:
445
172
  #
446
- # create_table "products", :force => true do |t|
447
- # t.column "shop_id", :integer
448
- # t.column "creator_id", :integer
449
- # t.column "name", :string, :default => "Untitled"
450
- # t.column "value", :string, :default => "Untitled"
451
- # t.column "created_at", :datetime
452
- # t.column "updated_at", :datetime
173
+ # create_table :products do |t|
174
+ # t.column :shop_id, :integer
175
+ # t.column :creator_id, :integer
176
+ # t.column :name, :string, default: "Untitled"
177
+ # t.column :value, :string, default: "Untitled"
178
+ # t.column :created_at, :datetime
179
+ # t.column :updated_at, :datetime
453
180
  # end
454
181
  #
455
- # Can also be written as follows using the short-hand:
182
+ # can also be written as follows using the short-hand:
456
183
  #
457
184
  # create_table :products do |t|
458
185
  # t.integer :shop_id, :creator_id
459
- # t.string :name, :value, :default => "Untitled"
186
+ # t.string :name, :value, default: "Untitled"
460
187
  # t.timestamps
461
188
  # end
462
189
  #
463
190
  # There's a short-hand method for each of the type values declared at the top. And then there's
464
- # TableDefinition#timestamps that'll add created_at and +updated_at+ as datetimes.
191
+ # TableDefinition#timestamps that'll add +created_at+ and +updated_at+ as datetimes.
465
192
  #
466
193
  # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
467
194
  # column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of
468
- # options, these will be used when creating the <tt>_type</tt> column. So what can be written like this:
195
+ # options, these will be used when creating the <tt>_type</tt> column. The <tt>:index</tt> option
196
+ # will also create an index, similar to calling <tt>add_index</tt>. So what can be written like this:
469
197
  #
470
198
  # create_table :taggings do |t|
471
199
  # t.integer :tag_id, :tagger_id, :taggable_id
472
200
  # t.string :tagger_type
473
- # t.string :taggable_type, :default => 'Photo'
201
+ # t.string :taggable_type, default: 'Photo'
474
202
  # end
203
+ # add_index :taggings, :tag_id, name: 'index_taggings_on_tag_id'
204
+ # add_index :taggings, [:tagger_id, :tagger_type]
475
205
  #
476
206
  # Can also be written as follows using references:
477
207
  #
478
208
  # create_table :taggings do |t|
479
- # t.references :tag
480
- # t.references :tagger, :polymorphic => true
481
- # t.references :taggable, :polymorphic => { :default => 'Photo' }
209
+ # t.references :tag, index: { name: 'index_taggings_on_tag_id' }
210
+ # t.references :tagger, polymorphic: true, index: true
211
+ # t.references :taggable, polymorphic: { default: 'Photo' }
482
212
  # end
483
213
  def column(name, type, options = {})
484
- column = self[name] || ColumnDefinition.new(@base, name, type)
485
- if options[:limit]
486
- column.limit = options[:limit]
487
- elsif native[type.to_sym].is_a?(Hash)
488
- column.limit = native[type.to_sym][:limit]
214
+ name = name.to_s
215
+ type = type.to_sym
216
+
217
+ if primary_key_column_name == name
218
+ raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
489
219
  end
490
- column.precision = options[:precision]
491
- column.scale = options[:scale]
492
- column.default = options[:default]
493
- column.null = options[:null]
494
- @columns << column unless @columns.include? column
220
+
221
+ @columns_hash[name] = new_column_definition(name, type, options)
495
222
  self
496
223
  end
497
224
 
498
- %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
499
- class_eval <<-EOV, __FILE__, __LINE__ + 1
500
- def #{column_type}(*args) # def string(*args)
501
- options = args.extract_options! # options = args.extract_options!
502
- column_names = args # column_names = args
503
- #
504
- column_names.each { |name| column(name, '#{column_type}', options) } # column_names.each { |name| column(name, 'string', options) }
505
- end # end
506
- EOV
225
+ def remove_column(name)
226
+ @columns_hash.delete name.to_s
227
+ end
228
+
229
+ [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
230
+ define_method column_type do |*args|
231
+ options = args.extract_options!
232
+ column_names = args
233
+ column_names.each { |name| column(name, column_type, options) }
234
+ end
235
+ end
236
+
237
+ # Adds index options to the indexes hash, keyed by column name
238
+ # This is primarily used to track indexes that need to be created after the table
239
+ #
240
+ # index(:account_id, name: 'index_projects_on_account_id')
241
+ def index(column_name, options = {})
242
+ indexes[column_name] = options
507
243
  end
508
244
 
509
245
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
@@ -517,24 +253,63 @@ module ActiveRecord
517
253
  def references(*args)
518
254
  options = args.extract_options!
519
255
  polymorphic = options.delete(:polymorphic)
256
+ index_options = options.delete(:index)
520
257
  args.each do |col|
521
258
  column("#{col}_id", :integer, options)
522
- column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
259
+ column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
260
+ index(polymorphic ? %w(id type).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
523
261
  end
524
262
  end
525
263
  alias :belongs_to :references
526
264
 
527
- # Returns a String whose contents are the column definitions
528
- # concatenated together. This string can then be prepended and appended to
529
- # to generate the final SQL to create the table.
530
- def to_sql
531
- @columns.map { |c| c.to_sql } * ', '
265
+ def new_column_definition(name, type, options) # :nodoc:
266
+ column = create_column_definition name, type
267
+ limit = options.fetch(:limit) do
268
+ native[type][:limit] if native[type].is_a?(Hash)
269
+ end
270
+
271
+ column.limit = limit
272
+ column.array = options[:array] if column.respond_to?(:array)
273
+ column.precision = options[:precision]
274
+ column.scale = options[:scale]
275
+ column.default = options[:default]
276
+ column.null = options[:null]
277
+ column.first = options[:first]
278
+ column.after = options[:after]
279
+ column.primary_key = type == :primary_key || options[:primary_key]
280
+ column
532
281
  end
533
282
 
534
283
  private
535
- def native
536
- @base.native_database_types
537
- end
284
+ def create_column_definition(name, type)
285
+ ColumnDefinition.new name, type
286
+ end
287
+
288
+ def primary_key_column_name
289
+ primary_key_column = columns.detect { |c| c.primary_key? }
290
+ primary_key_column && primary_key_column.name
291
+ end
292
+
293
+ def native
294
+ @native
295
+ end
296
+ end
297
+
298
+ class AlterTable # :nodoc:
299
+ attr_reader :adds
300
+
301
+ def initialize(td)
302
+ @td = td
303
+ @adds = []
304
+ end
305
+
306
+ def name; @td.name; end
307
+
308
+ def add_column(name, type, options)
309
+ name = name.to_s
310
+ type = type.to_sym
311
+ @adds << @td.new_column_definition(name, type, options)
312
+ end
538
313
  end
539
314
 
540
315
  # Represents an SQL table in an abstract way for updating a table.
@@ -545,6 +320,7 @@ module ActiveRecord
545
320
  # change_table :table do |t|
546
321
  # t.column
547
322
  # t.index
323
+ # t.rename_index
548
324
  # t.timestamps
549
325
  # t.change
550
326
  # t.change_default
@@ -577,7 +353,7 @@ module ActiveRecord
577
353
 
578
354
  # Adds a new column to the named table.
579
355
  # See TableDefinition#column for details of the options you can use.
580
- # ===== Example
356
+ #
581
357
  # ====== Creating a simple column
582
358
  # t.column(:name, :string)
583
359
  def column(column_name, type, options = {})
@@ -585,20 +361,19 @@ module ActiveRecord
585
361
  end
586
362
 
587
363
  # Checks to see if a column exists. See SchemaStatements#column_exists?
588
- def column_exists?(column_name, type = nil, options = nil)
364
+ def column_exists?(column_name, type = nil, options = {})
589
365
  @base.column_exists?(@table_name, column_name, type, options)
590
366
  end
591
367
 
592
368
  # Adds a new index to the table. +column_name+ can be a single Symbol, or
593
369
  # an Array of Symbols. See SchemaStatements#add_index
594
370
  #
595
- # ===== Examples
596
371
  # ====== Creating a simple index
597
372
  # t.index(:name)
598
373
  # ====== Creating a unique index
599
- # t.index([:branch_id, :party_id], :unique => true)
374
+ # t.index([:branch_id, :party_id], unique: true)
600
375
  # ====== Creating a named index
601
- # t.index([:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
376
+ # t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party')
602
377
  def index(column_name, options = {})
603
378
  @base.add_index(@table_name, column_name, options)
604
379
  end
@@ -608,8 +383,15 @@ module ActiveRecord
608
383
  @base.index_exists?(@table_name, column_name, options)
609
384
  end
610
385
 
611
- # Adds timestamps (created_at and updated_at) columns to the table. See SchemaStatements#add_timestamps
612
- # ===== Example
386
+ # Renames the given index on the table.
387
+ #
388
+ # t.rename_index(:user_id, :account_id)
389
+ def rename_index(index_name, new_index_name)
390
+ @base.rename_index(@table_name, index_name, new_index_name)
391
+ end
392
+
393
+ # Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps
394
+ #
613
395
  # t.timestamps
614
396
  def timestamps
615
397
  @base.add_timestamps(@table_name)
@@ -617,15 +399,15 @@ module ActiveRecord
617
399
 
618
400
  # Changes the column's definition according to the new options.
619
401
  # See TableDefinition#column for details of the options you can use.
620
- # ===== Examples
621
- # t.change(:name, :string, :limit => 80)
402
+ #
403
+ # t.change(:name, :string, limit: 80)
622
404
  # t.change(:description, :text)
623
405
  def change(column_name, type, options = {})
624
406
  @base.change_column(@table_name, column_name, type, options)
625
407
  end
626
408
 
627
409
  # Sets a new default value for a column. See SchemaStatements#change_column_default
628
- # ===== Examples
410
+ #
629
411
  # t.change_default(:qualification, 'new')
630
412
  # t.change_default(:authorized, 1)
631
413
  def change_default(column_name, default)
@@ -633,99 +415,80 @@ module ActiveRecord
633
415
  end
634
416
 
635
417
  # Removes the column(s) from the table definition.
636
- # ===== Examples
418
+ #
637
419
  # t.remove(:qualification)
638
420
  # t.remove(:qualification, :experience)
639
421
  def remove(*column_names)
640
- @base.remove_column(@table_name, column_names)
422
+ @base.remove_columns(@table_name, *column_names)
641
423
  end
642
424
 
643
425
  # Removes the given index from the table.
644
426
  #
645
- # ===== Examples
646
- # ====== Remove the suppliers_name_index in the suppliers table
647
- # t.remove_index :name
648
- # ====== Remove the index named accounts_branch_id_index in the accounts table
649
- # t.remove_index :column => :branch_id
650
- # ====== Remove the index named accounts_branch_id_party_id_index in the accounts table
651
- # t.remove_index :column => [:branch_id, :party_id]
652
- # ====== Remove the index named by_branch_party in the accounts table
653
- # t.remove_index :name => :by_branch_party
427
+ # ====== Remove the index_table_name_on_column in the table_name table
428
+ # t.remove_index :column
429
+ # ====== Remove the index named index_table_name_on_branch_id in the table_name table
430
+ # t.remove_index column: :branch_id
431
+ # ====== Remove the index named index_table_name_on_branch_id_and_party_id in the table_name table
432
+ # t.remove_index column: [:branch_id, :party_id]
433
+ # ====== Remove the index named by_branch_party in the table_name table
434
+ # t.remove_index name: :by_branch_party
654
435
  def remove_index(options = {})
655
436
  @base.remove_index(@table_name, options)
656
437
  end
657
438
 
658
- # Removes the timestamp columns (created_at and updated_at) from the table.
659
- # ===== Example
439
+ # Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
440
+ #
660
441
  # t.remove_timestamps
661
442
  def remove_timestamps
662
443
  @base.remove_timestamps(@table_name)
663
444
  end
664
445
 
665
446
  # Renames a column.
666
- # ===== Example
447
+ #
667
448
  # t.rename(:description, :name)
668
449
  def rename(column_name, new_column_name)
669
450
  @base.rename_column(@table_name, column_name, new_column_name)
670
451
  end
671
452
 
672
- # Adds a reference. Optionally adds a +type+ column.
453
+ # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
673
454
  # <tt>references</tt> and <tt>belongs_to</tt> are acceptable.
674
- # ===== Examples
675
- # t.references(:goat)
676
- # t.references(:goat, :polymorphic => true)
677
- # t.belongs_to(:goat)
455
+ #
456
+ # t.references(:user)
457
+ # t.belongs_to(:supplier, polymorphic: true)
458
+ #
678
459
  def references(*args)
679
460
  options = args.extract_options!
680
- polymorphic = options.delete(:polymorphic)
681
- args.each do |col|
682
- @base.add_column(@table_name, "#{col}_id", :integer, options)
683
- @base.add_column(@table_name, "#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
461
+ args.each do |ref_name|
462
+ @base.add_reference(@table_name, ref_name, options)
684
463
  end
685
464
  end
686
465
  alias :belongs_to :references
687
466
 
688
467
  # Removes a reference. Optionally removes a +type+ column.
689
468
  # <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
690
- # ===== Examples
691
- # t.remove_references(:goat)
692
- # t.remove_references(:goat, :polymorphic => true)
693
- # t.remove_belongs_to(:goat)
469
+ #
470
+ # t.remove_references(:user)
471
+ # t.remove_belongs_to(:supplier, polymorphic: true)
472
+ #
694
473
  def remove_references(*args)
695
474
  options = args.extract_options!
696
- polymorphic = options.delete(:polymorphic)
697
- args.each do |col|
698
- @base.remove_column(@table_name, "#{col}_id")
699
- @base.remove_column(@table_name, "#{col}_type") unless polymorphic.nil?
475
+ args.each do |ref_name|
476
+ @base.remove_reference(@table_name, ref_name, options)
700
477
  end
701
478
  end
702
- alias :remove_belongs_to :remove_references
479
+ alias :remove_belongs_to :remove_references
703
480
 
704
481
  # Adds a column or columns of a specified type
705
- # ===== Examples
482
+ #
706
483
  # t.string(:goat)
707
484
  # t.string(:goat, :sheep)
708
- %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
709
- class_eval <<-EOV, __FILE__, __LINE__ + 1
710
- def #{column_type}(*args) # def string(*args)
711
- options = args.extract_options! # options = args.extract_options!
712
- column_names = args # column_names = args
713
- #
714
- column_names.each do |name| # column_names.each do |name|
715
- column = ColumnDefinition.new(@base, name, '#{column_type}') # column = ColumnDefinition.new(@base, name, 'string')
716
- if options[:limit] # if options[:limit]
717
- column.limit = options[:limit] # column.limit = options[:limit]
718
- elsif native['#{column_type}'.to_sym].is_a?(Hash) # elsif native['string'.to_sym].is_a?(Hash)
719
- column.limit = native['#{column_type}'.to_sym][:limit] # column.limit = native['string'.to_sym][:limit]
720
- end # end
721
- column.precision = options[:precision] # column.precision = options[:precision]
722
- column.scale = options[:scale] # column.scale = options[:scale]
723
- column.default = options[:default] # column.default = options[:default]
724
- column.null = options[:null] # column.null = options[:null]
725
- @base.add_column(@table_name, name, column.sql_type, options) # @base.add_column(@table_name, name, column.sql_type, options)
726
- end # end
727
- end # end
728
- EOV
485
+ [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
486
+ define_method column_type do |*args|
487
+ options = args.extract_options!
488
+ args.each do |name|
489
+ @base.add_column(@table_name, name, column_type, options)
490
+ end
491
+ end
729
492
  end
730
493
 
731
494
  private
@@ -736,4 +499,3 @@ module ActiveRecord
736
499
 
737
500
  end
738
501
  end
739
-