4

I'm learning Ruby on Rails and got curious how the params method works. I understand what it does, but how?

Is there a built-in method that takes a hash string like so

"cat[name]"

and translates it to

{ :cat => { :name => <assigned_value> } } 

?

I have attempted to write the params method myself but am not sure how to write this functionality in ruby.

2 Answers 2

4

The GET parameters are set from ActionDispatch::Request#GET, which extends Rack::Request#GET, which uses Rack::QueryParser#parse_nested_query.

The POST parameters are set from ActionDispatch::Request#POST, which extends Rack::Request#POST, which uses Rack::Multipart#parse_multipart. That splays through several more files in lib/rack/multipart.

Sign up to request clarification or add additional context in comments.

Comments

1

Here is a reproduction of the functionality of the method (note: this is NOT how the method works). Helper methods of interest: #array_to_hash and #handle_nested_hash_array

require 'uri'

class Params
  def initialize(req, route_params = {})
    @params = {}

    route_params.keys.each do |key|
      handle_nested_hash_array([{key => route_params[key]}])
    end

    parse_www_encoded_form(req.query_string) if req.query_string
    parse_www_encoded_form(req.body) if req.body
  end

  def [](key)
    @params[key.to_sym] || @params[key.to_s]
  end

  def to_s
    @params.to_s
  end

  class AttributeNotFoundError < ArgumentError; end;

  private

  def parse_www_encoded_form(www_encoded_form)
    params_array = URI::decode_www_form(www_encoded_form).map do |k, v|
      [parse_key(k), v]
    end

    params_array.map! do |sub_array|
      array_to_hash(sub_array.flatten)
    end

    handle_nested_hash_array(params_array)
  end

  def handle_nested_hash_array(params_array)
    params_array.each do |working_hash|
      params = @params

      while true
        if params.keys.include?(working_hash.keys[0])
          params = params[working_hash.keys[0]]
          working_hash = working_hash[working_hash.keys[0]]
        else
          break
        end

        break if !working_hash.values[0].is_a?(Hash)
        break if !params.values[0].is_a?(Hash)
      end

      params.merge!(working_hash)
    end
  end

  def array_to_hash(params_array)
    return params_array.join if params_array.length == 1

    hash = {}
    hash[params_array[0]] = array_to_hash(params_array.drop(1))

    hash
  end

  def parse_key(key)
    key.split(/\]\[|\[|\]/)
  end
end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.