activerecord 4.0.4 → 4.1.16

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1632 -1797
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/examples/performance.rb +30 -18
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +2 -1
  8. data/lib/active_record/association_relation.rb +4 -0
  9. data/lib/active_record/associations/alias_tracker.rb +49 -29
  10. data/lib/active_record/associations/association.rb +9 -17
  11. data/lib/active_record/associations/association_scope.rb +59 -49
  12. data/lib/active_record/associations/belongs_to_association.rb +34 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +6 -1
  14. data/lib/active_record/associations/builder/association.rb +84 -54
  15. data/lib/active_record/associations/builder/belongs_to.rb +90 -58
  16. data/lib/active_record/associations/builder/collection_association.rb +47 -45
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +119 -25
  18. data/lib/active_record/associations/builder/has_many.rb +3 -3
  19. data/lib/active_record/associations/builder/has_one.rb +5 -7
  20. data/lib/active_record/associations/builder/singular_association.rb +6 -7
  21. data/lib/active_record/associations/collection_association.rb +121 -111
  22. data/lib/active_record/associations/collection_proxy.rb +73 -18
  23. data/lib/active_record/associations/has_many_association.rb +14 -11
  24. data/lib/active_record/associations/has_many_through_association.rb +33 -6
  25. data/lib/active_record/associations/has_one_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +46 -104
  27. data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
  28. data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
  29. data/lib/active_record/associations/join_dependency.rb +208 -168
  30. data/lib/active_record/associations/preloader/association.rb +69 -27
  31. data/lib/active_record/associations/preloader/collection_association.rb +2 -2
  32. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  33. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/through_association.rb +58 -26
  35. data/lib/active_record/associations/preloader.rb +63 -49
  36. data/lib/active_record/associations/singular_association.rb +6 -5
  37. data/lib/active_record/associations/through_association.rb +30 -9
  38. data/lib/active_record/associations.rb +116 -42
  39. data/lib/active_record/attribute_assignment.rb +6 -3
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
  41. data/lib/active_record/attribute_methods/dirty.rb +35 -26
  42. data/lib/active_record/attribute_methods/primary_key.rb +8 -1
  43. data/lib/active_record/attribute_methods/read.rb +56 -29
  44. data/lib/active_record/attribute_methods/serialization.rb +44 -12
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +13 -1
  46. data/lib/active_record/attribute_methods/write.rb +59 -26
  47. data/lib/active_record/attribute_methods.rb +82 -43
  48. data/lib/active_record/autosave_association.rb +209 -194
  49. data/lib/active_record/base.rb +6 -2
  50. data/lib/active_record/callbacks.rb +2 -2
  51. data/lib/active_record/coders/json.rb +13 -0
  52. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +5 -10
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +14 -24
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -13
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +6 -3
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +90 -0
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +45 -70
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -0
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -96
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +74 -66
  63. data/lib/active_record/connection_adapters/column.rb +1 -35
  64. data/lib/active_record/connection_adapters/connection_specification.rb +231 -43
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +10 -5
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +24 -17
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +22 -15
  68. data/lib/active_record/connection_adapters/postgresql/cast.rb +12 -4
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -44
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -14
  71. data/lib/active_record/connection_adapters/postgresql/quoting.rb +37 -12
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +20 -11
  73. data/lib/active_record/connection_adapters/postgresql_adapter.rb +98 -52
  74. data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
  75. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -60
  76. data/lib/active_record/connection_handling.rb +39 -5
  77. data/lib/active_record/core.rb +38 -54
  78. data/lib/active_record/counter_cache.rb +9 -10
  79. data/lib/active_record/dynamic_matchers.rb +6 -2
  80. data/lib/active_record/enum.rb +199 -0
  81. data/lib/active_record/errors.rb +22 -5
  82. data/lib/active_record/fixture_set/file.rb +2 -1
  83. data/lib/active_record/fixtures.rb +173 -76
  84. data/lib/active_record/gem_version.rb +15 -0
  85. data/lib/active_record/inheritance.rb +23 -9
  86. data/lib/active_record/integration.rb +54 -1
  87. data/lib/active_record/locking/optimistic.rb +7 -2
  88. data/lib/active_record/locking/pessimistic.rb +1 -1
  89. data/lib/active_record/log_subscriber.rb +6 -13
  90. data/lib/active_record/migration/command_recorder.rb +8 -2
  91. data/lib/active_record/migration.rb +91 -56
  92. data/lib/active_record/model_schema.rb +7 -14
  93. data/lib/active_record/nested_attributes.rb +25 -13
  94. data/lib/active_record/no_touching.rb +52 -0
  95. data/lib/active_record/null_relation.rb +26 -6
  96. data/lib/active_record/persistence.rb +23 -29
  97. data/lib/active_record/querying.rb +15 -12
  98. data/lib/active_record/railtie.rb +12 -61
  99. data/lib/active_record/railties/databases.rake +37 -56
  100. data/lib/active_record/readonly_attributes.rb +0 -6
  101. data/lib/active_record/reflection.rb +230 -79
  102. data/lib/active_record/relation/batches.rb +74 -24
  103. data/lib/active_record/relation/calculations.rb +52 -48
  104. data/lib/active_record/relation/delegation.rb +54 -39
  105. data/lib/active_record/relation/finder_methods.rb +210 -67
  106. data/lib/active_record/relation/merger.rb +15 -12
  107. data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
  108. data/lib/active_record/relation/predicate_builder/relation_handler.rb +17 -0
  109. data/lib/active_record/relation/predicate_builder.rb +81 -40
  110. data/lib/active_record/relation/query_methods.rb +185 -108
  111. data/lib/active_record/relation/spawn_methods.rb +8 -5
  112. data/lib/active_record/relation.rb +79 -84
  113. data/lib/active_record/result.rb +45 -6
  114. data/lib/active_record/runtime_registry.rb +5 -0
  115. data/lib/active_record/sanitization.rb +4 -4
  116. data/lib/active_record/schema_dumper.rb +18 -6
  117. data/lib/active_record/schema_migration.rb +31 -18
  118. data/lib/active_record/scoping/default.rb +5 -18
  119. data/lib/active_record/scoping/named.rb +14 -29
  120. data/lib/active_record/scoping.rb +5 -0
  121. data/lib/active_record/store.rb +67 -18
  122. data/lib/active_record/tasks/database_tasks.rb +66 -26
  123. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -10
  124. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  125. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  126. data/lib/active_record/timestamp.rb +6 -6
  127. data/lib/active_record/transactions.rb +10 -12
  128. data/lib/active_record/validations/presence.rb +1 -1
  129. data/lib/active_record/validations/uniqueness.rb +19 -9
  130. data/lib/active_record/version.rb +4 -7
  131. data/lib/active_record.rb +5 -7
  132. data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
  133. data/lib/rails/generators/active_record/migration.rb +18 -0
  134. data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
  135. data/lib/rails/generators/active_record.rb +2 -8
  136. metadata +18 -30
  137. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
  138. data/lib/active_record/associations/join_helper.rb +0 -45
  139. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  140. data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
  141. data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
  142. data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
  143. data/lib/active_record/test_case.rb +0 -96
@@ -119,6 +119,23 @@ module ActiveRecord
119
119
  # perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
120
120
  # in fixtures are to be considered a code smell.
121
121
  #
122
+ # Helper methods defined in a fixture will not be available in other fixtures, to prevent against
123
+ # unwanted inter-test dependencies. Methods used by multiple fixtures should be defined in a module
124
+ # that is included in <tt>ActiveRecord::FixtureSet.context_class</tt>.
125
+ #
126
+ # - define a helper method in `test_helper.rb`
127
+ # module FixtureFileHelpers
128
+ # def file_sha(path)
129
+ # Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
130
+ # end
131
+ # end
132
+ # ActiveRecord::FixtureSet.context_class.send :include, FixtureFileHelpers
133
+ #
134
+ # - use the helper method in a fixture
135
+ # photo:
136
+ # name: kitten.png
137
+ # sha: <%= file_sha 'files/kitten.png' %>
138
+ #
122
139
  # = Transactional Fixtures
123
140
  #
124
141
  # Test cases can use begin+rollback to isolate their changes to the database instead of having to
@@ -379,22 +396,16 @@ module ActiveRecord
379
396
 
380
397
  @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
381
398
 
382
- def self.find_table_name(fixture_set_name) # :nodoc:
383
- ActiveSupport::Deprecation.warn(
384
- "ActiveRecord::Fixtures.find_table_name is deprecated and shall be removed from future releases. Use ActiveRecord::Fixtures.default_fixture_model_name instead.")
385
- default_fixture_model_name(fixture_set_name)
386
- end
387
-
388
- def self.default_fixture_model_name(fixture_set_name) # :nodoc:
389
- ActiveRecord::Base.pluralize_table_names ?
399
+ def self.default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
400
+ config.pluralize_table_names ?
390
401
  fixture_set_name.singularize.camelize :
391
402
  fixture_set_name.camelize
392
403
  end
393
404
 
394
- def self.default_fixture_table_name(fixture_set_name) # :nodoc:
395
- "#{ ActiveRecord::Base.table_name_prefix }"\
405
+ def self.default_fixture_table_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
406
+ "#{ config.table_name_prefix }"\
396
407
  "#{ fixture_set_name.tr('/', '_') }"\
397
- "#{ ActiveRecord::Base.table_name_suffix }".to_sym
408
+ "#{ config.table_name_suffix }".to_sym
398
409
  end
399
410
 
400
411
  def self.reset_cache
@@ -442,9 +453,47 @@ module ActiveRecord
442
453
  cattr_accessor :all_loaded_fixtures
443
454
  self.all_loaded_fixtures = {}
444
455
 
445
- def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {})
456
+ class ClassCache
457
+ def initialize(class_names, config)
458
+ @class_names = class_names.stringify_keys
459
+ @config = config
460
+
461
+ # Remove string values that aren't constants or subclasses of AR
462
+ @class_names.delete_if { |k,klass|
463
+ unless klass.is_a? Class
464
+ klass = klass.safe_constantize
465
+ ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `set_fixture_class` will be removed in Rails 4.2. Use the class itself instead.")
466
+ end
467
+ !insert_class(@class_names, k, klass)
468
+ }
469
+ end
470
+
471
+ def [](fs_name)
472
+ @class_names.fetch(fs_name) {
473
+ klass = default_fixture_model(fs_name, @config).safe_constantize
474
+ insert_class(@class_names, fs_name, klass)
475
+ }
476
+ end
477
+
478
+ private
479
+
480
+ def insert_class(class_names, name, klass)
481
+ # We only want to deal with AR objects.
482
+ if klass && klass < ActiveRecord::Base
483
+ class_names[name] = klass
484
+ else
485
+ class_names[name] = nil
486
+ end
487
+ end
488
+
489
+ def default_fixture_model(fs_name, config)
490
+ ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
491
+ end
492
+ end
493
+
494
+ def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
446
495
  fixture_set_names = Array(fixture_set_names).map(&:to_s)
447
- class_names = class_names.stringify_keys
496
+ class_names = ClassCache.new class_names, config
448
497
 
449
498
  # FIXME: Apparently JK uses this.
450
499
  connection = block_given? ? yield : ActiveRecord::Base.connection
@@ -458,10 +507,12 @@ module ActiveRecord
458
507
  fixtures_map = {}
459
508
 
460
509
  fixture_sets = files_to_read.map do |fs_name|
510
+ klass = class_names[fs_name]
511
+ conn = klass ? klass.connection : connection
461
512
  fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
462
- connection,
513
+ conn,
463
514
  fs_name,
464
- class_names[fs_name] || default_fixture_model_name(fs_name),
515
+ klass,
465
516
  ::File.join(fixtures_directory, fs_name))
466
517
  end
467
518
 
@@ -481,12 +532,10 @@ module ActiveRecord
481
532
  conn.insert_fixture(row, fixture_set_name)
482
533
  end
483
534
  end
484
- end
485
535
 
486
- # Cap primary key sequences to max(pk).
487
- if connection.respond_to?(:reset_pk_sequence!)
488
- fixture_sets.each do |fs|
489
- connection.reset_pk_sequence!(fs.table_name)
536
+ # Cap primary key sequences to max(pk).
537
+ if conn.respond_to?(:reset_pk_sequence!)
538
+ conn.reset_pk_sequence!(fs.table_name)
490
539
  end
491
540
  end
492
541
  end
@@ -503,27 +552,36 @@ module ActiveRecord
503
552
  Zlib.crc32(label.to_s) % MAX_ID
504
553
  end
505
554
 
506
- attr_reader :table_name, :name, :fixtures, :model_class
555
+ # Superclass for the evaluation contexts used by ERB fixtures.
556
+ def self.context_class
557
+ @context_class ||= Class.new
558
+ end
559
+
560
+ attr_reader :table_name, :name, :fixtures, :model_class, :config
507
561
 
508
- def initialize(connection, name, class_name, path)
509
- @fixtures = {} # Ordered hash
562
+ def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
510
563
  @name = name
511
564
  @path = path
565
+ @config = config
566
+ @model_class = nil
567
+
568
+ if class_name.is_a?(String)
569
+ ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `FixtureSet.new` will be removed in Rails 4.2. Use the class itself instead.")
570
+ end
512
571
 
513
572
  if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
514
573
  @model_class = class_name
515
574
  else
516
- @model_class = class_name.constantize rescue nil
575
+ @model_class = class_name.safe_constantize if class_name
517
576
  end
518
577
 
519
- @connection = ( model_class.respond_to?(:connection) ?
520
- model_class.connection : connection )
578
+ @connection = connection
521
579
 
522
580
  @table_name = ( model_class.respond_to?(:table_name) ?
523
581
  model_class.table_name :
524
- self.class.default_fixture_table_name(name) )
582
+ self.class.default_fixture_table_name(name, config) )
525
583
 
526
- read_fixture_files
584
+ @fixtures = read_fixture_files path, @model_class
527
585
  end
528
586
 
529
587
  def [](x)
@@ -542,10 +600,10 @@ module ActiveRecord
542
600
  fixtures.size
543
601
  end
544
602
 
545
- # Return a hash of rows to be inserted. The key is the table, the value is
603
+ # Returns a hash of rows to be inserted. The key is the table, the value is
546
604
  # a list of rows to insert to that table.
547
605
  def table_rows
548
- now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
606
+ now = config.default_timezone == :utc ? Time.now.utc : Time.now
549
607
  now = now.to_s(:db)
550
608
 
551
609
  # allow a standard key to be used for doing defaults in YAML
@@ -557,7 +615,7 @@ module ActiveRecord
557
615
  rows[table_name] = fixtures.map do |label, fixture|
558
616
  row = fixture.to_hash
559
617
 
560
- if model_class && model_class < ActiveRecord::Base
618
+ if model_class
561
619
  # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
562
620
  if model_class.record_timestamps
563
621
  timestamp_column_names.each do |c_name|
@@ -567,7 +625,7 @@ module ActiveRecord
567
625
 
568
626
  # interpolate the fixture label
569
627
  row.each do |key, value|
570
- row[key] = label if value == "$LABEL"
628
+ row[key] = label if "$LABEL" == value
571
629
  end
572
630
 
573
631
  # generate a primary key if necessary
@@ -583,7 +641,7 @@ module ActiveRecord
583
641
  model_class
584
642
  end
585
643
 
586
- reflection_class.reflect_on_all_associations.each do |association|
644
+ reflection_class._reflections.values.each do |association|
587
645
  case association.macro
588
646
  when :belongs_to
589
647
  # Do not replace association name with association foreign key if they are named the same
@@ -597,14 +655,9 @@ module ActiveRecord
597
655
 
598
656
  row[fk_name] = ActiveRecord::FixtureSet.identify(value)
599
657
  end
600
- when :has_and_belongs_to_many
601
- if (targets = row.delete(association.name.to_s))
602
- targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
603
- table_name = association.join_table
604
- rows[table_name].concat targets.map { |target|
605
- { association.foreign_key => row[primary_key_name],
606
- association.association_foreign_key => ActiveRecord::FixtureSet.identify(target) }
607
- }
658
+ when :has_many
659
+ if association.options[:through]
660
+ add_join_records(rows, row, HasManyThroughProxy.new(association))
608
661
  end
609
662
  end
610
663
  end
@@ -615,11 +668,54 @@ module ActiveRecord
615
668
  rows
616
669
  end
617
670
 
671
+ class ReflectionProxy # :nodoc:
672
+ def initialize(association)
673
+ @association = association
674
+ end
675
+
676
+ def join_table
677
+ @association.join_table
678
+ end
679
+
680
+ def name
681
+ @association.name
682
+ end
683
+ end
684
+
685
+ class HasManyThroughProxy < ReflectionProxy # :nodoc:
686
+ def rhs_key
687
+ @association.foreign_key
688
+ end
689
+
690
+ def lhs_key
691
+ @association.through_reflection.foreign_key
692
+ end
693
+
694
+ def join_table
695
+ @association.through_reflection.table_name
696
+ end
697
+ end
698
+
618
699
  private
619
700
  def primary_key_name
620
701
  @primary_key_name ||= model_class && model_class.primary_key
621
702
  end
622
703
 
704
+ def add_join_records(rows, row, association)
705
+ # This is the case when the join table has no fixtures file
706
+ if (targets = row.delete(association.name.to_s))
707
+ table_name = association.join_table
708
+ lhs_key = association.lhs_key
709
+ rhs_key = association.rhs_key
710
+
711
+ targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
712
+ rows[table_name].concat targets.map { |target|
713
+ { lhs_key => row[primary_key_name],
714
+ rhs_key => ActiveRecord::FixtureSet.identify(target) }
715
+ }
716
+ end
717
+ end
718
+
623
719
  def has_primary_key_column?
624
720
  @has_primary_key_column ||= primary_key_name &&
625
721
  model_class.columns.any? { |c| c.name == primary_key_name }
@@ -638,12 +734,12 @@ module ActiveRecord
638
734
  @column_names ||= @connection.columns(@table_name).collect { |c| c.name }
639
735
  end
640
736
 
641
- def read_fixture_files
642
- yaml_files = Dir["#{@path}/{**,*}/*.yml"].select { |f|
737
+ def read_fixture_files(path, model_class)
738
+ yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
643
739
  ::File.file?(f)
644
- } + [yaml_file_path]
740
+ } + [yaml_file_path(path)]
645
741
 
646
- yaml_files.each do |file|
742
+ yaml_files.each_with_object({}) do |file, fixtures|
647
743
  FixtureSet::File.open(file) do |fh|
648
744
  fh.each do |fixture_name, row|
649
745
  fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
@@ -652,8 +748,8 @@ module ActiveRecord
652
748
  end
653
749
  end
654
750
 
655
- def yaml_file_path
656
- "#{@path}.yml"
751
+ def yaml_file_path(path)
752
+ "#{path}.yml"
657
753
  end
658
754
 
659
755
  end
@@ -696,7 +792,7 @@ module ActiveRecord
696
792
 
697
793
  def find
698
794
  if model_class
699
- model_class.find(fixture[model_class.primary_key])
795
+ model_class.unscoped.find(fixture[model_class.primary_key])
700
796
  else
701
797
  raise FixtureClassNotFound, "No class attached to find."
702
798
  end
@@ -725,14 +821,16 @@ module ActiveRecord
725
821
  class_attribute :use_transactional_fixtures
726
822
  class_attribute :use_instantiated_fixtures # true, false, or :no_instances
727
823
  class_attribute :pre_loaded_fixtures
824
+ class_attribute :config
728
825
 
729
826
  self.fixture_table_names = []
730
827
  self.use_transactional_fixtures = true
731
828
  self.use_instantiated_fixtures = false
732
829
  self.pre_loaded_fixtures = false
830
+ self.config = ActiveRecord::Base
733
831
 
734
832
  self.fixture_class_names = Hash.new do |h, fixture_set_name|
735
- h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name)
833
+ h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name, self.config)
736
834
  end
737
835
  end
738
836
 
@@ -745,13 +843,6 @@ module ActiveRecord
745
843
  # 'namespaced/fixture' => Another::Model
746
844
  #
747
845
  # The keys must be the fixture names, that coincide with the short paths to the fixture files.
748
- #--
749
- # It is also possible to pass the class name instead of the class:
750
- # set_fixture_class 'some_fixture' => 'SomeModel'
751
- # I think this option is redundant, i propose to deprecate it.
752
- # Isn't it easier to always pass the class itself?
753
- # (2011-12-20 alexeymuranov)
754
- #++
755
846
  def set_fixture_class(class_names = {})
756
847
  self.fixture_class_names = self.fixture_class_names.merge(class_names.stringify_keys)
757
848
  end
@@ -765,22 +856,22 @@ module ActiveRecord
765
856
  end
766
857
 
767
858
  self.fixture_table_names |= fixture_set_names
768
- require_fixture_classes(fixture_set_names)
859
+ require_fixture_classes(fixture_set_names, self.config)
769
860
  setup_fixture_accessors(fixture_set_names)
770
861
  end
771
862
 
772
863
  def try_to_load_dependency(file_name)
773
864
  require_dependency file_name
774
865
  rescue LoadError => e
775
- # Let's hope the developer has included it
776
- # Let's warn in case this is a subdependency, otherwise
777
- # subdependency error messages are totally cryptic
778
- if ActiveRecord::Base.logger
779
- ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
866
+ unless fixture_class_names.key?(file_name.pluralize)
867
+ if ActiveRecord::Base.logger
868
+ ActiveRecord::Base.logger.warn("Unable to load #{file_name}, make sure you added it to ActiveSupport::TestCase.set_fixture_class")
869
+ ActiveRecord::Base.logger.warn("underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
870
+ end
780
871
  end
781
872
  end
782
873
 
783
- def require_fixture_classes(fixture_set_names = nil)
874
+ def require_fixture_classes(fixture_set_names = nil, config = ActiveRecord::Base)
784
875
  if fixture_set_names
785
876
  fixture_set_names = fixture_set_names.map { |n| n.to_s }
786
877
  else
@@ -788,7 +879,7 @@ module ActiveRecord
788
879
  end
789
880
 
790
881
  fixture_set_names.each do |file_name|
791
- file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
882
+ file_name = file_name.singularize if config.pluralize_table_names
792
883
  try_to_load_dependency(file_name)
793
884
  end
794
885
  end
@@ -840,9 +931,7 @@ module ActiveRecord
840
931
  !self.class.uses_transaction?(method_name)
841
932
  end
842
933
 
843
- def setup_fixtures
844
- return if ActiveRecord::Base.configurations.blank?
845
-
934
+ def setup_fixtures(config = ActiveRecord::Base)
846
935
  if pre_loaded_fixtures && !use_transactional_fixtures
847
936
  raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
848
937
  end
@@ -856,7 +945,7 @@ module ActiveRecord
856
945
  if @@already_loaded_fixtures[self.class]
857
946
  @loaded_fixtures = @@already_loaded_fixtures[self.class]
858
947
  else
859
- @loaded_fixtures = load_fixtures
948
+ @loaded_fixtures = load_fixtures(config)
860
949
  @@already_loaded_fixtures[self.class] = @loaded_fixtures
861
950
  end
862
951
  @fixture_connections = enlist_fixture_connections
@@ -867,16 +956,14 @@ module ActiveRecord
867
956
  else
868
957
  ActiveRecord::FixtureSet.reset_cache
869
958
  @@already_loaded_fixtures[self.class] = nil
870
- @loaded_fixtures = load_fixtures
959
+ @loaded_fixtures = load_fixtures(config)
871
960
  end
872
961
 
873
962
  # Instantiate fixtures for every test if requested.
874
- instantiate_fixtures if use_instantiated_fixtures
963
+ instantiate_fixtures(config) if use_instantiated_fixtures
875
964
  end
876
965
 
877
966
  def teardown_fixtures
878
- return if ActiveRecord::Base.configurations.blank?
879
-
880
967
  # Rollback changes if a transaction is active.
881
968
  if run_in_transaction?
882
969
  @fixture_connections.each do |connection|
@@ -895,19 +982,19 @@ module ActiveRecord
895
982
  end
896
983
 
897
984
  private
898
- def load_fixtures
899
- fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
985
+ def load_fixtures(config)
986
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
900
987
  Hash[fixtures.map { |f| [f.name, f] }]
901
988
  end
902
989
 
903
990
  # for pre_loaded_fixtures, only require the classes once. huge speed improvement
904
991
  @@required_fixture_classes = false
905
992
 
906
- def instantiate_fixtures
993
+ def instantiate_fixtures(config)
907
994
  if pre_loaded_fixtures
908
995
  raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
909
996
  unless @@required_fixture_classes
910
- self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys
997
+ self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys, config
911
998
  @@required_fixture_classes = true
912
999
  end
913
1000
  ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
@@ -924,3 +1011,13 @@ module ActiveRecord
924
1011
  end
925
1012
  end
926
1013
  end
1014
+
1015
+ class ActiveRecord::FixtureSet::RenderContext # :nodoc:
1016
+ def self.create_subclass
1017
+ Class.new ActiveRecord::FixtureSet.context_class do
1018
+ def get_binding
1019
+ binding()
1020
+ end
1021
+ end
1022
+ end
1023
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveRecord
2
+ # Returns the version of the currently loaded ActiveRecord as a <tt>Gem::Version</tt>
3
+ def self.gem_version
4
+ Gem::Version.new VERSION::STRING
5
+ end
6
+
7
+ module VERSION
8
+ MAJOR = 4
9
+ MINOR = 1
10
+ TINY = 16
11
+ PRE = nil
12
+
13
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
+ end
15
+ end
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- # Determine whether to store the full constant name including namespace when using STI
8
+ # Determines whether to store the full constant name including namespace when using STI.
9
9
  class_attribute :store_full_sti_class, instance_writer: false
10
10
  self.store_full_sti_class = true
11
11
  end
@@ -13,10 +13,10 @@ module ActiveRecord
13
13
  module ClassMethods
14
14
  # Determines if one of the attributes passed in is the inheritance column,
15
15
  # and if the inheritance column is attr accessible, it initializes an
16
- # instance of the given subclass instead of the base class
16
+ # instance of the given subclass instead of the base class.
17
17
  def new(*args, &block)
18
18
  if abstract_class? || self == Base
19
- raise NotImplementedError, "#{self} is an abstract class and can not be instantiated."
19
+ raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
20
20
  end
21
21
 
22
22
  attrs = args.first
@@ -31,7 +31,8 @@ module ActiveRecord
31
31
  end
32
32
  end
33
33
 
34
- # True if this isn't a concrete subclass needing a STI type condition.
34
+ # Returns +true+ if this does not need STI type condition. Returns
35
+ # +false+ if STI type condition needs to be applied.
35
36
  def descends_from_active_record?
36
37
  if self == Base
37
38
  false
@@ -48,10 +49,12 @@ module ActiveRecord
48
49
  end
49
50
 
50
51
  def symbolized_base_class
52
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.symbolized_base_class is deprecated and will be removed without replacement.")
51
53
  @symbolized_base_class ||= base_class.to_s.to_sym
52
54
  end
53
55
 
54
56
  def symbolized_sti_name
57
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.symbolized_sti_name is deprecated and will be removed without replacement.")
55
58
  @symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
56
59
  end
57
60
 
@@ -120,13 +123,14 @@ module ActiveRecord
120
123
  begin
121
124
  constant = ActiveSupport::Dependencies.constantize(candidate)
122
125
  return constant if candidate == constant.to_s
123
- rescue NameError => e
124
- # We don't want to swallow NoMethodError < NameError errors
125
- raise e unless e.instance_of?(NameError)
126
+ # We don't want to swallow NoMethodError < NameError errors
127
+ rescue NoMethodError
128
+ raise
129
+ rescue NameError
126
130
  end
127
131
  end
128
132
 
129
- raise NameError, "uninitialized constant #{candidates.first}"
133
+ raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
130
134
  end
131
135
  end
132
136
 
@@ -162,7 +166,7 @@ module ActiveRecord
162
166
  end
163
167
 
164
168
  def type_condition(table = arel_table)
165
- sti_column = table[inheritance_column.to_sym]
169
+ sti_column = table[inheritance_column]
166
170
  sti_names = ([self] + descendants).map { |model| model.sti_name }
167
171
 
168
172
  sti_column.in(sti_names)
@@ -191,8 +195,18 @@ module ActiveRecord
191
195
  end
192
196
  end
193
197
 
198
+ def initialize_dup(other)
199
+ super
200
+ ensure_proper_type
201
+ end
202
+
194
203
  private
195
204
 
205
+ def initialize_internals_callback
206
+ super
207
+ ensure_proper_type
208
+ end
209
+
196
210
  # Sets the attribute used for single table inheritance to this class name if this is not the
197
211
  # ActiveRecord::Base descendant.
198
212
  # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/string/filters'
2
+
1
3
  module ActiveRecord
2
4
  module Integration
3
5
  extend ActiveSupport::Concern
@@ -45,10 +47,19 @@ module ActiveRecord
45
47
  # Product.new.cache_key # => "products/new"
46
48
  # Product.find(5).cache_key # => "products/5" (updated_at not available)
47
49
  # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
48
- def cache_key
50
+ #
51
+ # You can also pass a list of named timestamps, and the newest in the list will be
52
+ # used to generate the key:
53
+ #
54
+ # Person.find(5).cache_key(:updated_at, :last_reviewed_at)
55
+ def cache_key(*timestamp_names)
49
56
  case
50
57
  when new_record?
51
58
  "#{self.class.model_name.cache_key}/new"
59
+ when timestamp_names.any?
60
+ timestamp = max_updated_column_timestamp(timestamp_names)
61
+ timestamp = timestamp.utc.to_s(cache_timestamp_format)
62
+ "#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
52
63
  when timestamp = max_updated_column_timestamp
53
64
  timestamp = timestamp.utc.to_s(cache_timestamp_format)
54
65
  "#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
@@ -56,5 +67,47 @@ module ActiveRecord
56
67
  "#{self.class.model_name.cache_key}/#{id}"
57
68
  end
58
69
  end
70
+
71
+ module ClassMethods
72
+ # Defines your model's +to_param+ method to generate "pretty" URLs
73
+ # using +method_name+, which can be any attribute or method that
74
+ # responds to +to_s+.
75
+ #
76
+ # class User < ActiveRecord::Base
77
+ # to_param :name
78
+ # end
79
+ #
80
+ # user = User.find_by(name: 'Fancy Pants')
81
+ # user.id # => 123
82
+ # user_path(user) # => "/users/123-fancy-pants"
83
+ #
84
+ # Values longer than 20 characters will be truncated. The value
85
+ # is truncated word by word.
86
+ #
87
+ # user = User.find_by(name: 'David HeinemeierHansson')
88
+ # user.id # => 125
89
+ # user_path(user) # => "/users/125-david"
90
+ #
91
+ # Because the generated param begins with the record's +id+, it is
92
+ # suitable for passing to +find+. In a controller, for example:
93
+ #
94
+ # params[:id] # => "123-fancy-pants"
95
+ # User.find(params[:id]).id # => 123
96
+ def to_param(method_name = nil)
97
+ if method_name.nil?
98
+ super()
99
+ else
100
+ define_method :to_param do
101
+ if (default = super()) &&
102
+ (result = send(method_name).to_s).present? &&
103
+ (param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present?
104
+ "#{default}-#{param}"
105
+ else
106
+ default
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
59
112
  end
60
113
  end
@@ -66,7 +66,7 @@ module ActiveRecord
66
66
  send(lock_col + '=', previous_lock_value + 1)
67
67
  end
68
68
 
69
- def update_record(attribute_names = @attributes.keys) #:nodoc:
69
+ def _update_record(attribute_names = @attributes.keys) #:nodoc:
70
70
  return super unless locking_enabled?
71
71
  return 0 if attribute_names.empty?
72
72
 
@@ -84,7 +84,10 @@ module ActiveRecord
84
84
  relation.table[self.class.primary_key].eq(id).and(
85
85
  relation.table[lock_col].eq(self.class.quote_value(previous_lock_value, column_for_attribute(lock_col)))
86
86
  )
87
- ).arel.compile_update(arel_attributes_with_values_for_update(attribute_names))
87
+ ).arel.compile_update(
88
+ arel_attributes_with_values_for_update(attribute_names),
89
+ self.class.primary_key
90
+ )
88
91
 
89
92
  affected_rows = self.class.connection.update stmt
90
93
 
@@ -138,6 +141,7 @@ module ActiveRecord
138
141
 
139
142
  # Set the column to use for optimistic locking. Defaults to +lock_version+.
140
143
  def locking_column=(value)
144
+ @column_defaults = nil
141
145
  @locking_column = value.to_s
142
146
  end
143
147
 
@@ -149,6 +153,7 @@ module ActiveRecord
149
153
 
150
154
  # Quote the column name used for optimistic locking.
151
155
  def quoted_locking_column
156
+ ActiveSupport::Deprecation.warn "ActiveRecord::Base.quoted_locking_column is deprecated and will be removed in Rails 4.2 or later."
152
157
  connection.quote_column_name(locking_column)
153
158
  end
154
159