As you already know, JSON naming convention advocates the use of camelSpace and the Rails advocates the use of snake_case for parameter names.
What is the best way t
Another rails 5.1 solution that piggy backs off of the Sebastian Hoitz solution above. To clarify why we need to do this: in R5.1 deep_transform_keys! is no longer a method available to us, since params are no longer inheriting from HashWithIndifferentAccess. And overcomes the issue mentioned by Eliot Sykes where the initializer only works for application/json mime types. It does add overhead to all the requests though. (I'd love to see some initializers for ActionDispatch::Request.parameter_parsers[:multipart_form]
) though, since the initializer is a better place to be doing this, IMO.
before_action :normalize_key!
def normalize_keys!(val = params)
if val.class == Array
val.map { |v| normalize_keys! v }
else
if val.respond_to?(:keys)
val.keys.each do |k|
current_key_value = val[k]
val.delete k
val[k.to_s.underscore] = normalize_keys!(current_key_value)
end
end
val
end
val
end
you can try this:
class ApplicationController < ActionController::API
include ControllerHelper
before_action :deep_underscore_params!
def deep_underscore_params!(app_params = params)
app_params.transform_keys!(&:underscore)
app_params.each do |key, value|
deep_underscore_params!(value) if value.instance_of?(ActionController::Parameters)
end
app_params.reject! { |k, v| v.blank? }
end
end
Merging Sebastian Hoitz's answer with this gist, I could make it work on rails 4.2, strong parameters AND parameters wrapping with the wrap_parameters tagging method.
I couldn't make it work using a before_filter
, probably because the parameter wrapping is done before filtering.
In config/initializers/wrap_parameters.rb
:
# Convert json parameters, sent from Javascript UI, from camelCase to snake_case.
# This bridges the gap between javascript and ruby naming conventions.
module ActionController
module ParamsNormalizer
extend ActiveSupport::Concern
def process_action(*args)
deep_underscore_params!(request.parameters)
super
end
private
def deep_underscore_params!(val)
case val
when Array
val.map {|v| deep_underscore_params! v }
when Hash
val.keys.each do |k, v = val[k]|
val.delete k
val[k.underscore] = deep_underscore_params!(v)
end
val
else
val
end
end
end
end
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActiveSupport.on_load(:action_controller) do
wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
# Include the above defined concern
include ::ActionController::ParamsNormalizer
end
Solution for Rails 5
before_action :underscore_params!
def underscore_params!
underscore_hash = -> (hash) do
hash.transform_keys!(&:underscore)
hash.each do |key, value|
if value.is_a?(ActionController::Parameters)
underscore_hash.call(value)
elsif value.is_a?(Array)
value.each do |item|
next unless item.is_a?(ActionController::Parameters)
underscore_hash.call(item)
end
end
end
end
underscore_hash.call(params)
end
Example with camelCase to snake_case in rails console
2.3.1 :001 > params = ActionController::Parameters.new({"firstName"=>"john", "lastName"=>"doe", "email"=>"john@doe.com"})
=> <ActionController::Parameters {"firstName"=>"john", "lastName"=>"doe", "email"=>"john@doe.com"} permitted: false>
2.3.1 :002 > params.transform_keys(&:underscore)
=> <ActionController::Parameters {"first_name"=>"john", "last_name"=>"doe", "email"=>"john@doe.com"} permitted: false>
source:
http://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-transform_keys http://apidock.com/rails/String/underscore
UPDATE:
If you have nested attributes and Rails 6 you can do:
ActionController::Parameters convert to hash and then do deep transform:
params.permit!.to_h.deep_transform_keys { |key| key.to_s.underscore }
params.permit!.to_h.deep_transform_values { |value| value.to_s.underscore }
Please see:
http://apidock.com/rails/v6.0.0/Hash/deep_transform_values http://apidock.com/rails/v6.0.0/Hash/deep_transform_keys
I wanted to use Chris Healds version, but since I am using Rails 4 I have strong_parameters enabled so I had to change it up a bit.
This is the version that I came up with:
before_filter :deep_underscore_params!
def deep_underscore_params!(val = request.parameters)
case val
when Array
val.map { |v| deep_underscore_params!(v) }
when Hash
val.keys.each do |k, v = val[k]|
val.delete k
val[k.underscore] = deep_underscore_params!(v)
end
params = val
else
val
end
end