rails attribute changes in spite of not being in form
Asked Answered
N

2

1

I have rails app where users can assign tasks to each other. Every task has one assigner and one executor. By default the task creator(current_user) is always the assigner. Both of the assigner and executor are allowed to edit the same task.

My problem is the following. Let's say user.id=2 the assigner and user.id=1 is the executor. If the assigner (id=2) edits the task later on everything works fine, but if the executor (id=1) edits it then he becomes the assigner as well, so task.executor_id = 1 and task.assigner_id = 1. There is no option to change the assigner in the new/edit form. When assigner creates it he is the default assigner as current user and when sby tries to edit it he/she can't change the assigner. What causes this issue?

It's weird since it worked well earlier and I'm kinda sure that I have been experiencing this problem since I've changed to using the _task.html.erb partial with <%= render @tasks %> instead of going w/ the plain old @tasks.each do |task| version. I first thought it happened because I made some mistakes w/ the new AJAXified version, but after git reset --hard it seems to be something else.

def task_params
  params.require(:task).permit(:executor_id, :name, :content, :deadline, :task_name_company)
  .merge(assigner_id: current_user.id)
end

class Task < ActiveRecord::Base
  belongs_to :assigner, class_name: "User"
  belongs_to :executor, class_name: "User"

_task.html.erb

<% if current_user.id == task.assigner.id %>
  <tr style="background: #eaeaea" id="task_<%= task.id %>">
    <td class="col-md-3">
      <%= link_to user_path(id: task.executor.id) do %>
        <%= task.executor.profile.first_name %> <%= task.executor.profile.last_name%> / <%= task.executor.profile.company %>
      <% end %>
    </td>
<% else %>
  <tr id="task_<%= task.id %>">
    <td class = "col-md-3">
      <%= link_to user_path(id: task.assigner.id) do %>
        <%= task.assigner.profile.first_name %> <%= task.assigner.profile.last_name%> / <%= task.assigner.profile.company %>
      <% end %>
    </td>
<% end %>
    <td class="cold-md-4">
      <%= link_to user_task_path(id: task.id) do %>
        <%= task.content %>
      <% end %>
    </td>
    <td class="col-md-3">
      <%= task.deadline %>
    </td>
    <td class="col-md-2" style="padding:0px">
    <% if current_user.id == task.assigner.id %>
      <table class="table table-bordered inside-table" style="background:#eaeaea">
    <% else %>
      <table class="table table-bordered inside-table">
    <% end %>
        <tr>
          <td class="col-md-4" style="border-top:0px;border-bottom:0px;border-left:0px">
            <% if task.completed_at == nil %>
              <%= link_to complete_user_task_path(id: task.id), action: :complete, remote: true, method: :patch do %>
                <i class="fa fa-check"></i>
              <% end %>
            <% else %>
              <%= link_to uncomplete_user_task_path(id: task.id), action: :uncomplete, remote: true, method: :patch do %>
                <i class="fa fa-check"></i>
              <% end %>
            <% end %>
          </td>
          <td class="col-md-4" style="border-top:0px;border-bottom:0px;">
            <%= link_to edit_user_task_path(id: task.id), type: "button" do %>
              <i class="fa fa-pencil"></i>
            <% end %>
          </td>
          <td class="col-md-4" style="border-top:0px;border-bottom:0px;border-right:0px">
            <%= link_to user_task_path(id: task.id), method: :delete, data: { confirm: "Are you sure?" }, remote: true do %>
              <i class="fa fa-trash"></i>
            <% end %>
          </td>
        </tr>
      </table>
    </td>
  </tr>

_form.html.erb

<%= form_for ([@user, @task]) do |f| %>

  <%= render 'layouts/error_messages', object: f.object %>

  <div class="field form-group">
    <%= f.label :Name_or_Company %>
    <%= f.text_field :task_name_company, data: {autocomplete_source: user_tasknamecompanies_path}, class: "form-control task_name_company" %>
  </div>
  <div class="field form-group">
    <%= f.label :content %>
    <%= f.text_area :content, class: "form-control" %>
  </div>
  <div class="field form-group">
    <%= f.label :deadline %>
    <%= f.date_select :deadline, class: "form-control" %>
  </div>
  <div class="actions">
    <%= f.submit "Create Task", class: 'btn btn-primary', "data-sid" => current_user.id, "data-rip" => :executor_id %>
  </div>
<% end %>

UPDATE: Problem solved based on Rich Peck's and miler350's answers. I did the following:

before_action :set_assigner, only: :create

private

def set_assigner
  @task.assigner.id = current_user.id
end
Nightjar answered 26/10, 2015 at 23:41 Comment(0)
Y
3
def task_params
  params.require(:task).permit(:executor_id, :name, :content, :deadline, :task_name_company).merge(assigner_id: current_user.id)
end

You are setting the assigner id to current_user every time you pass in the params.

I would remove the merge, and just have strong params like this:

params.require(:task).permit(:executor_id, :name, :content, :deadline, :task_name_company, :assigner_id)

Then, in your create action, before you do:

@task.save

Add this:

@task.assigner_id = current_user.id

Then you can define your permissions however you choose (must be assigner, must be executor), but as long as you don't add anything crazy to update, it'll work fine.

Yahairayahata answered 27/10, 2015 at 0:26 Comment(2)
I have no task.save in my controller action, only IF task.save. What would be the preferred way to do this? hidden_field in the form, attr_readonly/attr_protected or before_create callback?Nightjar
that's fine. the @task.assigner_id = current_user.id just goes above the if @task.save line. same mechanics.Yahairayahata
S
1

By default the task creator(current_user) is always the assigner

This should not matter - someone is the assigner and someone is the executor.


miler350 is correct - your params are constantly setting your assigner_id: current_user.id (looks like one of my suggestions).

The fix is to remove the .merge from your params & set it in your controller action (as per miler350's answer & like this):

#app/controllers/tasks_controller.rb
class TasksController < ApplicationController

    def new
        @task = Task.new
    end

    def create
        @task = Task.new task_params
        @task.assigner = current_user
        @task.save
    end

    def edit
        @task = Task.find params[:id]
    end

    def update
        @task = Task.find params[:id]
        @task.update task_params
    end

    private

    def task_params
        params.require(:task).permit(:executor_id, :name, :content, :deadline, :task_name_company, :assigner_id)
    end

end

By letting users "edit" an assignment, you should not have different assigner / executor defined each time.

Stortz answered 27/10, 2015 at 13:47 Comment(3)
Rich, I solved it (can be found in the updated question), but I have an extra question. Why could I use your solution, I mean if I use a form it's compatible what you wrote in the create action? @task.save doesn't do the same as clicking on the submit button? So isn't get submitted twice?Nightjar
I upvoted the other answer! You don't need to use my own, I wrote it because it gives perspective on how the controller should workStortz
I mean how it's possible to use yours. I just don't understand it, so I'm wondering if possible to do the submit and @task.save at the same time.Nightjar

© 2022 - 2024 — McMap. All rights reserved.