I am trying to make an app in Rails 4.
I use scaffolding generators to make my resources starting points.
I'm noticing, when I ask questions on this board, that people comment on the form of my strong params definitions in the controllers.
The scaffold generator creates them this format:
def industry_params
params[:industry].permit(:sector, :icon)
end
Most resources that show how to whitelist strong params, show this format.
def industry_params
params.require(:industry).permit(:sector, :icon)
end
http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
Is there anything wrong with the way the rails scaffold generator creates this method?
The require method ensures that a specific parameter is present, and if it's not provided, the require method throws an error. It returns an instance of ActionController::Parameters for the key passed into require.
The permit method returns a copy of the parameters object, returning only the permitted keys and values.
As you can see while using scafffold's default system we need to check for is there is any value in the params, whereas require throw an error if its missing.
Related
In my rails app, I am using Kramdown to parse Markdown. I want to extend the functionality of the convert_a method in the HTML converter. Part of this involves accessing the database, but it is dependent on a parameter in the URL. Because I am not directly calling the method that I am overriding I cannot simply pass the method the params hash. Is there a way to access this hash, or even just get the current URL in a module in the lib directory?
to give a bit more context, the method call is in a helper method here:
# in app/helpers/myhelper.rb
def to_html(text)
Kramdown::Document.new(text, parse_block_html: true).to_custom_html
end
and here is the file in which I override the convert_a:
# in lib/custom_html.rb
class CustomHtml < Kramdown::Converter::Html
def convert_a(el, indent)
# use params[:foo] to make query
format_as_span_html(el.type, el.attr, inner(el, indent))
end
end
Edit:
To give a bit more context on where the overrided method is called. I am not extremely familiar with the Kramdown codebase, however it seems that when to_custom_html is called the following bit of code is run inside of Kramdown.rb:
output, warnings = Converter.const_get(name).convert(#root, #options)
which subsequently calls convert_#{el.type} on the internal kramdown elements.
You can pass additional options in Kramdown::Document#new, so just do something like Kramdown::Document.new(text, my_params: params). Then you can use the #options method of the converter to access your params.
There are similar questions like this, this, and this.
None help because the goal is to prevent logging of long parameters within a specific action instead of using config.filter_parameters. Also the answer must work for Rails 3.2.x while many answers are based on Rails 5.
One answer suggests calling request.filtered_parameters inside the controller method, but calling request.filtered_parameters.delete :long_param did not stop :long_param from getting logged.
config.filter_parameters takes a lambda function, so you can filter whatever you want. See answers here and here.
If you only want to filter long arguments for a specific action, well, you are making your life unnecessarily complicated. Either filter all long parameters, using your lambda to set a limit on parameter value length, or change the parameter key of the action you want to filter so that it is unique and and then just filter by that key.
This can be achieved with a little help from Middleware filter
Create new file app/middleware/filter_long_params.rb
class FilterLongParams
def initialize(app, long_params = [])
#app = app
#long_params = long_params
end
def call(env)
env["action_dispatch.parameter_filter"] += #long_params unless #long_params.empty?
status, headers, response = #app.call(env)
[status, headers, response]
end
end
Then add to your controller
class YourController
use FilterLongParams, [:long_param_to_be_filtered], only: :update
end
First parameter of use is the name of Middleware class, second parameter should be the array of parameters you want to be filtered, and third may be the usual scope of controller actions.
If Rails 3.2 don't autoload app/middleware path, use app/controllers instead.
I have a RESTful API (ActionController::API) service that accepts a list of params. The RESTful service takes a JSON body request (with Content-Type of application/json). When I debug the controller/action (right after the action def) and take a peak at the params list, it appears as follows:
<ActionController::Parameters {"given_name"=>"Mark", "subdomain"=>"development", "controller"=>"user", "action"=>"create", "user"=>{"given_name"=>"Mark"}} permitted: false>
EDIT
All the request has in it (when it is passed to the controller/action -- using POSTman):
{"given_name":"Mark"}
Notice that the object contains the given_name params twice. Is this normal behavior? I did not pass a "user" object (json object) to the controller/action? What is the permitted flag?
When I try to use (right now I'm just testing RESTful call and assigning any values that the user object will except... no validations have been programmed yet):
user = User.new(params)
I get the error:
#<ActiveModel::ForbiddenAttributesError: ActiveModel::ForbiddenAttributesError>
So, I've looked everywhere for the reasoning behind this (why is there a "user" key in the params list? What is the purpose of the permitted flag? Why am I getting an error when I try to assign params)?
EDIT
After doing some testing, I change the controller name from "user_controller" to "tester_controller" and setup the routes to point to the renamed controller.
It seems the "user" object in the params list above has changed to "tester". So why does the param list contain an "object" with all the passed params with the name of the controller? If that's the case, why is it needed?
Any help would be greatly appreciated.
By default Rails in API mode wraps JSON request parameters into a hash guessing its name from controller class. That's why changing the controller from User to Tester changes "object" name. You can read details here.
So if you don't need this "object" in your params just remove :json from :format array in config\initializers\wrap_parameters.rb. Or you can use fine-grained control at the controller level as described above.
You need to specify which attributes are acceptable for mass-assignment in your controller.
def create
#user = User.new(params.require(:user).permit(:given_name))
end
This prevents malicious users from making request posts that alter attributes internal to your application, like role in the case of a user object.
As mentioned above, a better explanation can be found in the guide referring to strong parameters.
You can't pass params to constructor, because params always contain :action and :controller keys. Attributes for new objects should be put in hash under key that identify model you want to create e.g. :user. I suggest you to consult rails guides, especially chapter 7 of "Form Helpers" guide.
If you want to learn more about strong parameters there is a chapter in rails guides for that too :)
This appears to be a violation of MVC, so I'll explain what I am trying to do:
My model makes a call to an API, and the URI of the API depends on the params hash. (The params hash stores the URL of the Rails app).
I created a module to mix into the model (because the model has nothing to do with the API call), but haven't figured out how to get the params into the module.
If possible, I would like to initialize the module with the params hash, but don't know where to do it. before_create on the model would work, but that is in the model.rb file which doesn't know about the params.
Couple ideas, depending on your needs:
Pass the parameters to the model with each call, if they change that much. You could do a class method or instance method -
Model.api_call params[:field]
#model.api_call params[:field]
Save the parameters as class variables in the model:
Model.set_parameters(params)
def self.set_parameters(params)
##params = params
end
# access it in methods with ##params
I have product with a foreign collection_id key
I want to pass an id to a controller.
To do so i have the following routes for my controller :
controller :magasin do
get "magasin" => "magasin#index"
end
The only view in my controller is magasin/index.html.erb
The link to magasin is link_to collection.nom, magasin_path(collection)
This kind of syntax usually works in controllers with models. Here my link is : http://localhost:3000/magasin.2 instead of http://localhost:3000/magasin/2
Later on i will need to call the same view with product_kind_id instead of collection_id and i will add sort by name/price ....
How can i have the ID as a normal argument (/:id)instead of a type(.id)?
A popular URL schema to follow is RESTful routing. Rails has this built-in and will set it up for you if you initialize your resource via rails generate scaffold Magasin nom:string.
If you put resources :magasins in your routing file, it will route /magasins to MagasinsController#index and /magasins/1 to MagasinsController#show with params[:id] set to "1". It will also set up a few other routes that will be useful in the future but for now will just raise an action not found exception.
You don't want to use the dot as an argument delimiter, since Rails places what comes after the dot in the request.format method or just in params[:format] (ordinarily accessed through the respond_to method that comes with the generated scaffolds). Save that dot for later when you are working on delivering alternative display formats like XML and JSON.
I realize I've said a lot in a small space, so feel free to ask any follow up questions once you've consulted the Rails Guide on the issue, and I'll be very glad to help!