如何在Sidekiq任务场景下渲染erb格式的EPUB内嵌CSS文件?
解决方案:在非控制器环境下渲染EPUB的ERB CSS模板
我来给你几个实用的方案,帮你在服务、Sidekiq任务或者模型回调这类非控制器环境里,顺利渲染epub/style.css.erb模板:
方案一:直接用Ruby ERB库手动渲染(最轻便)
不需要依赖Rails控制器的render_to_string,直接用Ruby标准库的ERB类来处理模板,完全脱离控制器环境,特别适合Sidekiq或回调场景。
代码示例:
class EpubGenerationService def generate_css # 读取模板文件的绝对路径 template_path = Rails.root.join('app', 'views', 'epub', 'style.css.erb') template_content = File.read(template_path) # 初始化ERB对象,绑定当前服务的上下文(模板里可以直接用服务的变量/方法) erb = ERB.new(template_content) # 渲染出最终的CSS字符串 erb.result(binding) end # 举个例子:模板里可以调用服务的方法或变量 def primary_color '#2c3e50' end attr_reader :font_size def initialize(font_size: '16px') @font_size = font_size end end
说明:
- 模板里可以直接使用服务中定义的变量(比如
@font_size)和方法(比如primary_color),因为binding会把当前服务的上下文传递给ERB。 - 这个方法完全不依赖Rails的控制器栈,性能好,也不会有请求上下文的问题。
方案二:用Rails视图渲染器(适合依赖视图助手的场景)
如果你的CSS模板用到了Rails的视图助手(比如asset_path、image_url这类),可以用Rails提供的ActionController::Renderer,它能在非控制器环境下模拟视图渲染环境,比手动创建控制器更优雅。
代码示例:
class EpubGenerationService def generate_css # 创建渲染器,指定一个控制器作为模板的上下文(用来继承视图助手) renderer = ApplicationController.renderer.new( http_host: Rails.application.config.action_controller.default_url_options[:host], https: Rails.env.production? ) # 渲染模板,传递需要的变量 renderer.render( template: 'epub/style.css', formats: [:css], layout: false, assigns: { font_family: 'Georgia', primary_color: '#2c3e50' } ) end end
说明:
assigns里的键会变成模板中的实例变量(比如assigns: { font_family: 'Georgia' }对应模板里的@font_family)。- 必须设置
http_host,不然像asset_path这类助手生成的路径会不正确。
方案三:创建轻量控制器专门渲染模板(兼容复杂场景)
如果上面两种方案满足不了你的需求(比如需要复用控制器里的过滤器逻辑,或者模板依赖复杂的请求参数),可以创建一个专门的控制器,然后在服务里模拟请求环境调用它的render_to_string。
第一步:创建控制器
class EpubTemplatesController < ApplicationController # 跳过所有不需要的过滤器,避免认证、CSRF检查等干扰 skip_before_action :verify_authenticity_token, :authenticate_user! def style_css # 从参数或服务传入的变量设置实例变量 @font_family = params[:font_family] || 'Arial' @primary_color = params[:primary_color] || '#2c3e50' # 渲染模板,指定格式为CSS,不使用布局 render template: 'epub/style.css', formats: [:css], layout: false end end
第二步:在服务里调用控制器
class EpubGenerationService def generate_css # 模拟请求环境 env = { 'HTTP_ACCEPT' => 'text/css', 'PATH_INFO' => '/epub_templates/style_css', 'REQUEST_METHOD' => 'GET', 'QUERY_STRING' => 'font_family=Georgia&primary_color=#2c3e50' } # 初始化控制器并设置请求/响应对象 controller = EpubTemplatesController.new controller.request = ActionDispatch::Request.new(env) controller.response = ActionDispatch::Response.new # 调用render_to_string得到CSS内容 controller.render_to_string(action: :style_css, layout: false) end end
说明:
- 这个方案适合需要完全模拟HTTP请求的场景,但相对繁琐,尽量优先用前两种方案。
- 一定要跳过不必要的过滤器,否则可能会出现认证失败、CSRF错误等问题。
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

