Rails - 如何在手动 sass 编译中传递 Sprockets::Context

Rails - How to pass Sprockets::Context in manual sass compiling

我正在使用以下代码片段手动编译 sass 清单,其中附加了一些变量覆盖。

template = File.read("#{Rails.root}/app/assets/schemes/#{scheme}/css/styles.css.scss")

scheme_variables.each do |key, value|
  template << "$#{key}:#{value};\n"
end

engine = Sass::Engine.new(template, { 
  :syntax => :scss,
  :cache => false,
  :read_cache => false,
  :style => :compressed,
  :filesystem_importer => Sass::Rails::SassImporter,
  :load_paths => MyApp::Application.assets.paths,
  :sprockets => {
    :context => ?,
    :environment => MyApp::Application.assets
  }
})
output = engine.render

Sass::Engine 构造函数需要选项散列中的链轮上下文和环境。我在上下文中输入了什么?我尝试的第一件事是...

:context => MyApp::Application.assets.context_class,

...但这给了我以下错误 "undefined method `font_path' for #" 当它击中我的一个 sass 资产助手时。

我尝试的第二件事是...

:context => ActionController::Base.helpers,

...这解决了资产助手问题,但在它尝试通过我的 glob 导入(例如 @import "mixins/*")时抛出以下错误 "undefined method `depend_on' for #"。

我正在使用 Rails 4.2 和 sass-rails 5.0.3.

如有任何建议,我们将不胜感激。谢谢!

我最终以稍微不同的方式解决了这个问题——使用 Sass::Rails::ScssTemplate 的渲染方法。基本上,我将修改后的 css 字符串写入文件并将其传递给 Sass::Rails::ScssTemplate 构造函数。然后我编译并在完成后删除临时文件。这感觉不太好,但对我来说效果很好。

scheme_css_dir = "#{Rails.root}/app/assets/schemes/#{scheme}/css"
css = File.read("#{scheme_css_dir}/styles.css.scss")

variables_str = ''
scheme_variables.each do |key, value|
  variables_str << "$#{key}:#{value};\n"
end

css.gsub!('@import "./variables";', variables_str)

file = Tempfile.new(['styles', '.css.scss'], scheme_css_dir)
file.write(css)
file.close

abs_path = file.path
relative_path = abs_path[Rails.root.to_s.size + 1..-1]

template = Sass::Rails::ScssTemplate.new(abs_path)
environment = Evrconnect::Application.assets
context = environment.context_class.new(
  :environment => environment,
  :name => relative_path,
  :filename => abs_path,
  :metadata => {}
)
output = template.render(context)

file.unlink

要回答您的原始问题,您需要提供当前的 view_context,或使用 ActionView::Base.new 创建一个。

http://apidock.com/rails/AbstractController/Rendering/view_context

通过使用 Sass::Rails::ScssTemplate,您可以使用以下代码段呈现 sass 代码:

template = '...' # Your sass code

logical_path = pathname = ''
environment = Rails.application.assets
context = environment.context_class.new(environment, logical_path, pathname)

template = Sass::Rails::ScssTemplate.new(pathname) { template }
output = template.render(context, {})

如果你想从文件中渲染,那么只需将其路径添加到路径名并将其资源路径添加到 logical_path。

对我来说,它适用于 Rails 4.2.5.1 和 sass-rails 5.0.4.

在 Rails 5.x 中,Sprockets v3.7.x

NOTE: Following method requires: Rails.application.config.assets.compile == true, i.e., set config.assets.compile = true. Check Sprockets README

我使用以下代码手动返回 sass/js 的编译源。

# For Scss Assets
def pdf_stylesheet_link_tag(name)
  if Rails.env.development?
    asset = Rails.application.assets.find_asset(name + '.scss')
    raw ('<style type="text/css">' + asset.source + '</style>')
  else
    raw ('<style type="text/css">' + pdf_asset_contents(name, '.css') + '</style>')
  end
end

# For Javascript Assets
def pdf_javascript_include_tag(name, *type)
  if Rails.env.development?
    if debug? # Can be used to check `debug == true` in params for current route
      javascript_include_tag(name)
    else
      asset = Rails.application.assets.find_asset(name + '.js')
      raw ('<script>' + asset.source + '</script>')
    end
  else
    javascript_tag pdf_asset_contents(name, '.js')
  end
end

以上代码实际上有助于动态传递 sass/js 的编译版本 到 Wicked_PDF,这实际上有助于在开发环境中为生成的 PDF 加载样式和 js。

pdf_stylesheet_link_tag 可以用作模板的助手,在 development/stage(其中,config.assets.precompile == false)中,而不是使用 wicked_pdf_stylesheet_link_tag(实际上需要一个路径到预编译的源文件)。

经过一夜的摸索,找到了今天的做法,分享给大家("today"意思是:rails 5.2.1 和 sprockets 3.7.2)。

按预期工作:不需要临时文件,接受@import,允许资产路径助手。

# Compile SASS and return the resulting string
# Pass the file path and name without 'sass' extension, relative to assets/stylesheets
def compile_sass(stylesheet) 
  # Load file content
  path = Rails.root.join 'app', 'assets', 'stylesheets', "#{stylesheet}.sass"
  sass = File.read path

  # Configure engine
  environment = Sprockets::Railtie.build_environment Rails.application
  scope = environment.context_class.new environment: environment, \
    filename: "/", metadata: {}
  scope.sass_config.merge! cache: false, style: :compressed

  # Compile
  engine = Sass::Rails::SassTemplate.new {sass}
  engine.render scope
end

其他 sass_config 选项可以在这里找到:https://github.com/sass/ruby-sass/blob/stable/doc-src/SASS_REFERENCE.md#options

以下适用于 rails 5.2.0、sprockets 3.7.2、sassc-rails 1.3.0 和 sassc 1.12.1:

 template = '...' # Your sass code

 environment = Sprockets::Railtie.build_environment(Rails.application)
 engine = SassC::Rails::SassTemplate.new

 engine.call(environment: environment,
             filename: '/', 
             data: template, 
             metadata: {})[:data]