Script to convert markdowns

This commit is contained in:
Jorge Manrubia
2025-05-25 23:14:40 +02:00
parent 032267f918
commit b8a810df8c
3 changed files with 127 additions and 8 deletions
+1 -1
View File
@@ -24,6 +24,7 @@ gem "thruster", require: false
gem "bcrypt", "~> 3.1.7"
gem "geared_pagination", "~> 1.2"
gem "rqrcode"
gem "redcarpet"
gem "rouge"
gem "jbuilder"
gem "actiontext-lexical", github: "basecamp/actiontext-lexical", branch: "initial"
@@ -52,4 +53,3 @@ group :test do
gem "webmock"
gem "vcr"
end
+10 -7
View File
@@ -26,15 +26,16 @@ GIT
GIT
remote: https://github.com/crmne/ruby_llm.git
revision: 8412d51e869f1ee8e5229831a9a0bb5e9b8f3847
revision: 87a86f70cb64e23d5b76994edf8080618f07e071
specs:
ruby_llm (1.2.0)
ruby_llm (1.3.0rc1)
base64
event_stream_parser (~> 1)
faraday (~> 2)
faraday-multipart (~> 1)
faraday-net_http (~> 3)
faraday-retry (~> 2)
faraday (>= 1.10.0)
faraday-multipart (>= 1)
faraday-net_http (>= 1)
faraday-retry (>= 1)
marcel (~> 1.0)
zeitwerk (~> 2)
GIT
@@ -205,7 +206,7 @@ GEM
addressable (>= 2.5.0)
globalid (1.2.1)
activesupport (>= 6.1)
hashdiff (1.1.2)
hashdiff (1.2.0)
i18n (1.14.7)
concurrent-ruby (~> 1.0)
image_processing (1.14.0)
@@ -327,6 +328,7 @@ GEM
rdoc (6.14.0)
erb
psych (>= 4.0.0)
redcarpet (3.6.1)
regexp_parser (2.10.0)
reline (0.6.1)
io-console (~> 0.5)
@@ -479,6 +481,7 @@ DEPENDENCIES
puma (>= 5.0)
rails!
rails_structured_logging!
redcarpet
rouge
rqrcode
rubocop-rails-omakase
+116
View File
@@ -0,0 +1,116 @@
#!/usr/bin/env ruby
require_relative "../config/environment"
require "redcarpet"
require "nokogiri"
class ActionText::Markdown < ApplicationRecord
belongs_to :record, polymorphic: true
end
class MarkdownToActionTextConverter
ATTACHMENT_URL_REGEX = %r{/u/(?<slug>[^\/\s\)]+)}
def initialize(html)
@doc = Nokogiri::HTML::DocumentFragment.parse(html)
@attachments = []
end
def convert
process_images
process_links
[ @doc.to_html, @attachments ]
end
private
def process_images
@doc.css("img").each do |img|
src = img["src"].presence
if src && match = src.match(ATTACHMENT_URL_REGEX)
if (attachment = find_attachment(match[:slug]))
puts "REEMPLAZO #{build_attachment_node(attachment).to_html}"
img.replace(build_attachment_node(attachment))
@attachments << attachment
end
end
end
end
def process_links
@doc.css("a").each do |link|
href = link["href"].presence
if href && match = href.match(ATTACHMENT_URL_REGEX)
if (attachment = find_attachment(match[:slug]))
link.replace(build_attachment_node(attachment))
@attachments << attachment
end
end
end
end
def build_attachment_node(attachment)
html = ActionText::Attachment.from_attachable(attachment).to_html
fragment = Nokogiri::HTML::DocumentFragment.parse(html)
node = fragment.at_css("action-text-attachment")
node["url"] = Rails.application.routes.url_helpers.rails_blob_path(attachment.blob, only_path: true)
fragment
end
def find_attachment(slug)
ActiveStorage::Attachment.find_by(slug: slug)
end
end
class RedcarpetRenderer
def self.render(markdown)
renderer = Redcarpet::Render::HTML.new
markdowner = Redcarpet::Markdown.new(renderer,
autolink: true,
tables: true,
fenced_code_blocks: true,
strikethrough: true,
superscript: true,
)
markdowner.render(markdown.to_s)
end
end
ApplicationRecord.with_each_tenant do |tenant|
puts "Processing tenant: #{tenant}"
ActionText::Markdown.find_each do |markdown|
next unless markdown.record
html = RedcarpetRenderer.render(markdown.content.to_s)
converter = MarkdownToActionTextConverter.new(html)
rich_text_html, attachments = converter.convert
rich_text = ActionText::RichText.create!(
name: markdown.name,
record: markdown.record,
body: rich_text_html
)
attachments.each do |attachment|
attachment.update!(record: rich_text)
end
puts "✓ Created rich text for #{markdown.record_type}##{markdown.record_id} (#{markdown.name})"
rescue => e
warn "✗ Failed to process markdown ##{markdown.id}: #{e.class} - #{e.message}"
end
end
# create_table "action_text_markdowns", force: :cascade do |t|
# t.text "content", default: "", null: false
# t.datetime "created_at", null: false
# t.string "name", null: false
# t.integer "record_id", null: false
# t.string "record_type", null: false
# t.datetime "updated_at", null: false
# t.index ["record_type", "record_id"], name: "index_action_text_markdowns_on_record"
# end