Repository navigation
Make slug generation lenient to arbitrary punctuation - #215
devin-ai-integration[bot] wants to merge 1 commit into
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
Tested this end-to-end against a local backend ( Results
🎥 Full test recording available in the Devin session. |
|
|
||
| public static String toSlug(String title) { | ||
| return title.toLowerCase().replaceAll("[\\&|[\\uFE30-\\uFFA0]|\\’|\\”|\\s\\?\\,\\.]+", "-"); | ||
| return title.toLowerCase().replaceAll("[^\\p{L}\\p{N}]+", "-").replaceAll("^-+|-+$", ""); |
There was a problem hiding this comment.
🟡 Punctuation-only titles create empty slugs
A punctuation-only title makes toSlug return an empty string. The article gets an unusable route, and another such title fails persistence.
Learn more
Titles only need to be nonblank, so strings such as "!!!" pass creation validation. The replacement first produces "-", then edge trimming removes it entirely. The empty slug is saved into the unique articles.slug column and is also used as the article's URL identifier. A later punctuation-only title passes the duplicate-title validator only if no empty slug exists, then otherwise is rejected as a duplicate or fails the unique constraint depending on the path.
Example: Creating an article titled "!!!" stores slug = ""; clients cannot address it through the normal /articles/{slug} route. Creating "???" generates the same empty slug.
Recommended fix: Reject titles whose normalized slug is empty, or generate a stable nonempty fallback before constructing and persisting the article. Apply the same rule in DuplicatedArticleValidator so validation and persistence agree.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
|
||
| public static String toSlug(String title) { | ||
| return title.toLowerCase().replaceAll("[\\&|[\\uFE30-\\uFFA0]|\\’|\\”|\\s\\?\\,\\.]+", "-"); | ||
| return title.toLowerCase().replaceAll("[^\\p{L}\\p{N}]+", "-").replaceAll("^-+|-+$", ""); |
There was a problem hiding this comment.
🟡 Combining marks corrupt Unicode slugs
A title containing combining marks makes toSlug replace those marks with hyphens. Accented and Indic words become fragmented, producing incorrect identifiers and collisions.
Learn more
Unicode combining marks use the \p{M} category, not \p{L} or \p{N}. They include decomposed accents and essential vowel signs in scripts such as Devanagari. The new negated class therefore replaces these marks with hyphens even though they are part of the surrounding word. Different marked forms can consequently normalize to the same slug, which the database requires to be unique.
Example: The decomposed title "e\u0301clair" becomes "e-clair" instead of preserving éclair. "हिन्दी भाषा" becomes "ह-न-द-भ-ष", discarding its vowel signs.
Recommended fix: Normalize the title consistently and preserve combining marks, for example by including \p{M} in the allowed categories. Add tests for decomposed accents and a script whose letters require combining marks.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
❌ Cannot revive Devin session - the session is too old. Please start a new session instead. |
Summary
Titles with unusual punctuation could produce ugly or unsafe slugs because
Article.toSlugonly replaced a hard-coded set of characters (& ? , ., some fullwidth forms, curly quotes, whitespace). This PR makes slug handling lenient to any punctuation without weakening the tests — per the repo's TDD convention, all five existingArticleTestslug expectations are kept unchanged and still pass.\p{L}\p{N}preserves non-Latin titles (e.g.中文:标题→中文-标题), so the existing i18n test still passes whileHello, World!→hello-world,100% #goals→100-goals, etc.Note: overlaps with open PR #214 (DJ-84), which solves the same problem for ASCII punctuation only.
Verified locally:
./gradlew clean test spotlessCheckgreen.Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/ff95e7360720497e81447c11af8e545e