How to not use Rails action parameter in controller - ruby-on-rails

I am implementing a third party API for Shipworks in a Rails server and the Shipworks client app is posting an action param with Shipworks specific semantics.
However the Rails routing logic overwrites this param to be the name of the controller method.
Is there a custom route I could write to get the value of that action param without it being overwritten to be the name of my controller method?

I fell into this trap today and came with this solution in controller method. It's Rails 4.1:
if request.method == "POST"
action = request.request_parameters['action']
else
action = request.query_parameters['action']
end

I figured it out. There is a raw_post method in AbstractRequest.
So you can do this to parse the raw post params:
def raw_post_to_hash
request.raw_post.split(/&/).inject({}) do |hash, setting|
key, val = setting.split(/=/)
hash[key.to_sym] = val
hash
end
end
and then just call raw_post_to_hash[:action] to access the original action param or any other param. There is probably an easier way.

Related

Rails override passed request params

In my Rails 7 API only app I'm receiving the request with these params:
def event_params
params.permit(:event, :envelopeId, :action, :recipient)
end
Inside the controller I need to set a guard based on event_params[:action] like below:
class EventsController < BaseController
def event_callback
return unless event_params[:action] == 'envelopefinished'
(...)
end
end
But it turns out the :action is default ActionController::Parameters parameter that represents the name of the action being performed (i.e., the method being called). Is it possible to get parameter event_params[:action] which was passed inside the JSON file without changing the JSON key name to something else?
Its actually the router that overwrites params[:action] and params[:controller] but you can still access the raw parameters through the request object:
request.POST["action"]
This odd looking method (yeah its a method not a constant) is from Rack::Request::Helpers and gives the parsed parameters from the request body.

Ruby on rails post request routing

I am a newbie in the world of ruby on rails, and trying to find out how routing works. I read some articles about it but something is not clear to me.
If I have a page, with a message sender form and try to send the data via post, I have to set the route sg like this:
post '/send', to: 'message#send'
with this it works fine. But what if I have an another page with another form and I want to link it to another controller/action(post request too). How can It make a disctinction between the 2 posts?
You can pass some special parameter and check it's value in controller, for instance:
class MessageController < ApplicationController
def send
if params[:kind] == 'some_value'
do_one_thing
else
do_anoter_thing
end
end
end
But in this case your action will become fat and ugly. Thus I suggest you to create new action and separate logic in a natural way:
post '/my_send_from_one_place', to: 'message#my_send_from_one_place'
post '/my_send_from_secong_place', to: 'message#my_send_from_secong_place'

Why does Rails use the params variable rather than passing arguments through the function?

As title says, why does Rails prefer to use the #params variable inside of a Controller action when you are responding to the action instead of passing the individual parameters through the function arguments when we call the function?
Other frameworks use this (i.e, ASP MVC) and I was just wondering if there was a reason for that design decision, because it doesn't seem very intuitive.
Ie. Why does Rails do
def index
name = params[:name]
end
Instead of
def index(name)
end
The point is, most of the actions in a controller handles the view REST-fully. The params comes from the user's browser when they interact with the page or send a new variable request to the page.
These requests are variable, and Rails makes it uniform by maintaining the parameters in params hash. If the following GET requests arrive:
http://localhost:3000/products?color=red&quality=best
the params hash will automatically be populated as {'color' => 'red', 'quality' => 'best'}. Rails doesn't expect your action to manually handle the parameters.
Similarly, consider you are getting a POST request from a page where a user filled a form. In that scenario, the params obtain the parameters which are composed with form helpers inside views.
Though in hyptothetical case you are dealing with general methods instead of actions, such as below, you will have to do it by passing arguments.
def show
if params['color'] == 'red'
#product = obtain_product('red')
else
#,..
end
end
def obtain_product(color)
Product.where('color = ?', color).first
end
Hope it is clear. :)
#kidorrails has a great answer, and I wanted to add to it:
If you wanted to pass the params to each method directly, it would go against the #1 Rails convention - keep it DRY. By having a separate params hash, you not only have access to all the params you want, but you can access them through as many methods as you need
For example, take strong_params:
#controller
def new
#model = Model.new
end
def create
#model = Model.new(strong_params)
#model.save
end
private
def strong_params
params.require(:model).permit(:your, :params)
end
As #apneadiving mentioned, the params hash is created in another part of the stack, meaning it's available over all the methods required. It's most efficient & versatile way to do it IMO

Why won't my params pass to my model?

I am trying to pass a simple variable in my params to a class method, however it doesnt seem to work. This seems elementary, but I'm still learning. Can someone explain why this doesn't work and offer an alternative? My code is below.
controller
#profile = current_user.profile
#dogs = Dog.by_profile(params[#profile])
model
def self.by_profile(profile)
Dog.where(kids_under_10: profile.kids_under_10 )
end
*note: profile.kids_under_10 is a boolean. When I manually replace it with true or false, everything works fine.
params is a special rails hash that contains url parameters. So your code is looking for a url parameter passed with the request containing the string version of your user profile. This is definitely not what you want to be doing.
When you're calling a rails model method, you call it with arguments like any other method: Dog.by_profile(#profile)
You don't want the params part, or you're trying to do something crazy that should be refactored :)
Your key value for params should look like params[:profile].
So try #dogs = Dog.by_profile(params[:profile]).
Because params is a hash that comes from a request. What you are doing is trying to search the hash with an instance variable which is wrong.
I think what you meant is to do params[:profile] or just #profile

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.

Resources