Class: Mensa::Tables::ExportsController

Inherits:
ApplicationController
  • Object
show all
Defined in:
app/controllers/mensa/tables/exports_controller.rb

Overview

Lists a user’s available downloads for a table and creates new export requests. Generating the CSV happens asynchronously in Mensa::ExportJob; both the export button badge and the downloads list are refreshed via Turbo streams once the job completes.

Instance Method Summary collapse

Instance Method Details

#createObject

Creates a new export for the current user and enqueues the job that generates and attaches the CSV.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'app/controllers/mensa/tables/exports_controller.rb', line 19

def create
  export = Mensa::Export.new(
    table_name: params[:table_id],
    table_view_id: params[:table_view_id].presence,
    user: current_mensa_user,
    format: params[:export_format].to_s.presence_in(Mensa::Export::FORMATS) || "csv_excel",
    scope: params[:scope].to_s.presence_in(Mensa::Export::SCOPES) || "all",
    repeat: params[:repeat].to_s.presence_in(Mensa::Export::REPEATS) || "",
    config: params.permit(:query, :page, order: {}, filters: {}).to_h,
    status: "pending"
  )

  if export.save
    Mensa::ExportJob.perform_later(export)

    respond_to do |format|
      format.turbo_stream { render turbo_stream: [list_stream, badge_stream] }
      format.json { render json: {id: export.id}, status: :created }
    end
  else
    respond_to do |format|
      format.turbo_stream { head :unprocessable_entity }
      format.json { render json: {errors: export.errors.full_messages}, status: :unprocessable_entity }
    end
  end
end

#destroyObject

Deletes an export from the current user’s download list. The attached asset is purged automatically when the record is destroyed.



48
49
50
51
52
53
54
55
56
57
58
# File 'app/controllers/mensa/tables/exports_controller.rb', line 48

def destroy
  export = exports.find(params[:id])
  export.destroy
  Mensa::Export.broadcast_refresh(export.table_name, export.user)

  respond_to do |format|
    format.turbo_stream { render turbo_stream: [list_stream, badge_stream] }
    format.json { head :no_content }
    format.html { redirect_back fallback_location: mensa.table_exports_path(params[:table_id]) }
  end
end

#downloadObject

Streams the generated CSV and then removes the export, purging the attached asset. Downloads are single-use: routing them through the controller (instead of a direct Active Storage link) gives us a hook to delete the Mensa::Export record and free the stored file afterwards.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'app/controllers/mensa/tables/exports_controller.rb', line 64

def download
  export = exports.find(params[:id])
  return head :not_found unless export.downloadable?

  data = export.asset.download
  filename = export.asset.filename.to_s.presence || export.filename.presence || "#{export.table_name}_export.csv"
  content_type = export.asset.content_type.presence || "text/csv"

  send_data data, filename: filename, type: content_type, disposition: "attachment"

  # One-off exports are single-use and are deleted after download.
  # Repeating exports stay in place and can be removed explicitly via the
  # trash action.
  begin
    if export.repeating?
      export.asset.purge_later
      Mensa::Export.broadcast_refresh(export.table_name, export.user)
    else
      export.destroy
      Mensa::Export.broadcast_refresh(export.table_name, export.user)
    end
  rescue => e
    Mensa.config.logger&.warn("Mensa::Export cleanup failed for #{export.id}: #{e.class}: #{e.message}")
  end
end

#indexObject

Returns the current downloads list for the table, used to refresh the contents of the export dialog when it is opened.



10
11
12
13
14
15
# File 'app/controllers/mensa/tables/exports_controller.rb', line 10

def index
  respond_to do |format|
    format.turbo_stream { render turbo_stream: list_stream }
    format.html { render partial: "mensa/exports/list", locals: list_locals }
  end
end