Rails app blueprint
Solid Queue
Add Avo configuration files to manage your Solid Queue jobs
Avo is so f***ing awesome. I am a java developer with 25 years of experience. I looked for a rapid application framework to build an internal ERP system for my small company. Its sooooo good. As a java developer without much ruby and rails know-how (only 1 or 2 tutorials), I could build up most of what I needed in ONE weekend. I wish I had that for java!!!.
Masiar Ighani
CEO Skillbyte
Run the following command to quickly apply this app blueprint
If you think this blueprint needs changes please submit a PR here.
Full blueprint
# Blueprint info:
# Link: https://avohq.io/blueprints/solid-queue
# Repository: https://github.com/avo-hq/solid_queue-app-template
# List of all Avo files with path and contents
files = {"app/avo/actions/retry.rb" => "class Avo::Actions::Retry < Avo::BaseAction\n self.name = \"Retry\"\n\n def handle(query:, fields:, current_user:, resource:, **args)\n query.each do |record|\n record.retry\n end\n end\nend\n", "app/avo/cards/failed_jobs.rb" => "class Avo::Cards::FailedJobs < Avo::Cards::MetricCard\n self.id = \"failed_count\"\n self.label = \"Failed count\"\n\n def query\n result SolidQueue::FailedExecution.count\n end\nend\n", "app/avo/cards/jobs_count.rb" => "class Avo::Cards::JobsCount < Avo::Cards::MetricCard\n self.id = \"jobs_count\"\n self.label = \"Jobs count\"\n\n def query\n type = arguments.fetch :type\n\n query = SolidQueue::Job.all\n\n if type == :scheduled\n query = query.finished.invert_where\n end\n\n result query.length\n end\nend\n", "app/avo/cards/processes.rb" => "class Avo::Cards::Processes < Avo::Cards::PartialCard\n self.id = \"processes\"\n self.label = \"Processes\"\n self.partial = \"avo/cards/processes\"\n\n def processes\n SolidQueue::Process.all\n end\nend\n\n", "app/avo/cards/queues.rb" => "class Avo::Cards::Queues < Avo::Cards::PartialCard\n self.id = \"queues\"\n self.label = \"Queues\"\n self.partial = \"avo/cards/queues\"\n\n def queues\n SolidQueue::Queue.all\n end\nend\n", "app/avo/dashboards/solid_queue.rb" => "class Avo::Dashboards::SolidQueue < Avo::Dashboards::BaseDashboard\n self.id = \"solid_queue\"\n self.name = \"Solid queue\"\n\n def cards\n card Avo::Cards::JobsCount,\n label: \"Scheduled jobs\",\n arguments: {\n type: :scheduled\n }\n card Avo::Cards::JobsCount,\n label: \"Total jobs\",\n arguments: {\n type: :total_jobs\n }\n card Avo::Cards::FailedJobs\n\n divider label: \"Queues information\"\n\n card Avo::Cards::Queues, cols: 3\n\n divider label: \"Processes information\"\n\n card Avo::Cards::Processes, cols: 3\n end\nend\n", "app/avo/resources/solid_queue_failed_execution.rb" => "class Avo::Resources::SolidQueueFailedExecution < Avo::BaseResource\n self.includes = []\n self.model_class = \"SolidQueue::FailedExecution\"\n self.authorization_policy = SolidQueue::FailedExecutionPolicy\n TITLE_BLOCK = -> { \"\#{record.job.class_name} - \#{record&.error.fetch(\"exception_class\")} '\#{record&.error&.fetch(\"message\")}'\" }\n self.title = TITLE_BLOCK\n\n self.row_controls = -> {\n action Avo::Actions::Retry\n }\n\n def fields\n field :id, as: :id\n field :title, as: :text, &TITLE_BLOCK\n field :error, as: :code, language: \"javascript\" do\n if record.error.present?\n JSON.pretty_generate(record.error.as_json)\n end\n end\n field :job, as: :belongs_to\n end\n\n def actions\n action Avo::Actions::Retry\n end\nend\n", "app/avo/resources/solid_queue_job.rb" => "class Avo::Resources::SolidQueueJob < Avo::BaseResource\n self.includes = []\n self.title = :class_name\n self.model_class = \"SolidQueue::Job\"\n self.authorization_policy = SolidQueue::JobPolicy\n\n def fields\n main_panel do\n field :id, as: :id\n field :queue_name, as: :text\n field :class_name, as: :text\n field :arguments_name, as: :code\n field :priority, as: :number\n field :scheduled_at, as: :date_time\n field :finished_at, as: :date_time\n field :queue_status, as: :badge, options: {success: :running, warning: :paused} do\n job_queue = SolidQueue::Queue.all.find do |queue|\n record.queue_name == queue.name\n end\n\n job_queue.paused? ? :paused : :running\n end\n sidebar do\n field :active_job_id, as: :text\n field :concurrency_key, as: :date_time\n end\n end\n\n # field :claimed_execution, as: :has_one\n # field :ready_execution, as: :has_one\n field :failed_execution, as: :has_one\n # has_one :ready_execution, dependent: :destroy\n # has_one :claimed_execution, dependent: :destroy\n # has_one :failed_execution, dependent: :destroy\n\n # has_one :scheduled_execution, dependent: :destroy\n end\n\n def scopes\n scope Avo::Scopes::Finished\n scope Avo::Scopes::Scheduled\n scope Avo::Scopes::Failed\n end\n\n def cards\n if view.index?\n card Avo::Cards::JobsCount,\n label: \"Scheduled jobs\",\n arguments: {\n type: :scheduled\n }\n card Avo::Cards::JobsCount,\n label: \"Total jobs\",\n arguments: {\n type: :total_jobs\n }\n end\n end\nend\n", "app/avo/resources/solid_queue_process.rb" => "class Avo::Resources::SolidQueueProcess < Avo::BaseResource\n self.includes = []\n self.model_class = \"SolidQueue::Process\"\n\n def fields\n field :id, as: :id\n field :kind, as: :text\n field :last_heartbeat_at, as: :date_time\n field :supervisor_id, as: :number\n field :pid, as: :number\n field :hostname, as: :number\n field :metadata, as: :code\n end\nend\n", "app/avo/scopes/failed.rb" => "class Avo::Scopes::Failed < Avo::Advanced::Scopes::BaseScope\n self.name = \"Failed\"\n self.description = \"Failed\"\n self.scope = -> { query.includes(:failed_execution).where.not(failed_execution: {id: nil}) }\n self.visible = -> { true }\nend\n", "app/avo/scopes/finished.rb" => "class Avo::Scopes::Finished < Avo::Advanced::Scopes::BaseScope\n self.name = \"Finished\"\n self.description = \"Finished\"\n self.scope = -> { query.finished }\n self.visible = -> { true }\nend\n", "app/avo/scopes/paused.rb" => "class Avo::Scopes::Paused < Avo::Advanced::Scopes::BaseScope\n self.name = \"Paused\"\n self.description = \"Paused\"\n self.scope = -> { query.all }\n self.visible = -> { true }\nend\n", "app/avo/scopes/scheduled.rb" => "class Avo::Scopes::Scheduled < Avo::Advanced::Scopes::BaseScope\n self.name = \"Scheduled\"\n self.description = \"Scheduled\"\n self.scope = -> { query.finished.invert_where }\n self.visible = -> { true }\nend\n", "app/controllers/avo/queue_clears_controller.rb" => "class Avo::QueueClearsController < ApplicationController\n before_action :set_queue_by_name\n\n def update\n @queue.clear\n\n redirect_to params[:frame_url]\n end\n\n private\n\n def set_queue_by_name\n @queue = SolidQueue::Queue.find_by_name params[:name]\n end\nend\n", "app/controllers/avo/queue_visibilities_controller.rb" => "class Avo::QueueVisibilitiesController < ApplicationController\n before_action :set_queue_by_name\n\n def update\n action = params[:queue_action]\n\n if action == \"pause\"\n @queue.pause\n elsif action == \"resume\"\n @queue.resume\n end\n\n redirect_to params[:frame_url]\n end\n\n private\n\n def set_queue_by_name\n @queue = SolidQueue::Queue.find_by_name params[:name]\n end\nend\n", "app/controllers/avo/solid_queue_failed_executions_controller.rb" => "# This controller has been generated to enable Rails' resource routes.\n# More information on https://docs.avohq.io/3.0/controllers.html\nclass Avo::SolidQueueFailedExecutionsController < Avo::ResourcesController\nend\n", "app/controllers/avo/solid_queue_jobs_controller.rb" => "# This controller has been generated to enable Rails' resource routes.\n# More information on https://docs.avohq.io/3.0/controllers.html\nclass Avo::SolidQueueJobsController < Avo::ResourcesController\nend\n", "app/controllers/avo/solid_queue_processes_controller.rb" => "# This controller has been generated to enable Rails' resource routes.\n# More information on https://docs.avohq.io/3.0/controllers.html\nclass Avo::SolidQueueProcessesController < Avo::ResourcesController\nend\n", "app/policies/solid_queue/failed_execution_policy.rb" => "class SolidQueue::FailedExecutionPolicy < ApplicationPolicy\n def index?\n true\n end\n\n def show?\n true\n end\n\n def act_on?\n true\n end\n\n def create?\n false\n end\n\n def update?\n false\n end\n\n def destroy?\n false\n end\n\n class Scope < Scope\n # NOTE: Be explicit about which records you allow access to!\n def resolve\n scope.all\n end\n end\nend\n", "app/policies/solid_queue/job_policy.rb" => "class SolidQueue::JobPolicy < ApplicationPolicy\n def index?\n true\n end\n\n def show?\n true\n end\n\n def create?\n false\n end\n\n def destroy?\n true\n end\n\n def create_failed_execution?\n false\n end\n\n def attach_failed_execution?\n false\n end\n\n def destroy_failed_execution?\n false\n end\n\n class Scope < Scope\n # NOTE: Be explicit about which records you allow access to!\n def resolve\n scope.all\n end\n end\nend\n", "app/policies/solid_queue/process_policy.rb" => "class SolidQueue::ProcessPolicy < ApplicationPolicy\n def index?\n true\n end\n\n def show?\n true\n end\n\n def create?\n false\n end\n\n def update?\n false\n end\n\n def destroy?\n false\n end\n\n class Scope < Scope\n def resolve\n scope.all\n end\n end\nend\n", "app/views/avo/cards/_processes.html.erb" => "<div class=\"flex-1 flex-initial mt-4 overflow-auto\">\n <table class=\"w-full\">\n <tbody class=\"divide-y\">\n <% @card.processes.each do |process| %>\n <tr class=\"hover:bg-blue-50\">\n <td class=\"px-1 py-1\">\n <div class=\"ml-3\">\n <%= process.kind %>\n </div>\n </td>\n <td class=\"px-1 py-1 text-sm text-right\">\n <div class=\"mr-3 flex justify-end gap-2\">\n <%= process.metadata %>\n </div>\n </td>\n <td class=\"text-sm text-right\">\n <div class=\"mr-3\">\n <%= process.last_heartbeat_at %>\n </div>\n </td>\n </tr>\n <% end %>\n </tbody>\n </table>\n</div>\n", "app/views/avo/cards/_queues.html.erb" => "<div class=\"flex-1 flex-initial mt-4 overflow-auto\">\n <table class=\"w-full\">\n <tbody class=\"divide-y\">\n <% @card.queues.each do |queue| %>\n <%\n status = queue.paused? ? :paused : :running\n label = status == :paused ? :resume : :pause\n %>\n <tr class=\"hover:bg-blue-50\">\n <td class=\"px-1 py-1\">\n <div class=\"ml-3\">\n <%= queue.name %> -\n <%= queue.size %> <%= \"job\".pluralize queue.size %>\n </div>\n </td>\n <td class=\"px-1 py-1 text-sm text-right\">\n <div class=\"mr-3 flex justify-end gap-2\">\n <%= render Avo::Fields::Common::BadgeViewerComponent.new value: status, options: {success: :running, warning: :paused} %>\n </div>\n </td>\n <td class=\"text-sm text-right\">\n <div class=\"mr-3\">\n <%= a_link avo.queue_visibility_path(name: queue.name, queue_action: label, frame_url: @card.frame_url), data: {turbo_method: :put}, size: :xs do %>\n <%= label.to_s.humanize %>\n <% end %>\n <%= a_link avo.queue_clear_path(name: queue.name, queue_action: label, frame_url: @card.frame_url), data: {turbo_method: :put, turbo_confirm: \"Are you sure you want to delete all jobs from this queue?\"}, size: :xs, color: :red do %>\n Clear queue\n <% end %>\n </div>\n </td>\n </tr>\n <% end %>\n </tbody>\n </table>\n</div>\n"}
# === Add routes ===
route_contents = <<-ROUTES
# Avo admin panel
if defined? ::Avo
Avo::Engine.routes.draw do
resource :queue_visibility, only: :update
resource :queue_clear, only: :update
end
end
ROUTES
route route_contents
# === Copy template files ===
files.each do |path, contents|
file path, contents
end