Optimize markdownify allocations with in-place gsub! and combined regex (#4805)

* Optimize markdownify allocations with in-place gsub and combined regex

Avoid chaining multiple full-string copies during markdown parsing. Duplicate
the input string once in ApplicationHelper#markdownify and modify it in-place
using gsub! in GrowstuffMarkdown#expand_crops! and #expand_members!.
Combine MEMBER_REGEX and MEMBER_AT_REGEX with Regexp.union to parse member
mentions in a single pass.

Co-authored-by: CloCkWeRX <365751+CloCkWeRX@users.noreply.github.com>

* Apply suggestion from @CloCkWeRX

* Apply suggestion from @CloCkWeRX

* Optimize markdownify allocations with in-place gsub and combined regex

Avoid chaining multiple full-string copies during markdown parsing. Duplicate
the input string once in ApplicationHelper#markdownify and modify it in-place
using gsub! in GrowstuffMarkdown#expand_crops! and #expand_members!.
Combine MEMBER_REGEX and MEMBER_AT_REGEX with Regexp.union to parse member
mentions in a single pass.

Co-authored-by: CloCkWeRX <365751+CloCkWeRX@users.noreply.github.com>

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-09-20 14:41:13 +09:30
1 parent c7eef27f9a
commit 748847edbc
2 files changed
+13 -9

No files matched your search

+4 -1
View File
@@ -59,7 +59,10 @@ module ApplicationHelper
# of HAML, Tilt, and dynamic compilation with interpolated ruby.
def markdownify(text)
translator = Haml::Filters::GrowstuffMarkdown.new
translator.expand_members!(translator.expand_crops!(text.to_s))
text = text.to_s.dup
translator.expand_crops!(text)
translator.expand_members!(text)
text
end
#
+9 -8
View File
@@ -6,31 +6,32 @@ class Haml::Filters
CROP_REGEX = /(?<!\\)\[([^\[\]]+?)\]\(crop\)/
MEMBER_REGEX = /(?<!\\)\[([^\[\]]+?)\]\(member\)/
MEMBER_AT_REGEX = /(?<!\\)(@\w+)/
MEMBER_COMBINED_REGEX = Regexp.union(MEMBER_REGEX, MEMBER_AT_REGEX)
MEMBER_ESCAPE_AT_REGEX = /(?<!\\)\\(?=@\w+)/
HOST = Rails.application.config.host
def expand_crops!(text)
# turn [tomato](crop) into [tomato](http://growstuff.org/crops/tomato)
text.gsub(CROP_REGEX) do
text.gsub!(CROP_REGEX) do
crop_str = Regexp.last_match(1)
# find crop case-insensitively
crop = Crop.where('lower(name) = ?', crop_str.downcase).first
crop_link crop, crop_str
end
text
end
def expand_members!(text)
# turn [jane](member) into [jane](http://growstuff.org/members/jane)
# turn @jane into [@jane](http://growstuff.org/members/jane)
[MEMBER_REGEX, MEMBER_AT_REGEX].each do |re|
text = text.gsub(re) do
member_str = Regexp.last_match(1)
member = find_member(member_str)
member_link(member, member_str)
end
text.gsub!(MEMBER_COMBINED_REGEX) do
member_str = Regexp.last_match(1) || Regexp.last_match(2)
member = find_member(member_str)
member_link(member, member_str)
end
text.gsub(MEMBER_ESCAPE_AT_REGEX, '')
text.gsub!(MEMBER_ESCAPE_AT_REGEX, '')
text
end
def member_link(member, link_text)