Strong Parameters, how to pass the required hash? - ruby-on-rails

I have a button from Users index view to Friendship controller.
But I can't figure out how to correctly pass the hash, which is required by the strong parameters.
Users index view:
<% #users.each do |user| %>
<tr>
<td><%= button_to 'Add friend', friendships_path(:friend_id => user.id), method: :post %></td>
</tr>
<% end %>
Friendship controller
def user_params
params.require(:user).permit(:friend_id)
end
How can I pass the ':user' hash along with the ':friend_id' ?
Thanks in advance.

You'll probably best with the following:
def user_params
params.permit(:friend_id)
end
Params
The params part of the system basically works by taking the params hash and "permitting" the relevant values based on it, as per the following...
When you submit a form_for (as this is built around an ActiveRecord object), you'll receive a params hash like this:
params => {
"[Model]" => {
"param_1" => "value",
"param_2" => "value",
"param_3" => "value"
}
}
In this instance, you'll use the require tag in strong_params:
def strong_params
params.require(:model).permit(:param_1, :param_2, :param_3)
end
--
The situation you have is that you want to use use a single "level" of params hash:
params => {
"friendly_id" => "value"
}
In this case, you'll want to use the following:
def strong_params
params.permit(:friendly_id)
end
Logs
The best way to get this working is to observe the params hash that you're dealing with.
You can view the logs and see the Params hash outputted each time you make a request to Rails - I would look at that, and determine the structure of your params hash in order to ascertain the type of strong_params method you should call

Related

Rails "param is missing or the value is empty" error

I have this link_to helper passing an :email_sequence instance and an extra :set_active params.
I then try to update the :email_sequence instance in the controller using strong params but I'm getting an error saying:
param is missing or the value is empty: email_sequence
link_to:
<%= link_to "Activate", admin_email_sequence_path(base_email.email_sequence, set_active: :true), method: :patch %>
Controller:
class Admin::EmailSequencesController < AdminController
before_action :set_email_sequence
def update
if #email_sequence.update(active: email_sequence_params[:set_active])
flash[:success] = "Sequence updated succesfully"
redirect_to_forwarder_or(params[:forwarding_uri], admin_account_emails_path)
end
end
private
def set_email_sequence
#email_sequence = current_account.email_sequences.find(params[:id])
end
def email_sequence_params
params.require(:email_sequence).permit(:set_active)
end
end
This is what gets sent in the params:
{"_method"=>"patch", "authenticity_token"=>"[FILTERED]", "set_active"=>"false", "id"=>"1"}
Can anybody tell me what am I doing wrong?
By params.require(:email_sequence).permit(:set_active) you expect parameters to be { email_sequence: {set_active: "ANY SCALAR VALUE HERE"} } but you pass only set_active you can fix it by permitting the only one parameter
params.permit(:set_active)
You don't need strong parameters here in the first place. Contrary to popular belief strong parameters does not magically sanitize the parameters. It just prevent mass assignment vulnerabilities by requiring that you whitelist the parameters when passing a hash of parameters. Since you are only using a single parameter there is no mass assignment vulnerability:
class Admin::EmailSequencesController < AdminController
before_action :set_email_sequence
def update
if #email_sequence.update(active: params[:set_active])
flash[:success] = "Sequence updated succesfully"
redirect_to_forwarder_or(params[:forwarding_uri], admin_account_emails_path)
else
# provide an error response!
end
end
private
def set_email_sequence
#email_sequence = current_account.email_sequences.find(params[:id])
end
end
If you later want to use multiple parameters nested in a hash the use of link_to is pretty questionable even if it can be done.
<%= link_to "Activate",
admin_email_sequence_path(
base_email.email_sequence,
"email_sequence[set_active]" => true,
"email_sequence[foo]" => "bar"
),
method: :patch
%>
Use button_to or form_with/form_for to create a form element and style the button to look the way you want instead as this places the parameters in request body instead of the query string.

Access params hash from other action in same controller wihtout reset

class UsersController < ApplicationController
def newpass
#somevariable = {
"a-var" => params[:token_id],
"b-var" => params[:client_id],
"c-var" => params[:user_id]}
end
def setpass
#I need to access this hash values without params getting reset
end
end
I have an action newpass with a corresponding view which calls setpass. I access URL's params in newpass action. I want to use these param values in setpass but the values get reset.
Using a link:
You need to send those values when your request hits setpass action in UsersController. You can create a button, or a link_to to send along those params values. Here is how:
<%= link_to "Go to", setpass_path(token_id: params[:token_id]) %>
You can pass params in _path method, and you will receive all those values inside the respective method in controller.
Using a form:
If you are using a form, and you would like to send those values along, hidden_field_tag is the best option here. You can do something like this:
<%= hidden_field_tag :token_id, params[:token_id] # this line must exist inside your form element %>

Pass a PORO from view to controller

What would be the best way to pass a Plain Old Ruby Object that I have in a view to a controller method?
It is not an object that is persisted in the DB. I would rather not refactor things and just want some ideas on best way to pass this object.
view
link_to "activate", activate_apis_path(my_ip_instance: #my_ip), class: "btn btn-primary btn-xs"
controller
#my_ip = params[:my_ip_instance]
#my_ip is just a string... want the whole object
(Rails 4.2)
Usually the best way is through a form. Consider creating a form with hidden fields for all of your #my_ip attributes.
<%= form_tag activate_apis_path do %>
<%= hidden_field_tag "my_ip_instance[foo]", #my_ip.foo %>
<%= hidden_field_tag "my_ip_instance[tomato]", #my_ip.tomato %>
<%= submit_tag "Activate", class: "btn btn-primary btn-xs" %>
<% end %>
(Extra credit: you could also loop over #my_ip's attributes to generate the hidden fields.)
Another way is to serialize #my_ip as JSON and then deserialize it in the controller. I think that is much messier though.
link_to "activate", activate_apis_path(my_ip_instance: #my_ip.to_json)
To make this work for a more complex object, you would need to write your own serializer/deserializer logic into the class as described in this post.
require "json"
class A
def initialize(string, number)
#string = string
#number = number
end
def to_json(*a)
{
"json_class" => self.class.name,
"data" => {"string" => #string, "number" => #number }
}.to_json(*a)
end
def self.json_create(o)
new(o["data"]["string"], o["data"]["number"])
end
end
What you basically need to do is to serialize the object. Rails provides built in options to serialize objects and Object.to_query which can be used to convert to query parameters.
But, passing around a bunch of state like that violates rest and is indicative of a poor application design. If it's a non-persisted object it should be passed as form parameters in a POST/PATCH request. In a GET request you should strive to initialize all objects needed from the headers, params from the request url and session.

I want to use one controller and html.erb files for my dynamic table. How I will do it in Ruby On Rails?

I stored all the tablename I've created to Menu table. And every time I add the table in Menu, it will automatically create a link under Menu list
see below.
I want each table in Menu to have a Listing, New, Edit, and Delete.
see below.
I have a controller prj_menus_controller, I will just pass the id of the table from Menu table.
here is the code for index and new in my controller.
Class PrjMenusController < ApplicationController
def index
#prj_menus = Menu.find(params[:id]).tablename.singularize.classify.constantize.all
end
def new
#prj_menu = Menu.find(params[:id]).tablename.singularize.classify.constantize.new
end
def create
#prj_menu = Menu.find(params[:id]).tablename.singularize.classify.constantize.new(prj_menu_params)
if #prj_menu.save
redirect_to :action => 'index'
else
render :new
end
end
private
def prj_menu_params
params.require("HERE IS MY PROBLEM").permit(:name)
end
end
and in my
new.html.erb
<%= simple_form_for (#prj_menu),:url => prj_menus_path, :method => :post do |f| %>
<%= f.input :name %>
<%= f.submit 'Save', class: 'btn btn-primary' %>
<%= link_to "Cancel", :back, {:class=>"btn btn-default"} %>
<% end %>
I can get the list in my index.html.erb, it is working. My problem is that I don't know how to get all params when I click the submit in new.html.erb. I got this hash
{"sample1_table"=>{"name"=>"test 6"}, "commit"=>"Save","controller"=>"prj_menus", "action"=>"create"}
It is correct but I don't know what to put in my controller. I tried this params.require(["#{#prj_menu}"]).permit(:name), it creates new record but params[:name] does not save.
I am still a noob to Ruby On Rails and I don't know what to search for this.
I think you are mostly confused on what parameter whitelisting does and how parameters are passed from the form to the controller.
I does not really matter if the name of the form hash matches the name of the database table. It just does in most cases since that makes the most sense. It's simply representative of the REST interface of your app.
Let's say you have a action which creates Pets:
POST /pets
And in our form we have a bunch of inputs like so:
<input name="pet[name]">
Rails will map create a params[:pet] hash { name: 'Spot' }. But we want to save the pets as an Animal.
class PetsController < ApplicationController
def new
#pet = Animal.new()
end
def create
#pet = Animal.new(pet_params)
if #pet.save
# ...
end
def pet_params
params.require(:pet).permit(:name)
end
end
Animal does not care what the params key is, it just gets a hash. But we also need to tell simple_form what parameter key we want to use since it looks at the model_name attribute.
simple_form_for(#pet, as: :pet)
Gives us pet[name] instead of animal[name].
I don't get why you are so adamant about making things so difficult for yourself though unless you are creating a database administration tool in the vein of PHP_MyAdmin. And even that case you don't even want to be altering the schema of the app database at runtime.
You are going to run into huge problems when it comes to creating effective queries for getting all the menus.

Rails - json.erb template

I have been trying to figure out a way to customize JSON with special fields, custom formats, etc etc. I have created an as_json and to_xml method in my model to formulate the object how I need. This works well but it is sloppy because some of my helper methods had to move into the model, because I need the formats in the helpers and model. I also think it is sloppy code and makes the model out of control.
I have been able to get a format with json.erb working but don't think it is working 100% correct either and the callback doesn't append either. Anyone get this working
Here is what I got so far.
api calls format.json
template called is items.json.erb
<% #items.each do |item| %>
<%= { :item => { :id => item.id, :name => item.name }.to_json.html_safe %>
<% end %>
This works but seems odd. Anyone have suggestions or have a way to do this?
btw did this for the callback to work
<%= params[:callback]+"(" if params[:callback] %>
<% #items.each do |item| %>
<%= { :item => { :id => item.id, :name => item.name }.to_json.html_safe %>
<% end %>
<%= ")" if params[:callback] %>
I think the best way to do this would be to skip the erb template if you don't absolutely need if for some reason. Then you could do something like this:
items = Item.all
render :json => items.to_json(:only => [:id, :name]), :callback => params[:callback]
You can override the to_json method in your model to add fields or call methods.
Based on your answer to polarblau, you should override the as_json method and use the :methods parameter to include method results in your json
class Item
def date
return "1 year and 8 months" #obviously use logic here
end
def as_json(args={})
super(:methods=>[:date], :only=>[:id=>:name])
end
end
Most likely, you'll want to either:
use custom finder sql to alter column names/perform calculations (it's much faster than Ruby):
MyModel.select('col_name_that_needs_renamed AS new_name').order('some_col DESC')
or a more complicated example:
MyModel.find_by_sql('SELECT col_name_that_needs_renamed AS new_name, foo_col*50 AS math WHERE foo=bar ORDER some_col LIMIT 8')
if there's something you can't do (or can't figure out) in SQL, you may have to revert to Ruby (although not recommended because it's significantly slower)
API Dock for to_json

Resources