-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CAPT-1633 Form objects for reminders | CAPT-1634 Encapsulate pre/post form rendering/submission logic per-journey (Part 1) #2742
Conversation
Review app deployed to https://s118d02-app-pr-2742-as.azurewebsites.net/additional-payments/claim |
ce0f1e0
to
72fa075
Compare
end | ||
|
||
def inject_sent_one_time_password_at_into_the_form | ||
params[:form]&.[]=(:sent_one_time_password_at, session[:sent_one_time_password_at]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we store the sent_one_time_password_at
on the Reminder
? Then we can access it in the form object which might make things a bit simpler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand your question, and I remember seeing you explain why that was a good idea for the Claim
model.
My thoughts are that storing sent_one_time_password_at
, a transient attribute, directly on the Reminder
model might introduce unnecessary coupling between the model and the form object. While with the existing approach there is coupling between the controller and the form object, this coupling is more acceptable and expected within the context of MVC architecture. Controllers (and the "state", e.g. in the session) are meant to interact with form objects to handle form submissions and orchestrate the flow of data within the application. Either way I don't feel too strong, and it might make sense to have a consistent approach, but I wouldn't consider it a blocker. Will have a think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure sent_one_time_password_at
is really transient 🤔 but I don't think it's too big a deal to keep it in the session 👍
app/controllers/journeys/additional_payments_for_teaching/reminders_form_callbacks.rb
Show resolved
Hide resolved
72fa075
to
8fa6931
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏 I think this is awesome work, thank you.
I do understand there will initially be a bit of extra cognitive load during debugging, but once you understand the callback structure (which you have documented well) it might actually make it a lot easier since the concerns are more clearly separated.
This is not the same problem as callbacks which are inserted in non-obvious ways since there is a fixed structure which will be commonly used as it unlocks the ability to support multiple journeys.
app/controllers/journeys/additional_payments_for_teaching/reminders_form_callbacks.rb
Show resolved
Hide resolved
This is a further abstraction; as a future step, the `model_name` should probably be more generic (e.g. using `Form`)
This is another step to avoid coupling a generic form object to a specific AR model. As a reminder, we keep `#persisted?` so Rails can make an informed decision to use PATCH instead of POST when submitting a form. By definition, our form objects are and will always be persisted "somewhere" (most likely not the DB in the future)
In this commit: - Organise forms by controller name, as slugs can be repeated on different controllers for the same journey (and map to a different form and view) - Use a generic and more abstract "form" model name for the new forms (it should probably be done for all the forms, but the changes would pollute this commit) - Add new reminders forms and unit tests
See comments in the code for more details
There should be a POST for every slug; default to the first one when not specified. The `new_reminder` path should probably be a more generic `/reminder`.
They are now moved to the form objects.
8fa6931
to
5ba8e23
Compare
This PR is the first iteration of CAPT-1634 applied to an isolated controller that handles a mini-journey for reminders (ECP/LUPP) and converts the reminders slugs to form objects as per CAPT-1633. In a follow-up PR, I will be adding the new concern to the claims controller and iterate with any improvements or changes required.
How to review / my notes
FormSubmittable
concern and associated request spec; I won't repeat the rationale here as it's explained extensively in various comments inside the concern; worth noting that:<path>/:slug
will dynamically define all the pre-post rendering/submission callback methods for that slug only. I have considered defining them as singleton methods so we could tap into them in a more declarative way (i.e. passing a block or a method name and options), but I didn't feel like pursuing it yet (it would probably mean storing all the callbacks and options in a variable for all slugs in a single req-res cycle):FormSubmittable
now as it helps define a specific trait of controllers that handle form-based page sequences*_action
), and are necessary for complex request cycles. I have added logging when filters are triggered; see an example here:PageSequence
logic (which should probably be revised anyway), and only expectsslugs
to be defined along withcurrent_slug
andnext_slug
, so it can be used with any sequenceForm#model_name
should probably be generic and not linked to any particular AR model. I'm using a generic "form" name for the newly converted reminders slugs, but it could be extended to all the other formspersonal-details
is quite different from our "shared" one, as in this case it only focuses onfull_name
andemail_address
. I don't think it's worth extracting validations and sharing, not in this PRemail-confirmation
is similar to the "shared" one, but in the shared one the OTP generation timestamp is an attribute of thecurrent_claim
... I didn't want that to be an attribute ofcurrent_reminder
, but rather a transient attribute for the form object, hence I have injected it into the session and proxied to the form object. A similar approach could be used for the shared onecurrent_data_object
wrapper as theclaim:
argument to initialising the form object; I think it's a good abstractionhttps://dfedigital.atlassian.net/browse/CAPT-1633
https://dfedigital.atlassian.net/browse/CAPT-1634