activerecord 1.11.1 → 1.12.1
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.
- data/CHANGELOG +198 -0
- data/lib/active_record.rb +19 -14
- data/lib/active_record/acts/list.rb +8 -6
- data/lib/active_record/acts/tree.rb +33 -10
- data/lib/active_record/aggregations.rb +1 -7
- data/lib/active_record/associations.rb +151 -82
- data/lib/active_record/associations/association_collection.rb +25 -0
- data/lib/active_record/associations/association_proxy.rb +9 -8
- data/lib/active_record/associations/belongs_to_association.rb +19 -5
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +44 -69
- data/lib/active_record/associations/has_many_association.rb +6 -14
- data/lib/active_record/associations/has_one_association.rb +5 -3
- data/lib/active_record/base.rb +344 -130
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +128 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +104 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +249 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +245 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +29 -464
- data/lib/active_record/connection_adapters/db2_adapter.rb +40 -10
- data/lib/active_record/connection_adapters/mysql_adapter.rb +131 -60
- data/lib/active_record/connection_adapters/oci_adapter.rb +106 -26
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +211 -62
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +193 -44
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +24 -15
- data/lib/active_record/fixtures.rb +47 -24
- data/lib/active_record/migration.rb +34 -5
- data/lib/active_record/observer.rb +32 -2
- data/lib/active_record/query_cache.rb +12 -11
- data/lib/active_record/schema.rb +58 -0
- data/lib/active_record/schema_dumper.rb +84 -0
- data/lib/active_record/transactions.rb +1 -3
- data/lib/active_record/validations.rb +40 -26
- data/lib/active_record/vendor/mysql.rb +6 -0
- data/lib/active_record/version.rb +9 -0
- data/rakefile +5 -16
- data/test/abstract_unit.rb +6 -11
- data/test/adapter_test.rb +58 -0
- data/test/ar_schema_test.rb +33 -0
- data/test/association_callbacks_test.rb +14 -0
- data/test/associations_go_eager_test.rb +56 -14
- data/test/associations_test.rb +245 -25
- data/test/base_test.rb +205 -34
- data/test/binary_test.rb +25 -42
- data/test/callbacks_test.rb +75 -0
- data/test/conditions_scoping_test.rb +136 -0
- data/test/connections/native_mysql/connection.rb +0 -4
- data/test/connections/native_sqlite3/in_memory_connection.rb +17 -0
- data/test/copy_table_sqlite.rb +64 -0
- data/test/deprecated_associations_test.rb +7 -6
- data/test/deprecated_finder_test.rb +3 -3
- data/test/finder_test.rb +33 -3
- data/test/fixtures/accounts.yml +5 -0
- data/test/fixtures/categories_ordered.yml +7 -0
- data/test/fixtures/category.rb +11 -1
- data/test/fixtures/comment.rb +22 -2
- data/test/fixtures/comments.yml +6 -0
- data/test/fixtures/companies.yml +15 -0
- data/test/fixtures/company.rb +24 -1
- data/test/fixtures/db_definitions/db2.drop.sql +5 -1
- data/test/fixtures/db_definitions/db2.sql +15 -1
- data/test/fixtures/db_definitions/mysql.drop.sql +2 -0
- data/test/fixtures/db_definitions/mysql.sql +17 -2
- data/test/fixtures/db_definitions/oci.drop.sql +37 -5
- data/test/fixtures/db_definitions/oci.sql +47 -4
- data/test/fixtures/db_definitions/oci2.drop.sql +1 -1
- data/test/fixtures/db_definitions/oci2.sql +2 -2
- data/test/fixtures/db_definitions/postgresql.drop.sql +4 -0
- data/test/fixtures/db_definitions/postgresql.sql +33 -4
- data/test/fixtures/db_definitions/sqlite.drop.sql +2 -0
- data/test/fixtures/db_definitions/sqlite.sql +16 -2
- data/test/fixtures/db_definitions/sqlserver.drop.sql +2 -0
- data/test/fixtures/db_definitions/sqlserver.sql +16 -2
- data/test/fixtures/developer.rb +1 -1
- data/test/fixtures/flowers.jpg +0 -0
- data/test/fixtures/keyboard.rb +3 -0
- data/test/fixtures/mixins.yml +11 -1
- data/test/fixtures/order.rb +4 -0
- data/test/fixtures/post.rb +4 -0
- data/test/fixtures/posts.yml +7 -0
- data/test/fixtures/project.rb +1 -0
- data/test/fixtures/subject.rb +4 -0
- data/test/fixtures/subscriber.rb +2 -4
- data/test/fixtures/topics.yml +2 -2
- data/test/fixtures_test.rb +79 -7
- data/test/inheritance_test.rb +2 -2
- data/test/lifecycle_test.rb +14 -6
- data/test/migration_test.rb +164 -6
- data/test/mixin_test.rb +78 -2
- data/test/pk_test.rb +25 -1
- data/test/readonly_test.rb +31 -0
- data/test/reflection_test.rb +4 -1
- data/test/schema_dumper_test.rb +19 -0
- data/test/schema_test_postgresql.rb +3 -2
- data/test/synonym_test_oci.rb +17 -0
- data/test/threaded_connections_test.rb +2 -1
- data/test/transactions_test.rb +109 -10
- data/test/validations_test.rb +70 -42
- metadata +25 -5
- data/test/fixtures/associations.png +0 -0
- data/test/thread_safety_test.rb +0 -36
@@ -16,7 +16,7 @@ module ActiveRecord
|
|
16
16
|
def self.sqlserver_connection(config) #:nodoc:
|
17
17
|
require_library_or_gem 'dbi' unless self.class.const_defined?(:DBI)
|
18
18
|
|
19
|
-
|
19
|
+
config = config.symbolize_keys
|
20
20
|
|
21
21
|
mode = config[:mode] ? config[:mode].to_s.upcase : 'ADO'
|
22
22
|
username = config[:username] ? config[:username].to_s : 'sa'
|
@@ -98,7 +98,7 @@ module ActiveRecord
|
|
98
98
|
|
99
99
|
# These methods will only allow the adapter to insert binary data with a length of 7K or less
|
100
100
|
# because of a SQL Server statement length policy.
|
101
|
-
def string_to_binary(value)
|
101
|
+
def self.string_to_binary(value)
|
102
102
|
value.gsub(/(\r|\n|\0|\x1a)/) do
|
103
103
|
case $1
|
104
104
|
when "\r" then "%00"
|
@@ -109,7 +109,7 @@ module ActiveRecord
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
-
def binary_to_string(value)
|
112
|
+
def self.binary_to_string(value)
|
113
113
|
value.gsub(/(%00|%01|%02|%03)/) do
|
114
114
|
case $1
|
115
115
|
when "%00" then "\r"
|
@@ -165,16 +165,16 @@ module ActiveRecord
|
|
165
165
|
def native_database_types
|
166
166
|
{
|
167
167
|
:primary_key => "int NOT NULL IDENTITY(1, 1) PRIMARY KEY",
|
168
|
-
:string => { :name => "varchar
|
169
|
-
:text => { :name => "text
|
170
|
-
:integer => { :name => "int
|
171
|
-
:float => { :name => "float
|
172
|
-
:datetime => { :name => "datetime
|
173
|
-
:timestamp => { :name => "datetime
|
174
|
-
:time => { :name => "datetime
|
175
|
-
:date => { :name => "datetime
|
176
|
-
:binary => { :name => "image
|
177
|
-
:boolean => { :name => "bit
|
168
|
+
:string => { :name => "varchar", :limit => 255 },
|
169
|
+
:text => { :name => "text" },
|
170
|
+
:integer => { :name => "int"},
|
171
|
+
:float => { :name => "float", :limit => 8 },
|
172
|
+
:datetime => { :name => "datetime" },
|
173
|
+
:timestamp => { :name => "datetime" },
|
174
|
+
:time => { :name => "datetime" },
|
175
|
+
:date => { :name => "datetime" },
|
176
|
+
:binary => { :name => "image"},
|
177
|
+
:boolean => { :name => "bit"}
|
178
178
|
}
|
179
179
|
end
|
180
180
|
|
@@ -204,7 +204,7 @@ module ActiveRecord
|
|
204
204
|
columns
|
205
205
|
end
|
206
206
|
|
207
|
-
def insert(sql, name = nil, pk = nil, id_value = nil)
|
207
|
+
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
208
208
|
begin
|
209
209
|
table_name = get_table_name(sql)
|
210
210
|
col = get_identity_column(table_name)
|
@@ -275,7 +275,7 @@ module ActiveRecord
|
|
275
275
|
case value
|
276
276
|
when String
|
277
277
|
if column && column.type == :binary
|
278
|
-
"'#{quote_string(column.string_to_binary(value))}'"
|
278
|
+
"'#{quote_string(column.class.string_to_binary(value))}'"
|
279
279
|
else
|
280
280
|
"'#{quote_string(value)}'"
|
281
281
|
end
|
@@ -293,6 +293,14 @@ module ActiveRecord
|
|
293
293
|
string.gsub(/\'/, "''")
|
294
294
|
end
|
295
295
|
|
296
|
+
def quoted_true
|
297
|
+
"1"
|
298
|
+
end
|
299
|
+
|
300
|
+
def quoted_false
|
301
|
+
"0"
|
302
|
+
end
|
303
|
+
|
296
304
|
def quote_column_name(name)
|
297
305
|
"[#{name}]"
|
298
306
|
end
|
@@ -332,6 +340,7 @@ module ActiveRecord
|
|
332
340
|
record = {}
|
333
341
|
row.column_names.each do |col|
|
334
342
|
record[col] = row[col]
|
343
|
+
record[col] = record[col].to_time if record[col].is_a? DBI::Timestamp
|
335
344
|
end
|
336
345
|
rows << record
|
337
346
|
end
|
@@ -2,6 +2,13 @@ require 'erb'
|
|
2
2
|
require 'yaml'
|
3
3
|
require 'csv'
|
4
4
|
|
5
|
+
module YAML #:nodoc:
|
6
|
+
class Omap #:nodoc:
|
7
|
+
def keys; map { |k, v| k } end
|
8
|
+
def values; map { |k, v| v } end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
5
12
|
# Fixtures are a way of organizing data that you want to test against; in short, sample data. They come in 3 flavours:
|
6
13
|
#
|
7
14
|
# 1. YAML fixtures
|
@@ -32,6 +39,20 @@ require 'csv'
|
|
32
39
|
# indented list of key/value pairs in the "key: value" format. Records are separated by a blank line for your viewing
|
33
40
|
# pleasure.
|
34
41
|
#
|
42
|
+
# Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type. See http://yaml.org/type/omap.html
|
43
|
+
# for the specification. You will need ordered fixtures when you have foreign key constraints on keys in the same table.
|
44
|
+
# This is commonly needed for tree structures. Example:
|
45
|
+
#
|
46
|
+
# --- !omap
|
47
|
+
# - parent:
|
48
|
+
# id: 1
|
49
|
+
# parent_id: NULL
|
50
|
+
# title: Parent
|
51
|
+
# - child:
|
52
|
+
# id: 2
|
53
|
+
# parent_id: 1
|
54
|
+
# title: Child
|
55
|
+
#
|
35
56
|
# = CSV fixtures
|
36
57
|
#
|
37
58
|
# Fixtures can also be kept in the Comma Separated Value format. Akin to YAML fixtures, CSV fixtures are stored
|
@@ -191,7 +212,7 @@ require 'csv'
|
|
191
212
|
# the results of your transaction until Active Record supports nested transactions or savepoints (in progress.)
|
192
213
|
# 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
|
193
214
|
# Use InnoDB, MaxDB, or NDB instead.
|
194
|
-
class Fixtures <
|
215
|
+
class Fixtures < YAML::Omap
|
195
216
|
DEFAULT_FILTER_RE = /\.ya?ml$/
|
196
217
|
|
197
218
|
def self.instantiate_fixtures(object, table_name, fixtures, load_instances=true)
|
@@ -232,12 +253,18 @@ class Fixtures < Hash
|
|
232
253
|
end
|
233
254
|
all_loaded_fixtures.merge! fixtures_map
|
234
255
|
|
256
|
+
|
235
257
|
connection.transaction do
|
236
258
|
fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures }
|
237
259
|
fixtures.each { |fixture| fixture.insert_fixtures }
|
238
260
|
end
|
239
|
-
|
240
|
-
|
261
|
+
|
262
|
+
# Cap primary key sequences to max(pk).
|
263
|
+
if connection.respond_to?(:reset_pk_sequence!)
|
264
|
+
table_names.flatten.each do |table_name|
|
265
|
+
connection.reset_pk_sequence!(table_name)
|
266
|
+
end
|
267
|
+
end
|
241
268
|
|
242
269
|
return fixtures.size > 1 ? fixtures : fixtures.first
|
243
270
|
ensure
|
@@ -245,28 +272,14 @@ class Fixtures < Hash
|
|
245
272
|
end
|
246
273
|
end
|
247
274
|
|
248
|
-
# Work around for PostgreSQL to have new fixtures created from id 1 and running.
|
249
|
-
def self.reset_sequences(connection, table_names)
|
250
|
-
table_names.flatten.each do |table|
|
251
|
-
table_class = Inflector.classify(table.to_s)
|
252
|
-
if Object.const_defined?(table_class)
|
253
|
-
pk = eval("#{table_class}::primary_key")
|
254
|
-
if pk == 'id'
|
255
|
-
connection.execute(
|
256
|
-
"SELECT setval('#{table.to_s}_id_seq', (SELECT MAX(id) FROM #{table.to_s}), true)",
|
257
|
-
'Setting Sequence'
|
258
|
-
)
|
259
|
-
end
|
260
|
-
end
|
261
|
-
end
|
262
|
-
end
|
263
275
|
|
264
276
|
attr_reader :table_name
|
265
277
|
|
266
278
|
def initialize(connection, table_name, fixture_path, file_filter = DEFAULT_FILTER_RE)
|
267
279
|
@connection, @table_name, @fixture_path, @file_filter = connection, table_name, fixture_path, file_filter
|
268
|
-
@class_name = Inflector.classify(@table_name)
|
269
280
|
|
281
|
+
@class_name = Inflector.classify(@table_name)
|
282
|
+
@table_name = ActiveRecord::Base.table_name_prefix + @table_name + ActiveRecord::Base.table_name_suffix
|
270
283
|
read_fixture_files
|
271
284
|
end
|
272
285
|
|
@@ -285,8 +298,12 @@ class Fixtures < Hash
|
|
285
298
|
if File.file?(yaml_file_path)
|
286
299
|
# YAML fixtures
|
287
300
|
begin
|
288
|
-
yaml = YAML::load(erb_render(IO.read(yaml_file_path)))
|
289
|
-
|
301
|
+
if yaml = YAML::load(erb_render(IO.read(yaml_file_path)))
|
302
|
+
yaml = yaml.value if yaml.respond_to?(:type_id) and yaml.respond_to?(:value)
|
303
|
+
yaml.each do |name, data|
|
304
|
+
self[name] = Fixture.new(data, @class_name)
|
305
|
+
end
|
306
|
+
end
|
290
307
|
rescue Exception=>boom
|
291
308
|
raise Fixture::FormatError, "a YAML error occured parsing #{yaml_file_path}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{boom.class}: #{boom}"
|
292
309
|
end
|
@@ -342,7 +359,15 @@ class Fixture #:nodoc:
|
|
342
359
|
end
|
343
360
|
|
344
361
|
def initialize(fixture, class_name)
|
345
|
-
|
362
|
+
case fixture
|
363
|
+
when Hash, YAML::Omap
|
364
|
+
@fixture = fixture
|
365
|
+
when String
|
366
|
+
@fixture = read_fixture_file(fixture)
|
367
|
+
else
|
368
|
+
raise ArgumentError, "Bad fixture argument #{fixture.inspect}"
|
369
|
+
end
|
370
|
+
|
346
371
|
@class_name = class_name
|
347
372
|
end
|
348
373
|
|
@@ -397,8 +422,6 @@ end
|
|
397
422
|
module Test #:nodoc:
|
398
423
|
module Unit #:nodoc:
|
399
424
|
class TestCase #:nodoc:
|
400
|
-
include ClassInheritableAttributes
|
401
|
-
|
402
425
|
cattr_accessor :fixture_path
|
403
426
|
class_inheritable_accessor :fixture_table_names
|
404
427
|
class_inheritable_accessor :use_transactional_fixtures
|
@@ -63,7 +63,7 @@ module ActiveRecord
|
|
63
63
|
# * <tt>change_column(table_name, column_name, type, options)</tt>: Changes the column to a different type using the same
|
64
64
|
# parameters as add_column.
|
65
65
|
# * <tt>remove_column(table_name, column_name)</tt>: Removes the column named +column_name+ from the table called +table_name+.
|
66
|
-
# * <tt>add_index(table_name, column_name)</tt>: Add a new index with the name of the column on the column.
|
66
|
+
# * <tt>add_index(table_name, column_name, index_type)</tt>: Add a new index with the name of the column on the column. Specify an optional index_type (e.g. UNIQUE).
|
67
67
|
# * <tt>remove_index(table_name, column_name)</tt>: Remove the index called the same as the column.
|
68
68
|
#
|
69
69
|
# == Irreversible transformations
|
@@ -120,6 +120,21 @@ module ActiveRecord
|
|
120
120
|
# execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
|
121
121
|
# end
|
122
122
|
# end
|
123
|
+
#
|
124
|
+
# == Using the class after changing table
|
125
|
+
#
|
126
|
+
# Some times you'll want to add a column in a migration and populate it immediately after. In that case, you'll need
|
127
|
+
# to make a call to Base#reset_column_information in order to ensure that the class has the latest column data from
|
128
|
+
# after the new column was added. Example:
|
129
|
+
#
|
130
|
+
# class MakeJoinUnique < ActiveRecord::Migration
|
131
|
+
# def self.up
|
132
|
+
# add_column :people, :salary, :integer
|
133
|
+
# Person.find(:all).each do |p|
|
134
|
+
# p.salary = SalaryCalculator.compute(p)
|
135
|
+
# end
|
136
|
+
# end
|
137
|
+
# end
|
123
138
|
class Migration
|
124
139
|
class << self
|
125
140
|
def up() end
|
@@ -127,6 +142,7 @@ module ActiveRecord
|
|
127
142
|
|
128
143
|
private
|
129
144
|
def method_missing(method, *arguments, &block)
|
145
|
+
arguments[0] = Migrator.proper_table_name(arguments.first) unless arguments.empty?
|
130
146
|
ActiveRecord::Base.connection.send(method, *arguments, &block)
|
131
147
|
end
|
132
148
|
end
|
@@ -135,6 +151,7 @@ module ActiveRecord
|
|
135
151
|
class Migrator#:nodoc:
|
136
152
|
class << self
|
137
153
|
def migrate(migrations_path, target_version = nil)
|
154
|
+
Base.connection.initialize_schema_information
|
138
155
|
case
|
139
156
|
when target_version.nil?, current_version < target_version
|
140
157
|
up(migrations_path, target_version)
|
@@ -153,9 +170,19 @@ module ActiveRecord
|
|
153
170
|
self.new(:down, migrations_path, target_version).migrate
|
154
171
|
end
|
155
172
|
|
173
|
+
def schema_info_table_name
|
174
|
+
Base.table_name_prefix + "schema_info" + Base.table_name_suffix
|
175
|
+
end
|
176
|
+
|
156
177
|
def current_version
|
157
|
-
Base.connection.select_one("SELECT version FROM
|
178
|
+
(Base.connection.select_one("SELECT version FROM #{schema_info_table_name}") || {"version" => 0})["version"].to_i
|
179
|
+
end
|
180
|
+
|
181
|
+
def proper_table_name(name)
|
182
|
+
# Use the ActiveRecord objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string
|
183
|
+
name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
|
158
184
|
end
|
185
|
+
|
159
186
|
end
|
160
187
|
|
161
188
|
def initialize(direction, migrations_path, target_version = nil)
|
@@ -191,10 +218,12 @@ module ActiveRecord
|
|
191
218
|
end
|
192
219
|
|
193
220
|
def migration_files
|
194
|
-
files = Dir["#{@migrations_path}/[0-9]*_*.rb"].
|
221
|
+
files = Dir["#{@migrations_path}/[0-9]*_*.rb"].sort_by do |f|
|
222
|
+
migration_version_and_name(f).first.to_i
|
223
|
+
end
|
195
224
|
down? ? files.reverse : files
|
196
225
|
end
|
197
|
-
|
226
|
+
|
198
227
|
def migration_class(migration_name)
|
199
228
|
migration_name.camelize.constantize
|
200
229
|
end
|
@@ -204,7 +233,7 @@ module ActiveRecord
|
|
204
233
|
end
|
205
234
|
|
206
235
|
def set_schema_version(version)
|
207
|
-
Base.connection.update("UPDATE
|
236
|
+
Base.connection.update("UPDATE #{self.class.schema_info_table_name} SET version = #{down? ? version.to_i - 1 : version.to_i}")
|
208
237
|
end
|
209
238
|
|
210
239
|
def up?
|
@@ -1,6 +1,33 @@
|
|
1
1
|
require 'singleton'
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
+
module Observing # :nodoc:
|
5
|
+
def self.append_features(base)
|
6
|
+
super
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
# Activates the observers assigned. Examples:
|
12
|
+
#
|
13
|
+
# # Calls PersonObserver.instance
|
14
|
+
# ActiveRecord::Base.observers = :person_observer
|
15
|
+
#
|
16
|
+
# # Calls Cacher.instance and GarbageCollector.instance
|
17
|
+
# ActiveRecord::Base.observers = :cacher, :garbage_collector
|
18
|
+
#
|
19
|
+
# # Same as above, just using explicit class references
|
20
|
+
# ActiveRecord::Base.observers = Cacher, GarbageCollector
|
21
|
+
def observers=(*observers)
|
22
|
+
observers = [ observers ].flatten.each do |observer|
|
23
|
+
observer.is_a?(Symbol) ?
|
24
|
+
observer.to_s.camelize.constantize.instance :
|
25
|
+
observer.instance
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
4
31
|
# Observer classes respond to lifecycle callbacks to implement trigger-like
|
5
32
|
# behavior outside the original class. This is a great way to reduce the
|
6
33
|
# clutter that normally comes when the model class is burdened with
|
@@ -48,7 +75,8 @@ module ActiveRecord
|
|
48
75
|
# == Triggering Observers
|
49
76
|
#
|
50
77
|
# In order to activate an observer, you need to call Observer.instance. In Rails, this can be done in controllers
|
51
|
-
# using the short-hand of for example observer :comment_observer.
|
78
|
+
# using the short-hand of for example observer :comment_observer. Or directly from Active Record, with
|
79
|
+
# ActiveRecord::Base.observer(:comment_observer).
|
52
80
|
class Observer
|
53
81
|
include Singleton
|
54
82
|
|
@@ -58,7 +86,9 @@ module ActiveRecord
|
|
58
86
|
end
|
59
87
|
|
60
88
|
def initialize
|
61
|
-
[ observed_class ].flatten
|
89
|
+
observed_classes = [ observed_class ].flatten
|
90
|
+
observed_subclasses_class = observed_classes.collect {|c| c.send(:subclasses) }.flatten!
|
91
|
+
(observed_classes + observed_subclasses_class).each do |klass|
|
62
92
|
klass.add_observer(self)
|
63
93
|
klass.send(:define_method, :after_find) unless klass.respond_to?(:after_find)
|
64
94
|
end
|
@@ -10,7 +10,7 @@ module ActiveRecord
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def select_all(sql, name = nil)
|
13
|
-
@query_cache[sql] ||= @connection.select_all(sql, name)
|
13
|
+
(@query_cache[sql] ||= @connection.select_all(sql, name)).dup
|
14
14
|
end
|
15
15
|
|
16
16
|
def select_one(sql, name = nil)
|
@@ -37,21 +37,22 @@ module ActiveRecord
|
|
37
37
|
end
|
38
38
|
|
39
39
|
private
|
40
|
-
def method_missing(method, *arguments)
|
41
|
-
@connection.send(method, *arguments)
|
40
|
+
def method_missing(method, *arguments, &proc)
|
41
|
+
@connection.send(method, *arguments, &proc)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
45
|
class Base
|
46
46
|
# Set the connection for the class with caching on
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
47
|
+
class << self
|
48
|
+
alias_method :connection_without_query_cache=, :connection=
|
49
|
+
|
50
|
+
def connection=(spec)
|
51
|
+
if spec.is_a?(ConnectionSpecification) and spec.config[:query_cache]
|
52
|
+
spec = QueryCache.new(self.send(spec.adapter_method, spec.config))
|
53
|
+
end
|
54
|
+
self.connection_without_query_cache = spec
|
55
|
+
end
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
# Allows programmers to programmatically define a schema in a portable
|
3
|
+
# DSL. This means you can define tables, indexes, etc. without using SQL
|
4
|
+
# directly, so your applications can more easily support multiple
|
5
|
+
# databases.
|
6
|
+
#
|
7
|
+
# Usage:
|
8
|
+
#
|
9
|
+
# ActiveRecord::Schema.define do
|
10
|
+
# create_table :authors do |t|
|
11
|
+
# t.column :name, :string, :null => false
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# add_index :authors, :name, :unique
|
15
|
+
#
|
16
|
+
# create_table :posts do |t|
|
17
|
+
# t.column :author_id, :integer, :null => false
|
18
|
+
# t.column :subject, :string
|
19
|
+
# t.column :body, :text
|
20
|
+
# t.column :private, :boolean, :default => false
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# add_index :posts, :author_id
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# ActiveRecord::Schema is only supported by database adapters that also
|
27
|
+
# support migrations, the two features being very similar.
|
28
|
+
class Schema < Migration
|
29
|
+
private_class_method :new
|
30
|
+
|
31
|
+
# Eval the given block. All methods available to the current connection
|
32
|
+
# adapter are available within the block, so you can easily use the
|
33
|
+
# database definition DSL to build up your schema (#create_table,
|
34
|
+
# #add_index, etc.).
|
35
|
+
#
|
36
|
+
# The +info+ hash is optional, and if given is used to define metadata
|
37
|
+
# about the current schema (like the schema's version):
|
38
|
+
#
|
39
|
+
# ActiveRecord::Schema.define(:version => 15) do
|
40
|
+
# ...
|
41
|
+
# end
|
42
|
+
def self.define(info={}, &block)
|
43
|
+
instance_eval(&block)
|
44
|
+
|
45
|
+
unless info.empty?
|
46
|
+
initialize_schema_information
|
47
|
+
cols = columns('schema_info')
|
48
|
+
|
49
|
+
info = info.map do |k,v|
|
50
|
+
v = quote(v, cols.detect { |c| c.name == k.to_s })
|
51
|
+
"#{k} = #{v}"
|
52
|
+
end
|
53
|
+
|
54
|
+
update "UPDATE schema_info SET #{info.join(", ")}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|