actionpack 3.0.0.beta → 3.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (118) hide show
  1. data/CHANGELOG +291 -260
  2. data/lib/abstract_controller.rb +5 -2
  3. data/lib/abstract_controller/assigns.rb +21 -0
  4. data/lib/abstract_controller/base.rb +13 -5
  5. data/lib/abstract_controller/collector.rb +2 -0
  6. data/lib/abstract_controller/helpers.rb +4 -14
  7. data/lib/abstract_controller/layouts.rb +50 -99
  8. data/lib/abstract_controller/logger.rb +2 -2
  9. data/lib/abstract_controller/rendering.rb +105 -173
  10. data/lib/abstract_controller/view_paths.rb +69 -0
  11. data/lib/action_controller.rb +1 -2
  12. data/lib/action_controller/base.rb +10 -32
  13. data/lib/action_controller/caching.rb +19 -18
  14. data/lib/action_controller/caching/actions.rb +17 -11
  15. data/lib/action_controller/caching/fragments.rb +5 -17
  16. data/lib/action_controller/caching/pages.rb +24 -24
  17. data/lib/action_controller/caching/sweeping.rb +1 -3
  18. data/lib/action_controller/deprecated.rb +0 -2
  19. data/lib/action_controller/deprecated/base.rb +143 -0
  20. data/lib/action_controller/metal.rb +29 -26
  21. data/lib/action_controller/metal/compatibility.rb +18 -87
  22. data/lib/action_controller/metal/cookies.rb +0 -1
  23. data/lib/action_controller/metal/head.rb +1 -0
  24. data/lib/action_controller/metal/helpers.rb +2 -2
  25. data/lib/action_controller/metal/hide_actions.rb +4 -6
  26. data/lib/action_controller/metal/http_authentication.rb +18 -33
  27. data/lib/action_controller/metal/implicit_render.rb +21 -0
  28. data/lib/action_controller/metal/instrumentation.rb +1 -1
  29. data/lib/action_controller/metal/mime_responds.rb +2 -1
  30. data/lib/action_controller/metal/rack_delegation.rb +3 -8
  31. data/lib/action_controller/metal/redirecting.rb +2 -1
  32. data/lib/action_controller/metal/renderers.rb +4 -2
  33. data/lib/action_controller/metal/rendering.rb +31 -44
  34. data/lib/action_controller/metal/request_forgery_protection.rb +41 -4
  35. data/lib/action_controller/metal/responder.rb +2 -0
  36. data/lib/action_controller/metal/session_management.rb +0 -36
  37. data/lib/action_controller/metal/streaming.rb +20 -47
  38. data/lib/action_controller/metal/testing.rb +0 -1
  39. data/lib/action_controller/metal/url_for.rb +11 -148
  40. data/lib/action_controller/middleware.rb +2 -1
  41. data/lib/action_controller/polymorphic_routes.rb +1 -2
  42. data/lib/action_controller/railtie.rb +63 -10
  43. data/lib/action_controller/railties/{subscriber.rb → log_subscriber.rb} +5 -12
  44. data/lib/action_controller/railties/url_helpers.rb +14 -0
  45. data/lib/action_controller/record_identifier.rb +20 -1
  46. data/lib/action_controller/test_case.rb +123 -12
  47. data/lib/action_dispatch.rb +1 -0
  48. data/lib/action_dispatch/http/cache.rb +20 -3
  49. data/lib/action_dispatch/http/filter_parameters.rb +40 -25
  50. data/lib/action_dispatch/http/mime_negotiation.rb +6 -17
  51. data/lib/action_dispatch/http/mime_type.rb +2 -7
  52. data/lib/action_dispatch/http/request.rb +12 -33
  53. data/lib/action_dispatch/http/response.rb +35 -15
  54. data/lib/action_dispatch/http/upload.rb +2 -0
  55. data/lib/action_dispatch/http/url.rb +5 -32
  56. data/lib/action_dispatch/middleware/callbacks.rb +1 -1
  57. data/lib/action_dispatch/middleware/cookies.rb +4 -3
  58. data/lib/action_dispatch/middleware/params_parser.rb +4 -3
  59. data/lib/action_dispatch/middleware/remote_ip.rb +51 -0
  60. data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -0
  61. data/lib/action_dispatch/middleware/session/cookie_store.rb +6 -8
  62. data/lib/action_dispatch/middleware/show_exceptions.rb +0 -14
  63. data/lib/action_dispatch/middleware/stack.rb +6 -2
  64. data/lib/action_dispatch/railtie.rb +3 -1
  65. data/lib/action_dispatch/routing.rb +2 -0
  66. data/lib/action_dispatch/routing/deprecated_mapper.rb +35 -7
  67. data/lib/action_dispatch/routing/mapper.rb +134 -48
  68. data/lib/action_dispatch/routing/route.rb +2 -2
  69. data/lib/action_dispatch/routing/route_set.rb +217 -158
  70. data/lib/action_dispatch/routing/url_for.rb +139 -0
  71. data/lib/action_dispatch/testing/assertions/response.rb +14 -61
  72. data/lib/action_dispatch/testing/assertions/routing.rb +25 -14
  73. data/lib/action_dispatch/testing/integration.rb +32 -50
  74. data/lib/action_dispatch/testing/performance_test.rb +3 -1
  75. data/lib/action_dispatch/testing/test_process.rb +2 -0
  76. data/lib/action_dispatch/testing/test_request.rb +2 -0
  77. data/lib/action_pack/version.rb +4 -3
  78. data/lib/action_view.rb +11 -6
  79. data/lib/action_view/base.rb +33 -121
  80. data/lib/action_view/context.rb +0 -2
  81. data/lib/action_view/helpers.rb +26 -23
  82. data/lib/action_view/helpers/active_model_helper.rb +28 -18
  83. data/lib/action_view/helpers/asset_tag_helper.rb +109 -54
  84. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  85. data/lib/action_view/helpers/cache_helper.rb +22 -1
  86. data/lib/action_view/helpers/capture_helper.rb +22 -22
  87. data/lib/action_view/helpers/date_helper.rb +6 -5
  88. data/lib/action_view/helpers/form_helper.rb +78 -63
  89. data/lib/action_view/helpers/form_options_helper.rb +6 -4
  90. data/lib/action_view/helpers/form_tag_helper.rb +26 -15
  91. data/lib/action_view/helpers/javascript_helper.rb +90 -10
  92. data/lib/action_view/helpers/number_helper.rb +315 -118
  93. data/lib/action_view/helpers/prototype_helper.rb +19 -46
  94. data/lib/action_view/helpers/record_tag_helper.rb +4 -4
  95. data/lib/action_view/helpers/tag_helper.rb +7 -24
  96. data/lib/action_view/helpers/text_helper.rb +8 -7
  97. data/lib/action_view/helpers/translation_helper.rb +7 -5
  98. data/lib/action_view/helpers/url_helper.rb +19 -16
  99. data/lib/action_view/locale/en.yml +45 -6
  100. data/lib/action_view/lookup_context.rb +190 -0
  101. data/lib/action_view/paths.rb +22 -63
  102. data/lib/action_view/railtie.rb +14 -4
  103. data/lib/action_view/railties/{subscriber.rb → log_subscriber.rb} +1 -1
  104. data/lib/action_view/render/layouts.rb +73 -0
  105. data/lib/action_view/render/partials.rb +15 -41
  106. data/lib/action_view/render/rendering.rb +27 -78
  107. data/lib/action_view/template.rb +20 -24
  108. data/lib/action_view/template/error.rb +22 -2
  109. data/lib/action_view/template/handlers/erb.rb +33 -9
  110. data/lib/action_view/template/handlers/rjs.rb +1 -2
  111. data/lib/action_view/template/resolver.rb +46 -104
  112. data/lib/action_view/template/text.rb +5 -12
  113. data/lib/action_view/test_case.rb +14 -23
  114. metadata +83 -40
  115. data/lib/abstract_controller/compatibility.rb +0 -18
  116. data/lib/abstract_controller/localized_cache.rb +0 -49
  117. data/lib/action_controller/metal/configuration.rb +0 -28
  118. data/lib/action_controller/url_rewriter.rb +0 -76
@@ -12,30 +12,17 @@ module ActionView
12
12
  #
13
13
  # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
14
14
  # as the locals hash.
15
- def render(options = {}, locals = {}, &block) #:nodoc:
15
+ def render(options = {}, locals = {}, &block)
16
16
  case options
17
17
  when Hash
18
- layout = options[:layout]
19
- options[:locals] ||= {}
20
-
21
18
  if block_given?
22
- return concat(_render_partial(options.merge(:partial => layout), &block))
19
+ _render_partial(options.merge(:partial => options[:layout]), &block)
23
20
  elsif options.key?(:partial)
24
- return _render_partial(options)
25
- end
26
-
27
- template = if options[:file]
28
- find(options[:file], {:formats => formats})
29
- elsif options[:inline]
30
- handler = Template.handler_class_for_extension(options[:type] || "erb")
31
- Template.new(options[:inline], "inline template", handler, {})
32
- elsif options[:text]
33
- Template::Text.new(options[:text])
34
- end
35
-
36
- if template
37
- layout = find(layout, {:formats => formats}) if layout
38
- _render_template(template, layout, :locals => options[:locals])
21
+ _render_partial(options)
22
+ else
23
+ template = _determine_template(options)
24
+ lookup_context.freeze_formats(template.formats, true)
25
+ _render_template(template, options[:layout], options)
39
26
  end
40
27
  when :update
41
28
  update_page(&block)
@@ -44,74 +31,36 @@ module ActionView
44
31
  end
45
32
  end
46
33
 
47
- # You can think of a layout as a method that is called with a block. _layout_for
48
- # returns the contents that are yielded to the layout. If the user calls yield
49
- # :some_name, the block, by default, returns content_for(:some_name). If the user
50
- # calls yield, the default block returns content_for(:layout).
51
- #
52
- # The user can override this default by passing a block to the layout.
53
- #
54
- # ==== Example
55
- #
56
- # # The template
57
- # <% render :layout => "my_layout" do %>Content<% end %>
58
- #
59
- # # The layout
60
- # <html><% yield %></html>
61
- #
62
- # In this case, instead of the default block, which would return content_for(:layout),
63
- # this method returns the block that was passed in to render layout, and the response
64
- # would be <html>Content</html>.
65
- #
66
- # Finally, the block can take block arguments, which can be passed in by yield.
67
- #
68
- # ==== Example
69
- #
70
- # # The template
71
- # <% render :layout => "my_layout" do |customer| %>Hello <%= customer.name %><% end %>
72
- #
73
- # # The layout
74
- # <html><% yield Struct.new(:name).new("David") %></html>
75
- #
76
- # In this case, the layout would receive the block passed into <tt>render :layout</tt>,
77
- # and the Struct specified in the layout would be passed into the block. The result
78
- # would be <html>Hello David</html>.
79
- def _layout_for(name = nil, &block)
80
- return @_content_for[name || :layout] if !block_given? || name
81
- capture(&block)
82
- end
83
-
84
- # This is the API to render a ViewContext's template from a controller.
85
- #
86
- # Internal Options:
87
- # _template:: The Template object to render
88
- # _layout:: The layout, if any, to wrap the Template in
89
- def render_template(options)
90
- _evaluate_assigns_and_ivars
91
- template, layout = options.values_at(:_template, :_layout)
92
- _render_template(template, layout, options)
34
+ # Determine the template to be rendered using the given options.
35
+ def _determine_template(options) #:nodoc:
36
+ if options.key?(:inline)
37
+ handler = Template.handler_class_for_extension(options[:type] || "erb")
38
+ Template.new(options[:inline], "inline template", handler, {})
39
+ elsif options.key?(:text)
40
+ Template::Text.new(options[:text], formats.try(:first))
41
+ elsif options.key?(:file)
42
+ with_fallbacks { find_template(options[:file], options[:prefix]) }
43
+ elsif options.key?(:template)
44
+ options[:template].respond_to?(:render) ?
45
+ options[:template] : find_template(options[:template], options[:prefix])
46
+ end
93
47
  end
94
48
 
95
- def _render_template(template, layout = nil, options = {})
49
+ # Renders the given template. An string representing the layout can be
50
+ # supplied as well.
51
+ def _render_template(template, layout = nil, options = {}) #:nodoc:
96
52
  locals = options[:locals] || {}
53
+ layout = find_layout(layout) if layout
97
54
 
98
55
  ActiveSupport::Notifications.instrument("action_view.render_template",
99
- :identifier => template.identifier, :layout => layout.try(:identifier)) do
56
+ :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
100
57
 
101
- content = template.render(self, locals)
58
+ content = template.render(self, locals) { |*name| _layout_for(*name) }
102
59
  @_content_for[:layout] = content
103
60
 
104
- if layout
105
- @_layout = layout.identifier
106
- content = _render_layout(layout, locals)
107
- end
108
-
61
+ content = _render_layout(layout, locals) if layout
109
62
  content
110
63
  end
111
64
  end
112
-
113
- def _render_layout(layout, locals, &block)
114
- layout.render(self, locals){ |*name| _layout_for(*name, &block) }
115
- end
116
65
  end
117
66
  end
@@ -1,8 +1,7 @@
1
1
  # encoding: utf-8
2
2
  # This is so that templates compiled in this file are UTF-8
3
-
4
- require 'set'
5
- require "action_view/template/resolver"
3
+ require 'active_support/core_ext/array/wrap'
4
+ require 'active_support/core_ext/object/blank'
6
5
 
7
6
  module ActionView
8
7
  class Template
@@ -16,28 +15,28 @@ module ActionView
16
15
  end
17
16
 
18
17
  extend Template::Handlers
19
- attr_reader :source, :identifier, :handler, :mime_type, :formats, :details
18
+
19
+ attr_reader :source, :identifier, :handler, :virtual_path, :formats
20
20
 
21
21
  def initialize(source, identifier, handler, details)
22
22
  @source = source
23
23
  @identifier = identifier
24
24
  @handler = handler
25
- @details = details
25
+
26
+ @virtual_path = details[:virtual_path]
26
27
  @method_names = {}
27
28
 
28
- format = details.delete(:format) || begin
29
- # TODO: Clean this up
30
- handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html"
31
- end
32
- @mime_type = Mime::Type.lookup_by_extension(format.to_s)
33
- @formats = [format.to_sym]
34
- @formats << :html if format == :js
35
- @details[:formats] = Array.wrap(format.to_sym)
29
+ format = details[:format] || :html
30
+ @formats = Array.wrap(format).map(&:to_sym)
36
31
  end
37
32
 
38
33
  def render(view, locals, &block)
39
- method_name = compile(locals, view)
40
- view.send(method_name, locals, &block)
34
+ # Notice that we use a bang in this instrumentation because you don't want to
35
+ # consume this in production. This is only slow if it's being listened to.
36
+ ActiveSupport::Notifications.instrument("action_view.render_template!", :virtual_path => @virtual_path) do
37
+ method_name = compile(locals, view)
38
+ view.send(method_name, locals, &block)
39
+ end
41
40
  rescue Exception => e
42
41
  if e.is_a?(Template::Error)
43
42
  e.sub_template_of(self)
@@ -47,21 +46,18 @@ module ActionView
47
46
  end
48
47
  end
49
48
 
50
- # TODO: Figure out how to abstract this
49
+ def mime_type
50
+ @mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
51
+ end
52
+
51
53
  def variable_name
52
- @variable_name ||= identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym
54
+ @variable_name ||= @virtual_path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
53
55
  end
54
56
 
55
- # TODO: Figure out how to abstract this
56
57
  def counter_name
57
58
  @counter_name ||= "#{variable_name}_counter".to_sym
58
59
  end
59
60
 
60
- # TODO: kill hax
61
- def partial?
62
- @details[:partial]
63
- end
64
-
65
61
  def inspect
66
62
  if defined?(Rails.root)
67
63
  identifier.sub("#{Rails.root}/", '')
@@ -87,7 +83,7 @@ module ActionView
87
83
 
88
84
  source = <<-end_src
89
85
  def #{method_name}(local_assigns)
90
- _old_virtual_path, @_virtual_path = @_virtual_path, #{@details[:virtual_path].inspect};_old_output_buffer = output_buffer;#{locals_code};#{code}
86
+ _old_virtual_path, @_virtual_path = @_virtual_path, #{@virtual_path.inspect};_old_output_buffer = output_buffer;#{locals_code};#{code}
91
87
  ensure
92
88
  @_virtual_path, self.output_buffer = _old_virtual_path, _old_output_buffer
93
89
  end
@@ -1,6 +1,26 @@
1
1
  require "active_support/core_ext/enumerable"
2
2
 
3
3
  module ActionView
4
+ class ActionViewError < StandardError #:nodoc:
5
+ end
6
+
7
+ class MissingTemplate < ActionViewError #:nodoc:
8
+ attr_reader :path
9
+
10
+ def initialize(paths, path, details, partial)
11
+ @path = path
12
+ display_paths = paths.compact.map{ |p| p.to_s.inspect }.join(", ")
13
+ template_type = if partial
14
+ "partial"
15
+ elsif path =~ /layouts/i
16
+ 'layout'
17
+ else
18
+ 'template'
19
+ end
20
+
21
+ super("Missing #{template_type} #{path} with #{details.inspect} in view paths #{display_paths}")
22
+ end
23
+ end
4
24
  class Template
5
25
  # The Template::Error exception is raised when the compilation of the template fails. This exception then gathers a
6
26
  # bunch of intimate details and uses it to report a very precise exception message.
@@ -73,11 +93,11 @@ module ActionView
73
93
  end
74
94
 
75
95
  def to_s
76
- "\n#{self.class} (#{message}) #{source_location}:\n" +
96
+ "\n#{self.class} (#{message}) #{source_location}:\n" +
77
97
  "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
78
98
  end
79
99
 
80
- # don't do anything nontrivial here. Any raised exception from here becomes fatal
100
+ # don't do anything nontrivial here. Any raised exception from here becomes fatal
81
101
  # (and can't be rescued).
82
102
  def backtrace
83
103
  @backtrace
@@ -3,10 +3,24 @@ require 'active_support/core_ext/string/output_safety'
3
3
  require 'erubis'
4
4
 
5
5
  module ActionView
6
+ class OutputBuffer < ActiveSupport::SafeBuffer
7
+ def <<(value)
8
+ super(value.to_s)
9
+ end
10
+ alias :append= :<<
11
+
12
+ def append_if_string=(value)
13
+ if value.is_a?(String) && !value.is_a?(NonConcattingString)
14
+ ActiveSupport::Deprecation.warn("<% %> style block helpers are deprecated. Please use <%= %>", caller)
15
+ self << value
16
+ end
17
+ end
18
+ end
19
+
6
20
  module Template::Handlers
7
21
  class Erubis < ::Erubis::Eruby
8
22
  def add_preamble(src)
9
- src << "@output_buffer = ActiveSupport::SafeBuffer.new;"
23
+ src << "@output_buffer = ActionView::OutputBuffer.new;"
10
24
  end
11
25
 
12
26
  def add_text(src, text)
@@ -14,16 +28,26 @@ module ActionView
14
28
  src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
15
29
  end
16
30
 
31
+ BLOCK_EXPR = /(do|\{)(\s*\|[^|]*\|)?\s*\Z/
32
+
17
33
  def add_expr_literal(src, code)
18
- if code =~ /\s*raw\s+(.*)/
19
- src << "@output_buffer.safe_concat((" << $1 << ").to_s);"
34
+ if code =~ BLOCK_EXPR
35
+ src << '@output_buffer.append= ' << code
36
+ else
37
+ src << '@output_buffer.append= (' << code << ');'
38
+ end
39
+ end
40
+
41
+ def add_stmt(src, code)
42
+ if code =~ BLOCK_EXPR
43
+ src << '@output_buffer.append_if_string= ' << code
20
44
  else
21
- src << '@output_buffer << ((' << code << ').to_s);'
45
+ super
22
46
  end
23
47
  end
24
48
 
25
49
  def add_expr_escaped(src, code)
26
- src << '@output_buffer << ' << escaped_expr(code) << ';'
50
+ src << '@output_buffer.append= ' << escaped_expr(code) << ';'
27
51
  end
28
52
 
29
53
  def add_postamble(src)
@@ -42,14 +66,14 @@ module ActionView
42
66
  self.erb_trim_mode = '-'
43
67
 
44
68
  self.default_format = Mime::HTML
45
-
46
- cattr_accessor :erubis_implementation
47
- self.erubis_implementation = Erubis
69
+
70
+ cattr_accessor :erb_implementation
71
+ self.erb_implementation = Erubis
48
72
 
49
73
  def compile(template)
50
74
  source = template.source.gsub(/\A(<%(#.*coding[:=]\s*(\S+)\s*)-?%>)\s*\n?/, '')
51
75
  erb = "<% __in_erb_template=true %>#{source}"
52
- result = self.class.erubis_implementation.new(erb, :trim=>(self.class.erb_trim_mode == "-")).src
76
+ result = self.class.erb_implementation.new(erb, :trim=>(self.class.erb_trim_mode == "-")).src
53
77
  result = "#{$2}\n#{result}" if $2
54
78
  result
55
79
  end
@@ -6,8 +6,7 @@ module ActionView
6
6
  self.default_format = Mime::JS
7
7
 
8
8
  def compile(template)
9
- "controller.response.content_type ||= Mime::JS;" +
10
- "update_page do |page|;#{template.source}\nend"
9
+ "update_page do |page|;#{template.source}\nend"
11
10
  end
12
11
 
13
12
  def default_format
@@ -1,164 +1,106 @@
1
1
  require "pathname"
2
2
  require "active_support/core_ext/class"
3
- require "active_support/core_ext/array/wrap"
4
3
  require "action_view/template"
5
4
 
6
5
  module ActionView
7
- # Abstract superclass
8
6
  class Resolver
9
-
10
- class_inheritable_accessor(:registered_details)
11
- self.registered_details = {}
12
-
13
- def self.register_detail(name, options = {})
14
- registered_details[name] = lambda do |val|
15
- val = Array.wrap(val || yield)
16
- val |= [nil] unless options[:allow_nil] == false
17
- val
18
- end
19
- end
20
-
21
- register_detail(:locale) { [I18n.locale] }
22
- register_detail(:formats) { Mime::SET.symbols }
23
- register_detail(:handlers, :allow_nil => false) do
24
- Template::Handlers.extensions
25
- end
26
-
27
- def initialize(options = {})
28
- @cache = options[:cache]
29
- @cached = {}
7
+ def initialize
8
+ @cached = Hash.new { |h1,k1| h1[k1] =
9
+ Hash.new { |h2,k2| h2[k2] = Hash.new { |h3, k3| h3[k3] = {} } } }
30
10
  end
31
11
 
32
- # Normalizes the arguments and passes it on to find_template
33
- def find(*args)
34
- find_all(*args).first
12
+ def clear_cache
13
+ @cached.clear
35
14
  end
36
15
 
37
- def find_all(name, details = {}, prefix = nil, partial = nil)
38
- details = normalize_details(details)
39
- name, prefix = normalize_name(name, prefix)
40
-
41
- cached([name, details, prefix, partial]) do
42
- find_templates(name, details, prefix, partial)
16
+ # Normalizes the arguments and passes it on to find_template.
17
+ def find_all(name, prefix=nil, partial=false, details={}, key=nil)
18
+ cached(key, prefix, name, partial) do
19
+ find_templates(name, prefix, partial, details)
43
20
  end
44
21
  end
45
22
 
46
23
  private
47
24
 
25
+ def caching?
26
+ @caching ||= !defined?(Rails.application) || Rails.application.config.cache_classes
27
+ end
28
+
48
29
  # This is what child classes implement. No defaults are needed
49
30
  # because Resolver guarantees that the arguments are present and
50
31
  # normalized.
51
- def find_templates(name, details, prefix, partial)
32
+ def find_templates(name, prefix, partial, details)
52
33
  raise NotImplementedError
53
34
  end
54
35
 
55
- def normalize_details(details)
56
- details = details.dup
57
- # TODO: Refactor this concern out of the resolver
58
- details.delete(:formats) if details[:formats] == [:"*/*"]
59
- registered_details.each do |k, v|
60
- details[k] = v.call(details[k])
61
- end
62
- details
63
- end
64
-
65
- # Support legacy foo.erb names even though we now ignore .erb
66
- # as well as incorrectly putting part of the path in the template
67
- # name instead of the prefix.
68
- def normalize_name(name, prefix)
69
- handlers = Template::Handlers.extensions.join('|')
70
- name = name.to_s.gsub(/\.(?:#{handlers})$/, '')
71
-
72
- parts = name.split('/')
73
- return parts.pop, [prefix, *parts].compact.join("/")
74
- end
75
-
76
- def cached(key)
77
- return yield unless @cache
78
- return @cached[key] if @cached.key?(key)
79
- @cached[key] = yield
36
+ def cached(key, prefix, name, partial)
37
+ return yield unless key && caching?
38
+ @cached[key][prefix][name][partial] ||= yield
80
39
  end
81
40
  end
82
41
 
83
42
  class PathResolver < Resolver
84
-
85
43
  EXTENSION_ORDER = [:locale, :formats, :handlers]
86
44
 
87
45
  def to_s
88
46
  @path.to_s
89
47
  end
90
- alias to_path to_s
91
-
92
- def find_templates(name, details, prefix, partial)
93
- path = build_path(name, details, prefix, partial)
94
- query(path, EXTENSION_ORDER.map { |ext| details[ext] })
95
- end
48
+ alias :to_path :to_s
96
49
 
97
50
  private
98
51
 
99
- def build_path(name, details, prefix, partial)
52
+ def find_templates(name, prefix, partial, details)
53
+ path = build_path(name, prefix, partial, details)
54
+ query(path, EXTENSION_ORDER.map { |ext| details[ext] }, details[:formats])
55
+ end
56
+
57
+ def build_path(name, prefix, partial, details)
100
58
  path = ""
101
59
  path << "#{prefix}/" unless prefix.empty?
102
60
  path << (partial ? "_#{name}" : name)
103
61
  path
104
62
  end
105
63
 
106
- def query(path, exts)
64
+ def query(path, exts, formats)
107
65
  query = File.join(@path, path)
66
+
108
67
  exts.each do |ext|
109
- query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << '}'
68
+ query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << ',}'
110
69
  end
111
70
 
112
71
  Dir[query].reject { |p| File.directory?(p) }.map do |p|
113
- Template.new(File.read(p), File.expand_path(p), *path_to_details(p))
72
+ handler, format = extract_handler_and_format(p, formats)
73
+ Template.new(File.read(p), File.expand_path(p), handler,
74
+ :virtual_path => path, :format => format)
114
75
  end
115
76
  end
116
77
 
117
- # # TODO: fix me
118
- # # :api: plugin
119
- def path_to_details(path)
120
- # [:erb, :format => :html, :locale => :en, :partial => true/false]
121
- if m = path.match(%r'((^|.*/)(_)?[\w-]+)((?:\.[\w-]+)*)\.(\w+)$')
122
- partial = m[3] == '_'
123
- details = (m[4]||"").split('.').reject { |e| e.empty? }
124
- handler = Template.handler_class_for_extension(m[5])
78
+ # Extract handler and formats from path. If a format cannot be a found neither
79
+ # from the path, or the handler, we should return the array of formats given
80
+ # to the resolver.
81
+ def extract_handler_and_format(path, default_formats)
82
+ pieces = File.basename(path).split(".")
83
+ pieces.shift
125
84
 
126
- format = Mime[details.last] && details.pop.to_sym
127
- locale = details.last && details.pop.to_sym
85
+ handler = Template.handler_class_for_extension(pieces.pop)
86
+ format = pieces.last && Mime[pieces.last] && pieces.pop.to_sym
87
+ format ||= handler.default_format if handler.respond_to?(:default_format)
88
+ format ||= default_formats
128
89
 
129
- virtual_path = (m[1].gsub("#{@path}/", "") << details.join("."))
130
-
131
- return handler, :format => format, :locale => locale, :partial => partial,
132
- :virtual_path => virtual_path
133
- end
90
+ [handler, format]
134
91
  end
135
92
  end
136
93
 
137
94
  class FileSystemResolver < PathResolver
138
- def initialize(path, options = {})
95
+ def initialize(path)
139
96
  raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
140
- super(options)
97
+ super()
141
98
  @path = Pathname.new(path).expand_path
142
99
  end
143
- end
144
-
145
- # TODO: remove hack
146
- class FileSystemResolverWithFallback < Resolver
147
- def initialize(path, options = {})
148
- super(options)
149
- @paths = [FileSystemResolver.new(path, options), FileSystemResolver.new("", options), FileSystemResolver.new("/", options)]
150
- end
151
100
 
152
- def find_templates(*args)
153
- @paths.each do |p|
154
- template = p.find_templates(*args)
155
- return template unless template.empty?
156
- end
157
- []
158
- end
159
-
160
- def to_s
161
- @paths.first.to_s
101
+ def eql?(resolver)
102
+ self.class.equal?(resolver.class) && to_path == resolver.to_path
162
103
  end
104
+ alias :== :eql?
163
105
  end
164
106
  end