actionpack 3.0.20 → 3.1.0.beta1

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 (161) hide show
  1. data/CHANGELOG +88 -142
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +5 -6
  4. data/lib/abstract_controller.rb +1 -0
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +24 -19
  7. data/lib/abstract_controller/callbacks.rb +19 -19
  8. data/lib/abstract_controller/helpers.rb +11 -13
  9. data/lib/abstract_controller/layouts.rb +4 -5
  10. data/lib/abstract_controller/railties/routes_helpers.rb +18 -0
  11. data/lib/abstract_controller/rendering.rb +34 -31
  12. data/lib/abstract_controller/url_for.rb +27 -0
  13. data/lib/abstract_controller/view_paths.rb +31 -6
  14. data/lib/action_controller.rb +5 -3
  15. data/lib/action_controller/base.rb +15 -16
  16. data/lib/action_controller/caching.rb +2 -2
  17. data/lib/action_controller/caching/actions.rb +11 -12
  18. data/lib/action_controller/caching/fragments.rb +41 -19
  19. data/lib/action_controller/caching/pages.rb +3 -9
  20. data/lib/action_controller/caching/sweeping.rb +0 -1
  21. data/lib/action_controller/deprecated.rb +1 -1
  22. data/lib/action_controller/log_subscriber.rb +1 -1
  23. data/lib/action_controller/metal.rb +78 -20
  24. data/lib/action_controller/metal/compatibility.rb +0 -9
  25. data/lib/action_controller/metal/conditional_get.rb +9 -9
  26. data/lib/action_controller/metal/data_streaming.rb +145 -0
  27. data/lib/action_controller/metal/force_ssl.rb +35 -0
  28. data/lib/action_controller/metal/head.rb +1 -1
  29. data/lib/action_controller/metal/helpers.rb +37 -44
  30. data/lib/action_controller/metal/hide_actions.rb +2 -3
  31. data/lib/action_controller/metal/http_authentication.rb +41 -38
  32. data/lib/action_controller/metal/implicit_render.rb +13 -13
  33. data/lib/action_controller/metal/instrumentation.rb +2 -2
  34. data/lib/action_controller/metal/mime_responds.rb +25 -19
  35. data/lib/action_controller/metal/params_wrapper.rb +224 -0
  36. data/lib/action_controller/metal/redirecting.rb +6 -2
  37. data/lib/action_controller/metal/renderers.rb +50 -36
  38. data/lib/action_controller/metal/rendering.rb +34 -25
  39. data/lib/action_controller/metal/request_forgery_protection.rb +18 -36
  40. data/lib/action_controller/metal/responder.rb +47 -12
  41. data/lib/action_controller/metal/streaming.rb +244 -138
  42. data/lib/action_controller/metal/testing.rb +0 -9
  43. data/lib/action_controller/metal/url_for.rb +12 -14
  44. data/lib/action_controller/railtie.rb +19 -37
  45. data/lib/action_controller/railties/paths.rb +24 -0
  46. data/lib/action_controller/record_identifier.rb +4 -10
  47. data/lib/action_controller/test_case.rb +36 -19
  48. data/lib/action_controller/vendor/html-scanner/html/node.rb +5 -5
  49. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +3 -3
  50. data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -0
  51. data/lib/action_dispatch.rb +4 -1
  52. data/lib/action_dispatch/http/cache.rb +5 -32
  53. data/lib/action_dispatch/http/filter_parameters.rb +3 -1
  54. data/lib/action_dispatch/http/mime_negotiation.rb +22 -3
  55. data/lib/action_dispatch/http/mime_type.rb +45 -5
  56. data/lib/action_dispatch/http/rack_cache.rb +58 -0
  57. data/lib/action_dispatch/http/request.rb +27 -41
  58. data/lib/action_dispatch/http/response.rb +56 -54
  59. data/lib/action_dispatch/http/upload.rb +1 -11
  60. data/lib/action_dispatch/http/url.rb +102 -42
  61. data/lib/action_dispatch/middleware/callbacks.rb +8 -25
  62. data/lib/action_dispatch/middleware/closed_error.rb +7 -0
  63. data/lib/action_dispatch/middleware/cookies.rb +37 -15
  64. data/lib/action_dispatch/middleware/flash.rb +80 -11
  65. data/lib/action_dispatch/middleware/params_parser.rb +2 -2
  66. data/lib/action_dispatch/middleware/reloader.rb +76 -0
  67. data/lib/action_dispatch/middleware/session/abstract_store.rb +56 -226
  68. data/lib/action_dispatch/middleware/session/cookie_store.rb +20 -44
  69. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -46
  70. data/lib/action_dispatch/middleware/show_exceptions.rb +15 -2
  71. data/lib/action_dispatch/middleware/stack.rb +50 -17
  72. data/lib/action_dispatch/middleware/static.rb +41 -29
  73. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +3 -3
  74. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
  75. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +3 -3
  76. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -2
  77. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +2 -6
  78. data/lib/action_dispatch/railtie.rb +8 -0
  79. data/lib/action_dispatch/routing.rb +13 -1
  80. data/lib/action_dispatch/routing/mapper.rb +345 -227
  81. data/lib/action_dispatch/routing/polymorphic_routes.rb +33 -13
  82. data/lib/action_dispatch/routing/redirection.rb +110 -0
  83. data/lib/action_dispatch/routing/route.rb +15 -13
  84. data/lib/action_dispatch/routing/route_set.rb +116 -90
  85. data/lib/action_dispatch/routing/routes_proxy.rb +35 -0
  86. data/lib/action_dispatch/routing/url_for.rb +25 -1
  87. data/lib/action_dispatch/testing/assertions/response.rb +8 -10
  88. data/lib/action_dispatch/testing/assertions/routing.rb +15 -15
  89. data/lib/action_dispatch/testing/assertions/selector.rb +13 -220
  90. data/lib/action_dispatch/testing/integration.rb +37 -28
  91. data/lib/action_dispatch/testing/performance_test.rb +1 -3
  92. data/lib/action_dispatch/testing/test_process.rb +1 -1
  93. data/lib/action_dispatch/testing/test_request.rb +9 -3
  94. data/lib/action_dispatch/testing/test_response.rb +4 -111
  95. data/lib/action_pack.rb +1 -1
  96. data/lib/action_pack/version.rb +3 -3
  97. data/lib/action_view.rb +39 -24
  98. data/lib/action_view/base.rb +61 -86
  99. data/lib/action_view/buffers.rb +43 -0
  100. data/lib/action_view/context.rb +21 -24
  101. data/lib/action_view/flows.rb +79 -0
  102. data/lib/action_view/helpers.rb +8 -6
  103. data/lib/action_view/helpers/active_model_helper.rb +0 -23
  104. data/lib/action_view/helpers/asset_paths.rb +79 -0
  105. data/lib/action_view/helpers/asset_tag_helper.rb +30 -500
  106. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +147 -0
  107. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +101 -0
  108. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +200 -0
  109. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +152 -0
  110. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  111. data/lib/action_view/helpers/cache_helper.rb +11 -19
  112. data/lib/action_view/helpers/capture_helper.rb +19 -8
  113. data/lib/action_view/helpers/controller_helper.rb +21 -0
  114. data/lib/action_view/helpers/csrf_helper.rb +22 -4
  115. data/lib/action_view/helpers/date_helper.rb +36 -22
  116. data/lib/action_view/helpers/form_helper.rb +199 -113
  117. data/lib/action_view/helpers/form_options_helper.rb +10 -11
  118. data/lib/action_view/helpers/form_tag_helper.rb +94 -22
  119. data/lib/action_view/helpers/javascript_helper.rb +24 -107
  120. data/lib/action_view/helpers/number_helper.rb +36 -33
  121. data/lib/action_view/helpers/output_safety_helper.rb +38 -0
  122. data/lib/action_view/helpers/record_tag_helper.rb +6 -6
  123. data/lib/action_view/helpers/rendering_helper.rb +90 -0
  124. data/lib/action_view/helpers/sanitize_helper.rb +2 -2
  125. data/lib/action_view/helpers/sprockets_helper.rb +69 -0
  126. data/lib/action_view/helpers/tag_helper.rb +34 -12
  127. data/lib/action_view/helpers/text_helper.rb +30 -145
  128. data/lib/action_view/helpers/translation_helper.rb +10 -17
  129. data/lib/action_view/helpers/url_helper.rb +70 -67
  130. data/lib/action_view/locale/en.yml +1 -1
  131. data/lib/action_view/lookup_context.rb +36 -14
  132. data/lib/action_view/{paths.rb → path_set.rb} +9 -8
  133. data/lib/action_view/railtie.rb +12 -4
  134. data/lib/action_view/renderer/abstract_renderer.rb +36 -0
  135. data/lib/action_view/{render/partials.rb → renderer/partial_renderer.rb} +147 -146
  136. data/lib/action_view/renderer/renderer.rb +54 -0
  137. data/lib/action_view/renderer/streaming_template_renderer.rb +106 -0
  138. data/lib/action_view/renderer/template_renderer.rb +74 -0
  139. data/lib/action_view/template.rb +91 -54
  140. data/lib/action_view/template/error.rb +11 -8
  141. data/lib/action_view/template/handler.rb +9 -1
  142. data/lib/action_view/template/handlers.rb +9 -9
  143. data/lib/action_view/template/handlers/builder.rb +4 -4
  144. data/lib/action_view/template/handlers/erb.rb +21 -41
  145. data/lib/action_view/template/resolver.rb +171 -57
  146. data/lib/action_view/template/text.rb +0 -4
  147. data/lib/action_view/test_case.rb +32 -16
  148. data/lib/action_view/testing/resolvers.rb +16 -10
  149. data/lib/sprockets/railtie.rb +100 -0
  150. metadata +162 -140
  151. checksums.yaml +0 -7
  152. data/lib/action_controller/deprecated/base.rb +0 -143
  153. data/lib/action_controller/deprecated/dispatcher.rb +0 -28
  154. data/lib/action_controller/deprecated/url_writer.rb +0 -14
  155. data/lib/action_dispatch/routing/deprecated_mapper.rb +0 -525
  156. data/lib/action_view/helpers/prototype_helper.rb +0 -851
  157. data/lib/action_view/helpers/raw_output_helper.rb +0 -18
  158. data/lib/action_view/helpers/scriptaculous_helper.rb +0 -263
  159. data/lib/action_view/render/layouts.rb +0 -83
  160. data/lib/action_view/render/rendering.rb +0 -67
  161. data/lib/action_view/template/handlers/rjs.rb +0 -17
@@ -1,4 +1,4 @@
1
- require "action_dispatch/http/mime_type"
1
+ require 'action_dispatch/http/mime_type'
2
2
  require 'active_support/core_ext/class/attribute'
3
3
 
4
4
  # Legacy TemplateHandler stub
@@ -7,6 +7,8 @@ module ActionView
7
7
  module Handlers #:nodoc:
8
8
  module Compilable
9
9
  def self.included(base)
10
+ ActiveSupport::Deprecation.warn "Including Compilable in your template handler is deprecated. " <<
11
+ "Since Rails 3, all the API your template handler needs to implement is to respond to #call."
10
12
  base.extend(ClassMethods)
11
13
  end
12
14
 
@@ -26,6 +28,12 @@ module ActionView
26
28
  class_attribute :default_format
27
29
  self.default_format = Mime::HTML
28
30
 
31
+ def self.inherited(base)
32
+ ActiveSupport::Deprecation.warn "Inheriting from ActionView::Template::Handler is deprecated. " <<
33
+ "Since Rails 3, all the API your template handler needs to implement is to respond to #call."
34
+ super
35
+ end
36
+
29
37
  def self.call(template)
30
38
  raise "Need to implement #{self.class.name}#call(template)"
31
39
  end
@@ -3,17 +3,11 @@ module ActionView #:nodoc:
3
3
  class Template
4
4
  module Handlers #:nodoc:
5
5
  autoload :ERB, 'action_view/template/handlers/erb'
6
- autoload :RJS, 'action_view/template/handlers/rjs'
7
6
  autoload :Builder, 'action_view/template/handlers/builder'
8
7
 
9
8
  def self.extended(base)
10
- base.register_default_template_handler :erb, ERB
11
- base.register_template_handler :rjs, RJS
12
- base.register_template_handler :builder, Builder
13
-
14
- # TODO: Depreciate old template extensions
15
- base.register_template_handler :rhtml, ERB
16
- base.register_template_handler :rxml, Builder
9
+ base.register_default_template_handler :erb, ERB.new
10
+ base.register_template_handler :builder, Builder.new
17
11
  end
18
12
 
19
13
  @@template_handlers = {}
@@ -48,7 +42,13 @@ module ActionView #:nodoc:
48
42
  end
49
43
 
50
44
  def handler_class_for_extension(extension)
51
- (extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers
45
+ ActiveSupport::Deprecation.warn "handler_class_for_extension is deprecated. " <<
46
+ "Please use handler_for_extension instead", caller
47
+ handler_for_extension(extension)
48
+ end
49
+
50
+ def handler_for_extension(extension)
51
+ registered_template_handler(extension) || @@default_template_handlers
52
52
  end
53
53
  end
54
54
  end
@@ -1,11 +1,11 @@
1
1
  module ActionView
2
2
  module Template::Handlers
3
- class Builder < Template::Handler
4
- include Compilable
5
-
3
+ class Builder
4
+ # Default format used by Builder.
5
+ class_attribute :default_format
6
6
  self.default_format = Mime::XML
7
7
 
8
- def compile(template)
8
+ def call(template)
9
9
  require 'builder'
10
10
  "xml = ::Builder::XmlMarkup.new(:indent => 2);" +
11
11
  "self.output_buffer = xml.target!;" +
@@ -1,34 +1,14 @@
1
1
  require 'active_support/core_ext/class/attribute_accessors'
2
- require 'active_support/core_ext/string/output_safety'
3
- require "action_view/template"
2
+ require 'action_view/template'
3
+ require 'action_view/template/handler'
4
4
  require 'erubis'
5
5
 
6
6
  module ActionView
7
- class OutputBuffer < ActiveSupport::SafeBuffer
8
- def initialize(*)
9
- super
10
- encode! if encoding_aware?
11
- end
12
-
13
- def <<(value)
14
- super(value.to_s)
15
- end
16
- alias :append= :<<
17
- alias :safe_append= :safe_concat
18
-
19
- def append_if_string=(value)
20
- if value.is_a?(String) && !value.is_a?(NonConcattingString)
21
- ActiveSupport::Deprecation.warn("<% %> style block helpers are deprecated. Please use <%= %>", caller)
22
- self << value
23
- end
24
- end
25
- end
26
-
27
7
  class Template
28
8
  module Handlers
29
9
  class Erubis < ::Erubis::Eruby
30
10
  def add_preamble(src)
31
- src << "@output_buffer = ActionView::OutputBuffer.new;"
11
+ src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
32
12
  end
33
13
 
34
14
  def add_text(src, text)
@@ -46,14 +26,6 @@ module ActionView
46
26
  end
47
27
  end
48
28
 
49
- def add_stmt(src, code)
50
- if code =~ BLOCK_EXPR
51
- src << '@output_buffer.append_if_string= ' << code
52
- else
53
- super
54
- end
55
- end
56
-
57
29
  def add_expr_escaped(src, code)
58
30
  if code =~ BLOCK_EXPR
59
31
  src << "@output_buffer.safe_append= " << code
@@ -67,28 +39,35 @@ module ActionView
67
39
  end
68
40
  end
69
41
 
70
- class ERB < Handler
71
- include Compilable
72
-
73
- ##
74
- # :singleton-method:
42
+ class ERB
75
43
  # Specify trim mode for the ERB compiler. Defaults to '-'.
76
- # See ERb documentation for suitable values.
77
- cattr_accessor :erb_trim_mode
44
+ # See ERB documentation for suitable values.
45
+ class_attribute :erb_trim_mode
78
46
  self.erb_trim_mode = '-'
79
47
 
48
+ # Default format used by ERB.
49
+ class_attribute :default_format
80
50
  self.default_format = Mime::HTML
81
51
 
82
- cattr_accessor :erb_implementation
52
+ # Default implementation used.
53
+ class_attribute :erb_implementation
83
54
  self.erb_implementation = Erubis
84
55
 
85
56
  ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
86
57
 
87
- def self.handles_encoding?
58
+ def self.call(template)
59
+ new.call(template)
60
+ end
61
+
62
+ def supports_streaming?
88
63
  true
89
64
  end
90
65
 
91
- def compile(template)
66
+ def handles_encoding?
67
+ true
68
+ end
69
+
70
+ def call(template)
92
71
  if template.source.encoding_aware?
93
72
  # First, convert to BINARY, so in case the encoding is
94
73
  # wrong, we can still find an encoding tag
@@ -114,6 +93,7 @@ module ActionView
114
93
  end
115
94
 
116
95
  private
96
+
117
97
  def valid_encoding(string, encoding)
118
98
  # If a magic encoding comment was found, tag the
119
99
  # String with this encoding. This is for a case
@@ -5,9 +5,35 @@ require "action_view/template"
5
5
  module ActionView
6
6
  # = Action View Resolver
7
7
  class Resolver
8
+ # Keeps all information about view path and builds virtual path.
9
+ class Path < String
10
+ attr_reader :name, :prefix, :partial, :virtual
11
+ alias_method :partial?, :partial
12
+
13
+ def initialize(name, prefix, partial)
14
+ @name, @prefix, @partial = name, prefix, partial
15
+ rebuild(@name, @prefix, @partial)
16
+ end
17
+
18
+ def rebuild(name, prefix, partial)
19
+ @virtual = ""
20
+ @virtual << "#{prefix}/" unless prefix.empty?
21
+ @virtual << (partial ? "_#{name}" : name)
22
+
23
+ self.replace(@virtual)
24
+ end
25
+ end
26
+
27
+ cattr_accessor :caching
28
+ self.caching = true
29
+
30
+ class << self
31
+ alias :caching? :caching
32
+ end
33
+
8
34
  def initialize
9
- @cached = Hash.new { |h1,k1| h1[k1] =
10
- Hash.new { |h2,k2| h2[k2] = Hash.new { |h3, k3| h3[k3] = {} } } }
35
+ @cached = Hash.new { |h1,k1| h1[k1] = Hash.new { |h2,k2|
36
+ h2[k2] = Hash.new { |h3,k3| h3[k3] = Hash.new { |h4,k4| h4[k4] = {} } } } }
11
37
  end
12
38
 
13
39
  def clear_cache
@@ -15,63 +41,96 @@ module ActionView
15
41
  end
16
42
 
17
43
  # Normalizes the arguments and passes it on to find_template.
18
- def find_all(name, prefix=nil, partial=false, details={}, key=nil)
19
- cached(key, prefix, name, partial) do
44
+ def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
45
+ cached(key, [name, prefix, partial], details, locals) do
20
46
  find_templates(name, prefix, partial, details)
21
47
  end
22
48
  end
23
49
 
24
50
  private
25
51
 
26
- def caching?
27
- @caching ||= !defined?(Rails.application) || Rails.application.config.cache_classes
28
- end
52
+ delegate :caching?, :to => "self.class"
29
53
 
30
54
  # This is what child classes implement. No defaults are needed
31
55
  # because Resolver guarantees that the arguments are present and
32
56
  # normalized.
33
57
  def find_templates(name, prefix, partial, details)
34
- raise NotImplementedError
58
+ raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details) method"
35
59
  end
36
60
 
37
- def cached(key, prefix, name, partial)
38
- return yield unless key && caching?
39
- @cached[key][prefix][name][partial] ||= yield
61
+ # Helpers that builds a path. Useful for building virtual paths.
62
+ def build_path(name, prefix, partial)
63
+ Path.new(name, prefix, partial)
64
+ end
65
+
66
+ # Handles templates caching. If a key is given and caching is on
67
+ # always check the cache before hitting the resolver. Otherwise,
68
+ # it always hits the resolver but check if the resolver is fresher
69
+ # before returning it.
70
+ def cached(key, path_info, details, locals) #:nodoc:
71
+ name, prefix, partial = path_info
72
+ locals = sort_locals(locals)
73
+
74
+ if key && caching?
75
+ @cached[key][name][prefix][partial][locals] ||= decorate(yield, path_info, details, locals)
76
+ else
77
+ fresh = decorate(yield, path_info, details, locals)
78
+ return fresh unless key
79
+
80
+ scope = @cached[key][name][prefix][partial]
81
+ cache = scope[locals]
82
+ mtime = cache && cache.map(&:updated_at).max
83
+
84
+ if !mtime || fresh.empty? || fresh.any? { |t| t.updated_at > mtime }
85
+ scope[locals] = fresh
86
+ else
87
+ cache
88
+ end
89
+ end
90
+ end
91
+
92
+ # Ensures all the resolver information is set in the template.
93
+ def decorate(templates, path_info, details, locals) #:nodoc:
94
+ cached = nil
95
+ templates.each do |t|
96
+ t.locals = locals
97
+ t.formats = details[:formats] || [:html] if t.formats.empty?
98
+ t.virtual_path ||= (cached ||= build_path(*path_info))
99
+ end
100
+ end
101
+
102
+ if :symbol.respond_to?("<=>")
103
+ def sort_locals(locals) #:nodoc:
104
+ locals.sort.freeze
105
+ end
106
+ else
107
+ def sort_locals(locals) #:nodoc:
108
+ locals = locals.map{ |l| l.to_s }
109
+ locals.sort!
110
+ locals.freeze
111
+ end
40
112
  end
41
113
  end
42
114
 
43
115
  class PathResolver < Resolver
44
- EXTENSION_ORDER = [:locale, :formats, :handlers]
116
+ EXTENSIONS = [:locale, :formats, :handlers]
117
+ DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}"
45
118
 
46
- def to_s
47
- @path.to_s
119
+ def initialize(pattern=nil)
120
+ @pattern = pattern || DEFAULT_PATTERN
121
+ super()
48
122
  end
49
- alias :to_path :to_s
50
123
 
51
- private
124
+ private
52
125
 
53
126
  def find_templates(name, prefix, partial, details)
54
- path = build_path(name, prefix, partial, details)
55
- query(path, EXTENSION_ORDER.map { |ext| details[ext] }, details[:formats])
56
- end
57
-
58
- def build_path(name, prefix, partial, details)
59
- path = ""
60
- path << "#{prefix}/" unless prefix.empty?
61
- path << (partial ? "_#{name}" : name)
62
- path
127
+ path = build_path(name, prefix, partial)
128
+ extensions = Hash[EXTENSIONS.map { |ext| [ext, details[ext]] }.flatten(0)]
129
+ query(path, extensions, details[:formats])
63
130
  end
64
131
 
65
132
  def query(path, exts, formats)
66
- query = escape_entry File.join(@path, path)
67
-
68
- exts.each do |ext|
69
- query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << ',}'
70
- end
71
-
72
- query.gsub!(/\{\.html,/, "{.html,.text.html,")
73
- query.gsub!(/\{\.text,/, "{.text,.text.plain,")
74
-
133
+ query = build_query(path, exts)
75
134
  templates = []
76
135
  sanitizer = Hash.new { |h,k| h[k] = Dir["#{File.dirname(k)}/*"] }
77
136
 
@@ -82,14 +141,31 @@ module ActionView
82
141
  contents = File.open(p, "rb") {|io| io.read }
83
142
 
84
143
  templates << Template.new(contents, File.expand_path(p), handler,
85
- :virtual_path => path, :format => format)
144
+ :virtual_path => path.virtual, :format => format, :updated_at => mtime(p))
86
145
  end
87
146
 
88
147
  templates
89
148
  end
90
149
 
91
- def escape_entry(entry)
92
- entry.gsub(/(\*|\[|\]|\{|\}|\?)/, "\\\\\\1")
150
+ # Helper for building query glob string based on resolver's pattern.
151
+ def build_query(path, exts)
152
+ query = @pattern.dup
153
+ query.gsub!(/\:prefix(\/)?/, path.prefix.empty? ? "" : "#{path.prefix}\\1") # prefix can be empty...
154
+ query.gsub!(/\:action/, path.partial? ? "_#{path.name}" : path.name)
155
+
156
+ exts.each { |ext, variants|
157
+ query.gsub!(/\:#{ext}/, "{#{variants.compact.uniq.join(',')}}")
158
+ }
159
+
160
+ query.gsub!('.{html,', '.{html,text.html,')
161
+ query.gsub!('.{text,', '.{text,text.plain,')
162
+
163
+ File.expand_path(query, @path)
164
+ end
165
+
166
+ # Returns the file mtime from the filesystem.
167
+ def mtime(p)
168
+ File.stat(p).mtime
93
169
  end
94
170
 
95
171
  # Extract handler and formats from path. If a format cannot be a found neither
@@ -98,38 +174,76 @@ module ActionView
98
174
  def extract_handler_and_format(path, default_formats)
99
175
  pieces = File.basename(path).split(".")
100
176
  pieces.shift
101
-
102
- handler = Template.handler_class_for_extension(pieces.pop)
103
-
104
- if pieces.last == "html" && pieces[-2] == "text"
105
- correct_path = path.gsub(/\.text\.html/, ".html")
106
- ActiveSupport::Deprecation.warn "The file `#{path}` uses the deprecated format `text.html`. Please rename it to #{correct_path}", caller
107
- end
108
-
109
- if pieces.last == "plain" && pieces[-2] == "text"
110
- correct_path = path.gsub(/\.text\.plain/, ".text")
111
- pieces.pop
112
- ActiveSupport::Deprecation.warn "The file `#{path}` uses the deprecated format `text.plain`. Please rename it to #{correct_path}", caller
113
- end
114
-
115
- format = pieces.last && Mime[pieces.last] && pieces.pop.to_sym
116
- format ||= handler.default_format if handler.respond_to?(:default_format)
117
- format ||= default_formats
118
-
177
+ handler = Template.handler_for_extension(pieces.pop)
178
+ format = pieces.last && Mime[pieces.last]
119
179
  [handler, format]
120
180
  end
121
181
  end
122
182
 
183
+ # A resolver that loads files from the filesystem. It allows to set your own
184
+ # resolving pattern. Such pattern can be a glob string supported by some variables.
185
+ #
186
+ # ==== Examples
187
+ #
188
+ # Default pattern, loads views the same way as previous versions of rails, eg. when you're
189
+ # looking for `users/new` it will produce query glob: `users/new{.{en},}{.{html,js},}{.{erb,haml},}`
190
+ #
191
+ # FileSystemResolver.new("/path/to/views", ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}")
192
+ #
193
+ # This one allows you to keep files with different formats in seperated subdirectories,
194
+ # eg. `users/new.html` will be loaded from `users/html/new.erb` or `users/new.html.erb`,
195
+ # `users/new.js` from `users/js/new.erb` or `users/new.js.erb`, etc.
196
+ #
197
+ # FileSystemResolver.new("/path/to/views", ":prefix/{:formats/,}:action{.:locale,}{.:formats,}{.:handlers,}")
198
+ #
199
+ # If you don't specify pattern then the default will be used.
200
+ #
201
+ # In order to use any of the customized resolvers above in a Rails application, you just need
202
+ # to configure ActionController::Base.view_paths in an initializer, for example:
203
+ #
204
+ # ActionController::Base.view_paths = FileSystemResolver.new(
205
+ # Rails.root.join("app/views"),
206
+ # ":prefix{/:locale}/:action{.:formats,}{.:handlers,}"
207
+ # )
208
+ #
209
+ # ==== Pattern format and variables
210
+ #
211
+ # Pattern have to be a valid glob string, and it allows you to use the
212
+ # following variables:
213
+ #
214
+ # * <tt>:prefix</tt> - usualy the controller path
215
+ # * <tt>:action</tt> - name of the action
216
+ # * <tt>:locale</tt> - possible locale versions
217
+ # * <tt>:formats</tt> - possible request formats (for example html, json, xml...)
218
+ # * <tt>:handlers</tt> - possible handlers (for example erb, haml, builder...)
219
+ #
123
220
  class FileSystemResolver < PathResolver
124
- def initialize(path)
221
+ def initialize(path, pattern=nil)
125
222
  raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
126
- super()
223
+ super(pattern)
127
224
  @path = File.expand_path(path)
128
225
  end
129
226
 
227
+ def to_s
228
+ @path.to_s
229
+ end
230
+ alias :to_path :to_s
231
+
130
232
  def eql?(resolver)
131
233
  self.class.equal?(resolver.class) && to_path == resolver.to_path
132
234
  end
133
235
  alias :== :eql?
134
236
  end
237
+
238
+ # The same as FileSystemResolver but does not allow templates to store
239
+ # a virtual path since it is invalid for such resolvers.
240
+ class FallbackFileSystemResolver < FileSystemResolver
241
+ def self.instances
242
+ [new(""), new("/")]
243
+ end
244
+
245
+ def decorate(*)
246
+ super.each { |t| t.virtual_path = nil }
247
+ end
248
+ end
135
249
  end