Skip to content

Instantly share code, notes, and snippets.

@RStankov
Created September 29, 2025 18:08
Show Gist options
  • Save RStankov/4d30e6bbd79a113af6e2369df3003636 to your computer and use it in GitHub Desktop.
Save RStankov/4d30e6bbd79a113af6e2369df3003636 to your computer and use it in GitHub Desktop.
class AsyncActionsController < ApplicationController
def show
async_action = AsyncAction.find_by!(
user: current_user,
token: params[:id],
)
if @async_action.completed?
case @async_action.action.fetch('kind')
in 'redirect'
redirect_to @async_action.action.fetch('path'), notice: @async_action.action['notice']
end
end
end
end
class AsyncAction::CompleteJob < ApplicationJob
def perform(action)
action.update!(completed_at: Time.current) if action.completed_at.nil?
end
end
# frozen_string_literal: true
class AsyncAction::ExpireCronJob < ApplicationJob
def perform
AsyncAction.expired.destroy_all
end
end
module AsyncAction::Redirect
extend self
def call(user:, path:, notice:, job_class:, job_params:)
action = AsyncAction.create!(
user: user,
action: {
'kind' => 'redirect',
'path' => path,
'notice' => notice,
},
)
batch = AngryBatch.new(label: "async_action: #{action.id}")
batch.enqueue job_class, *Array(job_params)
batch.on_complete AsyncAction::CompleteJob, action
batch.perform_later
action
end
end
class CreateAsyncAction < ActiveRecord::Migration[8.0]
def change
create_table :async_actions do |t|
t.references :user, null: true, foreign_key: true, index: true
t.string :token, null: false, index: { unique: true }
t.jsonb :action, null: false, default: {}
t.datetime :completed_at, null: true
t.timestamps
end
end
end
# frozen_string_literal: true
# == Schema Information
#
# Table name: async_actions
#
# id :bigint(8) not null, primary key
# action :jsonb not null
# completed_at :datetime
# token :string not null
# created_at :datetime not null
# updated_at :datetime not null
# user_id :bigint(8)
#
# Indexes
#
# index_async_actions_on_token (token) UNIQUE
# index_async_actions_on_user_id (user_id)
#
# Foreign Keys
#
# fk_rails_... (user_id => users.id)
#
class AsyncAction < ApplicationRecord
belongs_to :user, inverse_of: :async_actions
has_secure_token :token, length: 24
class << self
def expired
completed = where('completed_at > ?', 2.days.ago)
failed = where(completed_at: nil).where('created_at < ?', 4.weeks.ago)
completed.or(failed)
end
end
def completed?
completed_at.present?
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment