Skip to content

Commit b6eb52d

Browse files
committed
Refactor public controllers to use shared submission processing concern
1 parent 1fbdabd commit b6eb52d

File tree

7 files changed

+382
-101
lines changed

7 files changed

+382
-101
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
module SubmissionProcessing
2+
extend ActiveSupport::Concern
3+
4+
# Common method to process a submission for either form or code
5+
def process_submission
6+
# Initialize a new submission, handling the case where no fields are enabled
7+
@submission = @form.submissions.new
8+
@submission.device = @device
9+
10+
# Only attempt to update with submission params if they exist in the request
11+
# This handles forms that have no enabled fields
12+
@submission.assign_attributes(submission_params) if params[:submission].present?
13+
14+
# Associate the submission with the current user if logged in
15+
@submission.user = current_user if current_user
16+
17+
# Check if this is a code submission and if the code has already been claimed
18+
if defined?(@code) && @code && @code.claimed?
19+
@submission.errors.add(:base, "This code has already been used")
20+
return render "public_forms/show", status: :unprocessable_entity
21+
end
22+
23+
if @submission.save
24+
# Mark the code as claimed if this is a code submission
25+
@code.claim! if defined?(@code) && @code
26+
27+
# Send the submission email only if target_email_address is present
28+
send_submission_email
29+
30+
# Redirect to thanks page instead of rendering
31+
# This prevents form resubmission on refresh
32+
if defined?(@code) && @code
33+
redirect_to code_thanks_path(@code)
34+
else
35+
redirect_to form_thanks_path(@form.code, @device)
36+
end
37+
else
38+
render "public_forms/show", status: :unprocessable_entity
39+
end
40+
end
41+
42+
def send_submission_email
43+
if @form.target_email_address.present?
44+
begin
45+
SubmissionMailer.new_submission(@submission).deliver_later
46+
# Email will be sent asynchronously, so we mark it as pending
47+
# It will be updated when the job completes
48+
rescue => e
49+
# Log the error but continue with the form submission
50+
Rails.logger.error "Failed to queue submission email: #{e.message}"
51+
@submission.mark_as_failed! if @submission.respond_to?(:mark_as_failed!)
52+
end
53+
end
54+
end
55+
56+
def check_form_access
57+
# Redirect to login if the form requires login and user is not logged in
58+
if @form.require_login && !logged_in?
59+
store_location # Store the current URL to redirect back after login
60+
redirect_to login_path, alert: "You need to log in to access this form"
61+
end
62+
end
63+
64+
def submission_params
65+
if params[:submission].present?
66+
params.require(:submission).permit(
67+
:name,
68+
:email_address,
69+
:phone,
70+
:address,
71+
:postcode
72+
)
73+
else
74+
# Return an empty hash if there are no submission parameters
75+
{}
76+
end
77+
end
78+
end
Lines changed: 8 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,29 @@
11
class PublicCodesController < ApplicationController
2+
include SubmissionProcessing
3+
24
skip_before_action :require_login
35
before_action :set_code
6+
before_action :set_form_and_device
7+
before_action :check_form_access, only: [:show, :create]
48
layout "public_forms"
59

610
def show
7-
if @code.form.require_login? && !logged_in?
8-
store_location
9-
redirect_to login_path
10-
return
11-
end
12-
1311
# If code has already been claimed, show the thank you page
1412
if @code.claimed?
15-
# Still set the form and device for the thank you page
16-
@form = @code.form
17-
@device = @code.device
1813
render "public_forms/thanks"
1914
return
2015
end
2116

22-
@form = @code.form
23-
@device = @code.device
2417
@submission = Submission.new
2518
render "public_forms/show"
2619
end
2720

2821
def thanks
29-
@form = @code.form
30-
@device = @code.device
3122
render "public_forms/thanks"
3223
end
3324

3425
def create
35-
@form = @code.form
36-
@device = @code.device
37-
@submission = @form.submissions.new(submission_params)
38-
@submission.device = @device
39-
40-
# Check if the code has already been claimed
41-
if @code.claimed?
42-
@submission.errors.add(:base, "This code has already been used")
43-
return render "public_forms/show", status: :unprocessable_entity
44-
end
45-
46-
# Associate the submission with the current user if logged in
47-
@submission.user = current_user if current_user
48-
49-
if @submission.save
50-
# Mark the code as claimed
51-
@code.claim!
52-
53-
# Send the submission email only if target_email_address is present
54-
if @form.target_email_address.present?
55-
begin
56-
SubmissionMailer.new_submission(@submission).deliver_later
57-
# Email will be sent asynchronously, so we mark it as pending
58-
# It will be updated when the job completes
59-
rescue => e
60-
# Log the error but continue with the form submission
61-
Rails.logger.error "Failed to queue submission email: #{e.message}"
62-
@submission.mark_as_failed! if @submission.respond_to?(:mark_as_failed!)
63-
end
64-
end
65-
66-
# Redirect to thanks page instead of rendering
67-
redirect_to code_thanks_path(@code)
68-
else
69-
render "public_forms/show", status: :unprocessable_entity
70-
end
26+
process_submission
7127
end
7228

7329
private
@@ -79,13 +35,8 @@ def set_code
7935
redirect_to root_path
8036
end
8137

82-
def submission_params
83-
params.require(:submission).permit(
84-
:name,
85-
:email_address,
86-
:phone,
87-
:address,
88-
:postcode
89-
)
38+
def set_form_and_device
39+
@form = @code.form
40+
@device = @code.device
9041
end
9142
end

app/controllers/public_forms_controller.rb

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
class PublicFormsController < ApplicationController
22
require "rqrcode"
3+
include SubmissionProcessing
34

45
# Skip login for public form views, but check if the form requires login
56
skip_before_action :require_login
@@ -13,32 +14,7 @@ def show
1314
end
1415

1516
def create
16-
@submission = @form.submissions.new(submission_params)
17-
@submission.device = @device
18-
19-
# Associate the submission with the current user if logged in
20-
@submission.user = current_user if current_user
21-
22-
if @submission.save
23-
# Send the submission email only if target_email_address is present
24-
if @form.target_email_address.present?
25-
begin
26-
SubmissionMailer.new_submission(@submission).deliver_later
27-
# Email will be sent asynchronously, so we mark it as pending
28-
# It will be updated when the job completes
29-
rescue => e
30-
# Log the error but continue with the form submission
31-
Rails.logger.error "Failed to queue submission email: #{e.message}"
32-
@submission.mark_as_failed!
33-
end
34-
end
35-
36-
# Redirect to thanks page instead of rendering
37-
# This prevents form resubmission on refresh
38-
redirect_to form_thanks_path(@form.code, @device)
39-
else
40-
render :show, status: :unprocessable_entity
41-
end
17+
process_submission
4218
end
4319

4420
def thanks
@@ -91,22 +67,4 @@ def set_form_and_device_for_qr
9167
blank_qr = ChunkyPNG::Image.new(200, 200, ChunkyPNG::Color::WHITE)
9268
send_data blank_qr.to_s, type: "image/png", disposition: "inline"
9369
end
94-
95-
def check_form_access
96-
# Redirect to login if the form requires login and user is not logged in
97-
if @form.require_login && !logged_in?
98-
store_location # Store the current URL to redirect back after login
99-
redirect_to login_path, alert: "You need to log in to access this form"
100-
end
101-
end
102-
103-
def submission_params
104-
params.require(:submission).permit(
105-
:name,
106-
:email_address,
107-
:phone,
108-
:address,
109-
:postcode
110-
)
111-
end
11270
end
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
require "rails_helper"
2+
3+
RSpec.describe "EnsureFormCodes migration", type: :migration do
4+
let(:migration_path) { File.join(Rails.root, "db/migrate/20250523081500_ensure_form_codes.rb") }
5+
let(:migration_class) { "EnsureFormCodes" }
6+
7+
# Helper to create a form directly bypassing validations
8+
def create_form_without_code(attrs = {})
9+
attrs = {name: "Form without code", button_text: "Submit"}.merge(attrs)
10+
11+
# Override the generate_code callback temporarily
12+
Form.skip_callback(:validation, :before, :generate_code, raise: false)
13+
14+
form = Form.create!(attrs)
15+
16+
# Reset the code to nil
17+
form.update_column(:code, nil)
18+
19+
Form.set_callback(:validation, :before, :generate_code)
20+
21+
form.reload
22+
end
23+
24+
it "adds codes to all forms that don't have one" do
25+
# Create a form with a nil code
26+
form1 = create_form_without_code(name: "Form with nil code")
27+
expect(form1.code).to be_nil
28+
29+
# Create a form with an empty code
30+
form2 = create_form_without_code(name: "Form with empty code")
31+
form2.update_column(:code, "")
32+
form2.reload
33+
expect(form2.code).to eq("")
34+
35+
# Create a form with a valid code
36+
form3 = Form.create!(name: "Form with code", button_text: "Submit")
37+
original_code = form3.code
38+
expect(original_code).not_to be_nil
39+
40+
# Load and run the migration
41+
require migration_path
42+
migration = migration_class.constantize.new
43+
migration.up
44+
45+
# Check that forms have codes now
46+
form1.reload
47+
expect(form1.code).not_to be_nil
48+
expect(form1.code.length).to eq(12)
49+
50+
form2.reload
51+
expect(form2.code).not_to be_nil
52+
expect(form2.code.length).to eq(12)
53+
54+
# Check that existing codes are preserved
55+
form3.reload
56+
expect(form3.code).to eq(original_code)
57+
end
58+
end

0 commit comments

Comments
 (0)