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.
Files changed (112) hide show
  1. checksums.yaml +6 -6
  2. data/CHANGELOG.md +317 -336
  3. data/README.rdoc +3 -3
  4. data/examples/performance.rb +20 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +3 -6
  7. data/lib/active_record/associations/association.rb +3 -42
  8. data/lib/active_record/associations/association_scope.rb +3 -15
  9. data/lib/active_record/associations/builder/association.rb +6 -4
  10. data/lib/active_record/associations/builder/belongs_to.rb +3 -3
  11. data/lib/active_record/associations/builder/collection_association.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +4 -4
  13. data/lib/active_record/associations/builder/has_one.rb +5 -6
  14. data/lib/active_record/associations/builder/singular_association.rb +3 -16
  15. data/lib/active_record/associations/collection_association.rb +64 -31
  16. data/lib/active_record/associations/collection_proxy.rb +2 -37
  17. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +1 -0
  18. data/lib/active_record/associations/has_many_association.rb +5 -1
  19. data/lib/active_record/associations/has_many_through_association.rb +28 -9
  20. data/lib/active_record/associations/has_one_association.rb +15 -13
  21. data/lib/active_record/associations/join_dependency.rb +2 -2
  22. data/lib/active_record/associations/preloader.rb +14 -10
  23. data/lib/active_record/associations/through_association.rb +7 -3
  24. data/lib/active_record/associations.rb +92 -76
  25. data/lib/active_record/attribute_assignment.rb +221 -0
  26. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  27. data/lib/active_record/attribute_methods/dirty.rb +21 -11
  28. data/lib/active_record/attribute_methods/primary_key.rb +62 -25
  29. data/lib/active_record/attribute_methods/read.rb +73 -83
  30. data/lib/active_record/attribute_methods/serialization.rb +102 -0
  31. data/lib/active_record/attribute_methods/time_zone_conversion.rb +23 -17
  32. data/lib/active_record/attribute_methods/write.rb +31 -6
  33. data/lib/active_record/attribute_methods.rb +231 -30
  34. data/lib/active_record/autosave_association.rb +43 -22
  35. data/lib/active_record/base.rb +227 -1708
  36. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +150 -148
  37. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +85 -29
  38. data/lib/active_record/connection_adapters/abstract/database_statements.rb +6 -33
  39. data/lib/active_record/connection_adapters/abstract/query_cache.rb +10 -2
  40. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -6
  41. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -26
  42. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -19
  43. data/lib/active_record/connection_adapters/abstract_adapter.rb +77 -42
  44. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +674 -0
  45. data/lib/active_record/connection_adapters/column.rb +37 -11
  46. data/lib/active_record/connection_adapters/mysql2_adapter.rb +129 -581
  47. data/lib/active_record/connection_adapters/mysql_adapter.rb +137 -696
  48. data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -86
  49. data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
  50. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
  51. data/lib/active_record/connection_adapters/sqlite_adapter.rb +55 -32
  52. data/lib/active_record/counter_cache.rb +9 -4
  53. data/lib/active_record/dynamic_finder_match.rb +12 -0
  54. data/lib/active_record/dynamic_matchers.rb +84 -0
  55. data/lib/active_record/errors.rb +11 -1
  56. data/lib/active_record/explain.rb +85 -0
  57. data/lib/active_record/explain_subscriber.rb +25 -0
  58. data/lib/active_record/fixtures/file.rb +65 -0
  59. data/lib/active_record/fixtures.rb +56 -85
  60. data/lib/active_record/identity_map.rb +3 -4
  61. data/lib/active_record/inheritance.rb +174 -0
  62. data/lib/active_record/integration.rb +49 -0
  63. data/lib/active_record/locking/optimistic.rb +30 -25
  64. data/lib/active_record/locking/pessimistic.rb +23 -1
  65. data/lib/active_record/log_subscriber.rb +3 -3
  66. data/lib/active_record/migration/command_recorder.rb +8 -8
  67. data/lib/active_record/migration.rb +68 -35
  68. data/lib/active_record/model_schema.rb +366 -0
  69. data/lib/active_record/nested_attributes.rb +3 -2
  70. data/lib/active_record/persistence.rb +57 -11
  71. data/lib/active_record/querying.rb +58 -0
  72. data/lib/active_record/railtie.rb +31 -29
  73. data/lib/active_record/railties/controller_runtime.rb +3 -1
  74. data/lib/active_record/railties/databases.rake +191 -110
  75. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  76. data/lib/active_record/readonly_attributes.rb +26 -0
  77. data/lib/active_record/reflection.rb +7 -15
  78. data/lib/active_record/relation/batches.rb +5 -2
  79. data/lib/active_record/relation/calculations.rb +47 -15
  80. data/lib/active_record/relation/delegation.rb +49 -0
  81. data/lib/active_record/relation/finder_methods.rb +9 -7
  82. data/lib/active_record/relation/predicate_builder.rb +18 -7
  83. data/lib/active_record/relation/query_methods.rb +75 -9
  84. data/lib/active_record/relation/spawn_methods.rb +11 -2
  85. data/lib/active_record/relation.rb +78 -32
  86. data/lib/active_record/result.rb +1 -1
  87. data/lib/active_record/sanitization.rb +194 -0
  88. data/lib/active_record/schema_dumper.rb +12 -5
  89. data/lib/active_record/scoping/default.rb +142 -0
  90. data/lib/active_record/scoping/named.rb +202 -0
  91. data/lib/active_record/scoping.rb +152 -0
  92. data/lib/active_record/serialization.rb +1 -43
  93. data/lib/active_record/serializers/xml_serializer.rb +4 -45
  94. data/lib/active_record/session_store.rb +17 -15
  95. data/lib/active_record/store.rb +52 -0
  96. data/lib/active_record/test_case.rb +11 -7
  97. data/lib/active_record/timestamp.rb +17 -3
  98. data/lib/active_record/transactions.rb +27 -6
  99. data/lib/active_record/translation.rb +22 -0
  100. data/lib/active_record/validations/associated.rb +5 -4
  101. data/lib/active_record/validations/uniqueness.rb +7 -7
  102. data/lib/active_record/validations.rb +1 -1
  103. data/lib/active_record/version.rb +2 -2
  104. data/lib/active_record.rb +38 -3
  105. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  106. data/lib/rails/generators/active_record/migration/templates/migration.rb +12 -3
  107. data/lib/rails/generators/active_record/model/model_generator.rb +9 -1
  108. data/lib/rails/generators/active_record/model/templates/migration.rb +3 -5
  109. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  110. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
  111. metadata +30 -10
  112. 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. The CommandRecorder
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
- # * add_timestamp
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+. +command+ should be a method name and arguments.
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+. For example:
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 :value
72
+ # t.text :value
72
73
  # t.string :type
73
- # t.integer :position
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> and <tt>:unique</tt> (e.g.
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.find(:all).each { |tag| tag.destroy if tag.pages.empty? }
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.find(:all).each do |p|
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.find(:all).each do |p|
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
- recorder.inverse.each do |cmd, args|
387
- send(cmd, *args)
388
- end
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 arguments.empty? || method == :execute
444
- arguments[0] = Migrator.proper_table_name(arguments.first)
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 |name, path|
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 #{name} (originally #{migration.version})\n#{source}"
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].call(name, migration) if File.read(duplicate.filename) != source && 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
- FileUtils.cp(old_path, migration.filename)
496
+ File.open(migration.filename, "w") { |f| f.write source }
476
497
  copied << migration
477
- options[:on_copy].call(name, migration, old_path) if options[:on_copy]
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=>:migration
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
- files = Dir[*paths.map { |p| "#{p}/[0-9]*_*.rb" }]
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]*).rb/).first
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