- Validator moves to app/validators/projects/identifier_validator.rb
as Projects::IdentifierValidator. The path-style symbol on the
validates declaration ('projects/identifier') resolves to it.
Distinguishes domain-specific validators from globally reusable
ones (UrlValidator, JsonValidator, etc).
- Spec moves to spec/validators/projects/identifier_validator_spec.rb.
- Trim comments that explained framework conventions (validator
lookup, declaration order). Keep comments only where the WHY is
non-obvious to a Rails-fluent reader.
The IdentifierSlugScopes module already exposes .excluding_project(project)
for exactly this — dropping the validator out of the scope vocabulary
into a raw where.not was a layering leak. Behavior is identical.
Validator is named ProjectIdentifierValidator and YARD-documented
as Project-only. Project does not use STI, so the indirection
through record.class adds nothing and obscures the contract.
Calling Project.identifier_slugs and Project.classic_identifier_format?
directly reads cleaner and matches the explicit @param record [Project]
constraint.
Move five private validation methods out of the Projects::Identifier
concern into a top-level ActiveModel::EachValidator. The concern goes
from carrying ~50 lines of validation logic to declaring:
validates :identifier, project_identifier: true,
if: :identifier_changed?
Format rules, reserved-keyword check, and historical-reservation check
all live in ProjectIdentifierValidator. Mode dispatch (classic vs
semantic vs :semantic_conversion context) is internal to the validator
and easy to swap for a registry if a third format ever appears.
Picks up the follow-up suggested in #22931 (comment 4326208630).
Notes:
- Validator is top-level (ProjectIdentifierValidator) not namespaced,
matching the existing convention of UrlValidator / JsonValidator /
SecureContextUriValidator and so Rails' validator lookup for
`validates :identifier, project_identifier: true` resolves directly.
- Format constants (RESERVED_IDENTIFIERS, *_MAX_LENGTH) stay on the
concern since they're shared with acts_as_url's blacklist/limit, the
routing constraint, and suggesters — moving them to the validator
would couple unrelated subsystems to a validator namespace.
- Validator declaration order in the concern is significant: presence
+ uniqueness must run before `project_identifier: true` so the
historical-reservation check can short-circuit when uniqueness has
already flagged :taken.
Pure-validator behaviour (format checks, reserved keyword,
:semantic_conversion context, historical reservation) moves to
spec/validators/project_identifier_validator_spec.rb. Integration
tests (FriendlyId :history wiring, :saving_custom_fields,
.suggest_identifier, .identifier_slugs scopes) stay in the model spec.
Currently redirect_uri field of OAuth applications (Doorkeeper
Applications) did only check for 'localhost', which is not
complete. Other loopback URLs are also valid, such as
'http://127.0.0.1'.
Now, it is coherent with the allowed hosts of ::Storages::Storage