Arvinje
Arvinje

Reputation: 328

Editing params nested hash

Assume we have a rails params hash full of nested hashes and arrays. Is there a way to alter every string value (whether in nested hashes or arrays) which matches a certain criteria (e.g. regex) and still keep the output as a params hash (still containing nested hashes arrays?

I want to do some sort of string manipulation on some attributes before even assigning them to a model. Is there any better way to achieve this?

[UPDATE] Let's say we want to select the strings that have an h in the beginning and replace it with a 'b'. so we have: before: { a: "h343", b: { c: ["h2", "s21"] } }

after: { a: "b343", b: { c: ["b2", "s21"] } }

For some reasons I can't do this with model callbacks and stuff, so it should have be done before assigning to the respective attributes.

Upvotes: 0

Views: 1664

Answers (3)

regic
regic

Reputation: 56

Assuming you have a hash like this:

hash = { "hello" => { "hello" => "hello", "world" => { "hello" => "world", "world" => { "hello" => "world" } } }, "world" => "hello" }

Then add a function that transforms the "ello" part of all keys and values into "i" (meaning that "hello" and "yellow" will become "hi" and "yiw")

def transform_hash(hash, &block)
  hash.inject({}){ |result, (key,value)|
    value = value.is_a?(Hash) ? transform_hash(value, &block) : value.gsub(/ello/, 'i')
    block.call(result, key.gsub(/ello/, 'i'), value)
    result
  }
end

Use the function like:

new_hash = transform_hash(hash) {|hash, key, value| hash[key] = value }

This will transform your hash and it's values regardless of the nesting level. However, the values should be strings (or another Hash) otherwise you'll get an error. to solve this problem just change the value.is_a?(Hash) conditional a bit.

NOTE that I strongly recommend you NOT to change the keys of the hash!

Upvotes: 0

Richard Peck
Richard Peck

Reputation: 76784

still keep the output as a params hash (still containing nested hashes arrays

Sure.

You'll have to manipulate the params hash, which is done in the controller.

Whilst I don't have lots of experience with this I just spent a bunch of time testing -- you can use a blend of the ActionController::Parameters class and then using gsub! -- like this:

#app/controllers/your_controller.rb
class YourController < ApplicationController
   before_action :set_params, only: :create

   def create
      # Params are passed from the browser request
      @model = Model.new params_hash
   end

   private

   def params_hash
      params.require(:x).permit(:y).each do |k,v|
         v.gsub!(/[regex]/, 'string')
      end
   end
end

I tested this on one of our test apps, and it worked perfectly:

enter image description here

enter image description here

enter image description here

--

There are several important points.

Firstly, when you call a strong_params hash, params.permit creates a new hash out of the passed params. This means you can't just modify the passed params with params[:description] = etc. You have to do it to the permitted params.

Secondly, I could only get the .each block working with a bang-operator (gsub!), as this changes the value directly. I'd have to spend more time to work out how to do more elaborate changes.

--

Update

If you wanted to include nested hashes, you'd have to call another loop:

def params_hash
  params.require(:x).permit(:y).each do |k,v|
      if /_attributes/ ~= k
        k.each do |deep_k, deep_v|
           deep_v.gsub!(/[regex]/, 'string'
        end
      else
        v.gsub!(/[regex]/, 'string')
      end
  end
end

Upvotes: 1

max
max

Reputation: 102443

In general you should not alter the original params hash. When you use strong parameters to whitelist the params you are actually creating a copy of the params - which can be modified if you really need to.

def whitelist_params
  params.require(:foo).permit(:bar, :baz)
end

But if mapping the input to a model is too complex or you don't want to do it on the model layer you should consider using a service object.

Upvotes: 0

Related Questions