0

I have an update method in a controller. If params[:single] is a value of 1, I want to run a create_exception method that looks like this and not update the record:

  before_action :create_exception, only: %i[ update ]

  def update
    if @event.update(event_params) ...
  end

  private

  def create_exception
    if params[:single] == 1
      @exception = @event.event_exceptions.create(date: params[:exception_date])
      respond_to do |format|
        format.turbo_stream
        format.html { redirect_to events_path, notice: "Event was successfully updated." }
        format.json { head :no_content }
      end
    end
  end

My first problem is that the create_exception method does not seem to be firing when I update an event. It just calls update and updates the info on the record which is not what I want. If params[:single] = 1 I want to create an expection and redirect back to the schedule without updating the event.

What is the best way to accomplish this?

spacerobot
  • 265
  • 1
  • 5
  • 23
  • 2
    The passed parameter will be a string, not an integer. Change `params[:single] == 1` to `params[:single] == '1'` – AbM Jan 11 '23 at 14:59
  • 2
    I always prefer `params[:single].to_i` – Eyeslandic Jan 11 '23 at 16:49
  • Thanks. I have tried both of these options and it is still not catching this. If I inspect params I see params[:single] does equal 1. If I remove that if check from the exception create, it does work and create the exception/redirect. For some reason with this if check in, it is not seeing that param correctly to execute the method. – spacerobot Jan 11 '23 at 19:16
  • 1
    Can you share what the params looks like from the request logs? – Ghouse Mohamed Jan 11 '23 at 22:17

2 Answers2

2

Parameter values are always strings, so check params[:single] == "1".


before_action is useful for doing things that should happen universally before actions are run, like checking permissions.

It should not be used to alter the behavior of a single action. That is action-at-a-distance.

Instead, check your params in the update method and return early.

  def update
    return create_event_exception if params[:single] == "1"

    # update as normal
  end

  private

  def create_event_exception
      @exception = @event.event_exceptions.create(date: params[:exception_date])
      respond_to do |format|
        format.turbo_stream
        format.html { redirect_to events_path, notice: "Event was successfully updated." }
        format.json { head :no_content }
      end
  end

The full behavior of update is clear, and it does not rely on the reader knowing details of how before_action works.

Schwern
  • 153,029
  • 25
  • 195
  • 336
1

From the guide:

redirect_to and redirect_back do not halt and return immediately from method execution, but simply set HTTP responses. Statements occurring after them in a method will be executed. You can halt by an explicit return or some other halting mechanism, if needed.

It also says:

If a "before" filter renders or redirects, the action will not run. If there are additional filters scheduled to run after that filter, they are also cancelled.

So I think the problem, given the code you've shown us, it will only halt the execution if it hits the format.html { redirect_to events_path, notice: "Event was successfully updated." }

Are you hitting this controller with an HTML request? The format: turbo_stream and format: json aren't doing any redirect so they will not halt the execution of the action. If you are making a regular HTML request then can you add some of the web console code to your question for further troubleshooting?

Beartech
  • 6,173
  • 1
  • 18
  • 41
  • Thank you for this information. I have used rails for a bit now but there is still tons of stuff about ruby and rails in general I still need to learn. This is great. – spacerobot Jan 12 '23 at 04:44
  • Thanks, always new stuff to learn. I do find the documentation comes across contradictory or confusing at least. – Beartech Jan 12 '23 at 19:57