diff --git a/Gemfile b/Gemfile
index 473d9b60fd..215b3df34c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -18,6 +18,7 @@ gem "rubocop-performance"
gem "rubocop-rails"
gem "rubocop-rails-omakase"
+gem "minitest", "< 6"
gem "minitest-bisect"
gemspec
diff --git a/lib/active_resource/base.rb b/lib/active_resource/base.rb
index 3c8a4bee14..42965a890b 100644
--- a/lib/active_resource/base.rb
+++ b/lib/active_resource/base.rb
@@ -1472,6 +1472,12 @@ def dup
# is Json for the final object as it looked after the \save (which would include attributes like +created_at+
# that weren't part of the original submit).
#
+ # With save validations run by default. If any of them fail
+ # ActiveResource::ResourceInvalid gets raised, and nothing is POSTed to
+ # the remote system. To skip validations, pass validate: false. To
+ # validate withing a custom context, pass the :context option.
+ # See ActiveResource::Validations for more information.
+ #
# There's a series of callbacks associated with save. If any
# of the before_* callbacks throw +:abort+ the action is
# cancelled and save raises ActiveResource::ResourceInvalid.
@@ -1484,7 +1490,7 @@ def dup
# my_company.new? # => false
# my_company.size = 10
# my_company.save # sends PUT /companies/1 (update)
- def save
+ def save(**options)
run_callbacks :save do
new? ? create : _update
end
@@ -1495,16 +1501,17 @@ def save
# If the resource is new, it is created via +POST+, otherwise the
# existing resource is updated via +PUT+.
#
- # With save! validations always run. If any of them fail
+ # With save! validations run by default. If any of them fail
# ActiveResource::ResourceInvalid gets raised, and nothing is POSTed to
- # the remote system.
+ # the remote system. To skip validations, pass validate: false. To
+ # validate withing a custom context, pass the :context option.
# See ActiveResource::Validations for more information.
#
# There's a series of callbacks associated with save!. If any
# of the before_* callbacks throw +:abort+ the action is
# cancelled and save! raises ActiveResource::ResourceInvalid.
- def save!
- save || raise(ResourceInvalid.new(nil, self))
+ def save!(**options)
+ save(**options) || raise(ResourceInvalid.new(nil, self))
end
##
diff --git a/lib/active_resource/validations.rb b/lib/active_resource/validations.rb
index 4ad5ea6b50..9fa3e42fbb 100644
--- a/lib/active_resource/validations.rb
+++ b/lib/active_resource/validations.rb
@@ -283,7 +283,7 @@ def save_with_validation(options = {})
# ones. Otherwise we get an endless loop and can never change the
# fields so as to make the resource valid.
@remote_errors = nil
- if perform_validation && valid? || !perform_validation
+ if perform_validation && valid?(options[:context]) || !perform_validation
save_without_validation
true
else
diff --git a/test/cases/validations_test.rb b/test/cases/validations_test.rb
index 84e1d5fbe5..b8d6b55d1a 100644
--- a/test/cases/validations_test.rb
+++ b/test/cases/validations_test.rb
@@ -32,12 +32,30 @@ def test_fails_save!
assert_raise(ActiveResource::ResourceInvalid) { p.save! }
end
+ def test_save_with_context
+ p = new_project(summary: nil)
+ assert_not p.save(context: :completed)
+ assert_equal [ "can't be blank" ], p.errors.messages_for(:summary)
+ end
+
+ def test_save_bang_with_context
+ p = new_project(summary: nil)
+ assert_raise(ActiveResource::ResourceInvalid) { p.save!(context: :completed) }
+ assert_equal [ "can't be blank" ], p.errors.messages_for(:summary)
+ end
+
def test_save_without_validation
p = new_project(name: nil)
assert_not p.save
assert p.save(validate: false)
end
+ def test_save_bang_without_validation
+ p = new_project(name: nil)
+ assert_raises(ActiveResource::ResourceInvalid) { p.save! }
+ assert p.save!(validate: false)
+ end
+
def test_validate_callback
# we have a callback ensuring the description is longer than three letters
p = new_project(description: "a")
@@ -56,6 +74,14 @@ def test_client_side_validation_maximum
assert_equal [ "is too long (maximum is 10 characters)" ], project.errors[:description]
end
+ def test_validation_context
+ project = new_project(summary: "")
+
+ assert_predicate project, :valid?
+ assert_not project.valid?(:completed)
+ assert_equal [ "can't be blank" ], project.errors.messages_for(:summary)
+ end
+
def test_invalid_method
p = new_project
diff --git a/test/fixtures/project.rb b/test/fixtures/project.rb
index e9f41f0672..58c5e0a13f 100644
--- a/test/fixtures/project.rb
+++ b/test/fixtures/project.rb
@@ -11,6 +11,7 @@ class Project < ActiveResource::Base
validates :name, presence: true
validates :description, presence: false, length: { maximum: 10 }
validate :description_greater_than_three_letters
+ validates :summary, presence: { on: :completed }
# to test the validate *callback* works
def description_greater_than_three_letters