square-activerecord 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/CHANGELOG +6140 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +179 -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 +430 -0
  9. data/lib/active_record/associations.rb +2307 -0
  10. data/lib/active_record/associations/association_collection.rb +572 -0
  11. data/lib/active_record/associations/association_proxy.rb +299 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +82 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +115 -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 +30 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +56 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +145 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +64 -0
  27. data/lib/active_record/attribute_methods/write.rb +43 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1904 -0
  30. data/lib/active_record/callbacks.rb +284 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +364 -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 +333 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +73 -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 +539 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +217 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +657 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1031 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -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 +56 -0
  46. data/lib/active_record/dynamic_scope_match.rb +23 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1006 -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 +419 -0
  56. data/lib/active_record/observer.rb +125 -0
  57. data/lib/active_record/persistence.rb +290 -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 +411 -0
  63. data/lib/active_record/relation.rb +394 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +295 -0
  66. data/lib/active_record/relation/finder_methods.rb +363 -0
  67. data/lib/active_record/relation/predicate_builder.rb +48 -0
  68. data/lib/active_record/relation/query_methods.rb +303 -0
  69. data/lib/active_record/relation/spawn_methods.rb +132 -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 +359 -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 +190 -0
  81. data/lib/active_record/version.rb +10 -0
  82. data/lib/rails/generators/active_record.rb +19 -0
  83. data/lib/rails/generators/active_record/migration.rb +15 -0
  84. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  85. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  86. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  87. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  88. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  89. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  90. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  91. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  92. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  93. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  94. metadata +223 -0
@@ -0,0 +1,61 @@
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
+ def quote(value, column = nil)
41
+ if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
42
+ s = column.class.string_to_binary(value).unpack("H*")[0]
43
+ "x'#{s}'"
44
+ else
45
+ super
46
+ end
47
+ end
48
+
49
+ # Returns the current database encoding format as a string, eg: 'UTF-8'
50
+ def encoding
51
+ if @connection.respond_to?(:encoding)
52
+ @connection.encoding.to_s
53
+ else
54
+ encoding = @connection.execute('PRAGMA encoding')
55
+ encoding[0]['encoding']
56
+ end
57
+ end
58
+
59
+ end
60
+ end
61
+ 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]}]
364
+ rename.each { |a| column_mappings[a.last] = a.first }
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