Конвейер ресурсов Rails 3.1: как загрузить скрипты, специфичные для контроллера?

Если я сгенерирую новый контроллер в Rails 3.1, также автоматически будет добавлен файл javascript с именем контроллера. Во-первых, я думал, что этот файл javascript будет использоваться только при вызове соответствующего контроллера.

По умолчанию в application.js файле есть инструкция //= require_tree ., которая включает в себя каждый файл javascript в своем дереве.

Как я могу загрузить только скрипт, специфичный для контроллера?


person Mike Bevz    schedule 04.07.2011    source источник
comment
Возможно, это не очень хорошая идея. См. ответы на этот связанный вопрос: stackoverflow.com/questions/8250951/   -  person gdelfino    schedule 28.08.2012
comment
Напишите свой javascript так, чтобы он был специфичен для страницы, и тогда не беспокойтесь о том, что все смешано вместе. Если бы это был скомпилированный код, вы бы так и сделали, верно?   -  person Ziggy    schedule 30.03.2013
comment
Просмотр конкретного: stackoverflow.com/questions/6167805/   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 14.07.2014


Ответы (6)


Чтобы загрузить только необходимый файл name_of_the_js_file.js:

  1. удалить //=require_tree из application.js

  2. сохраните файл js (который вы хотите загрузить при загрузке определенной страницы) в конвейере активов

  3. добавить помощника в application_helper.rb

    def javascript(*files)
      content_for(:head) { javascript_include_tag(*files) }
    end
    
  4. yield в ваш макет:

    <%= yield(:head) %>
    
  5. добавьте это в свой файл просмотра:

    <% javascript 'name_of_the_js_file' %>
    

Тогда все должно быть в порядке

person Nguyen Chien Cong    schedule 22.07.2011
comment
Стоит отметить, что этот метод хорошо работает в продакшене. Например. если вы посмотрите на исходный код в рабочей среде, вы увидите, что файл javascript отдельного контроллера получает соответствующее имя для очистки кеша, как и основной файл application.js: ‹script src=/assets/mycontroller-923cef714b82e7dec46117f9aab7fb2c.js type=text /javascript›‹/script› - person cailinanne; 16.08.2011
comment
Да, потому что сам файл находится в конвейере ресурсов. Мы просто не хотим, чтобы это требовалось в application.js. - person Nguyen Chien Cong; 17.08.2011
comment
Похоже, это не работает в первом официальном выпуске Rails 3.1. - person Andrew Theis; 08.10.2011
comment
Можете быть более конкретными? может быть, я могу помочь. - person Nguyen Chien Cong; 10.10.2011
comment
не забудьте добавить в свой config/application.rb строку типа config.assets.precompile += %w(name_of_js_file.js), иначе у вас могут возникнуть проблемы с прекомпиляцией, как у меня. См. также jalada.co.uk/2012/01/23/ - person ZMorek; 25.02.2012
comment
у меня работает в rails 3.2.3 (требуется этот параметр config.assets.precompile, как указано ZMorek выше) - person Tatiana Tyu; 12.06.2012
comment
Вопрос новичка: что означает сохранение вашего js-файла, который вы хотите загрузить, когда определенная страница загружается в конвейер ресурсов? Чтобы иметь файл .js для конкретной страницы в app/assets/javascripts без ссылки на него в application.js? - person rassom; 13.09.2012
comment
Спасибо, @NguyenChienCong :) - person rassom; 18.09.2012
comment
Или вы можете просто сделать ‹%= stylesheet_link_tag params[:controller] %› или ‹%= javascript_include_tag params[:controller] %› в макете html.erb - person Ken W; 20.09.2012
comment
@Nguyen Chien Cong, не могли бы вы уточнить, что вы подразумеваете под доходностью в своем макете: - person Joe Essey; 23.04.2013
comment
@JoeEssey Под доходностью я имею в виду добавить строку ‹%= yield(:head) %› в ваш application.html.erb - person Nguyen Chien Cong; 23.04.2013
comment
Этот метод также работает для таблиц стилей. Поскольку я использовал кеширование в представлении страницы (но не в представлении макета), я решил добавить в макет вызов вспомогательного кода. Я определил, какой вид страницы использовать с помощью controller.action_name Пример: <% if controller.action_name == "index" stylesheet 'contacts_index' javascript 'contacts_index' end %> - person Scott Carter; 05.06.2013
comment
Хм. Пример лучшего формата: <% if controller.action_name == "index" then stylesheet 'contacts_index'; javascript 'contacts_index'; end %> - person Scott Carter; 05.06.2013
comment
Один компромисс, который вы делаете с этим подходом, заключается в том, что вы будете делать один дополнительный запрос для каждого включенного файла javascript. Кроме того, некоторые утверждают, что ваше смешивание javascript связано с представлениями html. - person Derrick Mar; 15.09.2015

Элегантным решением для этого является требование controller_name в вашем javascript_include_tag.

см. http://apidock.com/rails/ActionController/Metal/имя_контроллера/класс

<%= javascript_include_tag "application", controller_name %>

Controller_name.js будет загружен и также находится в активе, поэтому вы можете запросить другие файлы отсюда.

Например, рендеринг cars#index даст

<%= javascript_include_tag "application", "cars" %>

где cars.js может содержать

//= require wheel
//= require tyre

Наслаждаться !

person albandiguer    schedule 20.10.2011
comment
Хотя это очевидно после прочтения, решение не сразу пришло мне в голову. - person Andrew Burns; 23.02.2012
comment
Если у вас нет файла для каждого controller_name.js, вы можете столкнуться с некоторыми проблемами прекомпиляции и промахами кеша, особенно если вы явно не выполняете предварительную компиляцию каждого из них. - person ZMorek; 25.02.2012
comment
проблемы с предварительной компиляцией решаются настройкой config.assets.precompile — см. комментарий ZMorek для другого ответа на этот вопрос. - person Tatiana Tyu; 12.06.2012
comment
Лучше: ‹%= javascript_include_tag имя_контроллера, если путь_к_ресурсу(имя_контроллера) %› ‹%= тег_ссылки_стилей_имя_контроллера, медиа: все, если путь_к_ресурсу(имя_контроллера) %› - person Pencilcheck; 05.06.2013
comment
@Pencilcheck Ваше решение не работает. asset_path всегда возвращает путь, даже если файл не существует - person Alter Lagos; 13.09.2013

Я всегда включаю это в свои файлы макета. Он может расширить ваши js действия

<%= javascript_include_tag params[:controller] if AppName::Application.assets.find_asset("#{params[:controller]}.js") %>
<%= javascript_include_tag "#{params[:controller]}_#{params[:action]}"  if AppName::Application.assets.find_asset("#{params[:controller]}_#{params[:action]}.js") %>
person Le Duc Duy    schedule 22.10.2012
comment
Хотя мне это очень нравится, похоже, это не работает в производстве. - person janosrusiczki; 31.03.2013
comment
Лучшее решение, я полагаю. - person installero; 02.04.2013
comment
@kitsched - вам может потребоваться добавить все свои ресурсы в config.assets.precompile с помощью чего-то вроде stackoverflow.com/a/18992685/94668< /а> - person TomFuertes; 16.10.2013
comment
Спасибо, попробую. - person janosrusiczki; 16.10.2013
comment
Это лучшее решение для меня. И это работает на производстве. - person Jay; 25.04.2016

Вашу проблему можно решить по-разному.

Добавляйте активы динамически

Учтите, что это не очень хорошее решение для производственного режима, потому что особенности вашего контроллера не будут предварительно скомпилированы!

  1. Добавьте в помощник нашего приложения следующий метод:

    module ApplicationHelper
        def include_related_asset(asset)
        #          v-----{Change this}
            if !YourApp::Application.assets.find_asset(asset).nil?
                case asset.split('.')[-1]
                    when 'js'
                        javascript_include_tag asset
                    when 'css'
                        stylesheet_link_tag asset
                end
            end
        end
    end
    
  2. Вызовите вспомогательный метод в вашем layout-файле:

    <%= include_related_asset(params[:controller].to_param + '_' + params[:action].to_param . 'js') %>
    
  3. Создайте определенные активы для ваших действий контроллера. напр. controller_action.js

Не забудьте заменить YourApp на название вашего приложения.

Используйте yield

  1. Добавьте <%= yield :head%> в заголовок макета
  2. Включите свои активы из представлений действий:

    <% content_for :head do %>
    <%= javascript_include_tag 'controller_action' %>
    <% end %>
    

Дополнительную информацию см. в руководствах по Rails.

person Robin    schedule 25.06.2012

Мне нравится решение Альбандигера. С помощью которого я обнаружил, что активы javascript/coffeescript не предварительно компилируются индивидуально. Что вызывает всевозможные ошибки при попытке использовать javascript_path. Я поделюсь своим решением этой проблемы после того, как решу проблему, которую несколько человек упомянули в своих комментариях. В основном имеет дело только с частичным набором файлов контроллера с именами JavaScript.

Поэтому я создал помощник приложения, чтобы определить, существует ли файл в каталоге javascript независимо от расширения .coffee/.js:

module ApplicationHelper
  def javascript_asset_path(basename)
    Sprockets::Rails::Helper.assets.paths.select{|i|
      i =~ /javascript/ and i =~ /#{Rails.root}/
    }.each do |directory|
      if Dir.entries(directory).map {|i| i.split('.')[0]}.compact.
          include? basename
        return File.join(directory, basename)
      end
    end
    nil
  end
end

Этот метод вернет полный путь к файлу javascript, если он существует. В противном случае он возвращает ноль. Итак, следуя комментарию Pencilcheck, вы можете добавить этот метод для условного включения:

<%= javascript_include_tag(controller_name) if javascript_asset_path(controller_name) %>

И теперь у вас есть правильное условное включение. Теперь к вопросу о предварительно скомпилированных ассетах. Как правило, для оптимизации не требуется предварительная компиляция ресурсов по отдельности. Однако вы можете сделать это, если вам необходимо:

# Live Compilation
config.assets.compile = true

Вы можете добавить это в файл конфигурации вашей среды. Сначала протестируйте его в файле среды разработки. Опять же, это нецелесообразно. Конвейер ресурсов Rails использует Sprockets для оптимизации всего:

Sprockets загружает указанные файлы, при необходимости обрабатывает их, объединяет в один файл и затем сжимает (если Rails.application.config.assets.compress имеет значение true). Отдавая один файл, а не несколько, можно значительно сократить время загрузки страниц, поскольку браузер делает меньше запросов. Сжатие также уменьшает размер файлов, позволяя браузеру загружать их быстрее.

ПОЖАЛУЙСТА, ПРОЧТИТЕ документацию для получения дополнительной информации о механике http://guides.rubyonrails.org/asset_pipeline.html

Активы не компилируются индивидуально. Например, когда я пытаюсь:

<%= javascript_include_tag 'event' %>

Я получил:

Sprockets::Rails::Helper::AssetFilteredError: Ассет отфильтрован и не будет обслуживаться: добавьте Rails.application.config.assets.precompile += %w( event.js ) к config/initializers/assets.rb и перезапустите сервер

Таким образом, вы можете указать, какие активы должны быть предварительно скомпилированы по отдельности. Нам просто нужно добавить соответствующий контроллер с именем javascript files в наш инициализатор ресурсов. Ну, мы можем сделать это программно.

Чтобы получить список имен контроллеров, я буду использовать пример ecoologic:

all_controllers =  Dir[
    Rails.root.join('app/controllers/*_controller.rb')
  ].map { |path|
    path.match(/(\w+)_controller.rb/); $1
  }.compact

И теперь, чтобы получить имя всех файлов javascript, которые соответствуют базовому имени имени контроллера, вы можете использовать следующее:

javascripts_of_controllers = Sprockets::Rails::Helper.assets.paths.select{|a_path|
    a_path =~ /javascript/ and a_path =~ /#{Rails.root}/
  }.map {|a_path|
    Dir.entries(a_path)
  }.flatten.delete_if {|the_file|
    !the_file['.js']
  }.collect {|the_file|
    the_file if all_controllers.any? {|a_controller| the_file[a_controller]}
  }

Тогда вы можете попробовать:

# config/initializers/assets.rb
Rails.application.config.assets.precompile += javascripts_of_controllers

Это даст вам список всех файлов javascript без пути к каталогу, которые соответствуют имени вашего контроллера. Обратите внимание, что если имя вашего контроллера во множественном числе, имя javascript также должно быть множественным. Также обратите внимание, если контроллер в единственном числе, а файл javascript во множественном числе, он все равно будет включать его, потому что the_file[a_controller] будет успешным при частичном совпадении.

Не стесняйтесь попробовать это в настройках Rails.application.config.assets.precompile. Я знаю, что это правильно дает вам список файлов. Но я оставлю вас, чтобы проверить это. Дайте мне знать, если есть какие-либо нюансы, связанные с предварительной компиляцией таким образом, поскольку мне любопытно.

Очень подробное объяснение того, как выполняется предварительная компиляция ресурсов, см. в этом блоге: http://www.sitepoint.com/asset-precompile-works-part/

person 6ft Dan    schedule 05.11.2014
comment
где я должен разместить операторы all_controllers и javascripts_of_controllers? - person Konstantin Voronov; 29.06.2015
comment
Что ж. я поместил оба в свой инициализатор активов (assets.rb), но Sprockets::Rails::Helper.assets.paths был здесь нулевым, поэтому мне пришлось изменить его на Rails.application.config.assets.paths, остальное было в порядке. очень хорошее решение - person Konstantin Voronov; 29.06.2015

Недавно я нашел простой способ использовать сгенерированные скрипты для конкретного контроллера. Я использую для этого решения gem gon. Добавьте в контроллер:

class HomesController < ApplicationController
  before_filter :remember_controller

  private

  def remember_controller
    gon.controller = params[:controller]
  end
end

После этого откройте свой homes.js.cofee и добавьте в начало файла:

jQuery ->
  if gon.controller == "sermons"
    # Place all functions here...

Вот и все.

person ExiRe    schedule 09.08.2013