Format Params in Rails Server Logs - ruby-on-rails

I have a webhooks controller, and i want to be able to view the params that get printed to my server logs in development in a nice readable format. Is awesome_print good for this? I'm trying to use prettyprint, example below, but the format is still not very readable.
Trying to use prettyprint to format params
class DwollaWebhooksController < WebhooksController
require 'pp'
def create
pp params
case params[:topic]
when 'customer_funding_source_verified'
puts '----------customer_funding_source_verified-----------------'
end
end
Here's what that output looks like
<ActionController::Parameters {"id"=>"57dec892", "resourceId"=>"a0d172yx", "topic"=>"customer_bank_transfer_completed",...} permitted: false>
I'm looking for something that at least has proper indentation, multiple lines, etc

If you want to render the parameters in a "pretty" way, you can convert them to hash. Although as you have unpermitted params, you should use to_unsafe_h(), which gives you an unsafe, unfiltered ActiveSupport::HashWithIndifferentAccess representation of the parameters. So:
pp params.to_unsafe_h
which will output something like:
{"id"=>"57dec892",
"resourceId"=>"a0d172yx",
"topic"=>"customer_bank_transfer_completed"}

Related

Unexpected token when sending nested params to worker - Rails 6

I have a Sidekiq worker to which I send my controller params. My controller params look like this.
def my_params
params.require(:users).permit(employees: [:param1, param_requested_attributes: [:attribute]])
end
So when I send my JSON to the controller and check with byebug, params are correctly formatted, but when I send them to the worker like:
MyWorker.perform_async(my_params)
I iterate over each "employee" as:
my_params.each do |employee|
data = JSON.parse(raw_data.gsub('=>', ':')) # to correctly format my json data
end
and I get an "unexpected token error" because 'params_requested_attributes' looks like:
"params_requested_attributes"=>[<ActionController::Parameters> {"attribute"=>"value"} permitted: true> ]
My question is, how can I avoid this "ActionController::parameters" when trying to JSON.parse my params? It only happens when I try to use these nested_attributes. I just want a raw json, but for some reason I get this "action controller params".
Your problem is that my_params:
def my_params
params.require(:users).permit(...)
end
gives you an instance of ActionController::Parameters and the params_requested_attributes inside that is another instance of ActionController::Parameters. Then you enqueue a job with:
MyWorker.perform_async(my_params)
which has to serialize my_params into a database and that serialization isn't handling the inner ActionController::Parameters the way you want it to.
You could convert the parameters to a nested hash yourself:
MyWorker.perform_async(my_params.to_h)
to get rid of the the ActionController::Parameters and end up with plain hashes and arrays in your job.
If you really want JSON that your job manually unpacks then you'll have to massage my_params a little more. Perhaps you'd want to my_params.to_h and then JSON encode then nested params_requested_attributes. This sounds like a solution in search of a problem, I think passing my_params.to_h to the job is where you want to go.
JSON.parse turns a json string into a ruby object. So if you need just the raw json string you should use raw_data and skip the .gsub call.
However I cannot see from your example how raw_data relates to the employee value.

Updating objects in RoR with API call

I am trying to configure my ruby on rails application in such a manner that I can update values with http Patch calls from for example a Angular app. Currently I have the following method of which I expect it to work:
users_controller.rb
def safe_params
params.require(:id).permit(:email)
end
def update
user = User.find(params[:id])
user.update_attributes(safe_params)
render nothing: true, status: 204
end
However, I get the following error when I pass some simple JSON:
undefined method `permit' for "500":String
Passed JSON:
{"email":"newadres#live.com", "id":500}
Do you guys know what I am doing wrong?
I believe you are misunderstanding the purpose of require and permit.
require is generally used in combination with a Hash and a form, to make sure the controller receives an Hash that exists and contains some expected attributes. Note that require will either raise, or extract the value associated with the required key, and return that value.
permit works as a filter, it explicitly allows only certain fields. The returned value is the original params Hash, whitelisted.
In your case, require does not make any sense at all, unless you pass a nested JSON like this one
{"user": {"email":"newadres#live.com", "id":500}}
but even in that case, it would be
params.require(:user).permit(:email)
In your current scenario, the correct code is
params.permit(:email)
One way to fix this, keeping with the spirit of the Rails docs:
def safe_params
params.require(:user).permit(:email)
end
And update the json:
{"user": {"email":"newadres#live.com"}, "id": 500}
You should change the order between require and permit, like that
params.permit(:email).require(:id)
because permit returns the entire hash, while require returns the specific parameter
Reference here
UPDATE
However, as others pointed out, you shouldn't use require with a single attribute, as it is most commonly used for hashes instead

Ruby 2 Keyword Arguments and ActionController::Parameters

I have a rails 4 application that is running on ruby 2.1. I have a User model that looks something like
class User < ActiveModel::Base
def self.search(query: false, active: true, **extra)
# ...
end
end
As you can see in the search method I am attempting to use the new keyword arguments feature of ruby 2.
The problem is that when I call this code from in my controller all values get dumped into query.
params
{"action"=>"search", "controller"=>"users", query: "foobar" }
Please note that this is a ActionController::Parameters object and not a hash as it looks
UsersController
def search
#users = User.search(params)
end
I feel that this is because params is a ActionController::Parameters object and not a hash. However even calling to_h on params when passing it in dumps everything into query instead of the expected behavior. I think this is because the keys are now strings instead of symbols.
I know that I could build a new hash w/ symbols as the keys but this seems to be more trouble than it's worth. Ideas? Suggestions?
Keywords arguments must be passed as hash with symbols, not strings:
class Something
def initialize(one: nil)
end
end
irb(main):019:0> Something.new("one" => 1)
ArgumentError: wrong number of arguments (1 for 0)
ActionController::Parameters inherits from ActiveSupport::HashWithIndifferentAccess which defaults to string keys:
a = HashWithIndifferentAccess.new(one: 1)
=> {"one"=>1}
To make it symbols you can call symbolize_keys method. In your case: User.search(params.symbolize_keys)
I agree with Morgoth, however, with rails ~5 you will get a Deprecation Warning because ActionController::Parameters no longer inherits from hash. So instead you can do:
params.to_unsafe_hash.symbolize_keys
or if you have nested params as is often the case when building api endpoints:
params.to_unsafe_hash.deep_symbolize_keys
You might add a method to ApplicationController that looks something like this:
def unsafe_keyworded_params
#_unsafe_keyworded_params ||= params.to_unsafe_hash.deep_symbolized_keys
end
You most likely do need them to be symbols. Try this:
def search
#users = User.search(params.inject({}){|para,(k,v)| para[k.to_sym] = v; para}
end
I know it's not the ideal solution, but it is a one liner.
In this particular instance I think you're better off passing the params object and treating it as such rather than trying to be clever with the new functionality in Ruby 2.
For one thing, reading this is a lot clearer about where the variables are coming from and why they might be missing/incorrect/whatever:
def search(params)
raise ArgumentError, 'Required arguments are missing' unless params[:query].present?
# ... do stuff ...
end
What you're trying to do (in my opinion) only clouds the issue and confuses things when trying to debug problems:
def self.search(query: false, active: true, **extra)
# ...
end
# Method explicitly asks for particular arguments, but then you call it like this:
User.search(params)
Personally, I think that code is a bit smelly.
However ... personal opinion aside, how I would fix it would be to monkey-patch the ActionController::Parameters class and add a #to_h method which structured the data as you need it to pass to a method like this.
Using to_unsafe_hash is unsafe because it includes params that are not permitted. (See ActionController::Parameters#permit) A better approach is to use to_hash:
params.to_hash.symbolize_keys
or if you have nested params:
params.to_hash.deep_symbolize_keys
Reference: https://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-to_hash

Getting the rails 'params' without the defaults?

Is there a neat way in rails to get a hash of the params without the default ones of 'action' and 'controller'? Essentially without any param that wasn't added by me.
I've settled for:
parm = params.clone
parm.delete('action')
parm.delete('controller');
But wondering if there is a neater way to do this?
You could use except:
params.except(:action, :controller)
http://as.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Hash/Except.html
request.path_parameters
returns path_parameters
request.query_parameters
returns request_parameters
You are looking for the latter.
If you are working in a controller, you should also have access to the request object.
To make a long story short, rails and rack groom incoming GET/POST requests (form, xml, json) and pull out the parameters so that developers have a consistent way of accessing them.
ActionDispatch exposes the consolidated list of params via:
# ActionPack 3.1.8 - action_dispatch/http/parameters.rb
# Returns both GET and POST \parameters in a single hash.
def parameters
#env["action_dispatch.request.parameters"] ||= begin
params = request_parameters.merge(query_parameters)
params.merge!(path_parameters)
encode_params(params).with_indifferent_access
end
end
alias :params :parameters
As you can see, params is an alias for the parameters method which is a merged hash of two sub-hashes: request_parameters and path_parameters.
In your case, you don't want the path_parameters. Rather than using except, which forces you to know which path parameters you want to exclude, you can access your data via: request.request_parameters.
A word of caution: You may be better off using :except if you require the hash to be encoded and keys to be accessed as either strings or symbols. The last line of the parameters method handles that for you:
encode_params(params).with_indifferent_access
An alternative approach using except and ensuring that you are removing all rails non-request parameters:
path_params = request.path_parameters
params.except(*path_params.keys)
use
request.request_parameters
it excludes the path_parameters (controller and action)
I use
request.request_parameters.except(controller_name.singularize)
This strips out the nested object that is named after the active controller. For example with the following controller:
Class SessionController > ActionController::Base
def create
User.find_by(params[:email]).login(password: params[:password])
puts request.request_parameters
end
end
With the following posted value from a web form:
{email: 'test#example.com', password: 'password123'}
The console output will be:
{"email"=>"test#example.com", "password"=>"password123", "session"=>{"email"=>"test#example.com", "password"=>"password123"}}
The above lines of code avoid this.

How do you handle RESTful URL parameters in a Ruby on Rails application?

I am dealing with a very simple RESTful Rails application. There is a User model and I need to update it. Rails coders like to do:
if #user.update_attributes(params[:user])
...
And from what I understand about REST, this URL request should work:
curl -d "first_name=tony&last_name=something2&v=1.0&_method=put" http://localhost:3000/users/1.xml
However, it's quite obvious that will not work because each URL parameter will be parsed to the variable "params" and not "params[:user]"
I have a hackish fix for now, but I wanted to know how people usually handle this.
Thanks
It's just a matter of how Rails parses parameters. You can nest parameters in a hash using square brackets. Something like this should work:
curl -d "user[first_name]=tony&user[last_name]=something2&v=1.0&_method=put" http://localhost:3000/users/1.xml
This should turn into
{:user=>{:last_name=>"something", :first_name=>"tony"}}
in your params hash. This is how Rails form helpers build the params hash as well, they use the square brackets in the form input tag name attribute.
It's a tradeoff; You can have slightly ugly urls, but very simple controller/models. Or you can have nice urls but slightly ugly controller/models (for making custom parsing of parameters).
For example, you could add this method on your User model:
class User < ActiveRecord::Base
#class method
def self.new_from_params(params)
[:action, :method, :controller].each{|m| params.delete(m)}
# you might need to do more stuff nere - like removing additional params, etc
return new(params)
end
end
Now on your controller you can do this:
class UsersController < ApplicationController
def create
#handles nice and ugly urls
if(params[:user]) #user=User.new(params[:user])
else #user = User.new_from_params(params)
end
if(#user.valid?)
... etc
end
end
end
This will handle your post nicely, and also posts coming from forms.
I usually have this kind of behaviour when I need my clients to "copy and paste" urls around (i.e. on searches that they can send via email).

Resources