How to configure ActionController::Parameters to permit a moderately deep recursive structure

50 views Asked by At

I want my JavaScript front-end to pass a recursive data structure to my Rails API. How do I configure the parameter to permit to handle a structure of unknown depth?

Specifically, my data structure describes a hierarchy of categories. For example:

  • Category 1
    • Category 1a
    • Category 1b
      • Category 1b1
      • Category 1b2
    • Category 1c
  • Category 2
    • Category 2a
  • Category 3

The resulting JSON sent to the API will be an array of objects of the form {name: "Category 1", subcategories: [ ...]}. Where the subcategories object is also an array of { name: 'Foo', subcategories: [...]} For example:

[{"name":"Teaching","subcategories":[{"name":"MTH 201","subcategories":[{"name":"Lecture"},{"name":"Prep"},{"name":"Grading"},{"name":"Office Hours","subcategories":[{"name":"In Person"},{"name":"Zoom"}]}]},{"name":"MTH 202","subcategories":[{"name":"Lecture"},{"name":"Prep"},{"name":"Grading"},{"name":"Office Hours","subcategories":[{"name":"In Person"},{"name":"Zoom"}]}]}]},{"name":"Scholarship"},{"name":"Service","subcategories":[{"name":"Meetings"},{"name":"Paperwork"}]}]

I was able to successfully hard-code my depth to 3 like this:

permit(:name, subcategories: [:name, subcategories: [:name]]).to_h

But, three is not deep enough. Is there a way to either

  1. Allow a specific maximum depth (say, 8), without having to build a massive, recursive description like the one above, or

  2. Allow an input of any depth?

2

There are 2 answers

0
smathy On

There's no way to do that with the Rails helper, you'll need to recurse through the structure yourself checking that only the keys you expect are there.

0
Zack On

Here is what I did (based on the answer from @smathy):

Write a recursive function to build a description of a fixed depth:

  def self.make_description(depth)
    if (depth <= 1)
      return [:name]
    else 
      return [:name, subcategories: make_description(depth -1)]
    end
  end

Then, apply that to the call to permit:

 params[:categoryHierarchy].permit(:name, CategoryController.make_description(8))

This solution creates a hard-coded maximum depth; but, I doubt that will be in issue in practice. (I can't imagine a hierarchy being useful once it grows to 4 or 5.)