Rails 7 引擎如何使未编译的样式表可用于托管应用程序?

Rails 7 engine how to make uncompiled stylesheets available to host app?

所以我有一个找不到文件的问题。 我有一个在引擎 test/dummy 应用程序中以开发模式工作的引擎,该引擎允许编辑 sass 变量并将它们存储在主题 table 中,这些变量由 sass 部分如 _banner.scss 包含主样式表中使用的变量,如 $banner_color 然后导入到主样式表中,主样式表又使用 engine.rb 中的初始化程序预编译文件并包含在 app/config/engine_name_manifest.js.

这些文件都可以在本地虚拟应用程序的开发中使用,但由于资产正在编译,所以在最终的主机应用程序中不可用。

我有一个 rake 任务,它获取数据,更新相关部分,例如_banner.scss 使用主题 table 中的数据,但当然部分部分在主机应用程序中不可用,因为引擎已经编译了它们。 我正在寻找一种解决方案,允许我编辑原始的、未编译的样式表,然后重新编译它们。显然,我的 Capistrano 部署脚本将需要重新应用样式表更改每个部署,但这只是一个 rake 任务调用。 我应该采取什么方法?我是否应该找到一种方法将 css 文件复制到引擎初始化程序中的主机应用程序?我是否应该完全使用不同的方法,我已经开始研究传动轴,但这是取代 sass rails 的重要一步,我不确定这会有什么帮助

引擎

require "deface"
require 'ccs_cms_admin_dashboard'
require 'ccs_cms_custom_page'
require 'ccs_cms_core'
require 'css_menu'
#require 'tinymce-rails'
require 'delayed_job_active_record'
require 'daemons'
require 'sprockets/railtie'
require 'sassc-rails'

module CcsCms
  module PublicTheme
    class Engine < ::Rails::Engine
      isolate_namespace CcsCms::PublicTheme
      paths["app/views"] << "app/views/ccs_cms/public_theme"

      initializer "ccs_cms.assets.precompile" do |app|
        app.config.assets.precompile += %w( public_theme_manifest.js )
      end

      initializer :append_migrations do |app|
        unless app.root.to_s.match?(root.to_s)
          config.paths['db/migrate'].expanded.each do |p|
            app.config.paths['db/migrate'] << p
          end
        end
      end

      initializer :active_job_setup do |app|
        app.config.active_job.queue_adapter = :delayed_job
      end

      config.to_prepare do
        Dir.glob(Engine.root.join("app", "decorators", "**", "*_decorator*.rb")) do |c|
          Rails.configuration.cache_classes ? require(c) : load(c)
        end
      end

      config.generators do |g|
        g.test_framework :rspec,
          fixtures: false,
          request: false,
          view_specs: false,
          helper_specs: false,
          controller_specs: false,
          routing_specs: false
        g.fixture_replacement :factory_bot
        g.factory_bot dir: 'spec/factories'
      end

    end
  end
end

写cssCssclass

 class Css

  def get_stylesheet_path
    Rails.root.join("app/assets/stylesheets/ccs_cms/public_theme")
  end

  def write_css(theme)
    update_css_files_for(theme.banner, '_public_banner.scss', BANNER_ARRAY, BANNER_FIELD_MAP)
    update_css_files_for(theme.banner.font, '_public_banner_font.scss', BANNER_FONT_ARRAY, BANNER_FONT_FIELD_MAP)
  end

  private

    def update_css_files_for(model_record_to_use, css_file, array_to_use, field_map)
      amended_css = amend_css_for(model_record_to_use, css_file, array_to_use, field_map)
      create_css_files_for(css_file, amended_css)
    end

    def amend_css_for(model_record_to_use, file_name, array_to_use, field_map)
      original_css_array = IO.readlines("#{get_stylesheet_path}/#{file_name}")
      new_array = []
      original_css_array.each do |line|
        new_line = line
        array_to_use.each do |ma|
          if line.start_with?(ma)
            field_name = field_map[ma.to_sym]
            new_line = ma + ": #{model_record_to_use[field_name.to_sym]};"
            #puts("@@@@ original line: #{line}, ma: #{ma}, Field name: #{field_name}, value: #{theme[field_name]}")
            break
          end
        end
        new_array << new_line
      end
      new_array
    end

    # ---- File and I/O Handling ---- #

    def create_css_files_for(file_name, css_array)
      File.open("#{get_stylesheet_path}/#{file_name}", "w") do |file|
        file.puts css_array
      end
    end

end

感谢您的澄清。如果我在这里理解正确的话,我会接受它。

partials are not not available in a host app as the engine has already compiled them

部分仍然存在,预编译只是将 *.{css/js} 文件输出到 public/assets/ 中,这些文件在 app/assets/config/manifest.js.

中声明

要获取引擎文件,而不是 Rails.root 使用:

CcsCms::PublicTheme::Engine.root

Cssclass中,例如:

def get_stylesheet_path
 CcsCms::PublicTheme::Engine.root.join("app/assets/stylesheets/ccs_cms/public_theme")
end

支持更换主题引擎。可以在引擎初始化程序中将主题根设置为 Rails.configuration.theme_root 之类的内容,并在主应用程序中使用。

因为您的主题也是可配置的,我认为最好阅读主题的原始 sass 文件但不要修改它们,将它们复制到 tmp 文件夹并使用主题 table 中的值进行更新,然后使用 sass 在主应用程序中输出 theme.csshttps://sass-lang.com/documentation/cli/dart-sass

​# Compiles all Sass
$ sass tmp/theme/application.scss:app/stylesheets/theme.css

然后让Rails接管预编译过程

另一个选择是有一个 sass 配置文件并且只更新这个文件。这样就不会依赖于任何特定主题的文件结构。

import 'configuration' // sass variables with values from theme table
import 'banner'        // uses sass variables only
...

也只使用 css 变量,如果这是一个选项,并避免上述所有复杂性;没有预编译,没有在主题 table 更改时重新部署。

更新 css 个变量。

所以我们在同一页面上。我的意思是这些 css 变量: https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties。如果 Internet Explorer 不是您的首选,那么这是最佳解决方案。 设置是这样的:

<!-- app/views/layouts/application.erb -->
<!-- NOTE: with turbo this loads only once; full page refresh is needed when @theme changes -->
<head>
  <style>
   :root { --text-color: <%= @theme.text_color %>; }
  </style>

  <%= stylesheet_link_tag 'application' %>
</head>
/* app/assets/stylesheets/application.css */
p { color: var(--text-color); }

可能的修复以避免修改 css 文件。在 sass 文件中使用 erb 插值。无需每次更改主题配置时都进行修改。在开发中即时编译。在生产中,当主题配置更改时,必须重新预编译;不修改。

// _banner.scss.erb
p { color: <%= Theme.text_color %>; }

您甚至可以使用 amend_css_for 函数插入文字 erb 代码并节省一些时间。例如

new_line = ma + ": <%= Theme.#{model_name}.#{field_name} %>;" 

最后,如果您不想接触引擎文件,并且因为这些文件不是 main/host 应用程序的一部分(就像在文件系统中的两个独立文件夹中一样)。修改时必须复印一份;从 CcsCms::PublicTheme::Engine.root 读取到 Rails.root.

def get_stylesheet_path
  CcsCms::PublicTheme::Engine.root.join("app/assets/stylesheets/ccs_cms/public_theme")
end

# but save to main app

def create_css_files_for(file_name, css_array)
  File.open("#{Rails.root.join("app/assets/stylesheets/ccs_cms/public_theme")}/#{file_name}", "w") do |file|
    file.puts css_array
  end
end