Strong Parameters not allowing params through? - ruby-on-rails

Here's the problem I've been working on for the last few days:
I have task and completed_task models:
class Task < ActiveRecord::Base
belongs_to :user
has_many :completed_tasks
end
class CompletedTask < ActiveRecord::Base
belongs_to :task
end
I have a form that says:
<% #tasks.each do |task| %>
<td><%= link_to task.description, task_path(task) %></td>
<td><%= task.user.first_name %></td>
<td><%= task.value %></td>
<td><%= task.task_type %></td>
<td><%= task.frequency %></td
<td><%= task.active %></td>
<td><%= task.due_by %></td>
<%= button_to "Task Completed", new_completed_task_path(:completed =>[:task_id =>
task.id, :task_value => task.value}) %>
<%end%>
In my completed_task_controller, I have:
def new
#completed_task = CompletedTask.new(params[:completed])
end
def create
#completed_task = CompletedTask.new(completed_task_params)
end
When I click on the button to complete a task, I want it to create a record in the completed_tasks table but the params from the parent table are not flowing from the new action to the create action. I'm guessing it has to do with the strong parameters which I have set as:
private
def set_completed_task
#completed_task = CompletedTask.find(params[:id])
end
def completed_task_params
params.require(:completed_task).permit(:task_id, :task_value)
end
Here is the error I am getting:
ActiveModel::ForbiddenAttributesError
Extracted source (around line #19):
def new
#completed_task = CompletedTask.new(params[:completed])
end
Any ideas???

When you call the new method, at that point nothing has been returned from the form (it hasn't even been dsiplayed, yet)
You should just do
def new
#completed_task = CompletedTask.new
end
When the form is returned, then the create method would typically do
def create
#completed_task = CompletedTask.new(completed_task_params)
if #completed_task.save
# stuff to do when the record is saved, maybe redirect to show page or index
else
# stuff to do if record is not saved... probably redisplay new format with errors
end
end
EDIT: to clarify, the method completed_task_params (which you correctly coded) essentially flags the form attributes as acceptable. Had you done CompletedTask.new(params[:completed_task]) strong parameters would've been unhappy as the attributes weren't flagged as permitted.

Related

Updating rails 6 params with a link_to

Hello I have a lists of invoices that belong to a business and also the business belongs to a user, I am trying to have a button (link to) on a table in which all the invoices are listed for the user to be able to update the status of the invoice.
Pretty much if the user hits the link it will change from paid: true to paid: false and viseversa.
here are the routes:
resources :businesses do
resources :invoices
end
Here is the section of the table in which the link is:
<% if invoice.paid %>
<td><%= link_to "Mark as Not Paid", business_invoice_path(current_user, invoice), method: 'put', data: {paid: false} %></td>
<% else %>
<td><%= link_to "Mark as Paid", business_invoice_path(current_user, invoice), method: 'put', data: {paid: true}%></td>
<% end %>
Note: The paid column is a boolean on the db
Since, the paid column is present on Invoice, it is much better if you handle it at the controller or model level instead of getting the value from the form.
Remove if else conditions and combine it as below:
<%
invoice_text = invoice.paid ? 'Mark as Not Paid' : 'Mark as Paid'
%>
<td><%= link_to invoice_text, business_invoice_path(invoice), method: :put %></td>
In the Business::InvoicesController you can write the logic in update like this:
Business::InvoicesController < ApplicationController
before_action :authenticate_user!
before_action :set_invoice
def update
# TODO: find the business using the invoice
# have a check in place to authorize the
# transaction (if invoice belongs a business which
# belongs the current_user or not, if not then raise Unauthorized error)
# if business does belongs to the current_user then proceed to next step
# invert the value of paid column based on existing value
#invoice.update(paid: !#invoice.paid)
end
private
def set_invoice
#invoice = Invoice.find(params[:id])
end
end
With logic above, you can forget about maintain/finding the value of paid column since you have an option to revert the value of paid to true and back to false. Also, that I assumed you are using Devise for authentication.

undefined method `decorate' for #<Assignment::ActiveRecord_AssociationRelation

I'm trying to implement my first decorator for my view. I'm running into an issue though. When I try to render the view I get the title error undefined method "decorate" for #<Assignment::ActiveRecord_AssociationRelation. I'm not sure what I'm supposed to do? Any help would be great. Here is my code.
Assignment decorator:
class AssignmentDecorator < Draper::Decorator
delegate_all
decorates :assignment
def status
if finished
"Finished"
else
"Waiting"
end
end
end
Pages Controller:
class PagesController < ApplicationController
before_action :verify_account!, only: :dashboard
def home; end
def dashboard
#assignments = current_account.assignments.all.decorate
#invitation = Invitation.new
end
end
View:
<% #assignments.each do |assignment| %>
<tr class="assignment-rows">
<td><%= link_to assignment.name, account_assignment_path(assignment) %></td>
<td><%= assignment.assigned_workers %></td>
<td><%= assignment.status %></td>
</tr>
<% end %>
Error message:
Draper Docs:
https://github.com/drapergem/draper
If you read the docs ;) and look at the decorate_collection section:
https://github.com/drapergem/draper#collections
which states:
Note: In Rails 3, the .all method returns an array and not a query. Thus you cannot use the technique of Article.all.decorate in Rails 3. In Rails 4, .all returns a query so this techique would work fine.
So if you're using Rails 3 - you need to use decorate_collection... or you could (and probably should) upgrade to rails 4
Before
#assignments = current_account.assignments.all.decorate
After
#assignments = current_account.assignments.decorate.all

undefined method `page_leads' + controller show method

i am trying to create a list of leads and create a link to show each one details. so i create a controller like that :
class LeadsController < ApplicationController
def index
#leads = Leads.all
end
def show
#leads = Leads.find(params[:id])
end
def delete
end
private
def lead_params
params.require(:lead).permit(:name,:familyname,:email,:mmobile)
end
end
and a route like below :
Rails.application.routes.draw do
root 'pages#home'
get 'pages/home' => 'pages#home'
get 'leads/index'
resource :leads
get 'leads/:id/show'=> 'leads#show',:as => :leads_show end
but there are one problem and one question :
the question is when i write { Leads.find(params[:id]) } the editor doesn't recognize params[:id] . why?
and when i want to see http://127.0.0.1:3000/leads/index
i see the error like that :
undefined method `lead_path' for #<#:0x36da5f8>
Extracted source (around line #8):
<td><%= lead.familyname %></td>
6 <td><%= lead.mobile %></td>
7 <td><%= lead.email %></td>
8 <td><%= link_to 'show' , lead %></td>
</tr>
<% end %>
<p>Find me in app/views/leads/index.html.erb</p>
It should be
#lead = Lead.all
#lead = Lead.find(params[:id])
Model Name should be always singular, and ccontroller name will be plural
It should be resources :leads, and it will create all the seven actions like show, update, create..etc
And you don't have to call http://127.0.0.1:3000/leads/index this http://127.0.0.1:3000/leads, this will call index page
Can you post your params, so that we can see why you are not getting params[:id]

rails 4 post multiple child objects under multiple parent objects on the same form

I have nested resources with a parent model called main and a child model called temperature. Each main can have many temperatures (1 to many). Is there any way to post multiple temperatures, with each temperature going to a different main object, on one single form using form_for and fields_for (I would like to have just one submit button)? The following is my main's index view and main & temperature controllers. Thanks!
<% #mains.each do |main| %>
<tr>
<td>stuff.....</td>
<td><%= form_for main do |f| %>
<%= f.fields_for :temperature do |t| %>
temp: <%= t.text_field :temp %>
<% end %>
<% end %></td>
</tr>
class MainsController < ApplicationController
#stuff
def index
#mains = Main.all
end
private
def main_params
params.require(:main).permit(:freezer_id, :freezer_name, temperatures_attributes: [:id, :main_id, :temp, :date])
end
end
class TemperaturesController < ApplicationController
#stuff
def create
#main = Main.find(params[:main_id])
logger.debug 'testing 1'
#temp = #main.temperatures.create(temperature_params)
if #temp.save
redirect_to main_path(#main)
else
render 'mains/show'
end
end
private
def temperature_params
params.require(:temperature).permit(:temp, :date)
end
end
What you are trying to do is a bulk update/create on the database for diferent mains. Rails can't handle this on his convention as long as the require(param) is acting. You will need to create a bulk handler in the controller to create individual hashes from each entitiy in the array and then create a handler for the response of each model validations.
Not easy to do, but possible.
Some references, for APIS, nothing for forms, but as long as the view create the appropiate hash, you can reuse it.
http://www.l1ghtm4n.com/post/53259404576/patterns-for-rest-api-bulk-operations
https://github.com/arsduo/batch_api
http://forrst.com/posts/Run_any_rails_RESTful_action_more_than_once_bul-Yb5

Rails Nil Method Issue

I've been borrowing some code from an old ruby on rails 3 app of mine for a new rails 4 app. The code works on the old site, but on the new one it doesn't.
Here's my routes.rb:
scope ':username' do
resources :recipes
end
get "/:username" => "recipes#index"
Here's my controller index:
def index
#user = User.find_by_username params[:username]
#recipes = Recipe.all
end
and my view:
<% #recipes.each do |recipe| %>
<tr>
<td><%= recipe.name %></td>
<td><%= link_to recipe.name, recipe_path(username: #user.username, id: recipe.id) %></td>
<td><%= link_to 'Edit', edit_recipe_path(recipe) %></td>
<td><%= link_to 'Destroy', recipe, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
but the error pops up:
undefined method `username' for nil:NilClass
but the current user username is set to test so its not nil, and the error shouldn't be popping up.
Thanks for all help!
When you have variables that could be potentially be nil, you should handle the case where they are nil, unless you explicitly wish for it to fail in these circumstances.
The dynamic find_by methods return nil when no records are found. Hence, as alfonso pointed out, you are getting a null value for #user.
In this particular instance, I would question how you are using the username param; if recipes are associated with recipes, then I would set up a has_many :recipes in my user model, and belongs_to :user in my recipe model.
Since the user is the 'parent' here, I would opt to create a recipes action in the UsersController. It seems more logical to me to put the recipes that belong to a user in the user's controller, and access the recipes as a collection from the user.
Alternatively, if you are trying to show recipes associated with a user, I would put the action in the RecipesController, and get the user that belongs to a recipe by using the #user method set up from the belongs_to relationship in the database.
In either case, you'll want to guarantee that the users and/or any recipes are defined before trying to render a page. You might want to display a 501 error or something similar if a user doesn't exist that's trying to be accessed, etc.
If you insist that there should always be a user for a recipe, then you should add that type of validation to the recipes model, so that adding a recipe without a user is disallowed:
validates :user, :presence => true
Sorry if I went a little off tangent.
get "/:username"
Here - :username is in position of id
#user = User.find_by_username params[:username]
Here you are trying to find it by params.
Link should look like this http://localhost:3000/user_name/user_name?username=user_name
to find some user, and it is obviously not what you want to achieve.
get "/:id" => "recipes#index"
#user = User.find(params[:id])

Resources