activerecord 3.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 (93) hide show
  1. data/CHANGELOG +6023 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +162 -0
  5. data/examples/simple.rb +14 -0
  6. data/lib/active_record.rb +124 -0
  7. data/lib/active_record/aggregations.rb +277 -0
  8. data/lib/active_record/association_preload.rb +403 -0
  9. data/lib/active_record/associations.rb +2254 -0
  10. data/lib/active_record/associations/association_collection.rb +562 -0
  11. data/lib/active_record/associations/association_proxy.rb +295 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +78 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +137 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +116 -0
  17. data/lib/active_record/associations/has_one_association.rb +143 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +40 -0
  19. data/lib/active_record/associations/through_association_scope.rb +154 -0
  20. data/lib/active_record/attribute_methods.rb +60 -0
  21. data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +50 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +116 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -0
  27. data/lib/active_record/attribute_methods/write.rb +37 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1867 -0
  30. data/lib/active_record/callbacks.rb +288 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +365 -0
  32. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
  33. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +329 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -0
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +543 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +212 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +643 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1030 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -0
  43. data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
  44. data/lib/active_record/counter_cache.rb +115 -0
  45. data/lib/active_record/dynamic_finder_match.rb +53 -0
  46. data/lib/active_record/dynamic_scope_match.rb +32 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1008 -0
  49. data/lib/active_record/locale/en.yml +40 -0
  50. data/lib/active_record/locking/optimistic.rb +172 -0
  51. data/lib/active_record/locking/pessimistic.rb +55 -0
  52. data/lib/active_record/log_subscriber.rb +48 -0
  53. data/lib/active_record/migration.rb +617 -0
  54. data/lib/active_record/named_scope.rb +138 -0
  55. data/lib/active_record/nested_attributes.rb +417 -0
  56. data/lib/active_record/observer.rb +140 -0
  57. data/lib/active_record/persistence.rb +291 -0
  58. data/lib/active_record/query_cache.rb +36 -0
  59. data/lib/active_record/railtie.rb +91 -0
  60. data/lib/active_record/railties/controller_runtime.rb +38 -0
  61. data/lib/active_record/railties/databases.rake +512 -0
  62. data/lib/active_record/reflection.rb +403 -0
  63. data/lib/active_record/relation.rb +393 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +286 -0
  66. data/lib/active_record/relation/finder_methods.rb +355 -0
  67. data/lib/active_record/relation/predicate_builder.rb +41 -0
  68. data/lib/active_record/relation/query_methods.rb +261 -0
  69. data/lib/active_record/relation/spawn_methods.rb +112 -0
  70. data/lib/active_record/schema.rb +59 -0
  71. data/lib/active_record/schema_dumper.rb +195 -0
  72. data/lib/active_record/serialization.rb +60 -0
  73. data/lib/active_record/serializers/xml_serializer.rb +244 -0
  74. data/lib/active_record/session_store.rb +340 -0
  75. data/lib/active_record/test_case.rb +67 -0
  76. data/lib/active_record/timestamp.rb +88 -0
  77. data/lib/active_record/transactions.rb +356 -0
  78. data/lib/active_record/validations.rb +84 -0
  79. data/lib/active_record/validations/associated.rb +48 -0
  80. data/lib/active_record/validations/uniqueness.rb +185 -0
  81. data/lib/active_record/version.rb +9 -0
  82. data/lib/rails/generators/active_record.rb +27 -0
  83. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  84. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  85. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  86. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  87. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  88. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  89. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  90. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  91. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  92. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  93. metadata +224 -0
@@ -0,0 +1,53 @@
1
+ require 'active_record/connection_adapters/sqlite_adapter'
2
+
3
+ module ActiveRecord
4
+ class Base
5
+ # sqlite3 adapter reuses sqlite_connection.
6
+ def self.sqlite3_connection(config) # :nodoc:
7
+ # Require database.
8
+ unless config[:database]
9
+ raise ArgumentError, "No database file specified. Missing argument: database"
10
+ end
11
+
12
+ # Allow database path relative to Rails.root, but only if
13
+ # the database path is not the special path that tells
14
+ # Sqlite to build a database only in memory.
15
+ if defined?(Rails.root) && ':memory:' != config[:database]
16
+ config[:database] = File.expand_path(config[:database], Rails.root)
17
+ end
18
+
19
+ unless 'sqlite3' == config[:adapter]
20
+ raise ArgumentError, 'adapter name should be "sqlite3"'
21
+ end
22
+
23
+ unless self.class.const_defined?(:SQLite3)
24
+ require_library_or_gem(config[:adapter])
25
+ end
26
+
27
+ db = SQLite3::Database.new(
28
+ config[:database],
29
+ :results_as_hash => true
30
+ )
31
+
32
+ db.busy_timeout(config[:timeout]) unless config[:timeout].nil?
33
+
34
+ ConnectionAdapters::SQLite3Adapter.new(db, logger, config)
35
+ end
36
+ end
37
+
38
+ module ConnectionAdapters #:nodoc:
39
+ class SQLite3Adapter < SQLiteAdapter # :nodoc:
40
+
41
+ # Returns the current database encoding format as a string, eg: 'UTF-8'
42
+ def encoding
43
+ if @connection.respond_to?(:encoding)
44
+ @connection.encoding.to_s
45
+ else
46
+ encoding = @connection.execute('PRAGMA encoding')
47
+ encoding[0]['encoding']
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,401 @@
1
+ require 'active_record/connection_adapters/abstract_adapter'
2
+ require 'active_support/core_ext/kernel/requires'
3
+
4
+ module ActiveRecord
5
+ module ConnectionAdapters #:nodoc:
6
+ class SQLiteColumn < Column #:nodoc:
7
+ class << self
8
+ def string_to_binary(value)
9
+ value.gsub(/\0|\%/n) do |b|
10
+ case b
11
+ when "\0" then "%00"
12
+ when "%" then "%25"
13
+ end
14
+ end
15
+ end
16
+
17
+ def binary_to_string(value)
18
+ if value.respond_to?(:force_encoding) && value.encoding != Encoding::ASCII_8BIT
19
+ value = value.force_encoding(Encoding::ASCII_8BIT)
20
+ end
21
+
22
+ value.gsub(/%00|%25/n) do |b|
23
+ case b
24
+ when "%00" then "\0"
25
+ when "%25" then "%"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ # The SQLite adapter works with both the 2.x and 3.x series of SQLite with the sqlite-ruby
33
+ # drivers (available both as gems and from http://rubyforge.org/projects/sqlite-ruby/).
34
+ #
35
+ # Options:
36
+ #
37
+ # * <tt>:database</tt> - Path to the database file.
38
+ class SQLiteAdapter < AbstractAdapter
39
+ class Version
40
+ include Comparable
41
+
42
+ def initialize(version_string)
43
+ @version = version_string.split('.').map { |v| v.to_i }
44
+ end
45
+
46
+ def <=>(version_string)
47
+ @version <=> version_string.split('.').map { |v| v.to_i }
48
+ end
49
+ end
50
+
51
+ def initialize(connection, logger, config)
52
+ super(connection, logger)
53
+ @config = config
54
+ end
55
+
56
+ def adapter_name #:nodoc:
57
+ 'SQLite'
58
+ end
59
+
60
+ def supports_ddl_transactions?
61
+ sqlite_version >= '2.0.0'
62
+ end
63
+
64
+ def supports_migrations? #:nodoc:
65
+ true
66
+ end
67
+
68
+ def supports_primary_key? #:nodoc:
69
+ true
70
+ end
71
+
72
+ def requires_reloading?
73
+ true
74
+ end
75
+
76
+ def supports_add_column?
77
+ sqlite_version >= '3.1.6'
78
+ end
79
+
80
+ def disconnect!
81
+ super
82
+ @connection.close rescue nil
83
+ end
84
+
85
+ def supports_count_distinct? #:nodoc:
86
+ sqlite_version >= '3.2.6'
87
+ end
88
+
89
+ def supports_autoincrement? #:nodoc:
90
+ sqlite_version >= '3.1.0'
91
+ end
92
+
93
+ def native_database_types #:nodoc:
94
+ {
95
+ :primary_key => default_primary_key_type,
96
+ :string => { :name => "varchar", :limit => 255 },
97
+ :text => { :name => "text" },
98
+ :integer => { :name => "integer" },
99
+ :float => { :name => "float" },
100
+ :decimal => { :name => "decimal" },
101
+ :datetime => { :name => "datetime" },
102
+ :timestamp => { :name => "datetime" },
103
+ :time => { :name => "time" },
104
+ :date => { :name => "date" },
105
+ :binary => { :name => "blob" },
106
+ :boolean => { :name => "boolean" }
107
+ }
108
+ end
109
+
110
+
111
+ # QUOTING ==================================================
112
+
113
+ def quote_string(s) #:nodoc:
114
+ @connection.class.quote(s)
115
+ end
116
+
117
+ def quote_column_name(name) #:nodoc:
118
+ %Q("#{name}")
119
+ end
120
+
121
+ # Quote date/time values for use in SQL input. Includes microseconds
122
+ # if the value is a Time responding to usec.
123
+ def quoted_date(value) #:nodoc:
124
+ if value.acts_like?(:time) && value.respond_to?(:usec)
125
+ "#{super}.#{sprintf("%06d", value.usec)}"
126
+ else
127
+ super
128
+ end
129
+ end
130
+
131
+
132
+ # DATABASE STATEMENTS ======================================
133
+
134
+ def execute(sql, name = nil) #:nodoc:
135
+ log(sql, name) { @connection.execute(sql) }
136
+ end
137
+
138
+ def update_sql(sql, name = nil) #:nodoc:
139
+ super
140
+ @connection.changes
141
+ end
142
+
143
+ def delete_sql(sql, name = nil) #:nodoc:
144
+ sql += " WHERE 1=1" unless sql =~ /WHERE/i
145
+ super sql, name
146
+ end
147
+
148
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
149
+ super || @connection.last_insert_row_id
150
+ end
151
+ alias :create :insert_sql
152
+
153
+ def select_rows(sql, name = nil)
154
+ execute(sql, name).map do |row|
155
+ (0...(row.size / 2)).map { |i| row[i] }
156
+ end
157
+ end
158
+
159
+ def begin_db_transaction #:nodoc:
160
+ @connection.transaction
161
+ end
162
+
163
+ def commit_db_transaction #:nodoc:
164
+ @connection.commit
165
+ end
166
+
167
+ def rollback_db_transaction #:nodoc:
168
+ @connection.rollback
169
+ end
170
+
171
+ # SCHEMA STATEMENTS ========================================
172
+
173
+ def tables(name = nil) #:nodoc:
174
+ sql = <<-SQL
175
+ SELECT name
176
+ FROM sqlite_master
177
+ WHERE type = 'table' AND NOT name = 'sqlite_sequence'
178
+ SQL
179
+
180
+ execute(sql, name).map do |row|
181
+ row['name']
182
+ end
183
+ end
184
+
185
+ def columns(table_name, name = nil) #:nodoc:
186
+ table_structure(table_name).map do |field|
187
+ SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
188
+ end
189
+ end
190
+
191
+ def indexes(table_name, name = nil) #:nodoc:
192
+ execute("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
193
+ IndexDefinition.new(
194
+ table_name,
195
+ row['name'],
196
+ row['unique'].to_i != 0,
197
+ execute("PRAGMA index_info('#{row['name']}')").map { |col|
198
+ col['name']
199
+ })
200
+ end
201
+ end
202
+
203
+ def primary_key(table_name) #:nodoc:
204
+ column = table_structure(table_name).find { |field|
205
+ field['pk'].to_i == 1
206
+ }
207
+ column && column['name']
208
+ end
209
+
210
+ def remove_index!(table_name, index_name) #:nodoc:
211
+ execute "DROP INDEX #{quote_column_name(index_name)}"
212
+ end
213
+
214
+ def rename_table(name, new_name)
215
+ execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
216
+ end
217
+
218
+ # See: http://www.sqlite.org/lang_altertable.html
219
+ # SQLite has an additional restriction on the ALTER TABLE statement
220
+ def valid_alter_table_options( type, options)
221
+ type.to_sym != :primary_key
222
+ end
223
+
224
+ def add_column(table_name, column_name, type, options = {}) #:nodoc:
225
+ if supports_add_column? && valid_alter_table_options( type, options )
226
+ super(table_name, column_name, type, options)
227
+ else
228
+ alter_table(table_name) do |definition|
229
+ definition.column(column_name, type, options)
230
+ end
231
+ end
232
+ end
233
+
234
+ def remove_column(table_name, *column_names) #:nodoc:
235
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
236
+ column_names.flatten.each do |column_name|
237
+ alter_table(table_name) do |definition|
238
+ definition.columns.delete(definition[column_name])
239
+ end
240
+ end
241
+ end
242
+ alias :remove_columns :remove_column
243
+
244
+ def change_column_default(table_name, column_name, default) #:nodoc:
245
+ alter_table(table_name) do |definition|
246
+ definition[column_name].default = default
247
+ end
248
+ end
249
+
250
+ def change_column_null(table_name, column_name, null, default = nil)
251
+ unless null || default.nil?
252
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
253
+ end
254
+ alter_table(table_name) do |definition|
255
+ definition[column_name].null = null
256
+ end
257
+ end
258
+
259
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
260
+ alter_table(table_name) do |definition|
261
+ include_default = options_include_default?(options)
262
+ definition[column_name].instance_eval do
263
+ self.type = type
264
+ self.limit = options[:limit] if options.include?(:limit)
265
+ self.default = options[:default] if include_default
266
+ self.null = options[:null] if options.include?(:null)
267
+ end
268
+ end
269
+ end
270
+
271
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
272
+ unless columns(table_name).detect{|c| c.name == column_name.to_s }
273
+ raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
274
+ end
275
+ alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
276
+ end
277
+
278
+ def empty_insert_statement_value
279
+ "VALUES(NULL)"
280
+ end
281
+
282
+ protected
283
+ def select(sql, name = nil) #:nodoc:
284
+ execute(sql, name).map do |row|
285
+ record = {}
286
+ row.each do |key, value|
287
+ record[key.sub(/^"?\w+"?\./, '')] = value if key.is_a?(String)
288
+ end
289
+ record
290
+ end
291
+ end
292
+
293
+ def table_structure(table_name)
294
+ structure = @connection.table_info(quote_table_name(table_name))
295
+ raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
296
+ structure
297
+ end
298
+
299
+ def alter_table(table_name, options = {}) #:nodoc:
300
+ altered_table_name = "altered_#{table_name}"
301
+ caller = lambda {|definition| yield definition if block_given?}
302
+
303
+ transaction do
304
+ move_table(table_name, altered_table_name,
305
+ options.merge(:temporary => true))
306
+ move_table(altered_table_name, table_name, &caller)
307
+ end
308
+ end
309
+
310
+ def move_table(from, to, options = {}, &block) #:nodoc:
311
+ copy_table(from, to, options, &block)
312
+ drop_table(from)
313
+ end
314
+
315
+ def copy_table(from, to, options = {}) #:nodoc:
316
+ options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s))
317
+ create_table(to, options) do |definition|
318
+ @definition = definition
319
+ columns(from).each do |column|
320
+ column_name = options[:rename] ?
321
+ (options[:rename][column.name] ||
322
+ options[:rename][column.name.to_sym] ||
323
+ column.name) : column.name
324
+
325
+ @definition.column(column_name, column.type,
326
+ :limit => column.limit, :default => column.default,
327
+ :null => column.null)
328
+ end
329
+ @definition.primary_key(primary_key(from)) if primary_key(from)
330
+ yield @definition if block_given?
331
+ end
332
+
333
+ copy_table_indexes(from, to, options[:rename] || {})
334
+ copy_table_contents(from, to,
335
+ @definition.columns.map {|column| column.name},
336
+ options[:rename] || {})
337
+ end
338
+
339
+ def copy_table_indexes(from, to, rename = {}) #:nodoc:
340
+ indexes(from).each do |index|
341
+ name = index.name
342
+ if to == "altered_#{from}"
343
+ name = "temp_#{name}"
344
+ elsif from == "altered_#{to}"
345
+ name = name[5..-1]
346
+ end
347
+
348
+ to_column_names = columns(to).map { |c| c.name }
349
+ columns = index.columns.map {|c| rename[c] || c }.select do |column|
350
+ to_column_names.include?(column)
351
+ end
352
+
353
+ unless columns.empty?
354
+ # index name can't be the same
355
+ opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
356
+ opts[:unique] = true if index.unique
357
+ add_index(to, columns, opts)
358
+ end
359
+ end
360
+ end
361
+
362
+ def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
363
+ column_mappings = Hash[*columns.map {|name| [name, name]}.flatten]
364
+ rename.inject(column_mappings) {|map, a| map[a.last] = a.first; map}
365
+ from_columns = columns(from).collect {|col| col.name}
366
+ columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
367
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ','
368
+
369
+ quoted_to = quote_table_name(to)
370
+ @connection.execute "SELECT * FROM #{quote_table_name(from)}" do |row|
371
+ sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
372
+ sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
373
+ sql << ')'
374
+ @connection.execute sql
375
+ end
376
+ end
377
+
378
+ def sqlite_version
379
+ @sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)'))
380
+ end
381
+
382
+ def default_primary_key_type
383
+ if supports_autoincrement?
384
+ 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'
385
+ else
386
+ 'INTEGER PRIMARY KEY NOT NULL'
387
+ end
388
+ end
389
+
390
+ def translate_exception(exception, message)
391
+ case exception.message
392
+ when /column(s)? .* (is|are) not unique/
393
+ RecordNotUnique.new(message, exception)
394
+ else
395
+ super
396
+ end
397
+ end
398
+
399
+ end
400
+ end
401
+ end