Rails FileSystemResolver - Реализация пользовательского наследования шаблонов
Если у кого-то есть лучший способ добиться того же желаемого эффекта с другой структурой, я совершенно счастлив отказаться от этого дизайна.
Моя мотивация заключается в том, чтобы создать унаследованную систему шаблонов виджетов JavaScript для использования в Rails 3. Я хочу, чтобы виджеты JavaScript могли размещать пользователи на своей странице и иметь различные предопределенные шаблоны, из которых они могут выбирать.
В идеале каждый шаблон должен иметь "базовый" код JavaScript, и любые настройки могут быть наложены на несколько дочерних шаблонов (таким образом, создавая скин / новые функциональные возможности для шаблона).
Это сложно, потому что я хочу использовать несколько контроллеров для одного и того же шаблона.
Контроллеры
Дерево папок- приложение
- контроллеры
- виджеты
- модули
- деятельность
- содержание
- изображений
- сообщества
- модули
- деятельность
- содержание
- изображений
- пользователи
- модули
- деятельность
- содержание
- изображений
- виджеты
- контроллеры
Просмотры
Просто заметка о представлениях, в каждом каталоге "template" я разместил файл с именем config.rb. Этот файл определяет имя родительского шаблона и расположение папки. Я покажу, как это загружается в распознавателе шаблонов.
Примеры файлов конфигурации наследования
находится в app / views / widgets / community /modules/template_custom_2/config.rb
module InheritedTemplateConfig
def inherited_parent_base_path
"widgets/communities/modules"
end
def inherited_parent_template_name
"template_custom_1"
end
end
находится в app / views / widgets / community /modules/template_custom_1/config.rb
module InheritedTemplateConfig
def inherited_parent_base_path
"widgets/communities/modules"
end
def inherited_parent_template_name
"template"
end
end
находится в app / views / widgets / community /modules/template/config.rb
module InheritedTemplateConfig
def inherited_parent_base_path
"widgets/communities"
end
def inherited_parent_template_name
"template"
end
end
Примеры шаблонов файловнаходится в app / views / widgets / community /modules/template_custom_2/modules.js.erb
<%= render :partial => "(TEMPLATE)/same_template_test" %>
<%= render :partial => "(PARENT_TEMPLATE)/parent_template_test" %>
Дерево папок
- приложение
- Просмотры
- виджеты
- модули
- шаблон
- template_custom_1
- template_custom_2
- деятельность
- шаблон
- содержание
- шаблон
- изображений
- шаблон
- сообщества
- шаблон
- модули
- шаблон
- template_custom_a
- template_custom_b
- деятельность
- шаблон
- содержание
- шаблон
- изображений
- шаблон
- пользователи
- шаблон
- модули
- шаблон
- деятельность
- шаблон
- содержание
- шаблон
- изображений
- шаблон
- модули
- виджеты
- Просмотры
Итак, несколько примеров путей и где я ожидаю, что цепочка шаблонов папок разрешится.
- /widgets/modules.js
- / Виджеты / модули / шаблон
- /widgets/communities/1/modules.js?template=custom_a
- / Виджеты / сообщества / модули / template_custom_a
- / Виджеты / сообщества / модули / шаблон
- / Виджеты / модули / шаблон
Моя неудачная реализация
Сразу хочу сказать, это так близко к работе. К сожалению, мне не повезло в случае с функцией резолвера (PARENT_TEMPLATE) find_template, она повторяется бесконечно. Это потому, что у меня нет возможности узнать, какой шаблон пропустить при разрешении.
Единственное, что не работает, это рендеринг частичного использования <% render :partial => "(PARENT_TEMPLATE)/my_file" %>
class Widgets::Communities::ModulesController < ApplicationController
before_filter do
@community = Community.find(params[:community_id])
@template = params[:template].to_s
@template = "_#{@template}" if !@template.empty?
# Path to start searching for template files
prepend_view_path WidgetResolver.new("widgets/communities/modules", "template#{@template}")
end
def script
respond_to do |format|
format.js { render "modules" }
end
end
end
# Walks through from the sub_path to the base_path to find the template file
# This allows you to base one template on another template
class WigetResolver < ActionView::FileSystemResolver
attr_accessor :templates, :base_path
# Loads our current templates config, and any parent templates config
def load_template_config(base_path, template_name)
config_path = "#{Rails.root}/app/views/#{base_path}/#{template_name}/config.rb"
if File.exists?(config_path)
self.templates << {:template_name => template_name, :base_path => base_path} # This is a valid template, add it to our search path
load(config_path)
extend InheritedTemplateConfig
if !self.inherited_parent_base_path.nil? && !self.inherited_parent_template_name.nil?
self.load_template_config(self.inherited_parent_base_path, self.inherited_parent_template_name)
end
end
end
def initialize(base_path, template_name)
self.templates = []
# From our base config, load our complete config
self.load_template_config(base_path, template_name)
super("app/views")
end
def find_templates(name, prefix, partial, details)
# We want to use our custom template matching
if prefix.match(/^(TEMPLATE)/)
self.templates.each_with_index { |template,i|
result = super(name, "#{template[:base_path]}/#{template[:template_name]}", partial, details)
# We found the custom template path
return result if result.present?
}
# ----------------------------------------------------
# ERROR HERE - recurses to max depth
# ----------------------------------------------------
elsif prefix.match(/^(PARENT_TEMPLATE)/)
self.templates.each_with_index { |template,i|
# We need to skip template above the current partials template file path - HOW DO WE DO THIS?!
result = super(name, "#{template[:base_path]}/#{template[:template_name]}", partial, details)
# We found the custom template path
return result if result.present?
}
end
super(name, prefix, partial, details)
end
end
Итак - вопрос. Как я могу узнать, что текущий рабочий каталог partials находится в find_templates? Если бы я знал это, я мог бы пропустить все шаблоны над ним и никогда не получить бесконечную рекурсивность.