Сломанные предварительно скомпилированные ресурсы в Rails 3.1 при развертывании на подчиненный URI - PullRequest
8 голосов
/ 03 сентября 2011

Я нахожусь в процессе обновления приложения на Rails 3 для использования Rails 3.1 и как часть этого, использую новый конвейер ресурсов.Пока у меня все работает, кроме одной довольно раздражающей проблемы, которую я не могу решить.

Приложение и все его активы прекрасно работают в разработке, но в производственной среде оно развертывается в подчиненном URI с помощьюПассажир (http://the-host/sub-uri/).Проблема заключается в том, что ресурсы предварительно компилируются во время развертывания, и один из моих файлов CSS (ну, это .css.scss файл) использует хелпер image-url из гема sass-rails.Поскольку во время процесса предварительной компиляции пути жестко запрограммированы в предварительно скомпилированном файле CSS, в подпапке не учитывается:

В моем файле .css.scss:

body { background-image: image-url("bg.png"); }

Результат в скомпилированном application-<md5-hash-here>.css файле:

body { background-image: url(/assets/bg.png); }

Что должно быть для правильной работы:

body { background-image: url(/sub-uri/assets/bg.png); }

Этот сценарий просто требует слишком много?Если это так, мне придется вернуться к старому конвейерному способу без использования ресурсов и просто обслуживать мои изображения и CSS с public.Однако это похоже на то, о чем нужно было подумать и решить ...?Я пропускаю решение?


Edit 1: Следует отметить, что использование erb solution вместо этого дает тот же результат, что и следовало ожидать.


Редактировать 2: в ответ на комментарий Бенуа Гаррета

Нет, проблема не связана с config.assets.prefix.Я попытался установить это (/sub-uri/assets вместо значения по умолчанию /assets), но оказалось, что это было неправильно, похоже, этот параметр уже относится к корню приложения Rails, а не к серверу.,Удаление этого (и, следовательно, возвращение к дефолту) исправило все странные проблемы, которые вызывали (а их было много, все активы оказались в /sub-uri/sub-uri/assets - все это было очень странно).Единственная проблема заключается в том, что помощник и друзья image-url не получают вспомогательный URI при предварительной компиляции.Излишне говорить, что это логично, поскольку, когда он предварительно скомпилирован, он не может знать, что, когда он работает под управлением Passenger, он будет настроен таким образом.Мой вопрос состоит в том, как сообщить ему об этом и таким образом получить правильные пути в предварительно скомпилированном результате.Если это действительно возможно.

Мой текущий обходной путь - сослаться на iamge в CSS следующим образом: url(../images/bg.png) и поместить его в нетрубопроводное местоположение public/images.Едва ли это идеал, поскольку он не извлекает выгоду из отпечатков пальцев и всего, что обеспечивает конвейер.

Ответы [ 4 ]

4 голосов
/ 10 сентября 2011

Наконец-то я разработал пару обходных путей / решений.

1) С https://github.com/rails/sass-rails/issues/17 похоже, что это можно исправить в sass-rails. Я сам исправил обезьяны в соответствии с предложенным патчем в ссылке выше. Я просто устанавливаю необходимую переменную среды в строке прекомпиляции ресурса в deploy.rb.

Я делаю все свои обезьяньи патчи в одном файле config/initializers/gem_patches.rb. В этом файле я пропатчил этот метод как:

module Sass
  module Rails
    module Helpers
      protected
      def public_path(asset, kind)
        path = options[:custom][:resolver].public_path(asset, kind.pluralize)
        path = ENV['PRODUCTION_URI'] + path if ENV['PRODUCTION_URI']
        path
      end
    end
  end
end

2) В качестве альтернативы, если вы можете встраивать изображения в CSS, изменение таблицы стилей с расширением .erb и замена image-url("bg.png") на url(<%= asset_data_uri "bg.png" %>) будут работать без необходимости изменения sass-rails. asset-data-uri не существует как функция чистого Sass, поэтому вам нужно использовать помощник Rails asset_data_uri.

2 голосов
/ 25 августа 2012

Я использую Rails 3.1.3 и успешно внедряюсь в подчиненный URI. Я ничего не залатал обезьяной.

Ключевые проблемы с этой настройкой были лучше обсуждены здесь . Как видите, решение было применено к Rails 3.2 и никогда не портировано на 3.1.4.

Но я пришел к решению, использующему Rails 3.1.3 , которое подходит для моей установки.

Попробуйте это: (я не эксперт, просто пытаюсь внести свой вклад в решение проблемы, из-за которой меня мучили несколько часов ...)

environment.rb:

#at top:
ENV['RAILS_RELATIVE_URL_ROOT'] = '/rais'

production.rb:

config.assets.prefix = ENV['RAILS_RELATIVE_URL_ROOT'] ? ENV['RAILS_RELATIVE_URL_ROOT'] + '/assets' : '/assets'

routes.rb:

  Rais::Application.routes.draw do
       scope ENV['RAILS_RELATIVE_URL_ROOT'] || '/' do    #see config/environment.rb
             <<resources here>>
       end
  end

Как видите, я поместил assets.prefix внутри production.rb, а не в application.rb После этого вы делаете:

rake assets:clear
rake assets:precompile

и чем, тест с консоли:

RAILS_ENV=production rails console

Результаты:

foo = ActionView::Base.new
foo.stylesheet_link_tag 'application'
 => "<link href=\"/rais/assets/layout.css?body=1\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />\n<link href=\"/rais/assets/application.css?body=1\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />" 
foo.image_tag('arrow-up.png')
 => "<img alt=\"Arrow-up\" src=\"/rais/assets/arrow-up-ca314ad9b991768ad2b9dcbeeb8760de.png\" />" 
2 голосов
/ 06 декабря 2011

В последней версии Rails 3.1.3 вам нужно обезьяна исправить другой модуль, чтобы он работал

Это то, что я сделал

module Sprockets
  module Helpers
    module RailsHelper

      def asset_path(source, options = {})
        source = source.logical_path if source.respond_to?(:logical_path)
        path = asset_paths.compute_public_path(source, asset_prefix, options.merge(:body => true))
        path = options[:body] ? "#{path}?body=1" : path
        if !asset_paths.send(:has_request?)
          path = ENV['RAILS_RELATIVE_URL_ROOT'] + path if ENV['RAILS_RELATIVE_URL_ROOT']
        end
        path
      end

    end 
  end
end

И в моем deploy.rb у меня есть:

desc "precompile the assets"
namespace :assets do
  task :precompile_assets do
    run "cd #{release_path} && rm -rf public/assets/* && RAILS_ENV=production bundle exec rake assets:precompile RAILS_RELATIVE_URL_ROOT='/my_sub_uri'"
  end
end
before "deploy:symlink", "assets:precompile_assets"
0 голосов
/ 11 сентября 2011

После небольшого копания я нашел проблему.Проблема в Rails, а именно в Sprockets :: Helpers :: RailsHelper :: AssetPaths # compute_public_path.Sprockets :: Helpers :: RailsHelper :: AssetPaths наследуется от ActionView :: AssetPaths и переопределяет ряд методов.Когда compute_public_path вызывается через метод Sass :: Rails :: Resolver # public_path is sass-rails, помощник rails sprocket выполняет задачу разрешения ресурса.Sprockets :: Helpers :: RailsHelper :: AssetPaths # compute_public_path определяет супер, который является ActionView :: AssetPaths # compute_public_path.В этом методе есть условие has_request?для rewrite_relative_url_root, как показано ниже:

def compute_public_path(source, dir, ext = nil, include_host = true, protocol = nil)
  ...
  source = rewrite_relative_url_root(source, relative_url_root) if has_request?
  ...
end

def relative_url_root
  config = controller.config if controller.respond_to?(:config)
  config ||= config.action_controller if config.action_controller.present?
  config ||= config
  config.relative_url_root
end

Если вы посмотрите на внутреннюю часть rewrite_relative_url_root, она полагается на наличие запроса и возможность получить его из переменной контроллера для разрешения относительного корня URL.Проблема в том, что когда sprockets разрешает эти ресурсы для sass, у него нет контроллера и, следовательно, нет запроса.

Приведенное выше решение не работало в режиме разработки для меня.Вот решение, которое я сейчас использую, чтобы оно заработало:

module Sass
  module Rails
    module Helpers
      protected
      def public_path(asset, kind)
        resolver = options[:custom][:resolver]
        asset_paths = resolver.context.asset_paths
        path = resolver.public_path(asset, kind.pluralize)
        if !asset_paths.send(:has_request?) && ENV['RAILS_RELATIVE_URL_ROOT']
          path = ENV['RAILS_RELATIVE_URL_ROOT'] + path
        end
        path
      end
    end
  end
end
...