有没有办法将自定义文件夹添加到“部分路径”?

时间:2011-09-03 15:01:55

标签: ruby-on-rails partial-views

我在名为partials的文件夹中有几个部分,我使用render 'partials/name_of_my_partial'将它们渲染到我的视图中,这没关系。

无论如何,是否可以设置方式而不是使用render 'name_of_my_partial'并且rails自动检查此partials文件夹?

现在,我遇到Missing partial错误。

3 个答案:

答案 0 :(得分:10)

在rails 3.0中这是一个挑战,但在调查中我发现在rails 3.1中,它们改变了路径查找的工作方式,使其更加简单。 (我不知道他们改变了什么确切的版本,它可能早得多)。

在3.1中,这是相对简单的,因为它们引入了一种向路径查找发送多个前缀的方法。它们通过实例方法_prefixes检索。

对于所有控制器来说,通过简单地覆盖基本控制器(或者包含在基本控制器中的模块中,无论哪个)都可以很容易地使用任意前缀。

所以在3.1.x中(查找使用多个前缀):

class ApplicationController
  ...
  protected
  def _prefixes
    @_prefixes_with_partials ||= super | %w(partials)
  end
end 

在此更改之前,单个前缀用于查找,这使得这更加复杂。这可能不是最好的方法,但我过去解决了这个问题,试图通过我的“后备”前缀查找相同的路径来避免丢失模板错误。

在3.0.x中(其中lookup使用单个路径前缀)

# in an initializer
module YourAppPaths
  extend ActiveSupport::Concern

  included do
    # override the actionview base class method to wrap its paths with your
    # custom pathset class
    def self.process_view_paths(value)
      value.is_a?(::YourAppPaths::PathSet) ?
        value.dup : ::YourAppPaths::PathSet.new(Array.wrap(value))
    end
  end

  class PathSet < ::ActionView::PathSet
    # our simple subclass of pathset simply rescues and attempts to
    # find the same path under "partials", throwing out the original prefix
    def find(path, prefix, *args)
      super
    rescue ::ActionView::MissingTemplate
      super(path, "partials", *args)
    end
  end
end

ActionView::Base.end(:include, YourAppPaths)

答案 1 :(得分:2)

我还没有找到关于这个主题的其他资源,所以我在这里发布了我的努力。

Rails 3.2 + (也在4.2中测试)中,可以从控制器操作中访问和修改lookup_context.prefixes。因此,修改模板和模板的查找部分前缀包括您可以执行此操作的另一个路径:

class MyObjectsController < ApplicationController
  def show
    # WARNING: Keep reeding for issues with this approach!
    unless lookup_context.prefixes.first == "partials"
      lookup_context.prefixes.prepend "partials"
    end
  end
end

这样,如果 app / views / partials / 文件夹中有 show.html.erb 模板,则会呈现该模板。对于 show.html.erb 中引用的任何部分内容也是如此。

这里发生了什么?

方法lookup_context返回类型为ActionView::LookupContext的对象,该对象负责保存在ActionView中查找模板所需的所有信息。值得注意的是,它还允许您访问lookup_context.view_paths在此问题中要求的内容,但声音应该是这样。

警告

为所有将来的请求缓存lookup_context.prefixes数组的修改。因此,为了无故障地使用它,最好确保我们删除我们添加的任何前缀。

那么,有一种简单的方法吗?

不确定。对于我自己的项目,我已经创建了一个模块,我可以将其包含在需要此功能的任何控制器中(或者只是将其包含在ApplicationController中)。这是我的代码:

# Helper methods for adding ActionView::LookupContext prefixes on including
# controllers. The lookup_context.prefixes collection is persisted on cached
# controllers, so these helpers take care to remove the passed-in prefixes
# after calling the block.
#
# @example Expected Usage:
#   around_action only: :show do |_, block|
#     prepend_lookup_context_prefixes("my_optional_name_space/my_objects", &block)
#   end
#
#   around_action only: %i[index edit update] do |_, block|
#     append_penultimate_lookup_context_prefixes("my_optional_name_space/my_objects", &block)
#   end
module PrefixesHelper
  # Prepends the passed in prefixes to the current `lookup_context.prefixes`
  # array, calls the block, then removes the prefixes.
  #
  # @param [Array<String>] prefixes
  def prepend_lookup_context_prefixes(*prefixes, &block)
    lookup_context.prefixes.prepend(*prefixes)
    block.call
    remove_lookup_context_prefixes(*prefixes, index: 0)
  end

  # Sets the penultimate (2nd-to-last) prefixes in the current
  # `lookup_context.prefixes` array, calls the block, then removes the prefixes.
  #
  # @param [Array<String>] prefixes
  def append_penultimate_lookup_context_prefixes(*prefixes, &block)
    lookup_context.prefixes.insert(-2, *prefixes)
    block.call
    remove_lookup_context_prefixes(*prefixes.reverse, index: -2)
  end

  # Removes the passed in prefixes from the current `lookup_context.prefixes`
  # array. If index is passed in, then will only remove prefixes found at the
  # specified index in the array.
  #
  # @param [Array<String>] prefixes
  # @param [Integer] index
  def remove_lookup_context_prefixes(*prefixes, index: nil)
    prefixes.each do |prefix|
      if index
        if lookup_context.prefixes[index] == prefix
          lookup_context.prefixes.delete_at(index)
        end
      else
        lookup_context.prefixes.delete(prefix)
      end
    end
  end
end

如本模块中的注释所述,此模块的预期用法是通过控制器中的around_filter调用来调用其中包含的方法。这样,模块将在控制器操作产生后删除它添加的任何前缀。

例如:

around_filter only: :show do |_, block|
  prepend_lookup_context_prefixes("my_optional_name_space/my_objects", &block)
end

或者:

around_filter only: %i[index edit update] do |_, block|
  append_penultimate_lookup_context_prefixes("my_optional_name_space/my_objects", &block)
end

我还将此处发布的PrefixesHelper模块包含在一个gem中,我用它来为我的Rails应用添加一些不错的扩展名。如果您也想使用它,请参阅此处:https://github.com/pdobb/core_extensions

答案 2 :(得分:0)

注意:由于一些奇怪的行为以及与multi_fetch_fragments宝石不兼容,我已放弃了这种方法

如果您想为STI集合添加局部元素前缀,我会提供以下猴子补丁(用于Rails 3.2,Ruby 2.2 +)。

config / initializers / partial_renderer_prefix.rb

module ActionView
  class PartialRenderer
    def merge_prefix_into_object_path(prefix, object_path)
      # Begin monkey patch
      if @options.key?(:prefix)
        prefixes = [@options[:prefix]]
        return (prefixes << object_path).join("/")
      end

      # End monkey patch
      if prefix.include?(?/) && object_path.include?(?/)
        prefixes = []
        prefix_array = File.dirname(prefix).split('/')
        object_path_array = object_path.split('/')[0..-3] # skip model dir & partial

        prefix_array.each_with_index do |dir, index|
          break if dir == object_path_array[index]
          prefixes << dir
        end

        (prefixes << object_path).join("/")
      else
        object_path
      end
    end
  end
end

在视图中,像这样使用它:

<%= render @order_items, prefix: 'orders/timeline' %>

每个STI模型都有app/views/orders/timeline/product_order_items/_product_order_item.html.erbapp/views/orders/timeline/subscription_order_items/_subscription_order_item.html.erb零件的地方。