activerecord 3.1.9 → 3.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +6 -6
- data/CHANGELOG.md +317 -336
- data/README.rdoc +3 -3
- data/examples/performance.rb +20 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +3 -6
- data/lib/active_record/associations/association.rb +3 -42
- data/lib/active_record/associations/association_scope.rb +3 -15
- data/lib/active_record/associations/builder/association.rb +6 -4
- data/lib/active_record/associations/builder/belongs_to.rb +3 -3
- data/lib/active_record/associations/builder/collection_association.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +5 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -16
- data/lib/active_record/associations/collection_association.rb +64 -31
- data/lib/active_record/associations/collection_proxy.rb +2 -37
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +1 -0
- data/lib/active_record/associations/has_many_association.rb +5 -1
- data/lib/active_record/associations/has_many_through_association.rb +28 -9
- data/lib/active_record/associations/has_one_association.rb +15 -13
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/preloader.rb +14 -10
- data/lib/active_record/associations/through_association.rb +7 -3
- data/lib/active_record/associations.rb +92 -76
- data/lib/active_record/attribute_assignment.rb +221 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
- data/lib/active_record/attribute_methods/dirty.rb +21 -11
- data/lib/active_record/attribute_methods/primary_key.rb +62 -25
- data/lib/active_record/attribute_methods/read.rb +73 -83
- data/lib/active_record/attribute_methods/serialization.rb +102 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +23 -17
- data/lib/active_record/attribute_methods/write.rb +31 -6
- data/lib/active_record/attribute_methods.rb +231 -30
- data/lib/active_record/autosave_association.rb +43 -22
- data/lib/active_record/base.rb +227 -1708
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +150 -148
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +85 -29
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +6 -33
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +10 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -26
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -19
- data/lib/active_record/connection_adapters/abstract_adapter.rb +77 -42
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +674 -0
- data/lib/active_record/connection_adapters/column.rb +37 -11
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +129 -581
- data/lib/active_record/connection_adapters/mysql_adapter.rb +137 -696
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -86
- data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +55 -32
- data/lib/active_record/counter_cache.rb +9 -4
- data/lib/active_record/dynamic_finder_match.rb +12 -0
- data/lib/active_record/dynamic_matchers.rb +84 -0
- data/lib/active_record/errors.rb +11 -1
- data/lib/active_record/explain.rb +85 -0
- data/lib/active_record/explain_subscriber.rb +25 -0
- data/lib/active_record/fixtures/file.rb +65 -0
- data/lib/active_record/fixtures.rb +56 -85
- data/lib/active_record/identity_map.rb +3 -4
- data/lib/active_record/inheritance.rb +174 -0
- data/lib/active_record/integration.rb +49 -0
- data/lib/active_record/locking/optimistic.rb +30 -25
- data/lib/active_record/locking/pessimistic.rb +23 -1
- data/lib/active_record/log_subscriber.rb +3 -3
- data/lib/active_record/migration/command_recorder.rb +8 -8
- data/lib/active_record/migration.rb +68 -35
- data/lib/active_record/model_schema.rb +366 -0
- data/lib/active_record/nested_attributes.rb +3 -2
- data/lib/active_record/persistence.rb +57 -11
- data/lib/active_record/querying.rb +58 -0
- data/lib/active_record/railtie.rb +31 -29
- data/lib/active_record/railties/controller_runtime.rb +3 -1
- data/lib/active_record/railties/databases.rake +191 -110
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +26 -0
- data/lib/active_record/reflection.rb +7 -15
- data/lib/active_record/relation/batches.rb +5 -2
- data/lib/active_record/relation/calculations.rb +47 -15
- data/lib/active_record/relation/delegation.rb +49 -0
- data/lib/active_record/relation/finder_methods.rb +9 -7
- data/lib/active_record/relation/predicate_builder.rb +18 -7
- data/lib/active_record/relation/query_methods.rb +75 -9
- data/lib/active_record/relation/spawn_methods.rb +11 -2
- data/lib/active_record/relation.rb +78 -32
- data/lib/active_record/result.rb +1 -1
- data/lib/active_record/sanitization.rb +194 -0
- data/lib/active_record/schema_dumper.rb +12 -5
- data/lib/active_record/scoping/default.rb +142 -0
- data/lib/active_record/scoping/named.rb +202 -0
- data/lib/active_record/scoping.rb +152 -0
- data/lib/active_record/serialization.rb +1 -43
- data/lib/active_record/serializers/xml_serializer.rb +4 -45
- data/lib/active_record/session_store.rb +17 -15
- data/lib/active_record/store.rb +52 -0
- data/lib/active_record/test_case.rb +11 -7
- data/lib/active_record/timestamp.rb +17 -3
- data/lib/active_record/transactions.rb +27 -6
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations/associated.rb +5 -4
- data/lib/active_record/validations/uniqueness.rb +7 -7
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/version.rb +2 -2
- data/lib/active_record.rb +38 -3
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +12 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -1
- data/lib/rails/generators/active_record/model/templates/migration.rb +3 -5
- data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
- metadata +30 -10
- data/lib/active_record/named_scope.rb +0 -200
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module ActiveRecord
|
|
2
2
|
class Migration
|
|
3
|
-
# ActiveRecord::Migration::CommandRecorder records commands done during
|
|
4
|
-
# a migration and knows how to reverse those commands.
|
|
3
|
+
# <tt>ActiveRecord::Migration::CommandRecorder</tt> records commands done during
|
|
4
|
+
# a migration and knows how to reverse those commands. The CommandRecorder
|
|
5
5
|
# knows how to invert the following commands:
|
|
6
6
|
#
|
|
7
7
|
# * add_column
|
|
8
8
|
# * add_index
|
|
9
|
-
# *
|
|
9
|
+
# * add_timestamps
|
|
10
10
|
# * create_table
|
|
11
11
|
# * remove_timestamps
|
|
12
12
|
# * rename_column
|
|
@@ -20,21 +20,21 @@ module ActiveRecord
|
|
|
20
20
|
@delegate = delegate
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
# record +command+.
|
|
23
|
+
# record +command+. +command+ should be a method name and arguments.
|
|
24
24
|
# For example:
|
|
25
25
|
#
|
|
26
|
-
# recorder.record(:method_name, [:arg1, arg2])
|
|
26
|
+
# recorder.record(:method_name, [:arg1, :arg2])
|
|
27
27
|
def record(*command)
|
|
28
28
|
@commands << command
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
# Returns a list that represents commands that are the inverse of the
|
|
32
|
-
# commands stored in +commands+.
|
|
32
|
+
# commands stored in +commands+. For example:
|
|
33
33
|
#
|
|
34
34
|
# recorder.record(:rename_table, [:old, :new])
|
|
35
35
|
# recorder.inverse # => [:rename_table, [:new, :old]]
|
|
36
36
|
#
|
|
37
|
-
# This method will raise an IrreversibleMigration exception if it cannot
|
|
37
|
+
# This method will raise an +IrreversibleMigration+ exception if it cannot
|
|
38
38
|
# invert the +commands+.
|
|
39
39
|
def inverse
|
|
40
40
|
@commands.reverse.map { |name, args|
|
|
@@ -59,7 +59,7 @@ module ActiveRecord
|
|
|
59
59
|
private
|
|
60
60
|
|
|
61
61
|
def invert_create_table(args)
|
|
62
|
-
[:drop_table, args]
|
|
62
|
+
[:drop_table, [args.first]]
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def invert_rename_table(args)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require "active_support/core_ext/module/delegation"
|
|
2
2
|
require "active_support/core_ext/class/attribute_accessors"
|
|
3
3
|
require "active_support/core_ext/array/wrap"
|
|
4
|
+
require 'active_support/deprecation'
|
|
4
5
|
|
|
5
6
|
module ActiveRecord
|
|
6
7
|
# Exception that can be raised to stop migrations from going backwards.
|
|
@@ -68,9 +69,9 @@ module ActiveRecord
|
|
|
68
69
|
# create_table :system_settings do |t|
|
|
69
70
|
# t.string :name
|
|
70
71
|
# t.string :label
|
|
71
|
-
# t.text
|
|
72
|
+
# t.text :value
|
|
72
73
|
# t.string :type
|
|
73
|
-
# t.integer
|
|
74
|
+
# t.integer :position
|
|
74
75
|
# end
|
|
75
76
|
#
|
|
76
77
|
# SystemSetting.create :name => "notice",
|
|
@@ -116,8 +117,9 @@ module ActiveRecord
|
|
|
116
117
|
# +column_names+ from the table called +table_name+.
|
|
117
118
|
# * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
|
|
118
119
|
# with the name of the column. Other options include
|
|
119
|
-
# <tt>:name</tt
|
|
120
|
-
# <tt>{ :name => "users_name_index", :unique => true }</tt>)
|
|
120
|
+
# <tt>:name</tt>, <tt>:unique</tt> (e.g.
|
|
121
|
+
# <tt>{ :name => "users_name_index", :unique => true }</tt>) and <tt>:order</tt>
|
|
122
|
+
# (e.g. { :order => {:name => :desc} }</tt>).
|
|
121
123
|
# * <tt>remove_index(table_name, :column => column_name)</tt>: Removes the index
|
|
122
124
|
# specified by +column_name+.
|
|
123
125
|
# * <tt>remove_index(table_name, :name => index_name)</tt>: Removes the index
|
|
@@ -183,7 +185,7 @@ module ActiveRecord
|
|
|
183
185
|
#
|
|
184
186
|
# class RemoveEmptyTags < ActiveRecord::Migration
|
|
185
187
|
# def up
|
|
186
|
-
# Tag.
|
|
188
|
+
# Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
|
|
187
189
|
# end
|
|
188
190
|
#
|
|
189
191
|
# def down
|
|
@@ -229,7 +231,7 @@ module ActiveRecord
|
|
|
229
231
|
# def up
|
|
230
232
|
# add_column :people, :salary, :integer
|
|
231
233
|
# Person.reset_column_information
|
|
232
|
-
# Person.
|
|
234
|
+
# Person.all.each do |p|
|
|
233
235
|
# p.update_attribute :salary, SalaryCalculator.compute(p)
|
|
234
236
|
# end
|
|
235
237
|
# end
|
|
@@ -249,7 +251,7 @@ module ActiveRecord
|
|
|
249
251
|
# def up
|
|
250
252
|
# ...
|
|
251
253
|
# say_with_time "Updating salaries..." do
|
|
252
|
-
# Person.
|
|
254
|
+
# Person.all.each do |p|
|
|
253
255
|
# p.update_attribute :salary, SalaryCalculator.compute(p)
|
|
254
256
|
# end
|
|
255
257
|
# end
|
|
@@ -301,7 +303,7 @@ module ActiveRecord
|
|
|
301
303
|
#
|
|
302
304
|
# class TenderloveMigration < ActiveRecord::Migration
|
|
303
305
|
# def change
|
|
304
|
-
# create_table(:horses) do
|
|
306
|
+
# create_table(:horses) do |t|
|
|
305
307
|
# t.column :content, :text
|
|
306
308
|
# t.column :remind_at, :datetime
|
|
307
309
|
# end
|
|
@@ -344,12 +346,24 @@ module ActiveRecord
|
|
|
344
346
|
@name = self.class.name
|
|
345
347
|
@version = nil
|
|
346
348
|
@connection = nil
|
|
349
|
+
@reverting = false
|
|
347
350
|
end
|
|
348
351
|
|
|
349
352
|
# instantiate the delegate object after initialize is defined
|
|
350
353
|
self.verbose = true
|
|
351
354
|
self.delegate = new
|
|
352
355
|
|
|
356
|
+
def revert
|
|
357
|
+
@reverting = true
|
|
358
|
+
yield
|
|
359
|
+
ensure
|
|
360
|
+
@reverting = false
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def reverting?
|
|
364
|
+
@reverting
|
|
365
|
+
end
|
|
366
|
+
|
|
353
367
|
def up
|
|
354
368
|
self.class.delegate = self
|
|
355
369
|
return unless self.class.respond_to?(:up)
|
|
@@ -383,9 +397,11 @@ module ActiveRecord
|
|
|
383
397
|
end
|
|
384
398
|
@connection = conn
|
|
385
399
|
time = Benchmark.measure {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
400
|
+
self.revert {
|
|
401
|
+
recorder.inverse.each do |cmd, args|
|
|
402
|
+
send(cmd, *args)
|
|
403
|
+
end
|
|
404
|
+
}
|
|
389
405
|
}
|
|
390
406
|
else
|
|
391
407
|
time = Benchmark.measure { change }
|
|
@@ -440,8 +456,11 @@ module ActiveRecord
|
|
|
440
456
|
arg_list = arguments.map{ |a| a.inspect } * ', '
|
|
441
457
|
|
|
442
458
|
say_with_time "#{method}(#{arg_list})" do
|
|
443
|
-
unless
|
|
444
|
-
arguments
|
|
459
|
+
unless reverting?
|
|
460
|
+
unless arguments.empty? || method == :execute
|
|
461
|
+
arguments[0] = Migrator.proper_table_name(arguments.first)
|
|
462
|
+
arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
|
|
463
|
+
end
|
|
445
464
|
end
|
|
446
465
|
return super unless connection.respond_to?(method)
|
|
447
466
|
connection.send(method, *arguments, &block)
|
|
@@ -455,26 +474,28 @@ module ActiveRecord
|
|
|
455
474
|
|
|
456
475
|
destination_migrations = ActiveRecord::Migrator.migrations(destination)
|
|
457
476
|
last = destination_migrations.last
|
|
458
|
-
sources.each do |
|
|
477
|
+
sources.each do |scope, path|
|
|
459
478
|
source_migrations = ActiveRecord::Migrator.migrations(path)
|
|
460
479
|
|
|
461
480
|
source_migrations.each do |migration|
|
|
462
481
|
source = File.read(migration.filename)
|
|
463
|
-
source = "# This migration comes from #{
|
|
482
|
+
source = "# This migration comes from #{scope} (originally #{migration.version})\n#{source}"
|
|
464
483
|
|
|
465
484
|
if duplicate = destination_migrations.detect { |m| m.name == migration.name }
|
|
466
|
-
options[:on_skip]
|
|
485
|
+
if options[:on_skip] && duplicate.scope != scope.to_s
|
|
486
|
+
options[:on_skip].call(scope, migration)
|
|
487
|
+
end
|
|
467
488
|
next
|
|
468
489
|
end
|
|
469
490
|
|
|
470
491
|
migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
|
|
471
|
-
new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.rb")
|
|
492
|
+
new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
|
|
472
493
|
old_path, migration.filename = migration.filename, new_path
|
|
473
494
|
last = migration
|
|
474
495
|
|
|
475
|
-
|
|
496
|
+
File.open(migration.filename, "w") { |f| f.write source }
|
|
476
497
|
copied << migration
|
|
477
|
-
options[:on_copy].call(
|
|
498
|
+
options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
|
|
478
499
|
destination_migrations << migration
|
|
479
500
|
end
|
|
480
501
|
end
|
|
@@ -493,9 +514,9 @@ module ActiveRecord
|
|
|
493
514
|
|
|
494
515
|
# MigrationProxy is used to defer loading of the actual migration classes
|
|
495
516
|
# until they are needed
|
|
496
|
-
class MigrationProxy < Struct.new(:name, :version, :filename)
|
|
517
|
+
class MigrationProxy < Struct.new(:name, :version, :filename, :scope)
|
|
497
518
|
|
|
498
|
-
def initialize(name, version, filename)
|
|
519
|
+
def initialize(name, version, filename, scope)
|
|
499
520
|
super
|
|
500
521
|
@migration = nil
|
|
501
522
|
end
|
|
@@ -504,7 +525,7 @@ module ActiveRecord
|
|
|
504
525
|
File.basename(filename)
|
|
505
526
|
end
|
|
506
527
|
|
|
507
|
-
delegate :migrate, :announce, :write, :to
|
|
528
|
+
delegate :migrate, :announce, :write, :to => :migration
|
|
508
529
|
|
|
509
530
|
private
|
|
510
531
|
|
|
@@ -524,16 +545,16 @@ module ActiveRecord
|
|
|
524
545
|
attr_writer :migrations_paths
|
|
525
546
|
alias :migrations_path= :migrations_paths=
|
|
526
547
|
|
|
527
|
-
def migrate(migrations_paths, target_version = nil)
|
|
548
|
+
def migrate(migrations_paths, target_version = nil, &block)
|
|
528
549
|
case
|
|
529
550
|
when target_version.nil?
|
|
530
|
-
up(migrations_paths, target_version)
|
|
551
|
+
up(migrations_paths, target_version, &block)
|
|
531
552
|
when current_version == 0 && target_version == 0
|
|
532
553
|
[]
|
|
533
554
|
when current_version > target_version
|
|
534
|
-
down(migrations_paths, target_version)
|
|
555
|
+
down(migrations_paths, target_version, &block)
|
|
535
556
|
else
|
|
536
|
-
up(migrations_paths, target_version)
|
|
557
|
+
up(migrations_paths, target_version, &block)
|
|
537
558
|
end
|
|
538
559
|
end
|
|
539
560
|
|
|
@@ -545,12 +566,12 @@ module ActiveRecord
|
|
|
545
566
|
move(:up, migrations_paths, steps)
|
|
546
567
|
end
|
|
547
568
|
|
|
548
|
-
def up(migrations_paths, target_version = nil)
|
|
549
|
-
self.new(:up, migrations_paths, target_version).migrate
|
|
569
|
+
def up(migrations_paths, target_version = nil, &block)
|
|
570
|
+
self.new(:up, migrations_paths, target_version).migrate(&block)
|
|
550
571
|
end
|
|
551
572
|
|
|
552
|
-
def down(migrations_paths, target_version = nil)
|
|
553
|
-
self.new(:down, migrations_paths, target_version).migrate
|
|
573
|
+
def down(migrations_paths, target_version = nil, &block)
|
|
574
|
+
self.new(:down, migrations_paths, target_version).migrate(&block)
|
|
554
575
|
end
|
|
555
576
|
|
|
556
577
|
def run(direction, migrations_paths, target_version)
|
|
@@ -590,15 +611,23 @@ module ActiveRecord
|
|
|
590
611
|
migrations_paths.first
|
|
591
612
|
end
|
|
592
613
|
|
|
593
|
-
def migrations(paths)
|
|
614
|
+
def migrations(paths, *args)
|
|
615
|
+
if args.empty?
|
|
616
|
+
subdirectories = true
|
|
617
|
+
else
|
|
618
|
+
subdirectories = args.first
|
|
619
|
+
ActiveSupport::Deprecation.warn "The `subdirectories` argument to `migrations` is deprecated"
|
|
620
|
+
end
|
|
621
|
+
|
|
594
622
|
paths = Array.wrap(paths)
|
|
595
623
|
|
|
596
|
-
|
|
624
|
+
glob = subdirectories ? "**/" : ""
|
|
625
|
+
files = Dir[*paths.map { |p| "#{p}/#{glob}[0-9]*_*.rb" }]
|
|
597
626
|
|
|
598
627
|
seen = Hash.new false
|
|
599
628
|
|
|
600
629
|
migrations = files.map do |file|
|
|
601
|
-
version, name = file.scan(/([0-9]+)_([_a-z0-9]*)
|
|
630
|
+
version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first
|
|
602
631
|
|
|
603
632
|
raise IllegalMigrationNameError.new(file) unless version
|
|
604
633
|
version = version.to_i
|
|
@@ -609,7 +638,7 @@ module ActiveRecord
|
|
|
609
638
|
|
|
610
639
|
seen[version] = seen[name] = true
|
|
611
640
|
|
|
612
|
-
MigrationProxy.new(name, version, file)
|
|
641
|
+
MigrationProxy.new(name, version, file, scope)
|
|
613
642
|
end
|
|
614
643
|
|
|
615
644
|
migrations.sort_by(&:version)
|
|
@@ -652,7 +681,7 @@ module ActiveRecord
|
|
|
652
681
|
end
|
|
653
682
|
end
|
|
654
683
|
|
|
655
|
-
def migrate
|
|
684
|
+
def migrate(&block)
|
|
656
685
|
current = migrations.detect { |m| m.version == current_version }
|
|
657
686
|
target = migrations.detect { |m| m.version == @target_version }
|
|
658
687
|
|
|
@@ -669,6 +698,10 @@ module ActiveRecord
|
|
|
669
698
|
|
|
670
699
|
ran = []
|
|
671
700
|
runnable.each do |migration|
|
|
701
|
+
if block && !block.call(migration)
|
|
702
|
+
next
|
|
703
|
+
end
|
|
704
|
+
|
|
672
705
|
Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
|
|
673
706
|
|
|
674
707
|
seen = migrated.include?(migration.version.to_i)
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
require 'active_support/concern'
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ModelSchema
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
##
|
|
9
|
+
# :singleton-method:
|
|
10
|
+
# Accessor for the prefix type that will be prepended to every primary key column name.
|
|
11
|
+
# The options are :table_name and :table_name_with_underscore. If the first is specified,
|
|
12
|
+
# the Product class will look for "productid" instead of "id" as the primary column. If the
|
|
13
|
+
# latter is specified, the Product class will look for "product_id" instead of "id". Remember
|
|
14
|
+
# that this is a global setting for all Active Records.
|
|
15
|
+
cattr_accessor :primary_key_prefix_type, :instance_writer => false
|
|
16
|
+
self.primary_key_prefix_type = nil
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# :singleton-method:
|
|
20
|
+
# Accessor for the name of the prefix string to prepend to every table name. So if set
|
|
21
|
+
# to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
|
|
22
|
+
# etc. This is a convenient way of creating a namespace for tables in a shared database.
|
|
23
|
+
# By default, the prefix is the empty string.
|
|
24
|
+
#
|
|
25
|
+
# If you are organising your models within modules you can add a prefix to the models within
|
|
26
|
+
# a namespace by defining a singleton method in the parent module called table_name_prefix which
|
|
27
|
+
# returns your chosen prefix.
|
|
28
|
+
class_attribute :table_name_prefix, :instance_writer => false
|
|
29
|
+
self.table_name_prefix = ""
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# :singleton-method:
|
|
33
|
+
# Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
|
|
34
|
+
# "people_basecamp"). By default, the suffix is the empty string.
|
|
35
|
+
class_attribute :table_name_suffix, :instance_writer => false
|
|
36
|
+
self.table_name_suffix = ""
|
|
37
|
+
|
|
38
|
+
##
|
|
39
|
+
# :singleton-method:
|
|
40
|
+
# Indicates whether table names should be the pluralized versions of the corresponding class names.
|
|
41
|
+
# If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
|
|
42
|
+
# See table_name for the full rules on table/class naming. This is true, by default.
|
|
43
|
+
class_attribute :pluralize_table_names, :instance_writer => false
|
|
44
|
+
self.pluralize_table_names = true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
module ClassMethods
|
|
48
|
+
# Guesses the table name (in forced lower-case) based on the name of the class in the
|
|
49
|
+
# inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy
|
|
50
|
+
# looks like: Reply < Message < ActiveRecord::Base, then Message is used
|
|
51
|
+
# to guess the table name even when called on Reply. The rules used to do the guess
|
|
52
|
+
# are handled by the Inflector class in Active Support, which knows almost all common
|
|
53
|
+
# English inflections. You can add new inflections in config/initializers/inflections.rb.
|
|
54
|
+
#
|
|
55
|
+
# Nested classes are given table names prefixed by the singular form of
|
|
56
|
+
# the parent's table name. Enclosing modules are not considered.
|
|
57
|
+
#
|
|
58
|
+
# ==== Examples
|
|
59
|
+
#
|
|
60
|
+
# class Invoice < ActiveRecord::Base
|
|
61
|
+
# end
|
|
62
|
+
#
|
|
63
|
+
# file class table_name
|
|
64
|
+
# invoice.rb Invoice invoices
|
|
65
|
+
#
|
|
66
|
+
# class Invoice < ActiveRecord::Base
|
|
67
|
+
# class Lineitem < ActiveRecord::Base
|
|
68
|
+
# end
|
|
69
|
+
# end
|
|
70
|
+
#
|
|
71
|
+
# file class table_name
|
|
72
|
+
# invoice.rb Invoice::Lineitem invoice_lineitems
|
|
73
|
+
#
|
|
74
|
+
# module Invoice
|
|
75
|
+
# class Lineitem < ActiveRecord::Base
|
|
76
|
+
# end
|
|
77
|
+
# end
|
|
78
|
+
#
|
|
79
|
+
# file class table_name
|
|
80
|
+
# invoice/lineitem.rb Invoice::Lineitem lineitems
|
|
81
|
+
#
|
|
82
|
+
# Additionally, the class-level +table_name_prefix+ is prepended and the
|
|
83
|
+
# +table_name_suffix+ is appended. So if you have "myapp_" as a prefix,
|
|
84
|
+
# the table name guess for an Invoice class becomes "myapp_invoices".
|
|
85
|
+
# Invoice::Lineitem becomes "myapp_invoice_lineitems".
|
|
86
|
+
#
|
|
87
|
+
# You can also set your own table name explicitly:
|
|
88
|
+
#
|
|
89
|
+
# class Mouse < ActiveRecord::Base
|
|
90
|
+
# self.table_name = "mice"
|
|
91
|
+
# end
|
|
92
|
+
#
|
|
93
|
+
# Alternatively, you can override the table_name method to define your
|
|
94
|
+
# own computation. (Possibly using <tt>super</tt> to manipulate the default
|
|
95
|
+
# table name.) Example:
|
|
96
|
+
#
|
|
97
|
+
# class Post < ActiveRecord::Base
|
|
98
|
+
# def self.table_name
|
|
99
|
+
# "special_" + super
|
|
100
|
+
# end
|
|
101
|
+
# end
|
|
102
|
+
# Post.table_name # => "special_posts"
|
|
103
|
+
def table_name
|
|
104
|
+
reset_table_name unless defined?(@table_name)
|
|
105
|
+
@table_name
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def original_table_name #:nodoc:
|
|
109
|
+
deprecated_original_property_getter :table_name
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Sets the table name explicitly. Example:
|
|
113
|
+
#
|
|
114
|
+
# class Project < ActiveRecord::Base
|
|
115
|
+
# self.table_name = "project"
|
|
116
|
+
# end
|
|
117
|
+
#
|
|
118
|
+
# You can also just define your own <tt>self.table_name</tt> method; see
|
|
119
|
+
# the documentation for ActiveRecord::Base#table_name.
|
|
120
|
+
def table_name=(value)
|
|
121
|
+
@original_table_name = @table_name if defined?(@table_name)
|
|
122
|
+
@table_name = value && value.to_s
|
|
123
|
+
@quoted_table_name = nil
|
|
124
|
+
@arel_table = nil
|
|
125
|
+
@relation = Relation.new(self, arel_table)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def set_table_name(value = nil, &block) #:nodoc:
|
|
129
|
+
deprecated_property_setter :table_name, value, block
|
|
130
|
+
@quoted_table_name = nil
|
|
131
|
+
@arel_table = nil
|
|
132
|
+
@relation = Relation.new(self, arel_table)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Returns a quoted version of the table name, used to construct SQL statements.
|
|
136
|
+
def quoted_table_name
|
|
137
|
+
@quoted_table_name ||= connection.quote_table_name(table_name)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Computes the table name, (re)sets it internally, and returns it.
|
|
141
|
+
def reset_table_name #:nodoc:
|
|
142
|
+
if abstract_class?
|
|
143
|
+
self.table_name = if superclass == Base || superclass.abstract_class?
|
|
144
|
+
nil
|
|
145
|
+
else
|
|
146
|
+
superclass.table_name
|
|
147
|
+
end
|
|
148
|
+
elsif superclass.abstract_class?
|
|
149
|
+
self.table_name = superclass.table_name || compute_table_name
|
|
150
|
+
else
|
|
151
|
+
self.table_name = compute_table_name
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def full_table_name_prefix #:nodoc:
|
|
156
|
+
(parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# The name of the column containing the object's class when Single Table Inheritance is used
|
|
160
|
+
def inheritance_column
|
|
161
|
+
if self == Base
|
|
162
|
+
'type'
|
|
163
|
+
else
|
|
164
|
+
(@inheritance_column ||= nil) || superclass.inheritance_column
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def original_inheritance_column #:nodoc:
|
|
169
|
+
deprecated_original_property_getter :inheritance_column
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Sets the value of inheritance_column
|
|
173
|
+
def inheritance_column=(value)
|
|
174
|
+
@original_inheritance_column = inheritance_column
|
|
175
|
+
@inheritance_column = value.to_s
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def set_inheritance_column(value = nil, &block) #:nodoc:
|
|
179
|
+
deprecated_property_setter :inheritance_column, value, block
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def sequence_name
|
|
183
|
+
if base_class == self
|
|
184
|
+
@sequence_name ||= reset_sequence_name
|
|
185
|
+
else
|
|
186
|
+
(@sequence_name ||= nil) || base_class.sequence_name
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def original_sequence_name #:nodoc:
|
|
191
|
+
deprecated_original_property_getter :sequence_name
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def reset_sequence_name #:nodoc:
|
|
195
|
+
self.sequence_name = connection.default_sequence_name(table_name, primary_key)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Sets the name of the sequence to use when generating ids to the given
|
|
199
|
+
# value, or (if the value is nil or false) to the value returned by the
|
|
200
|
+
# given block. This is required for Oracle and is useful for any
|
|
201
|
+
# database which relies on sequences for primary key generation.
|
|
202
|
+
#
|
|
203
|
+
# If a sequence name is not explicitly set when using Oracle or Firebird,
|
|
204
|
+
# it will default to the commonly used pattern of: #{table_name}_seq
|
|
205
|
+
#
|
|
206
|
+
# If a sequence name is not explicitly set when using PostgreSQL, it
|
|
207
|
+
# will discover the sequence corresponding to your primary key for you.
|
|
208
|
+
#
|
|
209
|
+
# class Project < ActiveRecord::Base
|
|
210
|
+
# self.sequence_name = "projectseq" # default would have been "project_seq"
|
|
211
|
+
# end
|
|
212
|
+
def sequence_name=(value)
|
|
213
|
+
@original_sequence_name = @sequence_name if defined?(@sequence_name)
|
|
214
|
+
@sequence_name = value.to_s
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def set_sequence_name(value = nil, &block) #:nodoc:
|
|
218
|
+
deprecated_property_setter :sequence_name, value, block
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Indicates whether the table associated with this class exists
|
|
222
|
+
def table_exists?
|
|
223
|
+
connection.schema_cache.table_exists?(table_name)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Returns an array of column objects for the table associated with this class.
|
|
227
|
+
def columns
|
|
228
|
+
@columns ||= connection.schema_cache.columns[table_name].map do |col|
|
|
229
|
+
col = col.dup
|
|
230
|
+
col.primary = (col.name == primary_key)
|
|
231
|
+
col
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Returns a hash of column objects for the table associated with this class.
|
|
236
|
+
def columns_hash
|
|
237
|
+
@columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Returns a hash where the keys are column names and the values are
|
|
241
|
+
# default values when instantiating the AR object for this table.
|
|
242
|
+
def column_defaults
|
|
243
|
+
@column_defaults ||= Hash[columns.map { |c| [c.name, c.default] }]
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Returns an array of column names as strings.
|
|
247
|
+
def column_names
|
|
248
|
+
@column_names ||= columns.map { |column| column.name }
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
|
|
252
|
+
# and columns used for single table inheritance have been removed.
|
|
253
|
+
def content_columns
|
|
254
|
+
@content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Returns a hash of all the methods added to query each of the columns in the table with the name of the method as the key
|
|
258
|
+
# and true as the value. This makes it possible to do O(1) lookups in respond_to? to check if a given method for attribute
|
|
259
|
+
# is available.
|
|
260
|
+
def column_methods_hash #:nodoc:
|
|
261
|
+
@dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
|
|
262
|
+
attr_name = attr.to_s
|
|
263
|
+
methods[attr.to_sym] = attr_name
|
|
264
|
+
methods["#{attr}=".to_sym] = attr_name
|
|
265
|
+
methods["#{attr}?".to_sym] = attr_name
|
|
266
|
+
methods["#{attr}_before_type_cast".to_sym] = attr_name
|
|
267
|
+
methods
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Resets all the cached information about columns, which will cause them
|
|
272
|
+
# to be reloaded on the next request.
|
|
273
|
+
#
|
|
274
|
+
# The most common usage pattern for this method is probably in a migration,
|
|
275
|
+
# when just after creating a table you want to populate it with some default
|
|
276
|
+
# values, eg:
|
|
277
|
+
#
|
|
278
|
+
# class CreateJobLevels < ActiveRecord::Migration
|
|
279
|
+
# def up
|
|
280
|
+
# create_table :job_levels do |t|
|
|
281
|
+
# t.integer :id
|
|
282
|
+
# t.string :name
|
|
283
|
+
#
|
|
284
|
+
# t.timestamps
|
|
285
|
+
# end
|
|
286
|
+
#
|
|
287
|
+
# JobLevel.reset_column_information
|
|
288
|
+
# %w{assistant executive manager director}.each do |type|
|
|
289
|
+
# JobLevel.create(:name => type)
|
|
290
|
+
# end
|
|
291
|
+
# end
|
|
292
|
+
#
|
|
293
|
+
# def down
|
|
294
|
+
# drop_table :job_levels
|
|
295
|
+
# end
|
|
296
|
+
# end
|
|
297
|
+
def reset_column_information
|
|
298
|
+
connection.clear_cache!
|
|
299
|
+
undefine_attribute_methods
|
|
300
|
+
connection.schema_cache.clear_table_cache!(table_name) if table_exists?
|
|
301
|
+
|
|
302
|
+
@column_names = @content_columns = @column_defaults = @columns = @columns_hash = nil
|
|
303
|
+
@dynamic_methods_hash = @inheritance_column = nil
|
|
304
|
+
@arel_engine = @relation = nil
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def clear_cache! # :nodoc:
|
|
308
|
+
connection.schema_cache.clear!
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
private
|
|
312
|
+
|
|
313
|
+
# Guesses the table name, but does not decorate it with prefix and suffix information.
|
|
314
|
+
def undecorated_table_name(class_name = base_class.name)
|
|
315
|
+
table_name = class_name.to_s.demodulize.underscore
|
|
316
|
+
table_name = table_name.pluralize if pluralize_table_names
|
|
317
|
+
table_name
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Computes and returns a table name according to default conventions.
|
|
321
|
+
def compute_table_name
|
|
322
|
+
base = base_class
|
|
323
|
+
if self == base
|
|
324
|
+
# Nested classes are prefixed with singular parent table name.
|
|
325
|
+
if parent < ActiveRecord::Base && !parent.abstract_class?
|
|
326
|
+
contained = parent.table_name
|
|
327
|
+
contained = contained.singularize if parent.pluralize_table_names
|
|
328
|
+
contained += '_'
|
|
329
|
+
end
|
|
330
|
+
"#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}"
|
|
331
|
+
else
|
|
332
|
+
# STI subclasses always use their superclass' table.
|
|
333
|
+
base.table_name
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def deprecated_property_setter(property, value, block)
|
|
338
|
+
if block
|
|
339
|
+
ActiveSupport::Deprecation.warn(
|
|
340
|
+
"Calling set_#{property} is deprecated. If you need to lazily evaluate " \
|
|
341
|
+
"the #{property}, define your own `self.#{property}` class method. You can use `super` " \
|
|
342
|
+
"to get the default #{property} where you would have called `original_#{property}`."
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
define_attr_method property, value, false, &block
|
|
346
|
+
else
|
|
347
|
+
ActiveSupport::Deprecation.warn(
|
|
348
|
+
"Calling set_#{property} is deprecated. Please use `self.#{property} = 'the_name'` instead."
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
define_attr_method property, value, false
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def deprecated_original_property_getter(property)
|
|
356
|
+
ActiveSupport::Deprecation.warn("original_#{property} is deprecated. Define self.#{property} and call super instead.")
|
|
357
|
+
|
|
358
|
+
if !instance_variable_defined?("@original_#{property}") && respond_to?("reset_#{property}")
|
|
359
|
+
send("reset_#{property}")
|
|
360
|
+
else
|
|
361
|
+
instance_variable_get("@original_#{property}")
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
end
|