Skip to content

Commit a22645b

Browse files
authored
Move the register flow's remaining logic into BikeServices::Register (#4033)
`RegisterController` was still carrying registration logic that belongs in `BikeServices::Register`. Pure refactor, no behavior change. - **The service gains the flow's decisions** — `save_step_1`, `acknowledge_step` (which record a step's boxes write to), `complete`, `confirmation_token_valid?`, and the two steps' permitted-params lists. `claim_creator`, `create_bike_if_ready` and `create_bike` are concealed behind `complete`. - **`save_step_1` and `save_step_2` add their own errors to `b_param`**, so `create` and `update` now read the same way: save, or re-render 422 with whatever the service put there. Their copy moves from `controllers.register.{create,update}` to `shared.register_flow`, the way `OrganizedServices::UserMenuItems` holds its labels. - **`update` drops its `acknowledged?` guard and `create` its duplicated 422 render** — `complete` already returns nil when the registration isn't ready, and both branches redirected to the same place. The controller keeps what's actually HTTP: the session token, the redirects, the flashes, the b_param lookups, and signing in a confirmed address.
1 parent 253a5ce commit a22645b

4 files changed

Lines changed: 85 additions & 57 deletions

File tree

app/controllers/register_controller.rb

Lines changed: 22 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -54,45 +54,41 @@ def show
5454
end
5555

5656
def create
57-
@b_param.clean_params(create_params.as_json)
58-
# The 422 renders skip the derived meta title, which now needs the interpolation
59-
@page_title = I18n.t("meta_titles.register_create", cycle_type: @b_param.type)
60-
@b_param.errors.add(:base, translation(:email_required)) if @b_param.owner_email.blank?
61-
@b_param.errors.add(:base, translation(:manufacturer_required)) if @b_param.manufacturer_id.blank?
62-
if @b_param.errors.any?
63-
render Register::Step1::Component.new(b_param: @b_param, sequence: @registration_sequence, current_user:), status: :unprocessable_entity
64-
elsif @b_param.save
65-
# Step 2 says the link is on its way, so it goes out here rather than at the end
66-
BikeServices::Register.send_confirmation_email(@b_param)
67-
redirect_to step_path(2)
68-
else
69-
@b_param.errors.add(:base, translation(:unable_to_save))
70-
render Register::Step1::Component.new(b_param: @b_param, sequence: @registration_sequence, current_user:), status: :unprocessable_entity
57+
saved = BikeServices::Register.save_step_1(@b_param, bike_params: create_params,
58+
propulsion_type_motorized: params[:propulsion_type_motorized])
59+
unless saved
60+
# The 422 render skips the derived meta title, which now needs the interpolation
61+
@page_title = I18n.t("meta_titles.register_create", cycle_type: @b_param.type)
62+
return render(Register::Step1::Component.new(b_param: @b_param, sequence: @registration_sequence, current_user:),
63+
status: :unprocessable_entity)
7164
end
65+
66+
# Step 2 says the link is on its way, so it goes out here rather than at the end
67+
BikeServices::Register.send_confirmation_email(@b_param)
68+
redirect_to step_path(2)
7269
end
7370

7471
def update
7572
# Both read straight from params - update_params is stored as json, which an upload can't be
76-
saved_step_2 = BikeServices::Register.save_step_2(@b_param, user: current_user,
73+
saved = BikeServices::Register.save_step_2(@b_param, user: current_user,
7774
image: params.dig(:bike, :image), image_signed_id: params.dig(:bike, :image_signed_id),
7875
bike_params: update_params)
7976
# Saved either way, so the re-render has everything they entered
80-
unless saved_step_2
81-
@b_param.errors.add(:base, translation(:name_required))
77+
unless saved
8278
return render(Register::Step2::Component.new(b_param: @b_param, sequence: @registration_sequence, current_user:),
8379
status: :unprocessable_entity)
8480
end
8581

86-
# An e-vehicle's safety pages come between the details and the bike
87-
return redirect_to_current_step unless BikeServices::Register.acknowledged?(@b_param, sequence: @registration_sequence)
88-
8982
complete_registration
9083
end
9184

9285
# Each acknowledgment page posts here, and the review's final acknowledgment
9386
def acknowledge
9487
step = BikeServices::Register.permitted_step(@b_param, params[:step], sequence: @registration_sequence)
95-
unless save_acknowledgment(step)
88+
acknowledged = BikeServices::Register.acknowledge_step(@b_param, step,
89+
sequence: @registration_sequence, user: current_user,
90+
acknowledged_all: params[:acknowledged_all], checked: params[:acknowledged]&.to_unsafe_h&.values)
91+
unless acknowledged
9692
flash[:error] = translation(:acknowledge_everything)
9793
return redirect_to step_path(step)
9894
end
@@ -114,8 +110,7 @@ def confirm_email
114110
# Single use, so a second click has nothing left to do - the first one signed them in
115111
return redirect_to_current_step if @b_param.email_confirmed?
116112

117-
if @b_param.email_confirmation_token_expired? ||
118-
!Binxtils::Secure.compare?(params[:confirmation_token], @b_param.email_confirmation_token)
113+
unless BikeServices::Register.confirmation_token_valid?(@b_param, params[:confirmation_token])
119114
BikeServices::Register.send_confirmation_email(@b_param)
120115
flash[:error] = translation(:confirmation_link_expired)
121116
return redirect_to_current_step
@@ -135,22 +130,10 @@ def confirm_email
135130

136131
private
137132

138-
def save_acknowledgment(step)
139-
if step == "review"
140-
return BikeServices::Register.save_acknowledgment(@b_param, @registration_sequence,
141-
acknowledged_all: params[:acknowledged_all], user: current_user)
142-
end
143-
144-
BikeServices::Register.acknowledge_page(@b_param,
145-
BikeServices::Register.page_for_step(step, sequence: @registration_sequence),
146-
checked: params[:acknowledged]&.to_unsafe_h&.values)
147-
end
148-
149133
def complete_registration
150-
BikeServices::Register.claim_creator(@b_param, current_user)
151-
bike = BikeServices::Register.create_bike_if_ready(@b_param,
134+
bike = BikeServices::Register.complete(@b_param, user: current_user,
152135
sequence: @registration_sequence, ip_address: forwarded_ip_address)
153-
# No bike yet - everything stays on the b_param until the emailed link is clicked
136+
# No bike yet - everything stays on the b_param until the flow's remaining steps are done
154137
return redirect_to_current_step if bike.blank?
155138

156139
redirect_after_bike_creation(bike)
@@ -240,16 +223,11 @@ def redirect_after_bike_creation(bike)
240223
end
241224

242225
def create_params
243-
bike_params = params.require(:b_param).permit(:manufacturer_id, :cycle_type, :owner_email)
226+
params.require(:b_param).permit(*BikeServices::Register.permitted_step_1_params)
244227
.to_h.merge(BParam.status_hash_from_params(params))
245-
{bike: bike_params, propulsion_type_motorized: params[:propulsion_type_motorized]}
246228
end
247229

248230
def update_params
249-
params.fetch(:bike, {}).permit(:primary_frame_color_id, :secondary_frame_color_id,
250-
:tertiary_frame_color_id, :serial_number, :frame_size, :frame_size_number, :frame_size_unit,
251-
:bike_sticker, :phone, :status, :frame_model, :year, :user_name,
252-
:extra_registration_number, :organization_affiliation, :student_id,
253-
address_record_attributes: AddressRecord.permitted_params)
231+
params.fetch(:bike, {}).permit(*BikeServices::Register.permitted_step_2_params)
254232
end
255233
end

app/services/bike_services/register.rb

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ def acknowledge_page(b_param, page, checked:)
117117
b_param.save
118118
end
119119

120+
# What a step's submission agrees to: an acknowledgment page's own boxes, or the
121+
# single agreement the review ends with
122+
def acknowledge_step(b_param, step, sequence:, user:, acknowledged_all:, checked:)
123+
return save_acknowledgment(b_param, sequence, acknowledged_all:, user:) if step == "review"
124+
125+
acknowledge_page(b_param, page_for_step(step, sequence:), checked:)
126+
end
127+
120128
# The moment the pages become an agreement - promoted off the b_param onto a
121129
# record of its own, which outlives the registration
122130
def save_acknowledgment(b_param, sequence, acknowledged_all:, user: nil)
@@ -152,13 +160,44 @@ def send_confirmation_email(b_param)
152160
true
153161
end
154162

163+
# Time limited, so an old link proves nothing - the address gets a fresh one
164+
def confirmation_token_valid?(b_param, token)
165+
return false if b_param.email_confirmation_token_expired?
166+
167+
Binxtils::Secure.compare?(token, b_param.email_confirmation_token)
168+
end
169+
155170
# Whether a bike can be created now - Ownership requires a creator, so
156171
# anonymous registrations wait for the confirmation email to prove one
157172
def creator_available?(b_param)
158173
b_param.creator_id.present? || b_param.creation_organization&.auto_user_id.present? ||
159174
confirmed_email_creator_id(b_param).present?
160175
end
161176

177+
def permitted_step_1_params = %i[manufacturer_id cycle_type owner_email]
178+
179+
def permitted_step_2_params
180+
[:primary_frame_color_id, :secondary_frame_color_id, :tertiary_frame_color_id,
181+
:serial_number, :frame_size, :frame_size_number, :frame_size_unit, :bike_sticker,
182+
:phone, :status, :frame_model, :year, :user_name, :extra_registration_number,
183+
:organization_affiliation, :student_id,
184+
{address_record_attributes: AddressRecord.permitted_params}]
185+
end
186+
187+
# Step 1 is the least a registration can be: who owns it and what it is. The params are
188+
# merged in whether or not it passes, so a re-render still shows everything they entered
189+
def save_step_1(b_param, bike_params:, propulsion_type_motorized:)
190+
b_param.clean_params({bike: bike_params, propulsion_type_motorized:}.as_json)
191+
# Before save, which clears the errors it's about to re-run validations for
192+
b_param.errors.add(:base, translation(:email_required)) if b_param.owner_email.blank?
193+
b_param.errors.add(:base, translation(:manufacturer_required)) if b_param.manufacturer_id.blank?
194+
return false if b_param.errors.any?
195+
return true if b_param.save
196+
197+
b_param.errors.add(:base, translation(:unable_to_save))
198+
false
199+
end
200+
162201
# Step 2 merges over step 1 - creator claimed for signed-in users, the photo and the
163202
# fields into the params json. The photo arrives one of two ways: as bytes from a plain
164203
# file field, or as the signed id of a blob the browser already uploaded.
@@ -171,9 +210,21 @@ def save_step_2(b_param, user:, image:, image_signed_id:, bike_params:)
171210
completed = b_param.self_made?(user) || bike_params["user_name"].present?
172211
b_param.clean_params(step_2_params(bike_params, image_signed_id:, completed:).as_json)
173212
b_param.save
213+
b_param.errors.add(:base, translation(:name_required)) unless completed
174214
completed
175215
end
176216

217+
# Everything a submission does once its step is saved. Returns the bike, or nil while
218+
# the registration is still short of one - more to enter, or the email unconfirmed
219+
def complete(b_param, user:, sequence:, ip_address:)
220+
claim_creator(b_param, user)
221+
create_bike_if_ready(b_param, sequence:, ip_address:)
222+
end
223+
224+
#
225+
# private below here
226+
#
227+
177228
# Ownership requires a creator, and signing in can happen anywhere in the flow -
178229
# e.g. partway through the acknowledgment pages
179230
def claim_creator(b_param, user)
@@ -197,10 +248,6 @@ def create_bike(b_param, ip_address:)
197248
bike
198249
end
199250

200-
#
201-
# private below here
202-
#
203-
204251
def ready_for_bike?(b_param, sequence:)
205252
details_completed?(b_param) && acknowledged?(b_param, sequence:)
206253
end
@@ -273,8 +320,11 @@ def step_2_params(bike_params, image_signed_id:, completed:)
273320
{details_completed: completed, bike: bike_params, image_signed_id: image_signed_id.presence}.compact
274321
end
275322

276-
conceal :ready_for_bike?, :reusable?, :all_steps, :permitted_steps,
277-
:confirmed_email_creator_id, :owner_email_for, :assign_owner_email,
278-
:details_completed?, :step_2_params
323+
def translation(key) = I18n.t(key, scope: "shared.register_flow")
324+
325+
conceal :claim_creator, :create_bike_if_ready, :create_bike, :ready_for_bike?,
326+
:reusable?, :all_steps, :permitted_steps, :confirmed_email_creator_id,
327+
:owner_email_for, :assign_owner_email, :details_completed?, :step_2_params,
328+
:translation
279329
end
280330
end

config/i18n-tasks.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ ignore_unused:
172172
- "controllers.*" # our translation helper component blocks detection
173173
- "components.*" # our translation helper component blocks detection
174174
- "shared.organized_menu_items.*" # used dynamically by OrganizedServices::UserMenuItems
175+
- "shared.register_flow.*" # used dynamically by BikeServices::Register
175176
- "*_mailer.*"
176177
- "date.*"
177178
- "datetime.*"

config/locales/en.yml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1926,18 +1926,12 @@ en:
19261926
signed_in_as_other: >-
19271927
You're signed in as %{email}, so we kept you in that account. The registration
19281928
is still saved to the address we emailed.
1929-
create:
1930-
email_required: Email is required to register
1931-
manufacturer_required: Manufacturer is required to register
1932-
unable_to_save: We're unable to save that registration, please try again
19331929
find_b_param:
19341930
registration_not_found: We couldn't find that registration — start a new one
19351931
below
19361932
sign_in_confirmed_user:
19371933
unable_to_sign_in: We confirmed your email, but couldn't sign you in to that
19381934
account
1939-
update:
1940-
name_required: Owner name is required to register
19411935
search:
19421936
marketplace:
19431937
set_interpreted_params:
@@ -5071,6 +5065,11 @@ en:
50715065
bike: Bike
50725066
recovered_date: recovered %{date}
50735067
translated_from_english: translated from English
5068+
register_flow:
5069+
email_required: Email is required to register
5070+
manufacturer_required: Manufacturer is required to register
5071+
name_required: Owner name is required to register
5072+
unable_to_save: We're unable to save that registration, please try again
50745073
shops_map:
50755074
map_of_partner_locations: Map of partner locations
50765075
user_general_alert:

0 commit comments

Comments
 (0)