Σで終わるUnicodeユーザー名でプロフィールページ読み込みエラーが発生

まずはお手伝いいただき、本当にありがとうございます!これは興味深いバグですね。

実は、ルートが利用できないため、データベース経由でユーザー名を変更する必要があるかもしれません :sweat_smile:

クエリを提供していただけますか?

これは難しいと思います。なぜなら、ギリシャで Σ で始まる多くの名前が禁止されてしまい、結果的にそれらのユーザーの Facebook ログインが機能しなくなってしまうからです。特定の正規表現パターンを禁止することはできますか?そうすれば、Σ が少なくとも末尾にあることを保証できます。

「いいね!」 1

同じ問題で登録した別のメンバーもいます。ここで言いたいのは、私たちにとってこれは例外的なケースではないということです。実際、名前の末尾に Σ(または ς)を使用するギリシャ語の名前は数千人も存在します。

さらに残念なことに、私たちの主要な年齢層(40 歳以上)では、Facebook に名前が大文字で登録されているメンバーが非常に多いです(:sweat_smile:)。そのため、Facebook ログインを使用すると、その名前がユーザー名フィールドにそのままコピーされてしまいます。

「いいね!」 4

Postgres については心配する必要はありません。SQL 内では LOWER() 関数に依存せず、常に username_lower と比較すべきです。なぜなら username_lower は単にユーザー名を小文字にしたものではないからです。Unicode 正規化も適用しています。

同意します。現時点では User.normalize_username に回避策を追加すれば十分でしょう。Ruby のバグ報告でもすでに議論されており、簡単な解決策はないようです。ただし、幸運なことに、ユーザー名の最後の文字をチェックするだけで済むため、完全な文章で行うよりもはるかに簡単です。

「いいね!」 4

さて、このユーザー名はどう処理されるのでしょうか?

大文字:ΧΡΗΣΤΟΣ_ΠΑΝΑΓΙΩΤΑΚΟΠΟΥΛΟΣ
小文字:χρηστος_παναγιωτακοπουλος または χρηστοσ_παναγιωτακοπουλος

Node.js はこれを正しく処理しますが、ユーザー名の最後の文字のみを確認する限り、問題は解消されません。


(ギリシャ語の男性名は、名・姓ともにほぼすべてがΣで終わります)

「いいね!」 3

その通りです。それでも、完全な実装に比べればはるかに簡単でしょう。アンダースコアやダッシュ、もしかすると数字といった特定の記号だけを気にすればいいのですから。それは十分可能だと思います。

「いいね!」 2

特定のバグの議論から離れてしまうのは承知していますが、この問題を考えると、回避可能だったという事実は無視できません。

Discourse には、ユーザーを userId で参照する API ルートもあれば、username で参照するルートもあることがわかりました。これはもっと一貫性を持たせるべきではないでしょうか(userId を優先する形で)。

現在のカテゴリやタグのように、URL に username と userId の両方を含めるような実装はいかがでしょうか。例えば https://meta.discourse.org/u/chrispanag/4387 のようにです。

ただのアイデアです :sweat_smile:

「いいね!」 2

これは整理作業が必要になります。LOWER() の呼び出しをいくつか目撃しています。

「いいね!」 4

これは長年話題に上がっています:Update quotes and mentions when username is changed - #10 by sam

2016 年当時、@eviltrout はこれに反対していました。現在も同じ立場かどうかはわかりません。

さて、Discourse にはこの PR で回避策があります:

これにより、先頭にシグマ記号が含まれるユーザー名をすべて小文字化することで、Facebook の新規登録に関する問題に対処します。つまり、Spiros のユーザー名や末尾にシグマ記号を持つ他のユーザーの名前をすべて小文字化して修正するだけで、長期的にはこの問題は解消されます。

(PR のマージ待ち)

「いいね!」 4

@sam、本当にありがとうございます!あなたの助けは大きかったです。

これを Rails コンソールから実行する方法はありますか?(ユーザーが破損しているため、管理パネルからは対応できません…)

「いいね!」 1

はい、これで解決するはずです。

./launcher enter app
rails c
u = User.find_by(username: 'ΣΠΥΡΟΣ')
u.username = 'σπυρος'
u.username_lower = 'σπυρος'
u.save!
「いいね!」 4

ID を至る所で使うのはまだ好きではありませんが、それが理にかなっているケースが多数あることは理解しています。

そのような場合、id-username のような形式を好みます。ここで username は URL に含められる任意の文字列です。ルーター側で無視することも可能です。少なくともリンクを共有する際に、何がリンクされているのかを把握できます。

「いいね!」 6

PostgreSQL について、適切な_collation_を設定することで問題が解決するようです。

➜  discoursesmall git:(master) psql -d discourse_development
psql (13.1 (Ubuntu 13.1-1.pgdg20.10+1))
Type "help" for help.

discourse_development=# SELECT lower('ΣΠΥΡΟΣ');
 lower  
--------
 σπυροσ
(1 row)

discourse_development=# SELECT lower('ΣΠΥΡΟΣ' COLLATE "und-x-icu"); 
 lower  
--------
 σπυρος
(1 row)

出典: PostgreSQL: Re: BUG #15805: Problem with lower function for greek sigma (Σ) letter

「いいね!」 7

どうやら、同様の変換を持つ他の Unicode 文字もあるようです。例えば、「ß」→「SS」(ドイツ語)などです。

Discourse がこれらをどのように処理しているか調査するのは興味深いですね。

また、こちらのリソースもご参照ください:

個人的には、ユーザー向けの Stack Overflow スタイルのルーティングが大変気に入っています。

https://stackoverflow.com/users/17174/sam-saffron

Discourse 環境では、以下のように表現されます。

https://meta.discourse.org/u/17174/sam-saffron

これにより、両方の利点を享受できます。ただし、「URL に 17174 という数字が一切含まれてほしくない。ユーザー名は安定しているべきだ」というご意見も、完全に理解できます。

それでも、既存のルートでこれまで十分に機能してきました。ただ、数年に一度、ごく稀なケースが浮上することはあります。

「いいね!」 5

ユーザープロフィールページでは、少なくともユーザー名の代わりにIDを使用する(または両方を使用するが、上記のようにIDのみに依存する)ことが役立つと思います。そうすれば、何か問題が発生した場合でも、管理者はRailsコンソールでコマンドを実行する必要なく、DiscourseのUIを使ってユーザー名を変更できます。

私たちが懸念すべきは、JavaScript と Ruby における小文字への変換実装が異なる場合のみです。その逆ではありません。

ドイツ語の小文字「ß」を大文字に変換する際の統一された規則はありません。「SS」、「SZ」、あるいは新しい大文字「ẞ」に変換される可能性があります(はい、微妙な違いがあります)。この変換を逆転させることが可能なのは「ẞ」の場合のみであり、Ruby と JavaScript の両方で正しく機能します。


以下を行うべきだと考えます:

  1. 即座の問題を修正するための @sam のワークアラウンドをマージする
  2. SQL クエリから LOWER(username) を削除する。これは単に悪い(tm)行為だからだ(例:Unicode 正規化の欠如)
  3. Ruby が根本的な問題を修正することを願う
  4. 長期的には:ルートにユーザー ID を追加することを検討する。最も難しい部分は、引用符やメンションの扱い方をどうするかを明確にすることだろう。
「いいね!」 3

関連があるかどうかはわかりませんが、Unicode 名を持つユーザーがいくつかおり、それらが /log 内に散在しています。

ActionView::Template::Error (No route matches {:action=>"show", :controller=>"users", :username=>"ζηεδψ"}, possible unmatched constraints: [:username])