Search code examples
ruby-on-railscocoon-gemancestry

Rails custom validate method


I'm using the ancestry gem in order to create subcategories. After that when I create an item (item model) I use the following group select in order to associate the item with the subcategories it belongs to. The group select has the categories and subcategories grouped together.

<%= form.input :category, collection: @categories, as: :grouped_select, group_method: :children, label: false, include_blank: true %>

@categories = Category.where(ancestry: nil).order('name ASC')

Also I'm using the the cocoon gem in order to create and thus associate many subcategories to an item.

Now I would like to add a customvalidate method inside a model which will allow the user to only add subcategories that belong to the same main category, or otherwise return an error:

errors.add(:category, "you can only choose subcategories from he same category")   

I'm kind of stuck on how to create this validate method. Maybe first I should find the subcategories that are being added:

subcategory = Category.find(category)

And then find the category that the subcategory belongs to with this:

subcategory.root.name

But after that I have now idea what to do.

How can I create this validate method in order to allow the user to only add subcategories that belong to the same main category, or otherwise return an error?

Thank you in advance. Help would be very much appreciated on this one.


Solution

  • If I understand, all Categories assigned to an Item must be subcategories of the same Category. And I assume they have to be subcategories.

    Write a custom validation method in Item which verifies all the subcategories have parents, and all the subcategories are the same.

    class Item < ApplicationRecord
      has_many categories
    
      validate :categories_have_the_same_parent
    
      private def categories_have_the_same_parent
        if categories.any? { |cat| !cat.ancestry_id }
          errors.add(
            :categories,
            :not_a_subcategory,
            "All categories must be sub-categories"
          )
        end
    
        if categories.each_cons(2).any? { |a,b| a.ancestry_id != b.ancestry_id }
          errors.add(
            :categories,
            :different_ancestors,
            "All categories must have the same parent."
          }
        end
      end
    end
    

    The diagnostics on this can be improved to include the offending categories.

    Then, in the form, populate the Item with the form information and check the Item's validation as normal.

    class ItemController < ApplicationController
      def create
        @item = Item.create!(...params...)
    
        ...creation succeeded...
      rescue ActiveRecord::RecordInvalid => e
        ...creation failed...
      end
    end