Home > Software design >  Require structure with array of hash maps
Require structure with array of hash maps

Time:10-19

I'm using Ruby on Rails 7 to build a REST API that accepts a payload via HTTP PATCH that looks like this:

{
  "answers": [
    {"question_id": 1, "content": "My answer content ONE"},
    {"question_id": 2, "content": "My answer content TWO"}
  ]
}

I want to validate the structure of this response in my controller using strong parameters.

I've tried a bunch of similar SO posts but none of them seem to work for me.

Here is what I've tried so far:

params = ActionController::Parameters.new(answers: [{question_id: 1, content: 'some answer'}])

params.require(answers: [:question_id, :content])
# I get: `require': param is missing or the value is empty: {:answers=>[:question_id, :content]} (ActionController::ParameterMissing) 

params.require(:answers).require([:question_id, :content])
# I get: NoMethodError on the second .require

params.require(answers: [[:question_id, :content]])
# I get: param is missing or the value is empty: {:answers=>[[:question_id, :content]]}

Any pointers on what I am doing wrong would be greatly appreciated.

CodePudding user response:

I found a solution just after I posted the question.

The solution which worked is:

def answer_params
  params.require(:answer).each do |answer_param|
    answer_param.require(%i[question_id, answer])
  end
end

This allows me to ensure that all values are provided in the correct structure and use it in my controller method like this:

def update
  answer_params.each do |answer_param|
    puts answer_param['question_id']
    puts answer_param['answer']
  end
end

CodePudding user response:

Why insist on validating the params only through ActionController::Parameters? I have always found limitations with the params API for nested structures (to be specific more deep structures for API I have built) like you have and in such cases I always validate the data in the most basic manner. For e.g. for the data structure you have shown I would validate it in following manner:

def validate_answers_data
  received_answers_arr = params[:answers]

  if received_answers_arr.blank?
    # return error requires answers not received
    return
  end

  whitelisted_answers_data = []

  received_answers_arr.each do |h|
    question_id = h[:question_id]
    answer_content = h[:content]

    if question_id.present? && answer_content.present?
      whitelisted_answers_data << { question_id: question_id, content: answer_content }
    end
  end

  if whitelisted_answers_data.blank?
    # return error answers data received found invalid
    return
  end


  whitelisted_answers_data
end

Params API is definitely useful for whitelisting data but when it poses limitations for a particular data structure, then instead of trying to insist on implementing the validation through only that API, it is better to implement through pure Ruby constructs which is the option always open.

Hope you find this suggestion helpful.

CodePudding user response:

You can define the method to ensure params structure like below:

def answer_params
 params.permit(answers: %i[question_id content])
end

and then use this method in your update action like:

def update
  answer_params[:answers].each do |answer_param|
    puts answer_param['question_id']
    puts answer_param['answer']
  end
end
  • Related