From Frustration to Contribution: Fixing a 6.5-Year-Old Ruby Bug
2025年 12月 08日 月曜日
執筆者: Jasveen Singh Sandral
From Frustration to Contribution: Fixing a 6.5-Year-Old Ruby Bug
個人プロジェクトの不満から Ruby への貢献へ

“Sometimes the bugs you hit become the contributions you make”
“時として、遭遇したバグが貢献のきっかけになる”
How It Started / きっかけ
I was working on a small personal project for fun - parsing some data files that used \n (LF) as the row separator. Nothing fancy, just a weekend hack.
週末に趣味で小さなプロジェクトを作っていました。\n(LF)を行区切りとして使うデータファイルを解析するだけの、シンプルなものでした。
Then I hit this error:
すると、こんなエラーが出ました:
CSV.parse(my_data, row_sep: "\n")
# => CSV::MalformedCSVError: Unquoted fields do not allow new line <"\r"> in line 1.Wait, what? I explicitly set row_sep: "\n". Why is it complaining about \r?
え?row_sep: "\n" を明示的に指定しているのに、なぜ \r についてエラーが出るの?
Down the Rabbit Hole / 原因調査
My data had some \r characters inside field values (not as line endings). I thought: “That should be fine - I told Ruby my rows end with \n, not \r.”
私のデータにはフィールド値の中に \r 文字が含まれていました(行末ではなく)。「\n で行が終わると指定しているから、問題ないはず」と思っていました。
I dug into Ruby’s CSV source code and found this in lib/csv/parser.rb:
Ruby の CSV ソースコードを調べて、lib/csv/parser.rb でこれを見つけました:
def prepare_unquoted
no_unquoted_values = "\r\n".encode(@encoding) # Hardcoded!
# ...
endThere it was. "\r\n" was hardcoded as forbidden characters, completely ignoring my row_sep setting!
これが原因でした。"\r\n" が禁止文字としてハードコードされていて、私の row_sep 設定を完全に無視していたのです!
Not Just Me / 私だけじゃなかった
I searched GitHub and found Issue #60 - someone had reported this exact problem back in December 2018. The maintainer kou-san had even sketched out a solution direction.
GitHub で検索すると、Issue #60 を見つけました。2018年12月に誰かがまったく同じ問題を報告していました。メンテナーの kou さんが解決策の方向性まで示していました。
6.5 years. This bug had been open for 6.5 years!
6.5年。 このバグは 6.5 年間オープンだったのです!
I decided: instead of just working around it, I’ll fix it.
私は決めました:回避策を使う代わりに、これを修正しようと。
The Fix / 修正
The solution was simple - just one line:
解決策はシンプルでした - たった1行:
# Before / 修正前
no_unquoted_values = "\r\n".encode(@encoding)
# After / 修正後
no_unquoted_values = Regexp.escape(@row_separator).encode(@encoding)Now the parser only blocks characters that are actually in your row separator:
これでパーサーは、実際に行区切りに含まれる文字だけをブロックするようになりました:
row_sep: "\n"→\ris allowed in fields / フィールド内の\rは許可row_sep: "\r\n"→ both blocked (correct!) / 両方ブロック(正しい動作!)row_sep: "|"→ both\rand\nallowed / 両方許可
Code Review Journey / コードレビューの旅
I submitted PR #346 and went through 6+ rounds of review with kou-san. Every round taught me something:
PR #346 を提出し、kou さんと 6 ラウンド以上のレビューを経験しました。毎回何かを学びました:
1. Simplify! / シンプルに!
My first attempt:
@row_separator.chars.map { |c| Regexp.escape(c) }.join # Over-engineeredAfter review:
Regexp.escape(@row_separator) # Clean!2. Test Names Matter / テスト名は重要
# Bad: What does this test?
def test_malformed_csv_cr_first_line
# Good: Crystal clear
def test_unquoted_cr_with_lf_row_separator3. Let Code Speak / コードに語らせる
If your test name is clear, you don’t need a comment explaining it.
テスト名が明確なら、説明コメントは不要です。
It Works! / 動いた!
# My original problem - now works!
CSV.parse("field1,field\rwith\rcr,field3\n", row_sep: "\n")
# => [["field1", "field\rwith\rcr", "field3"]]
# Custom separators work too
CSV.parse("a\rb|c\rd", row_sep: "|")
# => [["a\rb"], ["c\rd"]]
# CRLF still protected
CSV.parse("a,b\rc\r\n", row_sep: "\r\n")
# => MalformedCSVError (correct!)Merged! / マージ!
On July 5, 2025, kou-san merged my PR. A weekend frustration became my third contribution to Ruby.
2025年7月5日、kou さんが私の PR をマージしてくれました。週末の不満が、Ruby への 3 回目のコントリビューションになりました。
What I Learned / 学んだこと
-
Your frustration might be a bug - If something feels wrong, investigate. You might find a real issue.
不満はバグかもしれない - 何かおかしいと感じたら調査しよう。本当の問題が見つかるかも。
-
Old issues are opportunities - Nobody had touched this for 6.5 years. Sometimes fresh eyes help.
古い Issue はチャンス - 6.5 年間誰も手をつけていなかった。新しい視点が役立つことも。
-
One line can matter - Small fixes can help many people.
1行でも価値がある - 小さな修正でも多くの人の役に立つ。
-
Maintainers are kind - kou-san was patient and helpful throughout the review process.
メンテナーは親切 - kou さんはレビュー中ずっと丁寧で親切だった。