Rails 3.1 Asset Pipeline 不预编译来自不同目录的文件
我目前有一个 Rails 3.1 应用程序,它为多个不同的客户托管多个站点。 每个站点都由一个模型表示,该模型知道域名和资产存储位置的路径以及一些其他信息。
我一直使用 app/sites/domain-name
作为存储特定于给定站点的资产、视图和区域设置的位置,并运行自定义中间件和控制器操作来修改负载链轮的路径,设置视图路径等。
中间件(使用 application.rb
中的 config.middleware.use "Configurator"
加载):
class Configurator
def initialize(app)
@app = app
end
def call(env)
@env = env
clear_existing_paths
prepend_local_assets
@app.call(env)
end
def current_site
# find site using @env
end
def current_site_path
Rails.root.join('app', 'sites', current_site.path)
end
def clear_existing_paths
paths = Rails.application.assets.paths
Rails.application.assets.clear_paths
paths.each do |path|
next if path.include?("app/sites/")
Rails.application.assets.append_path path
end
end
def prepend_local_assets
path = current_site_path.join('assets')
return unless Dir.exists?(path)
['images', 'javascripts', 'misc', 'stylesheets'].each do |subdir|
asset_dir = path.join(subdir).to_s
next unless Dir.exists?(asset_dir)
next if Rails.application.assets.paths.include? asset_dir
Rails.application.assets.prepend_path asset_dir
end
end
end
来自 application.rb
的资产位:
module MyApp
class Application < Rails::Application
...
config.middleware.use "Configurator"
config.assets.enabled = true
config.assets.version = '1.0'
end
end
以及 环境/生产.rb:
MyApp::Application.configure do
config.serve_static_assets = false
config.assets.compress = true
config.assets.compile = true
config.assets.digest = true
# Defaults to Rails.root.join("public/assets")
# config.assets.manifest = YOUR_PATH
end
问题是,虽然此设置有效,但它阻止我预编译未与整个应用程序共享的资源,从而使它们从外观上一遍又一遍地生成。
有什么方法可以告诉预编译器找到位于此处的资产,并创建这些资产的版本吗?每个站点都有一个 site.css.scss
和一个 site.js.coffee
文件,可能需要站点目录中的其他资源。如果我可以将其预编译为 public/assets/domain-name/site.(js|css)
那就太好了,这样我就可以在需要时轻松地为资产设置一个单独的子域进一步优化
最终解决方案(2012-02-22)
在实现了 Brian 的建议后,我最终将
主样式表/javascript 存储在app/sites/
,其中 sitename
是此网站的域,shortname
是域的主要部分,没有子域或 com|org|net|ccTLD。
修改了所有视图和样式表以将 shortname
添加到我的资源路径中。
在 config/application.rb 中:
{ "sitename" => "shortname", ... }.each_pair do |domain, short|
%w{stylesheets javascripts}.each do |dir|
config.assets.paths << Rails.root.join("app/sites/#{domain}/assets/#{dir}").to_s
end # Had to specify each dir to make it work
config.assets.precompile += ["#{short}/site.css", "#{short}/site.js"]
end
运行 rake assets:precompile 时,会创建包含该站点所有资源的 public/assets/shortname ,和 public/assets
也拥有所有共享资产。非常适合我的需要。
由于所有内容最终都在 public/assets
中,因此我能够删除 Configurator
中间件,因为默认配置能够找到所有资产
I currently have a Rails 3.1 app that hosts multiple sites for several different customers.
Each site is represented by a model that knows the domain name and path to where the assets are stored, along with some other info.
I've been using app/sites/domain-name
as a location for storing assets, views and locales that are specific to a given site, and are running a custom middleware and controller actions for modifying the load path for Sprockets, setting up the view paths and so on.
middleware (loaded using config.middleware.use "Configurator"
in application.rb
):
class Configurator
def initialize(app)
@app = app
end
def call(env)
@env = env
clear_existing_paths
prepend_local_assets
@app.call(env)
end
def current_site
# find site using @env
end
def current_site_path
Rails.root.join('app', 'sites', current_site.path)
end
def clear_existing_paths
paths = Rails.application.assets.paths
Rails.application.assets.clear_paths
paths.each do |path|
next if path.include?("app/sites/")
Rails.application.assets.append_path path
end
end
def prepend_local_assets
path = current_site_path.join('assets')
return unless Dir.exists?(path)
['images', 'javascripts', 'misc', 'stylesheets'].each do |subdir|
asset_dir = path.join(subdir).to_s
next unless Dir.exists?(asset_dir)
next if Rails.application.assets.paths.include? asset_dir
Rails.application.assets.prepend_path asset_dir
end
end
end
Asset bits from application.rb
:
module MyApp
class Application < Rails::Application
...
config.middleware.use "Configurator"
config.assets.enabled = true
config.assets.version = '1.0'
end
end
And environments/production.rb
:
MyApp::Application.configure do
config.serve_static_assets = false
config.assets.compress = true
config.assets.compile = true
config.assets.digest = true
# Defaults to Rails.root.join("public/assets")
# config.assets.manifest = YOUR_PATH
end
The problem is that while this setup works, it prevents me from precompiling the assets that is not shared with the entire app, making them be generated over and over again, from the looks of it.
Is there any way I could tell the precompiler to find the assets located here, and create a version of those as well? Each site has a site.css.scss
and a site.js.coffee
file that might require other assets inside the site-dir. Would be nice if I could get it precompiled to public/assets/domain-name/site.(js|css)
, so I could easily set up a separate subdomain for assets down the line when I need to optimize further
Final solution (2012-02-22)
After implementing what was suggested by Brian, I have ended up with
Main stylesheet/javascript stored in app/sites/<sitename>/assets/<shortname>/site.css|js
, where sitename
is the domain for this site, and shortname
is the main part of the domain, with no subdomain or com|org|net|ccTLD.
Modified all views and stylesheets to prepend shortname
to my asset paths.
In config/application.rb
:
{ "sitename" => "shortname", ... }.each_pair do |domain, short|
%w{stylesheets javascripts}.each do |dir|
config.assets.paths << Rails.root.join("app/sites/#{domain}/assets/#{dir}").to_s
end # Had to specify each dir to make it work
config.assets.precompile += ["#{short}/site.css", "#{short}/site.js"]
end
When running rake assets:precompile
this creates public/assets/shortname
filled with all the assets for that site, and public/assets
have all the shared assets as well. Works great for my needs.
And since everything ended up in public/assets
, I was able to drop the Configurator
-middleware, since the default configuration was able to find all the assets
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为问题是,通过将每个站点添加为路径,链轮只能找到
我使用指南针尝试过的第一个 site.scss ,但对于普通链轮来说应该是相同的。
我还没有尝试过你的配置器方法,但调整我的安排似乎很简单。
您可以将您的 app/sites/* 更改为更标准的文件排列吗?
更改您的 config/application.rb,并添加您的每个站点。
这将在每个主机上预生成所有样式:
在您的视图/布局/应用程序中,您需要配置站点名称的路径:
在我抓取资产:清理并预编译后,我在公共场合看到了这一点:
I think the problem is, by adding each site as a path, sprockets only finds the first site.scss
I've tried this using compass, but it should be the same for plain sprockets.
I haven't tried your configurator approach, but it looks straightforward to adapt my arrangement to it.
Can you change your app/sites/* to the more standard file arrangement?
Change your config/application.rb, and add each of your sites.
This will pregenerate all styles, on each of your hosts:
In your view/layouts/application, you'll need to configure the path to sitename:
After I rake assets:clean and precompile, I see this in public:
设法找到资产
看起来 Rails 至少在我更改 config/environments/Production.rb 以包含和
config/application.rb
以包含时这给了我一个预编译版本,但仅限于第一个拥有
site.css
的站点。我想将它们重命名为sitename.css
,或者添加一个额外的子目录sitename/site.css
也可能有效。Looks like Rails manages to find the assets at least when I changed
config/environments/production.rb
to includeand
config/application.rb
to includeThis gives me a precompiled version, but only for the first site to have a
site.css
. I guess renaming them tositename.css
, or add a extra subdirectorysitename/site.css
might work as well.