aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/CHANGELOG.md8
-rw-r--r--activerecord/lib/active_record/validations/associated.rb10
-rw-r--r--activerecord/test/cases/validations/association_validation_test.rb12
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb35
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb2
-rw-r--r--railties/test/generators/namespaced_generators_test.rb24
6 files changed, 68 insertions, 23 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index c0e3840b40..840ad0d7ce 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,11 @@
+* Improve support for non Active Record objects on `validates_associated`
+
+ Skipping `marked_for_destruction?` when the associated object does not responds
+ to it make easier to validate virtual associations built on top of Active Model
+ objects and/or serialized objects that implement a `valid?` instance method.
+
+ *Kassio Borges*, *Lucas Mazza*
+
* Change connection management middleware to return a new response with
a body proxy, rather than mutating the original.
diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb
index 32fbaf0a91..b14db85167 100644
--- a/activerecord/lib/active_record/validations/associated.rb
+++ b/activerecord/lib/active_record/validations/associated.rb
@@ -2,10 +2,16 @@ module ActiveRecord
module Validations
class AssociatedValidator < ActiveModel::EachValidator #:nodoc:
def validate_each(record, attribute, value)
- if Array.wrap(value).reject {|r| r.marked_for_destruction? || r.valid?}.any?
- record.errors.add(attribute, :invalid, options.merge(:value => value))
+ if Array(value).reject { |r| valid_object?(r) }.any?
+ record.errors.add(attribute, :invalid, options.merge(value: value))
end
end
+
+ private
+
+ def valid_object?(record)
+ (record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?
+ end
end
module ClassMethods
diff --git a/activerecord/test/cases/validations/association_validation_test.rb b/activerecord/test/cases/validations/association_validation_test.rb
index bff5ffa65e..584a3dc0d8 100644
--- a/activerecord/test/cases/validations/association_validation_test.rb
+++ b/activerecord/test/cases/validations/association_validation_test.rb
@@ -45,6 +45,18 @@ class AssociationValidationTest < ActiveRecord::TestCase
assert t.valid?
end
+ def test_validates_associated_without_marked_for_destruction
+ reply = Class.new do
+ def valid?
+ true
+ end
+ end
+ Topic.validates_associated(:replies)
+ t = Topic.new
+ t.define_singleton_method(:replies) { [reply.new] }
+ assert t.valid?
+ end
+
def test_validates_associated_with_custom_message_using_quotes
Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
Topic.validates_presence_of :content
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index 90bb2c38c3..896c28ad8b 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -75,30 +75,12 @@ module ActiveSupport
# Increment an integer value in the cache.
def increment(name, amount = 1, options = nil)
- synchronize do
- options = merged_options(options)
- if num = read(name, options)
- num = num.to_i + amount
- write(name, num, options)
- num
- else
- nil
- end
- end
+ modify_value(name, amount, options)
end
# Decrement an integer value in the cache.
def decrement(name, amount = 1, options = nil)
- synchronize do
- options = merged_options(options)
- if num = read(name, options)
- num = num.to_i - amount
- write(name, num, options)
- num
- else
- nil
- end
- end
+ modify_value(name, -amount, options)
end
def delete_matched(matcher, options = nil)
@@ -167,6 +149,19 @@ module ActiveSupport
!!entry
end
end
+
+ private
+
+ def modify_value(name, amount, options)
+ synchronize do
+ options = merged_options(options)
+ if num = read(name, options)
+ num = num.to_i + amount
+ write(name, num, options)
+ num
+ end
+ end
+ end
end
end
end
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
index bc3c9b3f6b..17a86f376b 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
@@ -1,5 +1,5 @@
<% if namespaced? -%>
-require_dependency "<%= namespaced_file_path %>/application_controller"
+require_dependency "<%= namespaced_path %>/application_controller"
<% end -%>
<% module_namespacing do -%>
diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb
index c4ee6602c5..590f06e19a 100644
--- a/railties/test/generators/namespaced_generators_test.rb
+++ b/railties/test/generators/namespaced_generators_test.rb
@@ -396,4 +396,28 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Stylesheets (should not be removed)
assert_file "app/assets/stylesheets/scaffold.css"
end
+
+ def test_api_scaffold_with_namespace_on_invoke
+ run_generator [ "admin/role", "name:string", "description:string", "--api" ]
+
+ # Model
+ assert_file "app/models/test_app/admin.rb", /module TestApp\n module Admin/
+ assert_file "app/models/test_app/admin/role.rb", /module TestApp\n class Admin::Role < ActiveRecord::Base/
+ assert_file "test/models/test_app/admin/role_test.rb", /module TestApp\n class Admin::RoleTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/test_app/admin/roles.yml"
+ assert_migration "db/migrate/create_test_app_admin_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/^ namespace :admin do\n resources :roles\n end$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/test_app/admin/roles_controller.rb" do |content|
+ assert_match(/module TestApp\n class Admin::RolesController < ApplicationController/, content)
+ assert_match(%r(require_dependency "test_app/application_controller"), content)
+ end
+ assert_file "test/controllers/test_app/admin/roles_controller_test.rb",
+ /module TestApp\n class Admin::RolesControllerTest < ActionController::TestCase/
+ end
end