aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/CHANGELOG.md11
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb4
-rw-r--r--activerecord/lib/active_record/inheritance.rb13
-rw-r--r--activerecord/lib/active_record/migration.rb3
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb80
-rw-r--r--activerecord/lib/active_record/railties/console_sandbox.rb3
-rw-r--r--activerecord/test/cases/inheritance_test.rb11
-rw-r--r--activerecord/test/models/autoloadable/extra_firm.rb2
-rw-r--r--railties/Rakefile2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt4
-rw-r--r--railties/test/application/console_test.rb18
-rw-r--r--railties/test/commands/console_test.rb21
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)