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
@@ -1,583 +0,0 @@
1
- require 'active_record/connection_adapters/abstract_adapter'
2
- require 'active_record/connection_adapters/statement_pool'
3
- require 'active_support/core_ext/string/encoding'
4
- require 'arel/visitors/bind_visitor'
5
-
6
- module ActiveRecord
7
- module ConnectionAdapters #:nodoc:
8
- class SQLiteColumn < Column #:nodoc:
9
- class << self
10
- def binary_to_string(value)
11
- if value.respond_to?(:force_encoding) && value.encoding != Encoding::ASCII_8BIT
12
- value = value.force_encoding(Encoding::ASCII_8BIT)
13
- end
14
- value
15
- end
16
- end
17
- end
18
-
19
- # The SQLite adapter works with both the 2.x and 3.x series of SQLite with the sqlite-ruby
20
- # drivers (available both as gems and from http://rubyforge.org/projects/sqlite-ruby/).
21
- #
22
- # Options:
23
- #
24
- # * <tt>:database</tt> - Path to the database file.
25
- class SQLiteAdapter < AbstractAdapter
26
- class Version
27
- include Comparable
28
-
29
- def initialize(version_string)
30
- @version = version_string.split('.').map { |v| v.to_i }
31
- end
32
-
33
- def <=>(version_string)
34
- @version <=> version_string.split('.').map { |v| v.to_i }
35
- end
36
- end
37
-
38
- class StatementPool < ConnectionAdapters::StatementPool
39
- def initialize(connection, max)
40
- super
41
- @cache = Hash.new { |h,pid| h[pid] = {} }
42
- end
43
-
44
- def each(&block); cache.each(&block); end
45
- def key?(key); cache.key?(key); end
46
- def [](key); cache[key]; end
47
- def length; cache.length; end
48
-
49
- def []=(sql, key)
50
- while @max <= cache.size
51
- dealloc(cache.shift.last[:stmt])
52
- end
53
- cache[sql] = key
54
- end
55
-
56
- def clear
57
- cache.values.each do |hash|
58
- dealloc hash[:stmt]
59
- end
60
- cache.clear
61
- end
62
-
63
- private
64
- def cache
65
- @cache[$$]
66
- end
67
-
68
- def dealloc(stmt)
69
- stmt.close unless stmt.closed?
70
- end
71
- end
72
-
73
- class BindSubstitution < Arel::Visitors::SQLite # :nodoc:
74
- include Arel::Visitors::BindVisitor
75
- end
76
-
77
- def initialize(connection, logger, config)
78
- super(connection, logger)
79
- @statements = StatementPool.new(@connection,
80
- config.fetch(:statement_limit) { 1000 })
81
- @config = config
82
-
83
- if config.fetch(:prepared_statements) { true }
84
- @visitor = Arel::Visitors::SQLite.new self
85
- else
86
- @visitor = BindSubstitution.new self
87
- end
88
- end
89
-
90
- def adapter_name #:nodoc:
91
- 'SQLite'
92
- end
93
-
94
- # Returns true if SQLite version is '2.0.0' or greater, false otherwise.
95
- def supports_ddl_transactions?
96
- sqlite_version >= '2.0.0'
97
- end
98
-
99
- # Returns true if SQLite version is '3.6.8' or greater, false otherwise.
100
- def supports_savepoints?
101
- sqlite_version >= '3.6.8'
102
- end
103
-
104
- # Returns true, since this connection adapter supports prepared statement
105
- # caching.
106
- def supports_statement_cache?
107
- true
108
- end
109
-
110
- # Returns true, since this connection adapter supports migrations.
111
- def supports_migrations? #:nodoc:
112
- true
113
- end
114
-
115
- # Returns true.
116
- def supports_primary_key? #:nodoc:
117
- true
118
- end
119
-
120
- # Returns true.
121
- def supports_explain?
122
- true
123
- end
124
-
125
- def requires_reloading?
126
- true
127
- end
128
-
129
- # Returns true if SQLite version is '3.1.6' or greater, false otherwise.
130
- def supports_add_column?
131
- sqlite_version >= '3.1.6'
132
- end
133
-
134
- # Disconnects from the database if already connected. Otherwise, this
135
- # method does nothing.
136
- def disconnect!
137
- super
138
- clear_cache!
139
- @connection.close rescue nil
140
- end
141
-
142
- # Clears the prepared statements cache.
143
- def clear_cache!
144
- @statements.clear
145
- end
146
-
147
- # Returns true if SQLite version is '3.2.6' or greater, false otherwise.
148
- def supports_count_distinct? #:nodoc:
149
- sqlite_version >= '3.2.6'
150
- end
151
-
152
- # Returns true if SQLite version is '3.1.0' or greater, false otherwise.
153
- def supports_autoincrement? #:nodoc:
154
- sqlite_version >= '3.1.0'
155
- end
156
-
157
- def supports_index_sort_order?
158
- sqlite_version >= '3.3.0'
159
- end
160
-
161
- def native_database_types #:nodoc:
162
- {
163
- :primary_key => default_primary_key_type,
164
- :string => { :name => "varchar", :limit => 255 },
165
- :text => { :name => "text" },
166
- :integer => { :name => "integer" },
167
- :float => { :name => "float" },
168
- :decimal => { :name => "decimal" },
169
- :datetime => { :name => "datetime" },
170
- :timestamp => { :name => "datetime" },
171
- :time => { :name => "time" },
172
- :date => { :name => "date" },
173
- :binary => { :name => "blob" },
174
- :boolean => { :name => "boolean" }
175
- }
176
- end
177
-
178
-
179
- # QUOTING ==================================================
180
-
181
- def quote_string(s) #:nodoc:
182
- @connection.class.quote(s)
183
- end
184
-
185
- def quote_column_name(name) #:nodoc:
186
- %Q("#{name.to_s.gsub('"', '""')}")
187
- end
188
-
189
- # Quote date/time values for use in SQL input. Includes microseconds
190
- # if the value is a Time responding to usec.
191
- def quoted_date(value) #:nodoc:
192
- if value.respond_to?(:usec)
193
- "#{super}.#{sprintf("%06d", value.usec)}"
194
- else
195
- super
196
- end
197
- end
198
-
199
- if "<3".encoding_aware?
200
- def type_cast(value, column) # :nodoc:
201
- return value.to_f if BigDecimal === value
202
- return super unless String === value
203
- return super unless column && value
204
-
205
- value = super
206
- if column.type == :string && value.encoding == Encoding::ASCII_8BIT
207
- logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
208
- value = value.encode Encoding::UTF_8
209
- end
210
- value
211
- end
212
- else
213
- def type_cast(value, column) # :nodoc:
214
- return super unless BigDecimal === value
215
-
216
- value.to_f
217
- end
218
- end
219
-
220
- # DATABASE STATEMENTS ======================================
221
-
222
- def explain(arel, binds = [])
223
- sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
224
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
225
- end
226
-
227
- class ExplainPrettyPrinter
228
- # Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
229
- # the output of the SQLite shell:
230
- #
231
- # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
232
- # 0|1|1|SCAN TABLE posts (~100000 rows)
233
- #
234
- def pp(result) # :nodoc:
235
- result.rows.map do |row|
236
- row.join('|')
237
- end.join("\n") + "\n"
238
- end
239
- end
240
-
241
- def exec_query(sql, name = nil, binds = [])
242
- log(sql, name, binds) do
243
-
244
- # Don't cache statements without bind values
245
- if binds.empty?
246
- stmt = @connection.prepare(sql)
247
- cols = stmt.columns
248
- records = stmt.to_a
249
- stmt.close
250
- stmt = records
251
- else
252
- cache = @statements[sql] ||= {
253
- :stmt => @connection.prepare(sql)
254
- }
255
- stmt = cache[:stmt]
256
- cols = cache[:cols] ||= stmt.columns
257
- stmt.reset!
258
- stmt.bind_params binds.map { |col, val|
259
- type_cast(val, col)
260
- }
261
- end
262
-
263
- ActiveRecord::Result.new(cols, stmt.to_a)
264
- end
265
- end
266
-
267
- def exec_delete(sql, name = 'SQL', binds = [])
268
- exec_query(sql, name, binds)
269
- @connection.changes
270
- end
271
- alias :exec_update :exec_delete
272
-
273
- def last_inserted_id(result)
274
- @connection.last_insert_row_id
275
- end
276
-
277
- def execute(sql, name = nil) #:nodoc:
278
- log(sql, name) { @connection.execute(sql) }
279
- end
280
-
281
- def update_sql(sql, name = nil) #:nodoc:
282
- super
283
- @connection.changes
284
- end
285
-
286
- def delete_sql(sql, name = nil) #:nodoc:
287
- sql += " WHERE 1=1" unless sql =~ /WHERE/i
288
- super sql, name
289
- end
290
-
291
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
292
- super
293
- id_value || @connection.last_insert_row_id
294
- end
295
- alias :create :insert_sql
296
-
297
- def select_rows(sql, name = nil)
298
- exec_query(sql, name).rows
299
- end
300
-
301
- def create_savepoint
302
- execute("SAVEPOINT #{current_savepoint_name}")
303
- end
304
-
305
- def rollback_to_savepoint
306
- execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
307
- end
308
-
309
- def release_savepoint
310
- execute("RELEASE SAVEPOINT #{current_savepoint_name}")
311
- end
312
-
313
- def begin_db_transaction #:nodoc:
314
- log('begin transaction',nil) { @connection.transaction }
315
- end
316
-
317
- def commit_db_transaction #:nodoc:
318
- log('commit transaction',nil) { @connection.commit }
319
- end
320
-
321
- def rollback_db_transaction #:nodoc:
322
- log('rollback transaction',nil) { @connection.rollback }
323
- end
324
-
325
- # SCHEMA STATEMENTS ========================================
326
-
327
- def tables(name = 'SCHEMA', table_name = nil) #:nodoc:
328
- sql = <<-SQL
329
- SELECT name
330
- FROM sqlite_master
331
- WHERE type = 'table' AND NOT name = 'sqlite_sequence'
332
- SQL
333
- sql << " AND name = #{quote_table_name(table_name)}" if table_name
334
-
335
- exec_query(sql, name).map do |row|
336
- row['name']
337
- end
338
- end
339
-
340
- def table_exists?(name)
341
- name && tables('SCHEMA', name).any?
342
- end
343
-
344
- # Returns an array of +SQLiteColumn+ objects for the table specified by +table_name+.
345
- def columns(table_name, name = nil) #:nodoc:
346
- table_structure(table_name).map do |field|
347
- case field["dflt_value"]
348
- when /^null$/i
349
- field["dflt_value"] = nil
350
- when /^'(.*)'$/
351
- field["dflt_value"] = $1.gsub(/''/, "'")
352
- when /^"(.*)"$/
353
- field["dflt_value"] = $1.gsub(/""/, '"')
354
- end
355
-
356
- SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
357
- end
358
- end
359
-
360
- # Returns an array of indexes for the given table.
361
- def indexes(table_name, name = nil) #:nodoc:
362
- exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
363
- IndexDefinition.new(
364
- table_name,
365
- row['name'],
366
- row['unique'] != 0,
367
- exec_query("PRAGMA index_info('#{row['name']}')", 'SCHEMA').map { |col|
368
- col['name']
369
- })
370
- end
371
- end
372
-
373
- def primary_key(table_name) #:nodoc:
374
- column = table_structure(table_name).find { |field|
375
- field['pk'] == 1
376
- }
377
- column && column['name']
378
- end
379
-
380
- def remove_index!(table_name, index_name) #:nodoc:
381
- exec_query "DROP INDEX #{quote_column_name(index_name)}"
382
- end
383
-
384
- # Renames a table.
385
- #
386
- # Example:
387
- # rename_table('octopuses', 'octopi')
388
- def rename_table(name, new_name)
389
- exec_query "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
390
- end
391
-
392
- # See: http://www.sqlite.org/lang_altertable.html
393
- # SQLite has an additional restriction on the ALTER TABLE statement
394
- def valid_alter_table_options( type, options)
395
- type.to_sym != :primary_key
396
- end
397
-
398
- def add_column(table_name, column_name, type, options = {}) #:nodoc:
399
- if supports_add_column? && valid_alter_table_options( type, options )
400
- super(table_name, column_name, type, options)
401
- else
402
- alter_table(table_name) do |definition|
403
- definition.column(column_name, type, options)
404
- end
405
- end
406
- end
407
-
408
- def remove_column(table_name, *column_names) #:nodoc:
409
- raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
410
-
411
- if column_names.flatten!
412
- message = 'Passing array to remove_columns is deprecated, please use ' +
413
- 'multiple arguments, like: `remove_columns(:posts, :foo, :bar)`'
414
- ActiveSupport::Deprecation.warn message, caller
415
- end
416
-
417
- column_names.each do |column_name|
418
- alter_table(table_name) do |definition|
419
- definition.columns.delete(definition[column_name])
420
- end
421
- end
422
- end
423
- alias :remove_columns :remove_column
424
-
425
- def change_column_default(table_name, column_name, default) #:nodoc:
426
- alter_table(table_name) do |definition|
427
- definition[column_name].default = default
428
- end
429
- end
430
-
431
- def change_column_null(table_name, column_name, null, default = nil)
432
- unless null || default.nil?
433
- exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
434
- end
435
- alter_table(table_name) do |definition|
436
- definition[column_name].null = null
437
- end
438
- end
439
-
440
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
441
- alter_table(table_name) do |definition|
442
- include_default = options_include_default?(options)
443
- definition[column_name].instance_eval do
444
- self.type = type
445
- self.limit = options[:limit] if options.include?(:limit)
446
- self.default = options[:default] if include_default
447
- self.null = options[:null] if options.include?(:null)
448
- self.precision = options[:precision] if options.include?(:precision)
449
- self.scale = options[:scale] if options.include?(:scale)
450
- end
451
- end
452
- end
453
-
454
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
455
- unless columns(table_name).detect{|c| c.name == column_name.to_s }
456
- raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
457
- end
458
- alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
459
- end
460
-
461
- def empty_insert_statement_value
462
- "VALUES(NULL)"
463
- end
464
-
465
- protected
466
- def select(sql, name = nil, binds = []) #:nodoc:
467
- exec_query(sql, name, binds).to_a
468
- end
469
-
470
- def table_structure(table_name)
471
- structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
472
- raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
473
- structure
474
- end
475
-
476
- def alter_table(table_name, options = {}) #:nodoc:
477
- altered_table_name = "altered_#{table_name}"
478
- caller = lambda {|definition| yield definition if block_given?}
479
-
480
- transaction do
481
- move_table(table_name, altered_table_name,
482
- options.merge(:temporary => true))
483
- move_table(altered_table_name, table_name, &caller)
484
- end
485
- end
486
-
487
- def move_table(from, to, options = {}, &block) #:nodoc:
488
- copy_table(from, to, options, &block)
489
- drop_table(from)
490
- end
491
-
492
- def copy_table(from, to, options = {}) #:nodoc:
493
- from_primary_key = primary_key(from)
494
- options[:primary_key] = from_primary_key if from_primary_key != 'id'
495
- unless options[:primary_key]
496
- options[:id] = !columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == from_primary_key
497
- end
498
- create_table(to, options) do |definition|
499
- @definition = definition
500
- columns(from).each do |column|
501
- column_name = options[:rename] ?
502
- (options[:rename][column.name] ||
503
- options[:rename][column.name.to_sym] ||
504
- column.name) : column.name
505
-
506
- @definition.column(column_name, column.type,
507
- :limit => column.limit, :default => column.default,
508
- :precision => column.precision, :scale => column.scale,
509
- :null => column.null)
510
- end
511
- @definition.primary_key(from_primary_key) if from_primary_key
512
- yield @definition if block_given?
513
- end
514
-
515
- copy_table_indexes(from, to, options[:rename] || {})
516
- copy_table_contents(from, to,
517
- @definition.columns.map {|column| column.name},
518
- options[:rename] || {})
519
- end
520
-
521
- def copy_table_indexes(from, to, rename = {}) #:nodoc:
522
- indexes(from).each do |index|
523
- name = index.name
524
- if to == "altered_#{from}"
525
- name = "temp_#{name}"
526
- elsif from == "altered_#{to}"
527
- name = name[5..-1]
528
- end
529
-
530
- to_column_names = columns(to).map { |c| c.name }
531
- columns = index.columns.map {|c| rename[c] || c }.select do |column|
532
- to_column_names.include?(column)
533
- end
534
-
535
- unless columns.empty?
536
- # index name can't be the same
537
- opts = { :name => name.gsub(/(^|_)(#{from})_/, "\\1#{to}_") }
538
- opts[:unique] = true if index.unique
539
- add_index(to, columns, opts)
540
- end
541
- end
542
- end
543
-
544
- def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
545
- column_mappings = Hash[columns.map {|name| [name, name]}]
546
- rename.each { |a| column_mappings[a.last] = a.first }
547
- from_columns = columns(from).collect {|col| col.name}
548
- columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
549
- quoted_columns = columns.map { |col| quote_column_name(col) } * ','
550
-
551
- quoted_to = quote_table_name(to)
552
- exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
553
- sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
554
- sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
555
- sql << ')'
556
- exec_query sql
557
- end
558
- end
559
-
560
- def sqlite_version
561
- @sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)'))
562
- end
563
-
564
- def default_primary_key_type
565
- if supports_autoincrement?
566
- 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'
567
- else
568
- 'INTEGER PRIMARY KEY NOT NULL'
569
- end
570
- end
571
-
572
- def translate_exception(exception, message)
573
- case exception.message
574
- when /column(s)? .* (is|are) not unique/
575
- RecordNotUnique.new(message, exception)
576
- else
577
- super
578
- end
579
- end
580
-
581
- end
582
- end
583
- end
@@ -1,68 +0,0 @@
1
- module ActiveRecord
2
-
3
- # = Active Record Dynamic Finder Match
4
- #
5
- # Refer to ActiveRecord::Base documentation for Dynamic attribute-based finders for detailed info
6
- #
7
- class DynamicFinderMatch
8
- def self.match(method)
9
- finder = :first
10
- bang = false
11
- instantiator = nil
12
-
13
- case method.to_s
14
- when /^find_(all_|last_)?by_([_a-zA-Z]\w*)$/
15
- finder = :last if $1 == 'last_'
16
- finder = :all if $1 == 'all_'
17
- names = $2
18
- when /^find_by_([_a-zA-Z]\w*)\!$/
19
- bang = true
20
- names = $1
21
- when /^find_or_create_by_([_a-zA-Z]\w*)\!$/
22
- bang = true
23
- instantiator = :create
24
- names = $1
25
- when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
26
- instantiator = $1 == 'initialize' ? :new : :create
27
- names = $2
28
- else
29
- return nil
30
- end
31
-
32
- new(finder, instantiator, bang, names.split('_and_'))
33
- end
34
-
35
- def initialize(finder, instantiator, bang, attribute_names)
36
- @finder = finder
37
- @instantiator = instantiator
38
- @bang = bang
39
- @attribute_names = attribute_names
40
- end
41
-
42
- attr_reader :finder, :attribute_names, :instantiator
43
-
44
- def finder?
45
- @finder && !@instantiator
46
- end
47
-
48
- def instantiator?
49
- @finder == :first && @instantiator
50
- end
51
-
52
- def creator?
53
- @finder == :first && @instantiator == :create
54
- end
55
-
56
- def bang?
57
- @bang
58
- end
59
-
60
- def save_record?
61
- @instantiator == :create
62
- end
63
-
64
- def save_method
65
- bang? ? :save! : :save
66
- end
67
- end
68
- end
@@ -1,23 +0,0 @@
1
- module ActiveRecord
2
-
3
- # = Active Record Dynamic Scope Match
4
- #
5
- # Provides dynamic attribute-based scopes such as <tt>scoped_by_price(4.99)</tt>
6
- # if, for example, the <tt>Product</tt> has an attribute with that name. You can
7
- # chain more <tt>scoped_by_* </tt> methods after the other. It acts like a named
8
- # scope except that it's dynamic.
9
- class DynamicScopeMatch
10
- def self.match(method)
11
- return unless method.to_s =~ /^scoped_by_([_a-zA-Z]\w*)$/
12
- new(true, $1 && $1.split('_and_'))
13
- end
14
-
15
- def initialize(scope, attribute_names)
16
- @scope = scope
17
- @attribute_names = attribute_names
18
- end
19
-
20
- attr_reader :scope, :attribute_names
21
- alias :scope? :scope
22
- end
23
- end