actionview 4.2.10 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +141 -272
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/action_view/base.rb +33 -21
  6. data/lib/action_view/buffers.rb +1 -1
  7. data/lib/action_view/context.rb +1 -1
  8. data/lib/action_view/dependency_tracker.rb +52 -20
  9. data/lib/action_view/digestor.rb +86 -83
  10. data/lib/action_view/flows.rb +9 -11
  11. data/lib/action_view/gem_version.rb +3 -3
  12. data/lib/action_view/helpers/active_model_helper.rb +8 -8
  13. data/lib/action_view/helpers/asset_tag_helper.rb +74 -38
  14. data/lib/action_view/helpers/asset_url_helper.rb +160 -59
  15. data/lib/action_view/helpers/atom_feed_helper.rb +16 -16
  16. data/lib/action_view/helpers/cache_helper.rb +90 -35
  17. data/lib/action_view/helpers/capture_helper.rb +7 -6
  18. data/lib/action_view/helpers/controller_helper.rb +3 -2
  19. data/lib/action_view/helpers/csrf_helper.rb +3 -3
  20. data/lib/action_view/helpers/date_helper.rb +156 -108
  21. data/lib/action_view/helpers/debug_helper.rb +3 -4
  22. data/lib/action_view/helpers/form_helper.rb +475 -94
  23. data/lib/action_view/helpers/form_options_helper.rb +87 -47
  24. data/lib/action_view/helpers/form_tag_helper.rb +88 -57
  25. data/lib/action_view/helpers/javascript_helper.rb +10 -10
  26. data/lib/action_view/helpers/number_helper.rb +76 -59
  27. data/lib/action_view/helpers/output_safety_helper.rb +34 -4
  28. data/lib/action_view/helpers/record_tag_helper.rb +12 -99
  29. data/lib/action_view/helpers/rendering_helper.rb +3 -3
  30. data/lib/action_view/helpers/sanitize_helper.rb +17 -14
  31. data/lib/action_view/helpers/tag_helper.rb +198 -73
  32. data/lib/action_view/helpers/tags/base.rb +132 -97
  33. data/lib/action_view/helpers/tags/check_box.rb +17 -17
  34. data/lib/action_view/helpers/tags/collection_check_boxes.rb +9 -33
  35. data/lib/action_view/helpers/tags/collection_helpers.rb +68 -36
  36. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +3 -11
  37. data/lib/action_view/helpers/tags/collection_select.rb +2 -2
  38. data/lib/action_view/helpers/tags/date_select.rb +36 -36
  39. data/lib/action_view/helpers/tags/datetime_field.rb +1 -1
  40. data/lib/action_view/helpers/tags/grouped_collection_select.rb +2 -2
  41. data/lib/action_view/helpers/tags/label.rb +5 -1
  42. data/lib/action_view/helpers/tags/password_field.rb +1 -1
  43. data/lib/action_view/helpers/tags/placeholderable.rb +1 -1
  44. data/lib/action_view/helpers/tags/radio_button.rb +4 -4
  45. data/lib/action_view/helpers/tags/search_field.rb +12 -9
  46. data/lib/action_view/helpers/tags/select.rb +9 -9
  47. data/lib/action_view/helpers/tags/text_area.rb +1 -1
  48. data/lib/action_view/helpers/tags/text_field.rb +5 -6
  49. data/lib/action_view/helpers/tags/translator.rb +15 -13
  50. data/lib/action_view/helpers/text_helper.rb +47 -30
  51. data/lib/action_view/helpers/translation_helper.rb +60 -30
  52. data/lib/action_view/helpers/url_helper.rb +132 -104
  53. data/lib/action_view/helpers.rb +1 -1
  54. data/lib/action_view/layouts.rb +59 -54
  55. data/lib/action_view/log_subscriber.rb +56 -7
  56. data/lib/action_view/lookup_context.rb +76 -61
  57. data/lib/action_view/model_naming.rb +1 -1
  58. data/lib/action_view/path_set.rb +28 -19
  59. data/lib/action_view/railtie.rb +30 -6
  60. data/lib/action_view/record_identifier.rb +51 -25
  61. data/lib/action_view/renderer/abstract_renderer.rb +19 -15
  62. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +55 -0
  63. data/lib/action_view/renderer/partial_renderer.rb +208 -206
  64. data/lib/action_view/renderer/renderer.rb +2 -6
  65. data/lib/action_view/renderer/streaming_template_renderer.rb +46 -48
  66. data/lib/action_view/renderer/template_renderer.rb +65 -66
  67. data/lib/action_view/rendering.rb +16 -9
  68. data/lib/action_view/routing_url_for.rb +25 -17
  69. data/lib/action_view/tasks/cache_digests.rake +23 -0
  70. data/lib/action_view/template/error.rb +14 -13
  71. data/lib/action_view/template/handlers/builder.rb +7 -7
  72. data/lib/action_view/template/handlers/erb/deprecated_erubis.rb +9 -0
  73. data/lib/action_view/template/handlers/erb/erubi.rb +81 -0
  74. data/lib/action_view/template/handlers/erb/erubis.rb +81 -0
  75. data/lib/action_view/template/handlers/erb.rb +9 -76
  76. data/lib/action_view/template/handlers/html.rb +9 -0
  77. data/lib/action_view/template/handlers/raw.rb +1 -3
  78. data/lib/action_view/template/handlers.rb +8 -6
  79. data/lib/action_view/template/html.rb +2 -4
  80. data/lib/action_view/template/resolver.rb +133 -109
  81. data/lib/action_view/template/text.rb +5 -8
  82. data/lib/action_view/template/types.rb +15 -17
  83. data/lib/action_view/template.rb +51 -28
  84. data/lib/action_view/test_case.rb +32 -27
  85. data/lib/action_view/testing/resolvers.rb +29 -31
  86. data/lib/action_view/version.rb +1 -1
  87. data/lib/action_view/view_paths.rb +26 -32
  88. data/lib/action_view.rb +5 -5
  89. data/lib/assets/compiled/rails-ujs.js +685 -0
  90. metadata +23 -23
  91. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -0,0 +1,81 @@
1
+ gem "erubis"
2
+ require "erubis"
3
+
4
+ module ActionView
5
+ class Template
6
+ module Handlers
7
+ class ERB
8
+ class Erubis < ::Erubis::Eruby
9
+ # :nodoc: all
10
+ def add_preamble(src)
11
+ @newline_pending = 0
12
+ src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
13
+ end
14
+
15
+ def add_text(src, text)
16
+ return if text.empty?
17
+
18
+ if text == "\n"
19
+ @newline_pending += 1
20
+ else
21
+ src << "@output_buffer.safe_append='"
22
+ src << "\n" * @newline_pending if @newline_pending > 0
23
+ src << escape_text(text)
24
+ src << "'.freeze;"
25
+
26
+ @newline_pending = 0
27
+ end
28
+ end
29
+
30
+ # Erubis toggles <%= and <%== behavior when escaping is enabled.
31
+ # We override to always treat <%== as escaped.
32
+ def add_expr(src, code, indicator)
33
+ case indicator
34
+ when "=="
35
+ add_expr_escaped(src, code)
36
+ else
37
+ super
38
+ end
39
+ end
40
+
41
+ BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
42
+
43
+ def add_expr_literal(src, code)
44
+ flush_newline_if_pending(src)
45
+ if BLOCK_EXPR.match?(code)
46
+ src << "@output_buffer.append= " << code
47
+ else
48
+ src << "@output_buffer.append=(" << code << ");"
49
+ end
50
+ end
51
+
52
+ def add_expr_escaped(src, code)
53
+ flush_newline_if_pending(src)
54
+ if BLOCK_EXPR.match?(code)
55
+ src << "@output_buffer.safe_expr_append= " << code
56
+ else
57
+ src << "@output_buffer.safe_expr_append=(" << code << ");"
58
+ end
59
+ end
60
+
61
+ def add_stmt(src, code)
62
+ flush_newline_if_pending(src)
63
+ super
64
+ end
65
+
66
+ def add_postamble(src)
67
+ flush_newline_if_pending(src)
68
+ src << "@output_buffer.to_s"
69
+ end
70
+
71
+ def flush_newline_if_pending(src)
72
+ if @newline_pending > 0
73
+ src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
74
+ @newline_pending = 0
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -1,87 +1,20 @@
1
- require 'erubis'
2
-
3
1
  module ActionView
4
2
  class Template
5
3
  module Handlers
6
- class Erubis < ::Erubis::Eruby
7
- def add_preamble(src)
8
- @newline_pending = 0
9
- src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
10
- end
11
-
12
- def add_text(src, text)
13
- return if text.empty?
14
-
15
- if text == "\n"
16
- @newline_pending += 1
17
- else
18
- src << "@output_buffer.safe_append='"
19
- src << "\n" * @newline_pending if @newline_pending > 0
20
- src << escape_text(text)
21
- src << "'.freeze;"
22
-
23
- @newline_pending = 0
24
- end
25
- end
26
-
27
- # Erubis toggles <%= and <%== behavior when escaping is enabled.
28
- # We override to always treat <%== as escaped.
29
- def add_expr(src, code, indicator)
30
- case indicator
31
- when '=='
32
- add_expr_escaped(src, code)
33
- else
34
- super
35
- end
36
- end
37
-
38
- BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
39
-
40
- def add_expr_literal(src, code)
41
- flush_newline_if_pending(src)
42
- if code =~ BLOCK_EXPR
43
- src << '@output_buffer.append= ' << code
44
- else
45
- src << '@output_buffer.append=(' << code << ');'
46
- end
47
- end
48
-
49
- def add_expr_escaped(src, code)
50
- flush_newline_if_pending(src)
51
- if code =~ BLOCK_EXPR
52
- src << "@output_buffer.safe_expr_append= " << code
53
- else
54
- src << "@output_buffer.safe_expr_append=(" << code << ");"
55
- end
56
- end
57
-
58
- def add_stmt(src, code)
59
- flush_newline_if_pending(src)
60
- super
61
- end
62
-
63
- def add_postamble(src)
64
- flush_newline_if_pending(src)
65
- src << '@output_buffer.to_s'
66
- end
67
-
68
- def flush_newline_if_pending(src)
69
- if @newline_pending > 0
70
- src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
71
- @newline_pending = 0
72
- end
73
- end
74
- end
4
+ autoload :Erubis, "action_view/template/handlers/erb/deprecated_erubis"
75
5
 
76
6
  class ERB
7
+ autoload :Erubi, "action_view/template/handlers/erb/erubi"
8
+ autoload :Erubis, "action_view/template/handlers/erb/erubis"
9
+
77
10
  # Specify trim mode for the ERB compiler. Defaults to '-'.
78
11
  # See ERB documentation for suitable values.
79
12
  class_attribute :erb_trim_mode
80
- self.erb_trim_mode = '-'
13
+ self.erb_trim_mode = "-"
81
14
 
82
15
  # Default implementation used.
83
16
  class_attribute :erb_implementation
84
- self.erb_implementation = Erubis
17
+ self.erb_implementation = Erubi
85
18
 
86
19
  # Do not escape templates of these mime types.
87
20
  class_attribute :escape_whitelist
@@ -108,7 +41,7 @@ module ActionView
108
41
  # expression
109
42
  template_source = template.source.dup.force_encoding(Encoding::ASCII_8BIT)
110
43
 
111
- erb = template_source.gsub(ENCODING_TAG, '')
44
+ erb = template_source.gsub(ENCODING_TAG, "")
112
45
  encoding = $2
113
46
 
114
47
  erb.force_encoding valid_encoding(template.source.dup, encoding)
@@ -118,8 +51,8 @@ module ActionView
118
51
 
119
52
  self.class.erb_implementation.new(
120
53
  erb,
121
- :escape => (self.class.escape_whitelist.include? template.type),
122
- :trim => (self.class.erb_trim_mode == "-")
54
+ escape: (self.class.escape_whitelist.include? template.type),
55
+ trim: (self.class.erb_trim_mode == "-")
123
56
  ).src
124
57
  end
125
58
 
@@ -0,0 +1,9 @@
1
+ module ActionView
2
+ module Template::Handlers
3
+ class Html < Raw
4
+ def call(template)
5
+ "ActionView::OutputBuffer.new #{super}"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -2,9 +2,7 @@ module ActionView
2
2
  module Template::Handlers
3
3
  class Raw
4
4
  def call(template)
5
- escaped = template.source.gsub(/:/, '\:')
6
-
7
- '%q:' + escaped + ':;'
5
+ "#{template.source.inspect}.html_safe;"
8
6
  end
9
7
  end
10
8
  end
@@ -2,14 +2,16 @@ module ActionView #:nodoc:
2
2
  # = Action View Template Handlers
3
3
  class Template
4
4
  module Handlers #:nodoc:
5
- autoload :ERB, 'action_view/template/handlers/erb'
6
- autoload :Builder, 'action_view/template/handlers/builder'
7
- autoload :Raw, 'action_view/template/handlers/raw'
5
+ autoload :Raw, "action_view/template/handlers/raw"
6
+ autoload :ERB, "action_view/template/handlers/erb"
7
+ autoload :Html, "action_view/template/handlers/html"
8
+ autoload :Builder, "action_view/template/handlers/builder"
8
9
 
9
10
  def self.extended(base)
10
- base.register_default_template_handler :erb, ERB.new
11
+ base.register_default_template_handler :raw, Raw.new
12
+ base.register_template_handler :erb, ERB.new
13
+ base.register_template_handler :html, Html.new
11
14
  base.register_template_handler :builder, Builder.new
12
- base.register_template_handler :raw, Raw.new
13
15
  base.register_template_handler :ruby, :source.to_proc
14
16
  end
15
17
 
@@ -42,7 +44,7 @@ module ActionView #:nodoc:
42
44
  end
43
45
 
44
46
  def template_handler_extensions
45
- @@template_handlers.keys.map {|key| key.to_s }.sort
47
+ @@template_handlers.keys.map(&:to_s).sort
46
48
  end
47
49
 
48
50
  def registered_template_handler(extension)
@@ -11,12 +11,10 @@ module ActionView #:nodoc:
11
11
  end
12
12
 
13
13
  def identifier
14
- 'html template'
14
+ "html template"
15
15
  end
16
16
 
17
- def inspect
18
- 'html template'
19
- end
17
+ alias_method :inspect, :identifier
20
18
 
21
19
  def to_str
22
20
  ERB::Util.h(@string)
@@ -1,10 +1,9 @@
1
1
  require "pathname"
2
2
  require "active_support/core_ext/class"
3
3
  require "active_support/core_ext/module/attribute_accessors"
4
- require 'active_support/core_ext/string/filters'
5
4
  require "action_view/template"
6
5
  require "thread"
7
- require "thread_safe"
6
+ require "concurrent/map"
8
7
 
9
8
  module ActionView
10
9
  # = Action View Resolver
@@ -36,23 +35,28 @@ module ActionView
36
35
 
37
36
  # Threadsafe template cache
38
37
  class Cache #:nodoc:
39
- class SmallCache < ThreadSafe::Cache
38
+ class SmallCache < Concurrent::Map
40
39
  def initialize(options = {})
41
- super(options.merge(:initial_capacity => 2))
40
+ super(options.merge(initial_capacity: 2))
42
41
  end
43
42
  end
44
43
 
45
44
  # preallocate all the default blocks for performance/memory consumption reasons
46
- PARTIAL_BLOCK = lambda {|cache, partial| cache[partial] = SmallCache.new}
47
- PREFIX_BLOCK = lambda {|cache, prefix| cache[prefix] = SmallCache.new(&PARTIAL_BLOCK)}
48
- NAME_BLOCK = lambda {|cache, name| cache[name] = SmallCache.new(&PREFIX_BLOCK)}
49
- KEY_BLOCK = lambda {|cache, key| cache[key] = SmallCache.new(&NAME_BLOCK)}
45
+ PARTIAL_BLOCK = lambda { |cache, partial| cache[partial] = SmallCache.new }
46
+ PREFIX_BLOCK = lambda { |cache, prefix| cache[prefix] = SmallCache.new(&PARTIAL_BLOCK) }
47
+ NAME_BLOCK = lambda { |cache, name| cache[name] = SmallCache.new(&PREFIX_BLOCK) }
48
+ KEY_BLOCK = lambda { |cache, key| cache[key] = SmallCache.new(&NAME_BLOCK) }
50
49
 
51
50
  # usually a majority of template look ups return nothing, use this canonical preallocated array to save memory
52
51
  NO_TEMPLATES = [].freeze
53
52
 
54
53
  def initialize
55
54
  @data = SmallCache.new(&KEY_BLOCK)
55
+ @query_cache = SmallCache.new
56
+ end
57
+
58
+ def inspect
59
+ "#<#{self.class.name}:0x#{(object_id << 1).to_s(16)} keys=#{@data.size} queries=#{@query_cache.size}>"
56
60
  end
57
61
 
58
62
  # Cache the templates returned by the block
@@ -71,28 +75,54 @@ module ActionView
71
75
  end
72
76
  end
73
77
 
78
+ def cache_query(query) # :nodoc:
79
+ if Resolver.caching?
80
+ @query_cache[query] ||= canonical_no_templates(yield)
81
+ else
82
+ yield
83
+ end
84
+ end
85
+
74
86
  def clear
75
87
  @data.clear
88
+ @query_cache.clear
76
89
  end
77
90
 
78
- private
91
+ # Get the cache size. Do not call this
92
+ # method. This method is not guaranteed to be here ever.
93
+ def size # :nodoc:
94
+ size = 0
95
+ @data.each_value do |v1|
96
+ v1.each_value do |v2|
97
+ v2.each_value do |v3|
98
+ v3.each_value do |v4|
99
+ size += v4.size
100
+ end
101
+ end
102
+ end
103
+ end
79
104
 
80
- def canonical_no_templates(templates)
81
- templates.empty? ? NO_TEMPLATES : templates
105
+ size + @query_cache.size
82
106
  end
83
107
 
84
- def templates_have_changed?(cached_templates, fresh_templates)
85
- # if either the old or new template list is empty, we don't need to (and can't)
86
- # compare modification times, and instead just check whether the lists are different
87
- if cached_templates.blank? || fresh_templates.blank?
88
- return fresh_templates.blank? != cached_templates.blank?
108
+ private
109
+
110
+ def canonical_no_templates(templates)
111
+ templates.empty? ? NO_TEMPLATES : templates
89
112
  end
90
113
 
91
- cached_templates_max_updated_at = cached_templates.map(&:updated_at).max
114
+ def templates_have_changed?(cached_templates, fresh_templates)
115
+ # if either the old or new template list is empty, we don't need to (and can't)
116
+ # compare modification times, and instead just check whether the lists are different
117
+ if cached_templates.blank? || fresh_templates.blank?
118
+ return fresh_templates.blank? != cached_templates.blank?
119
+ end
92
120
 
93
- # if a template has changed, it will be now be newer than all the cached templates
94
- fresh_templates.any? { |t| t.updated_at > cached_templates_max_updated_at }
95
- end
121
+ cached_templates_max_updated_at = cached_templates.map(&:updated_at).max
122
+
123
+ # if a template has changed, it will be now be newer than all the cached templates
124
+ fresh_templates.any? { |t| t.updated_at > cached_templates_max_updated_at }
125
+ end
96
126
  end
97
127
 
98
128
  cattr_accessor :caching
@@ -111,18 +141,22 @@ module ActionView
111
141
  end
112
142
 
113
143
  # Normalizes the arguments and passes it on to find_templates.
114
- def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
144
+ def find_all(name, prefix = nil, partial = false, details = {}, key = nil, locals = [])
115
145
  cached(key, [name, prefix, partial], details, locals) do
116
- find_templates(name, prefix, partial, details, false)
146
+ find_templates(name, prefix, partial, details)
117
147
  end
118
148
  end
119
149
 
120
- def find_all_anywhere(name, prefix, partial=false, details={}, key=nil, locals=[])
150
+ def find_all_anywhere(name, prefix, partial = false, details = {}, key = nil, locals = [])
121
151
  cached(key, [name, prefix, partial], details, locals) do
122
152
  find_templates(name, prefix, partial, details, true)
123
153
  end
124
154
  end
125
155
 
156
+ def find_all_with_query(query) # :nodoc:
157
+ @cache.cache_query(query) { find_template_paths(File.join(@path, query)) }
158
+ end
159
+
126
160
  private
127
161
 
128
162
  delegate :caching?, to: :class
@@ -130,8 +164,8 @@ module ActionView
130
164
  # This is what child classes implement. No defaults are needed
131
165
  # because Resolver guarantees that the arguments are present and
132
166
  # normalized.
133
- def find_templates(name, prefix, partial, details, outside_app_allowed)
134
- raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details, outside_app_allowed) method"
167
+ def find_templates(name, prefix, partial, details, outside_app_allowed = false)
168
+ raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details, outside_app_allowed = false) method"
135
169
  end
136
170
 
137
171
  # Helpers that builds a path. Useful for building virtual paths.
@@ -143,9 +177,9 @@ module ActionView
143
177
  # always check the cache before hitting the resolver. Otherwise,
144
178
  # it always hits the resolver but if the key is present, check if the
145
179
  # resolver is fresher before returning it.
146
- def cached(key, path_info, details, locals) #:nodoc:
180
+ def cached(key, path_info, details, locals)
147
181
  name, prefix, partial = path_info
148
- locals = locals.map { |x| x.to_s }.sort!
182
+ locals = locals.map(&:to_s).sort!
149
183
 
150
184
  if key
151
185
  @cache.cache(key, name, prefix, partial, locals) do
@@ -157,7 +191,7 @@ module ActionView
157
191
  end
158
192
 
159
193
  # Ensures all the resolver information is set in the template.
160
- def decorate(templates, path_info, details, locals) #:nodoc:
194
+ def decorate(templates, path_info, details, locals)
161
195
  cached = nil
162
196
  templates.each do |t|
163
197
  t.locals = locals
@@ -170,117 +204,103 @@ module ActionView
170
204
 
171
205
  # An abstract class that implements a Resolver with path semantics.
172
206
  class PathResolver < Resolver #:nodoc:
173
- EXTENSIONS = { :locale => ".", :formats => ".", :variants => "+", :handlers => "." }
207
+ EXTENSIONS = { locale: ".", formats: ".", variants: "+", handlers: "." }
174
208
  DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}"
175
209
 
176
- def initialize(pattern=nil)
210
+ def initialize(pattern = nil)
177
211
  @pattern = pattern || DEFAULT_PATTERN
178
212
  super()
179
213
  end
180
214
 
181
215
  private
182
216
 
183
- def find_templates(name, prefix, partial, details, outside_app_allowed = false)
184
- path = Path.build(name, prefix, partial)
185
- query(path, details, details[:formats], outside_app_allowed)
186
- end
217
+ def find_templates(name, prefix, partial, details, outside_app_allowed = false)
218
+ path = Path.build(name, prefix, partial)
219
+ query(path, details, details[:formats], outside_app_allowed)
220
+ end
187
221
 
188
- def query(path, details, formats, outside_app_allowed)
189
- query = build_query(path, details)
222
+ def query(path, details, formats, outside_app_allowed)
223
+ query = build_query(path, details)
190
224
 
191
- template_paths = find_template_paths query
192
- template_paths = reject_files_external_to_app(template_paths) unless outside_app_allowed
225
+ template_paths = find_template_paths(query)
226
+ template_paths = reject_files_external_to_app(template_paths) unless outside_app_allowed
193
227
 
194
- template_paths.map { |template|
195
- handler, format, variant = extract_handler_and_format_and_variant(template, formats)
196
- contents = File.binread(template)
228
+ template_paths.map do |template|
229
+ handler, format, variant = extract_handler_and_format_and_variant(template)
230
+ contents = File.binread(template)
197
231
 
198
- Template.new(contents, File.expand_path(template), handler,
199
- :virtual_path => path.virtual,
200
- :format => format,
201
- :variant => variant,
202
- :updated_at => mtime(template)
203
- )
204
- }
205
- end
232
+ Template.new(contents, File.expand_path(template), handler,
233
+ virtual_path: path.virtual,
234
+ format: format,
235
+ variant: variant,
236
+ updated_at: mtime(template)
237
+ )
238
+ end
239
+ end
206
240
 
207
- def reject_files_external_to_app(files)
208
- files.reject { |filename| !inside_path?(@path, filename) }
209
- end
241
+ def reject_files_external_to_app(files)
242
+ files.reject { |filename| !inside_path?(@path, filename) }
243
+ end
210
244
 
211
- if RUBY_VERSION >= '2.2.0'
212
245
  def find_template_paths(query)
213
- Dir[query].reject { |filename|
246
+ Dir[query].uniq.reject do |filename|
214
247
  File.directory?(filename) ||
215
248
  # deals with case-insensitive file systems.
216
249
  !File.fnmatch(query, filename, File::FNM_EXTGLOB)
217
- }
250
+ end
218
251
  end
219
- else
220
- def find_template_paths(query)
221
- # deals with case-insensitive file systems.
222
- sanitizer = Hash.new { |h,dir| h[dir] = Dir["#{dir}/*"] }
223
252
 
224
- Dir[query].reject { |filename|
225
- File.directory?(filename) ||
226
- !sanitizer[File.dirname(filename)].include?(filename)
227
- }
253
+ def inside_path?(path, filename)
254
+ filename = File.expand_path(filename)
255
+ path = File.join(path, "")
256
+ filename.start_with?(path)
228
257
  end
229
- end
230
258
 
231
- def inside_path?(path, filename)
232
- filename = File.expand_path(filename)
233
- path = File.join(path, '')
234
- filename.start_with?(path)
235
- end
259
+ # Helper for building query glob string based on resolver's pattern.
260
+ def build_query(path, details)
261
+ query = @pattern.dup
236
262
 
237
- # Helper for building query glob string based on resolver's pattern.
238
- def build_query(path, details)
239
- query = @pattern.dup
263
+ prefix = path.prefix.empty? ? "" : "#{escape_entry(path.prefix)}\\1"
264
+ query.gsub!(/:prefix(\/)?/, prefix)
240
265
 
241
- prefix = path.prefix.empty? ? "" : "#{escape_entry(path.prefix)}\\1"
242
- query.gsub!(/\:prefix(\/)?/, prefix)
266
+ partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
267
+ query.gsub!(/:action/, partial)
243
268
 
244
- partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
245
- query.gsub!(/\:action/, partial)
269
+ details.each do |ext, candidates|
270
+ if ext == :variants && candidates == :any
271
+ query.gsub!(/:#{ext}/, "*")
272
+ else
273
+ query.gsub!(/:#{ext}/, "{#{candidates.compact.uniq.join(',')}}")
274
+ end
275
+ end
246
276
 
247
- details.each do |ext, variants|
248
- query.gsub!(/\:#{ext}/, "{#{variants.compact.uniq.join(',')}}")
277
+ File.expand_path(query, @path)
249
278
  end
250
279
 
251
- File.expand_path(query, @path)
252
- end
280
+ def escape_entry(entry)
281
+ entry.gsub(/[*?{}\[\]]/, '\\\\\\&'.freeze)
282
+ end
253
283
 
254
- def escape_entry(entry)
255
- entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
256
- end
284
+ # Returns the file mtime from the filesystem.
285
+ def mtime(p)
286
+ File.mtime(p)
287
+ end
257
288
 
258
- # Returns the file mtime from the filesystem.
259
- def mtime(p)
260
- File.mtime(p)
261
- end
289
+ # Extract handler, formats and variant from path. If a format cannot be found neither
290
+ # from the path, or the handler, we should return the array of formats given
291
+ # to the resolver.
292
+ def extract_handler_and_format_and_variant(path)
293
+ pieces = File.basename(path).split(".".freeze)
294
+ pieces.shift
262
295
 
263
- # Extract handler, formats and variant from path. If a format cannot be found neither
264
- # from the path, or the handler, we should return the array of formats given
265
- # to the resolver.
266
- def extract_handler_and_format_and_variant(path, default_formats)
267
- pieces = File.basename(path).split(".")
268
- pieces.shift
269
-
270
- extension = pieces.pop
271
- unless extension
272
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
273
- The file #{path} did not specify a template handler. The default is
274
- currently ERB, but will change to RAW in the future.
275
- MSG
276
- end
296
+ extension = pieces.pop
277
297
 
278
- handler = Template.handler_for_extension(extension)
279
- format, variant = pieces.last.split(EXTENSIONS[:variants], 2) if pieces.last
280
- format &&= Template::Types[format]
298
+ handler = Template.handler_for_extension(extension)
299
+ format, variant = pieces.last.split(EXTENSIONS[:variants], 2) if pieces.last
300
+ format &&= Template::Types[format]
281
301
 
282
- [handler, format, variant]
283
- end
302
+ [handler, format, variant]
303
+ end
284
304
  end
285
305
 
286
306
  # A resolver that loads files from the filesystem. It allows setting your own
@@ -306,7 +326,7 @@ module ActionView
306
326
  #
307
327
  # ActionController::Base.view_paths = FileSystemResolver.new(
308
328
  # Rails.root.join("app/views"),
309
- # ":prefix{/:locale}/:action{.:formats,}{+:variants,}{.:handlers,}"
329
+ # ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}",
310
330
  # )
311
331
  #
312
332
  # ==== Pattern format and variables
@@ -322,7 +342,7 @@ module ActionView
322
342
  # * <tt>:handlers</tt> - possible handlers (for example erb, haml, builder...)
323
343
  #
324
344
  class FileSystemResolver < PathResolver
325
- def initialize(path, pattern=nil)
345
+ def initialize(path, pattern = nil)
326
346
  raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
327
347
  super(pattern)
328
348
  @path = File.expand_path(path)
@@ -345,7 +365,11 @@ module ActionView
345
365
  query = escape_entry(File.join(@path, path))
346
366
 
347
367
  exts = EXTENSIONS.map do |ext, prefix|
348
- "{#{details[ext].compact.uniq.map { |e| "#{prefix}#{e}," }.join}}"
368
+ if ext == :variants && details[ext] == :any
369
+ "{#{prefix}*,}"
370
+ else
371
+ "{#{details[ext].compact.uniq.map { |e| "#{prefix}#{e}," }.join}}"
372
+ end
349
373
  end.join
350
374
 
351
375
  query + exts