From 58219f13d704a41747bdb46ce62e221588dd3626 Mon Sep 17 00:00:00 2001 From: nakamura Date: Fri, 28 Feb 2025 06:36:56 +0000 Subject: [PATCH] Add support for custom validation contexts in resource creation and update --- .../administrate/application_controller.rb | 33 ++++++++++++++++-- docs/customizing_controller_actions.md | 27 +++++++++++++++ .../admin/application_controller_spec.rb | 34 +++++++++++++++++++ 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/app/controllers/administrate/application_controller.rb b/app/controllers/administrate/application_controller.rb index 3c13d9c281..dac316a93c 100644 --- a/app/controllers/administrate/application_controller.rb +++ b/app/controllers/administrate/application_controller.rb @@ -48,7 +48,7 @@ def create @resource = resource = new_resource(resource_params) authorize_resource(resource) - if resource.save + if resource.save(context: validation_contexts_on_create(resource)) yield(resource) if block_given? redirect_to( after_resource_created_path(resource), @@ -63,7 +63,9 @@ def create def update @resource = resource = requested_resource - if resource.update(resource_params) + resource.assign_attributes(resource_params) + + if resource.save(context: validation_contexts_on_update(resource)) redirect_to( after_resource_updated_path(resource), notice: translate_with_resource("update.success"), @@ -277,6 +279,33 @@ def authorize_resource(resource) end end + # Override this if you want to contextualize the resource differently. + # + # @param resource A resource to be contextualized. + # @return nothing + def contextualize_resource(resource) + end + + # Override this if you want to provide additional validation contexts. + # + # @param resource [ActiveRecord::Base] The resource to be validated. + # @return [Array] The validation contexts to be used. + def validation_contexts_on_create(resource) + default_validation_contexts(resource) + end + + # Override this if you want to provide additional validation contexts. + # + # @param resource [ActiveRecord::Base] The resource to be validated. + # @return [Array] The validation contexts to be used. + def validation_contexts_on_update(resource) + default_validation_contexts(resource) + end + + def default_validation_contexts(resource) + resource.new_record? ? [:create] : [:update] + end + def paginate_resources(resources) resources.page(params[:_page]).per(records_per_page) end diff --git a/docs/customizing_controller_actions.md b/docs/customizing_controller_actions.md index 73d98ca1e6..18bbcd60b2 100644 --- a/docs/customizing_controller_actions.md +++ b/docs/customizing_controller_actions.md @@ -107,3 +107,30 @@ def create end end ``` + +## Validation Contexts + +You can customize the context when saving a resource in the controller. +For example, with the following settings, regular admins are required to input a name, but super admins can update the resource *without* a name. + +```ruby +# Model +validates :name, presence: true, on: :save_by_regular_admin + +# Controller +def validation_contexts_on_create(resource) + if current_user.super_admin? + super + [:save_by_super_admin] + else + super + [:save_by_regular_admin] + end +end + +def validation_contexts_on_update(resource) + if current_user.super_admin? + super + [:save_by_super_admin] + else + super + [:save_by_regular_admin] + end +end +``` diff --git a/spec/controllers/admin/application_controller_spec.rb b/spec/controllers/admin/application_controller_spec.rb index 031a2ad8e5..f4bbab1c36 100644 --- a/spec/controllers/admin/application_controller_spec.rb +++ b/spec/controllers/admin/application_controller_spec.rb @@ -100,4 +100,38 @@ def authorized_action?(resource, _action) .to raise_error(Administrate::NotAuthorizedError) end end + + describe "validation contexts" do + render_views + controller(Admin::ProductsController) do + def validation_contexts_on_create(resource) + super + [:some_unclear_situation] + end + + def validation_contexts_on_update(resource) + super + [:some_unclear_situation] + end + end + + context "on create" do + it "raise a validation error due to custom validation context" do + params = attributes_for(:product) + post :create, params: {product: params} + + expect(response).to have_http_status(:unprocessable_entity) + expect(response.body).to include("Product meta tag can't be blank") + end + end + + context "on update" do + it "raise a validation error due to custom validation context" do + product = create(:product, product_meta_tag: nil) + params = {name: "New Name"} + put :update, params: {id: product.to_param, product: params} + + expect(response).to have_http_status(:unprocessable_entity) + expect(response.body).to include("Product meta tag can't be blank") + end + end + end end