undefined method `permit' for "titleofcoolstuff":String - ruby-on-rails

I am just trying to get two parameters from a view to my controller. I'm using Rails 4.2.x and strong params are killing me.
One param, :query, resolves correctly. However the second param, :location, throws the error in the questions title. I have Googled the issue but everyone's scenario seems to be different and their solutions (relatively) unique.
The view in question is index.html.erb which only contains a simple search form.
<%= form_tag("/searches", action: "create", method: "post") do %>
<div>Job Title</div>
<%= text_field_tag(:query) %>
<div>Location</div>
<%= text_field_tag(:location) %>
<%= submit_tag("Go") %>
<% end %>
The controller in question is searches_controller.rb.
class SearchesController < ApplicationController
def index
binding.pry
end
def show
binding.pry
end
def update
end
def create
#query = search_params["query"].to_s || nil
#location = search_params[:location].to_s || nil
binding.pry
end
def delete
end
private
def search_params
params.require(:query).permit(:location)
end
end
The stack trace points to the search_params method, and shows me that I have the following params in the controller
{
"utf8"=>"✓",
"authenticity_token"=>"DEcTwT/NnSY3S3n25zZGXD+KRZcsRkWj9bmN57AMNivFbMXwHF5Vf/psgzSMkZPBa+OWJgafXYGdW+o5KN3xxg==",
"query"=>"titleofcoolstuff",
"location"=>"milwauke",
"commit"=>"Go"
}
What am I missing?

Strong parameters is for providing a hash of attributes, for example:
<%= form_for #user do |f| %>
## form
<% end %>
This may send parameters like this:
"user" => { "name"=> "Your Name", "age" => "23", "location" => "USA" }
Strong parameters in this case would be instructing rails to process the users hash of attributes and specifically these attributes, like this:
params.require(:user).permit(:name, :age, :location)
In your case, you are passing in individual parameters (not hashes of attributes), so if you want to grab them, you grab them explicitly:
def create
#query = params[:query].to_s || nil
#location = params[:location].to_s || nil
#do something
end
No need for strong parameters to whitelist model attributes here. Hope this helps.

In your case
"query"=>"titleofcoolstuff",
"location"=>"milwauke",
"commit"=>"Go"
since your data are not wrapped with any keys (they are at the root) so you can simply access them using like params[:query].
Whitelisting/Strong params
We need to whitelist params only for mass assignment. like #user.update(user_params) Here, unless the params sent by users in user_params are whitelisted i.e. permitted using .permit method; the update method will throw an exception ActiveModel::ForbiddenAttributes.
In your case since your not updating anything you do not need to create strong params for it.
def create
#query = params["query"].to_s || nil
#location = params[:location].to_s || nil
binding.pry
end
If you are gonna do mass assignment in future you have to whitelist your params
For more info see https://cbabhusal.wordpress.com/2015/10/02/rails-strong-params-whilisting-params-implementation-details/

Related

Can I have two different input fields and one column in the database?

I want to have two input fields, but only one column in the database.
The first input is stored data in numbers and the other one is stored data in numbers divided by 24.
You can put data only in one field.
Is there any possible way to do this?
UPD:
Migration:
def change
add_column :employees, :hourly_payment , :integer
end
View:
employees/_form.html.erb
<%= simple_form_for #employee do |form| %>
<%= form.input :name %>
<%= form.input :hourly_payment %>
<%= form.input :monthly_payment %>
<%= form.button :submit, class: "btn btn-success" %>
<% end %>
Your model and database tables are the internals of your application and are not actually tied to the view by anything except how easy ActiveRecord makes it to use convention over configuration to link the two*.
In Rails style MVC the controller is responsible for passing user input to the model. Usually you would just do this with simple mass assignment:
class UsersController < ApplicationController
def create
#user = User.new(user_params)
# ...
end
private
def user_params
params.require(:user)
.permit(:email, :salary)
end
end
This is basically just passing a whitelisted hash of parameters straight to the model as is and it all gets passed to the setters that ActiveRecord magically created for you by reading the database schema.
But there is nothing stopping you from assigning attributes manually:
class UsersController < ApplicationController
def create
#user = User.new(user_params) do |user|
user.salary = calculated_salary
end
# ...
end
private
def user_params
params.require(:user)
.permit(:email)
end
def calculated_salary
if params[:user][:hourly_payment].present?
params[:user][:hourly_payment]
elsif params[:user][:monthly_payment].present?
params[:user][:monthly_payment].to_i / 168
else
0 # sorry no cookies for you
end
end
end
Or monkeying with the parameters object:
def user_params
params.require(:user)
.permit(:email)
.merge(salary: calculated_salary)
end
It is after all just a hash on steroids. The only thing that Rails will prevent you from is passing a parameters object that has not been whitelisted.
There is no stone tablet for what you can do in a controller. The only thing to bear in mind is that controllers are notoriously hard to test and fat controllers are a recipe for disaster.
If you're doing anything more complicated there are better solutions such as form objects, decorators or service objects.
You'll need to create a view for that. Here is an example of migration:
def up
sql = %(CREATE OR REPLACE VIEW my_view_models AS
SELECT m.*,m.int_field*12 as my_new_value from my_models m
)
self.connection.execute(sql)
end
def down
self.connection.execute('DROP VIEW IF EXISTS my_view_models')
end
Then, you can access your value with method my_new_value on your model. You'll need to change the name of the default table matching your model.
class MyModel
self.table_name = 'my_view_models'
end
And access it via
MyModel.first.my_new_value

Using strong params when assigning model attributes in controller

Hi i'm running into an issue with my code where i'm not sure where to use strong params. In this case I have a document object being set with a mix of preset values and values coming from my form for example.
class DocumentsController < ApplicationController
def add_document
document_name = params[:document_name]
document_parent_id = params[:doc_parent_id]
#document = Document.new(name: document_name, parent_id: document_parent_id, document_owner_id: current_user_id, created_by: current_user.name)
#document.save
#do flash stuff here
end
So the form is only submitting the document name and document parent id through the params hash. Should these two values be whitelisted using strong params? And If so how can I use strong params to create the new document with the other values that aren't coming from my form.
Thanks.
1/ Yes it should be whitelisted.
def add_document
# stuff
#document = Document.new(document_params.merge(
document_owner_id: current_user_id,
created_by: current_user.name
))
# stuff
end
def document_params
params.require(:document).permit(:name, :parent_id)
end
2/ To submit not from form, you just need to submit nested attribute document inside params alongside with other params:
{ document: { name: '<Name>', parent_id: '<Id>' }, other_params: '...' }
class DocumentsController < ApplicationController
def add_document
#document = Document.new document_params.merge(document_owner_id: current_user_id, created_by: current_user.name)
#document.save
end
private
def document_params
params.permit(:document_name, :doc_parent_id)
end
end
Your code really could do with improving a lot.
Firstly, Rails 4+ convention is to have a "top level" param value of the model (in your case document):
params: {
document: {
document_name: "x",
doc_parent_id: "y"
}
}
This would allow you to call the strong params method properly:
def document_params
params.require(:document).permit(:document_name, :doc_parent_id)
end
The way to achieve this is to use form_for (which should be used in conjunction with a RESTful controller):
#app/views/documents/new.html.erb
<%= form_for #document do |f| %>
<%= f.text_field :document_name %>
<%= f.submit %>
<% end %>
#app/controllers/documents_controller.rb
class DocumentsController < ApplicationController
def new
#document = Document.new
end
def create
#document = Document.new document_params
#document.save
end
end
--
Finally, you also need to make sure your model attribute names work well.
You're currently using document_name as an attribute name. If it were my application, I'd call it name, allowing you to call #document.name in the future.
The same for your other attributes:
document_name -> "name"
doc_parent_id -> "parent_id"
document_owner_id -> "owner_id"

Ruby on Rails, undefined method merge

At the beginning let me excuse for easy question for those who are experienced but for me it is difficult right now. In the Rails in one of the views/_form.html.erb, I want to change line below (it works):
<%= f.collection_select :user_id, User.all, :id, :email %>
into hidden field that will hold the id of the user that is logged in. I try to change it into:
<% f.hidden_field :user_id, :id %>
but it throws an error:
NoMethodError in Orders#new
undefined method `merge' for :id:Symbol
Can sb help me to solve that?
Use this (if you already have current_user method available):
<%= f.hidden_field :user_id, :value => current_user.id %>
If you don't have current_user method implemented, in your corresponding controller, you can have something like this:
#current_user = User.find(params[:user_id])
Then, in your view, you can do:
<%= f.hidden_field :user_id, :value => #current_user.id %>
Update
After the above conversation, if you want to use session to store the user_id, then you can do something like this.
You can create a SessionsHelper module (which you can include in your ApplicationController) where you can define a log_in method:
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
(You can also put this: session[:user_id] = user.id in the create action where you create an user.)
You can also define a current_user method in this module:
# Returns the current logged-in user (if any).
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
Here are some other useful helper methods that you can add in this module:
# Returns true if the given user is the current user.
def current_user?(user)
user == current_user
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
# Logs out the current user.
def log_out
session.delete(:user_id)
#current_user = nil
end
Finally, I would suggest you to take a look at Devise gem which is a very popular authentication solution for Rails application.
This error means that it expects a hash, but you are putting an empty symbol.
You need to send a hash
If you have the current_user method:
<%= f.hidden_field :user_id, :value => current_user.id %>
If you don't, you may use this in your controller
id = User.find(someid)
And keep the same code in the view.
Look # the documentation for more info
The first two answers are correct. Here's why
f.hidden_field is a method on the form object you created somewhere either in that partial or in a file/partial that includes it. The documentation linked by Hristo Georgiev is for the hidden_field method from ActionView::Helpers::FormHelper (link). The one you're trying to call is from ActionView::Helpers::FormBuilder (link)
hidden_field works just as you seem to be expecting it to, ie
hidden_field(:user_object, :id)
these are two different methods
f.hidden_field works a bit differently, because it already has access to some of the information used to build the hidden field. It expects a method to call on the form object and an optional hash which it converts to attributes on the hidden field (thus the :value => user.id hash)
Assuming you had the following form
form_for(#user) do |f|
...
end
And you wanted the id to be a hidden field, you would put this within that block
f.hidden_field(:id)
That would generate the following HTML
<input type="hidden" id="user_id" name="user[id]" value="1" />
See this line from ActionView
TL;DR
You're calling the FormBuilder#hidden_field method with the arguments expected by FormHelper#hidden_field

Rails form_for populates model with Nil

I am having trouble populating my model with my parameter passed from form_for as it assigns Nil to the id, my column (decrypted), and created/updated at fields.
Below is my view (new.html.erb):
<%= form_for #decrypt_text, url: { action: "create" } do |f| %>
<div class="field">
<%= f.label :decrypt_text %><br>
<%= f.text_area :decrypted %><br>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
and my controller:
class DecryptController < ApplicationController
def new
#decrypt_text = Dmessage.new
end
def create
#decrypt_text = Dmessage.new(params[:decrypted])
p #decrypt_text
if #decrypt_text.save
redirect_to '/decrypt/display'
else
redirect_to '/'
end
end
def display
#displayme = Dmessage.order("created_at").last
end
end
and just in case, my model:
class CreateDmessages < ActiveRecord::Migration
def change
create_table :dmessages do |t|
t.text :decrypted
t.timestamps null: false
end
end
end
I know it's assigning nil values because this:
p #decrypt_text
prints out:
#<Dmessage id: nil, decrypted: nil, created_at: nil, updated_at: nil>
I really am not sure what I am missing but I am fairly new to Rails. Any help is appreciated!
First of all, remove url: { action: "create" } from your form. It is unnecessary for the reason that Rails is smart enough to understand that if #decrypt_text doesn't exist, it will trigger the necessary create action.
However, what I really think is causing you trouble is that you are not making use of strong parameters in your application (#decrypt_text = Dmessage.new(params[:decrypted])). You are trying to initialize a params hash directly, which is really dangerous. As of Rails version 4.x it is not allowed to initialize params hashes directly in this way, you must use strong params, to prevent mass assignment.
These would be the changes that I would personally make in your controller file...
class DecryptController < ApplicationController
def new
#decrypt_text = Dmessage.new
end
def create
#decrypt_text = Dmessage.new(decrypt_params)
if #decrypt_text.save
redirect_to '/decrypt/display'
else
redirect_to '/'
end
end
def display
#displayme = Dmessage.order("created_at").last
end
private
def decrypt_params
params.require(:dmessage).permit(:decrypted)
end
end
if u are using rails 4.x then your have to make use of strong parameters as of Rails version 4.x it is not allowed to initialize params hashes directly.
Add following private method to your controller,
def decrypt_params
params.require(:dmessage).permit(:decrypted)
end
As you are using Rails 3, the problem is this line
#decrypt_text = Dmessage.new(params[:decrypted])
It should be
#decrypt_text = Dmessage.new(params[:dmessage])

Ruby on rails can't create with params

I have a from created in Ruby on rails. The code the form looks like this:
<%= simple_form_for(#action) do |f|%>
<%= render 'shared/error_messages' %>
<%=f.label :action_name, "Action name"%>
<%=f.text_field :action_name%></br>
<%=f.input :startDate,:as => :datetime_picker, :label =>"Start date"%>
<%=f.input :endDate,:as => :datetime_picker, :label =>"End date"%>
<%=f.label :contentURL, "Content url"%>
<%=f.text_field :contentURL%></br>
<%= f.button :submit, class: "btn btn-large btn-primary" %>
<%end%>
But when I click the submit button I get this error:
undefined method `permit' for "create":String
def action_params
params.require(:action).permit(:action_name, :startDate,:endDate,:contentURL)
All other forms a working ok, I guess it is something really obvious, just can't see it :(
I really appreciate any help, solving this problem.
Thanks!!
EDIT:
Controller code:
def create
action = Action.new(action_params)
if #action.save
flash[:success] = "New Action saved"
redirect_to "/"
else
render 'new'
end
end
private
def action_params
params.require(:action).permit(:action_name, :startDate,:endDate,:contentURL)
end
In Rails 4, you must use Strong Parameters in your controllers. Here's some explanation from the official blog. And some example:
class PeopleController < ActionController::Base
# This will raise an ActiveModel::ForbiddenAttributes exception because it's using mass assignment
# without an explicit permit step.
def create
Person.create(params[:person])
end
# This will pass with flying colors as long as there's a person key in the parameters, otherwise
# it'll raise a ActionController::MissingParameter exception, which will get caught by
# ActionController::Base and turned into that 400 Bad Request reply.
def update
redirect_to current_account.people.find(params[:id]).tap do |person|
person.update_attributes!(person_params)
end
end
private
# Using a private method to encapsulate the permissible parameters is just a good pattern
# since you'll be able to reuse the same permit list between create and update. Also, you
# can specialize this method with per-user checking of permissible attributes.
def person_params
params.required(:person).permit(:name, :age)
end
end
Notice how, in the last lines, under the private keyword, the person_params method is defined, which declares the permitted fields to be assigned by the create and update methods on top. And it's the person_params that is used for updating - the valid example - instead of the raw params array.

Resources