diff options
-rw-r--r-- | activerecord/CHANGELOG.md | 11 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb | 4 | ||||
-rw-r--r-- | activerecord/lib/active_record/inheritance.rb | 13 | ||||
-rw-r--r-- | activerecord/lib/active_record/migration.rb | 3 | ||||
-rw-r--r-- | activerecord/lib/active_record/nested_attributes.rb | 80 | ||||
-rw-r--r-- | activerecord/lib/active_record/railties/console_sandbox.rb | 3 | ||||
-rw-r--r-- | activerecord/test/cases/inheritance_test.rb | 11 | ||||
-rw-r--r-- | activerecord/test/models/autoloadable/extra_firm.rb | 2 | ||||
-rw-r--r-- | railties/Rakefile | 2 | ||||
-rw-r--r-- | railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt | 4 | ||||
-rw-r--r-- | railties/test/application/console_test.rb | 18 | ||||
-rw-r--r-- | railties/test/commands/console_test.rb | 21 |
12 files changed, 114 insertions, 58 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 2db3c0cdee..63d5bbb9ee 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,11 @@ ## Rails 4.0.0 (unreleased) ## +* Fix ActiveRecord `subclass_from_attrs` when `eager_load` is false. + It cannot find subclass because all classes are loaded automatically + when it needs. + + *Dmitry Vorotilin* + * When `:name` option is provided to `remove_index`, use it if there is no index by the conventional name. @@ -18,7 +24,7 @@ Example: sql = Post.connection.unprepared_statement do - Post.first.comments.to_sql + Post.first.comments.to_sql end *Cédric Fabianski* @@ -100,7 +106,8 @@ Example: class ChangeEnum < ActiveRecord::Migration - self.disable_ddl_transaction! + disable_ddl_transaction! + def up execute "ALTER TYPE model_size ADD VALUE 'new_value'" end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 9d624a6648..e2deb6bfcd 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -687,12 +687,14 @@ module ActiveRecord index_name = index_name(table_name, options) unless index_name_exists?(table_name, index_name, true) - if options.has_key? :name + if options.is_a?(Hash) && options.has_key?(:name) options_without_column = options.dup options_without_column.delete :column index_name_without_column = index_name(table_name, options_without_column) + return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false) end + raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist" end diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index 13a8352f13..f54865c86e 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -170,11 +170,16 @@ module ActiveRecord # this will ignore the inheritance column and return nil def subclass_from_attrs(attrs) subclass_name = attrs.with_indifferent_access[inheritance_column] - return nil if subclass_name.blank? || subclass_name == self.name - unless subclass = subclasses.detect { |sub| sub.name == subclass_name } - raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass_name} is not a subclass of #{name}") + + if subclass_name.present? && subclass_name != self.name + subclass = subclass_name.safe_constantize + + unless subclasses.include?(subclass) + raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass_name} is not a subclass of #{name}") + end + + subclass end - subclass end end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 62e8881c4c..3d71b784e7 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -339,7 +339,8 @@ module ActiveRecord # you can turn the automatic transactions off. # # class ChangeEnum < ActiveRecord::Migration - # self.disable_ddl_transaction! + # disable_ddl_transaction! + # # def up # execute "ALTER TYPE model_size ADD VALUE 'new_value'" # end diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index c5bd11edbf..602ab9e2f4 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -269,23 +269,36 @@ module ActiveRecord self.nested_attributes_options = nested_attributes_options type = (reflection.collection? ? :collection : :one_to_one) - - # def pirate_attributes=(attributes) - # assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options) - # end - generated_feature_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1 - if method_defined?(:#{association_name}_attributes=) - remove_method(:#{association_name}_attributes=) - end - def #{association_name}_attributes=(attributes) - assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes) - end - eoruby + generate_association_writer(association_name, type) else raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?" end end end + + private + + # Generates a writer method for this association. Serves as a point for + # accessing the objects in the association. For example, this method + # could generate the following: + # + # def pirate_attributes=(attributes) + # assign_nested_attributes_for_one_to_one_association(:pirate, attributes) + # end + # + # This redirects the attempts to write objects in an association through + # the helper methods defined below. Makes it seem like the nested + # associations are just regular associations. + def generate_association_writer(association_name, type) + generated_feature_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1 + if method_defined?(:#{association_name}_attributes=) + remove_method(:#{association_name}_attributes=) + end + def #{association_name}_attributes=(attributes) + assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes) + end + eoruby + end end # Returns ActiveRecord::AutosaveAssociation::marked_for_destruction? It's @@ -371,20 +384,7 @@ module ActiveRecord raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})" end - if limit = options[:limit] - limit = case limit - when Symbol - send(limit) - when Proc - limit.call - else - limit - end - - if limit && attributes_collection.size > limit - raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead." - end - end + check_record_limit!(options[:limit], attributes_collection) if attributes_collection.is_a? Hash keys = attributes_collection.keys @@ -433,6 +433,29 @@ module ActiveRecord end end + # Takes in a limit and checks if the attributes_collection has too many + # records. The method will take limits in the form of symbols, procs, and + # number-like objects (anything that can be compared with an integer). + # + # Will raise an TooManyRecords error if the attributes_collection is + # larger than the limit. + def check_record_limit!(limit, attributes_collection) + if limit + limit = case limit + when Symbol + send(limit) + when Proc + limit.call + else + limit + end + + if limit && attributes_collection.size > limit + raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead." + end + end + end + # Updates a record with the +attributes+ or marks it for destruction if # +allow_destroy+ is +true+ and has_destroy_flag? returns +true+. def assign_to_or_mark_for_destruction(record, attributes, allow_destroy) @@ -452,6 +475,11 @@ module ActiveRecord has_destroy_flag?(attributes) || call_reject_if(association_name, attributes) end + # Determines if a record with the particular +attributes+ should be + # rejected by calling the reject_if Symbol or Proc (if defined). + # The reject_if option is defined by +accepts_nested_attributes_for+. + # + # Returns false if there is a +destroy_flag+ on the attributes. def call_reject_if(association_name, attributes) return false if has_destroy_flag?(attributes) case callback = self.nested_attributes_options[association_name][:reject_if] diff --git a/activerecord/lib/active_record/railties/console_sandbox.rb b/activerecord/lib/active_record/railties/console_sandbox.rb index 25a591aa43..604a220303 100644 --- a/activerecord/lib/active_record/railties/console_sandbox.rb +++ b/activerecord/lib/active_record/railties/console_sandbox.rb @@ -1,4 +1,5 @@ -ActiveRecord::Base.connection.begin_transaction +ActiveRecord::Base.connection.begin_transaction(joinable: false) + at_exit do ActiveRecord::Base.connection.rollback_transaction end diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index 189066eb41..b91146db4e 100644 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -179,6 +179,17 @@ class InheritanceTest < ActiveRecord::TestCase assert_raise(ActiveRecord::SubclassNotFound) { Company.new(:type => 'Account') } end + def test_new_with_autoload_paths + path = File.expand_path('../../models/autoloadable', __FILE__) + ActiveSupport::Dependencies.autoload_paths << path + + firm = Company.new(:type => 'ExtraFirm') + assert_equal ExtraFirm, firm.class + ensure + ActiveSupport::Dependencies.autoload_paths.reject! { |p| p == path } + ActiveSupport::Dependencies.clear + end + def test_inheritance_condition assert_equal 10, Company.count assert_equal 2, Firm.count diff --git a/activerecord/test/models/autoloadable/extra_firm.rb b/activerecord/test/models/autoloadable/extra_firm.rb new file mode 100644 index 0000000000..5578ba0d9b --- /dev/null +++ b/activerecord/test/models/autoloadable/extra_firm.rb @@ -0,0 +1,2 @@ +class ExtraFirm < Company +end diff --git a/railties/Rakefile b/railties/Rakefile index 8576275aea..eb068fc526 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -6,6 +6,8 @@ require 'rbconfig' task :default => :test + +desc "Run all unit tests" task :test => 'test:isolated' namespace :test do diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index 5669fe6d64..c40eef145f 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -24,10 +24,10 @@ <%- unless options.skip_sprockets? -%> # Compress JavaScripts and CSS. - config.assets.js_compressor = :uglifier + config.assets.js_compressor = :uglifier # config.assets.css_compressor = :sass - # Whether to fallback to assets pipeline if a precompiled asset is missed. + # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false # Generate digests for assets URLs. diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb index 588682d3d4..592bd73924 100644 --- a/railties/test/application/console_test.rb +++ b/railties/test/application/console_test.rb @@ -119,9 +119,11 @@ class FullStackConsoleTest < ActiveSupport::TestCase assert output.include?(expected), "#{expected.inspect} expected, but got:\n\n#{output}" end - def write_prompt(command) + def write_prompt(command, expected_output = nil) @master.puts command assert_output command + assert_output expected_output if expected_output + assert_output "> " end def kill(pid) @@ -143,21 +145,17 @@ class FullStackConsoleTest < ActiveSupport::TestCase def test_sandbox pid = spawn_console - write_prompt "Post.count" - assert_output "=> 0" - + write_prompt "Post.count", "=> 0" write_prompt "Post.create" - assert_output "=> " - - write_prompt "Post.count" - assert_output "=> 1" + write_prompt "Post.count", "=> 1" kill pid pid = spawn_console - write_prompt "Post.count" - assert_output "=> 0" + write_prompt "Post.count", "=> 0" + write_prompt "Post.transaction { Post.create; raise }" + write_prompt "Post.count", "=> 0" ensure kill pid end diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb index 6be4a5fe89..3c784b43be 100644 --- a/railties/test/commands/console_test.rb +++ b/railties/test/commands/console_test.rb @@ -58,10 +58,7 @@ class Rails::ConsoleTest < ActiveSupport::TestCase end def test_console_defaults_to_IRB - config = mock("config", console: nil) - app = mock("app", config: config) - app.expects(:load_console).returns(nil) - + app = build_app(console: nil) assert_equal IRB, Rails::Console.new(app).console end @@ -127,13 +124,15 @@ class Rails::ConsoleTest < ActiveSupport::TestCase end def app - @app ||= begin - config = mock("config", console: FakeConsole) - app = mock("app", config: config) - app.stubs(:sandbox=).returns(nil) - app.expects(:load_console) - app - end + @app ||= build_app(console: FakeConsole) + end + + def build_app(config) + config = mock("config", config) + app = mock("app", config: config) + app.stubs(:sandbox=).returns(nil) + app.expects(:load_console) + app end def parse_arguments(args) |