简体   繁体   中英

Rails before_action that takes long time

I'm using something like this in my Controller class:

before_action :do_something, only: [:new, :create]

def new
   @myModel = MyModel.new
end

def create
   @myModel = MyModel.create_with_data(params)

   respond_to do |format|
     if @myModel.errors.empty?
        format.html { redirect_to @myModel, notice: 'MyModel was successfully created.' }
        format.json { render :show, status: :created, location: @myModel }
     else
        logger.error 'MyModelController: Creating mymodel failed!'
        format.html { render :new }
        format.json { render json: @myModel.errors, status: :unprocessable_entity }
     end
end

def do_something
   # Very long method
end

Now, the problem is, that my do_something method takes very long to execute. If user will make mistake while creating new myModel field, he will need to wait for this method to execute, just to display him some error.

Let's say, that I don't want front-end validation and I can't reduce the time of do_something method. How can I pass somehow data from do_something that executed before new method is called, so it won't be needed to execute again before create method?

I would consider using an optimistic saving approach instead. Much like how unconfirmed users are usually treated.

You insert the record in the database but set a state which denotes that it's not a full fledged record. You then update the record when it is confirmed.

It gives the user quick feedback and you can free your web process from waiting for do_something .

class MyModel < ActiveRecord::Base
  # create an Int column named status
  # make sure it has an index and defaults to 0.
  enum status: [:unverified, :verified]
end

So, you would do something like this in your controller:

def create
  # Don't do params assignment / whitelisting in your models.
  # models should not be aware of parameters.
  @my_model = MyModel.new(my_model_params) do |m|
    # This should be handled by the DB default but 
    # this is a belt and suspenders approach to be 200% sure
    # that a record does not slip through.
    m.unverified!
  end

  if @my_model.save
    MyModelVerificationJob.perform_later(@my_model)
    respond_to do |format|
      format.html do
        flash[:notice] = "We are currently verifying..."
        redirect_to :edit, @my_model
      end
      # ...
    end
  else
    # we already know something is fishy so just render the 
    # form again.
    respond_to do |format|
      # ...
    end
  end
end 

private
  def my_model_params
    params.require(:my_model).permit(...)
  end

This example is with Rails 5 Active Jobs but you can look into Resque or Sidekiq as well.

How exactly to handle the callback from your do_something method depends on what kind of user interaction is required. If the user needs to correct a form for example you would cache the results from the background job and prompt the user to visit a page where they correct the form.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM