omg-actionview 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +25 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +40 -0
  5. data/app/assets/javascripts/rails-ujs.esm.js +686 -0
  6. data/app/assets/javascripts/rails-ujs.js +630 -0
  7. data/lib/action_view/base.rb +316 -0
  8. data/lib/action_view/buffers.rb +165 -0
  9. data/lib/action_view/cache_expiry.rb +69 -0
  10. data/lib/action_view/context.rb +32 -0
  11. data/lib/action_view/dependency_tracker/erb_tracker.rb +159 -0
  12. data/lib/action_view/dependency_tracker/ruby_tracker.rb +43 -0
  13. data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
  14. data/lib/action_view/dependency_tracker.rb +41 -0
  15. data/lib/action_view/deprecator.rb +7 -0
  16. data/lib/action_view/digestor.rb +130 -0
  17. data/lib/action_view/flows.rb +75 -0
  18. data/lib/action_view/gem_version.rb +17 -0
  19. data/lib/action_view/helpers/active_model_helper.rb +54 -0
  20. data/lib/action_view/helpers/asset_tag_helper.rb +680 -0
  21. data/lib/action_view/helpers/asset_url_helper.rb +473 -0
  22. data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
  23. data/lib/action_view/helpers/cache_helper.rb +315 -0
  24. data/lib/action_view/helpers/capture_helper.rb +236 -0
  25. data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
  26. data/lib/action_view/helpers/controller_helper.rb +42 -0
  27. data/lib/action_view/helpers/csp_helper.rb +26 -0
  28. data/lib/action_view/helpers/csrf_helper.rb +35 -0
  29. data/lib/action_view/helpers/date_helper.rb +1266 -0
  30. data/lib/action_view/helpers/debug_helper.rb +38 -0
  31. data/lib/action_view/helpers/form_helper.rb +2765 -0
  32. data/lib/action_view/helpers/form_options_helper.rb +927 -0
  33. data/lib/action_view/helpers/form_tag_helper.rb +1088 -0
  34. data/lib/action_view/helpers/javascript_helper.rb +96 -0
  35. data/lib/action_view/helpers/number_helper.rb +165 -0
  36. data/lib/action_view/helpers/output_safety_helper.rb +70 -0
  37. data/lib/action_view/helpers/rendering_helper.rb +218 -0
  38. data/lib/action_view/helpers/sanitize_helper.rb +201 -0
  39. data/lib/action_view/helpers/tag_helper.rb +621 -0
  40. data/lib/action_view/helpers/tags/base.rb +138 -0
  41. data/lib/action_view/helpers/tags/check_box.rb +65 -0
  42. data/lib/action_view/helpers/tags/checkable.rb +18 -0
  43. data/lib/action_view/helpers/tags/collection_check_boxes.rb +37 -0
  44. data/lib/action_view/helpers/tags/collection_helpers.rb +118 -0
  45. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
  46. data/lib/action_view/helpers/tags/collection_select.rb +33 -0
  47. data/lib/action_view/helpers/tags/color_field.rb +26 -0
  48. data/lib/action_view/helpers/tags/date_field.rb +14 -0
  49. data/lib/action_view/helpers/tags/date_select.rb +75 -0
  50. data/lib/action_view/helpers/tags/datetime_field.rb +39 -0
  51. data/lib/action_view/helpers/tags/datetime_local_field.rb +29 -0
  52. data/lib/action_view/helpers/tags/datetime_select.rb +10 -0
  53. data/lib/action_view/helpers/tags/email_field.rb +10 -0
  54. data/lib/action_view/helpers/tags/file_field.rb +26 -0
  55. data/lib/action_view/helpers/tags/grouped_collection_select.rb +34 -0
  56. data/lib/action_view/helpers/tags/hidden_field.rb +14 -0
  57. data/lib/action_view/helpers/tags/label.rb +84 -0
  58. data/lib/action_view/helpers/tags/month_field.rb +14 -0
  59. data/lib/action_view/helpers/tags/number_field.rb +20 -0
  60. data/lib/action_view/helpers/tags/password_field.rb +14 -0
  61. data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
  62. data/lib/action_view/helpers/tags/radio_button.rb +32 -0
  63. data/lib/action_view/helpers/tags/range_field.rb +10 -0
  64. data/lib/action_view/helpers/tags/search_field.rb +27 -0
  65. data/lib/action_view/helpers/tags/select.rb +45 -0
  66. data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
  67. data/lib/action_view/helpers/tags/tel_field.rb +10 -0
  68. data/lib/action_view/helpers/tags/text_area.rb +24 -0
  69. data/lib/action_view/helpers/tags/text_field.rb +33 -0
  70. data/lib/action_view/helpers/tags/time_field.rb +23 -0
  71. data/lib/action_view/helpers/tags/time_select.rb +10 -0
  72. data/lib/action_view/helpers/tags/time_zone_select.rb +25 -0
  73. data/lib/action_view/helpers/tags/translator.rb +39 -0
  74. data/lib/action_view/helpers/tags/url_field.rb +10 -0
  75. data/lib/action_view/helpers/tags/week_field.rb +14 -0
  76. data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
  77. data/lib/action_view/helpers/tags.rb +47 -0
  78. data/lib/action_view/helpers/text_helper.rb +568 -0
  79. data/lib/action_view/helpers/translation_helper.rb +161 -0
  80. data/lib/action_view/helpers/url_helper.rb +812 -0
  81. data/lib/action_view/helpers.rb +68 -0
  82. data/lib/action_view/layouts.rb +434 -0
  83. data/lib/action_view/locale/en.yml +56 -0
  84. data/lib/action_view/log_subscriber.rb +132 -0
  85. data/lib/action_view/lookup_context.rb +299 -0
  86. data/lib/action_view/model_naming.rb +14 -0
  87. data/lib/action_view/path_registry.rb +57 -0
  88. data/lib/action_view/path_set.rb +84 -0
  89. data/lib/action_view/railtie.rb +132 -0
  90. data/lib/action_view/record_identifier.rb +118 -0
  91. data/lib/action_view/render_parser/prism_render_parser.rb +139 -0
  92. data/lib/action_view/render_parser/ripper_render_parser.rb +350 -0
  93. data/lib/action_view/render_parser.rb +40 -0
  94. data/lib/action_view/renderer/abstract_renderer.rb +186 -0
  95. data/lib/action_view/renderer/collection_renderer.rb +204 -0
  96. data/lib/action_view/renderer/object_renderer.rb +34 -0
  97. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +120 -0
  98. data/lib/action_view/renderer/partial_renderer.rb +267 -0
  99. data/lib/action_view/renderer/renderer.rb +107 -0
  100. data/lib/action_view/renderer/streaming_template_renderer.rb +107 -0
  101. data/lib/action_view/renderer/template_renderer.rb +115 -0
  102. data/lib/action_view/rendering.rb +190 -0
  103. data/lib/action_view/routing_url_for.rb +149 -0
  104. data/lib/action_view/tasks/cache_digests.rake +25 -0
  105. data/lib/action_view/template/error.rb +264 -0
  106. data/lib/action_view/template/handlers/builder.rb +25 -0
  107. data/lib/action_view/template/handlers/erb/erubi.rb +85 -0
  108. data/lib/action_view/template/handlers/erb.rb +157 -0
  109. data/lib/action_view/template/handlers/html.rb +11 -0
  110. data/lib/action_view/template/handlers/raw.rb +11 -0
  111. data/lib/action_view/template/handlers.rb +66 -0
  112. data/lib/action_view/template/html.rb +33 -0
  113. data/lib/action_view/template/inline.rb +22 -0
  114. data/lib/action_view/template/raw_file.rb +25 -0
  115. data/lib/action_view/template/renderable.rb +30 -0
  116. data/lib/action_view/template/resolver.rb +212 -0
  117. data/lib/action_view/template/sources/file.rb +17 -0
  118. data/lib/action_view/template/sources.rb +13 -0
  119. data/lib/action_view/template/text.rb +32 -0
  120. data/lib/action_view/template/types.rb +50 -0
  121. data/lib/action_view/template.rb +580 -0
  122. data/lib/action_view/template_details.rb +66 -0
  123. data/lib/action_view/template_path.rb +66 -0
  124. data/lib/action_view/test_case.rb +449 -0
  125. data/lib/action_view/testing/resolvers.rb +44 -0
  126. data/lib/action_view/unbound_template.rb +67 -0
  127. data/lib/action_view/version.rb +10 -0
  128. data/lib/action_view/view_paths.rb +117 -0
  129. data/lib/action_view.rb +104 -0
  130. metadata +275 -0
@@ -0,0 +1,264 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/enumerable"
4
+ require "active_support/syntax_error_proxy"
5
+
6
+ module ActionView
7
+ # = Action View Errors
8
+ class ActionViewError < StandardError # :nodoc:
9
+ end
10
+
11
+ class EncodingError < StandardError # :nodoc:
12
+ end
13
+
14
+ class WrongEncodingError < EncodingError # :nodoc:
15
+ def initialize(string, encoding)
16
+ @string, @encoding = string, encoding
17
+ end
18
+
19
+ def message
20
+ @string.force_encoding(Encoding::ASCII_8BIT)
21
+ "Your template was not saved as valid #{@encoding}. Please " \
22
+ "either specify #{@encoding} as the encoding for your template " \
23
+ "in your text editor, or mark the template with its " \
24
+ "encoding by inserting the following as the first line " \
25
+ "of the template:\n\n# encoding: <name of correct encoding>.\n\n" \
26
+ "The source of your template was:\n\n#{@string}"
27
+ end
28
+ end
29
+
30
+ class MissingTemplate < ActionViewError # :nodoc:
31
+ attr_reader :path, :paths, :prefixes, :partial
32
+
33
+ def initialize(paths, path, prefixes, partial, details, *)
34
+ if partial && path.present?
35
+ path = path.sub(%r{([^/]+)$}, "_\\1")
36
+ end
37
+
38
+ @path = path
39
+ @paths = paths
40
+ @prefixes = Array(prefixes)
41
+ @partial = partial
42
+ template_type = if partial
43
+ "partial"
44
+ elsif /layouts/i.match?(path)
45
+ "layout"
46
+ else
47
+ "template"
48
+ end
49
+
50
+ searched_paths = @prefixes.map { |prefix| [prefix, path].join("/") }
51
+
52
+ out = "Missing #{template_type} #{searched_paths.join(", ")} with #{details.inspect}.\n\nSearched in:\n"
53
+ out += paths.compact.map { |p| " * #{p.to_s.inspect}\n" }.join
54
+ super out
55
+ end
56
+
57
+ if defined?(DidYouMean::Correctable) && defined?(DidYouMean::Jaro)
58
+ include DidYouMean::Correctable
59
+
60
+ class Results # :nodoc:
61
+ Result = Struct.new(:path, :score)
62
+
63
+ def initialize(size)
64
+ @size = size
65
+ @results = []
66
+ end
67
+
68
+ def to_a
69
+ @results.map(&:path)
70
+ end
71
+
72
+ def should_record?(score)
73
+ if @results.size < @size
74
+ true
75
+ else
76
+ score < @results.last.score
77
+ end
78
+ end
79
+
80
+ def add(path, score)
81
+ if should_record?(score)
82
+ @results << Result.new(path, score)
83
+ @results.sort_by!(&:score)
84
+ @results.pop if @results.size > @size
85
+ end
86
+ end
87
+ end
88
+
89
+ # Apps may have thousands of candidate templates so we attempt to
90
+ # generate the suggestions as efficiently as possible.
91
+ # First we split templates into prefixes and basenames, so that those can
92
+ # be matched separately.
93
+ def corrections
94
+ candidates = paths.flat_map(&:all_template_paths).uniq
95
+
96
+ if partial
97
+ candidates.select!(&:partial?)
98
+ else
99
+ candidates.reject!(&:partial?)
100
+ end
101
+
102
+ # Group by possible prefixes
103
+ files_by_dir = candidates.group_by(&:prefix)
104
+ files_by_dir.transform_values! do |files|
105
+ files.map do |file|
106
+ # Remove prefix
107
+ File.basename(file.to_s)
108
+ end
109
+ end
110
+
111
+ # No suggestions if there's an exact match, but wrong details
112
+ if prefixes.any? { |prefix| files_by_dir[prefix]&.include?(path) }
113
+ return []
114
+ end
115
+
116
+ cached_distance = Hash.new do |h, args|
117
+ h[args] = -DidYouMean::Jaro.distance(*args)
118
+ end
119
+
120
+ results = Results.new(6)
121
+
122
+ files_by_dir.keys.index_with do |dirname|
123
+ prefixes.map do |prefix|
124
+ cached_distance[[prefix, dirname]]
125
+ end.min
126
+ end.sort_by(&:last).each do |dirname, dirweight|
127
+ # If our directory's score makes it impossible to find a better match
128
+ # we can prune this search branch.
129
+ next unless results.should_record?(dirweight - 1.0)
130
+
131
+ files = files_by_dir[dirname]
132
+
133
+ files.each do |file|
134
+ fileweight = cached_distance[[path, file]]
135
+ score = dirweight + fileweight
136
+
137
+ results.add(File.join(dirname, file), score)
138
+ end
139
+ end
140
+
141
+ if partial
142
+ results.to_a.map { |res| res.sub(%r{_([^/]+)\z}, "\\1") }
143
+ else
144
+ results.to_a
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ class Template
151
+ # The Template::Error exception is raised when the compilation or rendering of the template
152
+ # fails. This exception then gathers a bunch of intimate details and uses it to report a
153
+ # precise exception message.
154
+ class Error < ActionViewError # :nodoc:
155
+ SOURCE_CODE_RADIUS = 3
156
+
157
+ # Override to prevent #cause resetting during re-raise.
158
+ attr_reader :cause
159
+
160
+ attr_reader :template
161
+
162
+ def initialize(template)
163
+ super($!.message)
164
+ @cause = $!
165
+ if @cause.is_a?(SyntaxError)
166
+ @cause = ActiveSupport::SyntaxErrorProxy.new(@cause)
167
+ end
168
+ @template, @sub_templates = template, nil
169
+ end
170
+
171
+ def backtrace
172
+ @cause.backtrace
173
+ end
174
+
175
+ def backtrace_locations
176
+ @cause.backtrace_locations
177
+ end
178
+
179
+ def file_name
180
+ @template.identifier
181
+ end
182
+
183
+ def sub_template_message
184
+ if @sub_templates
185
+ "Trace of template inclusion: " +
186
+ @sub_templates.collect(&:inspect).join(", ")
187
+ else
188
+ ""
189
+ end
190
+ end
191
+
192
+ def source_extract(indentation = 0)
193
+ return [] unless num = line_number
194
+ num = num.to_i
195
+
196
+ source_code = @template.encode!.split("\n")
197
+
198
+ start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
199
+ end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
200
+
201
+ indent = end_on_line.to_s.size + indentation
202
+ return [] unless source_code = source_code[start_on_line..end_on_line]
203
+
204
+ formatted_code_for(source_code, start_on_line, indent)
205
+ end
206
+
207
+ def sub_template_of(template_path)
208
+ @sub_templates ||= []
209
+ @sub_templates << template_path
210
+ end
211
+
212
+ def line_number
213
+ @line_number ||=
214
+ if file_name
215
+ regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
216
+ $1 if message =~ regexp || backtrace.find { |line| line =~ regexp }
217
+ end
218
+ end
219
+
220
+ def annotated_source_code
221
+ source_extract(4)
222
+ end
223
+
224
+ private
225
+ def source_location
226
+ if line_number
227
+ "on line ##{line_number} of "
228
+ else
229
+ "in "
230
+ end + file_name
231
+ end
232
+
233
+ def formatted_code_for(source_code, line_counter, indent)
234
+ indent_template = "%#{indent}s: %s"
235
+ source_code.map do |line|
236
+ line_counter += 1
237
+ indent_template % [line_counter, line]
238
+ end
239
+ end
240
+ end
241
+ end
242
+
243
+ TemplateError = Template::Error
244
+
245
+ class SyntaxErrorInTemplate < TemplateError # :nodoc:
246
+ def initialize(template, offending_code_string)
247
+ @offending_code_string = offending_code_string
248
+ super(template)
249
+ end
250
+
251
+ def message
252
+ <<~MESSAGE
253
+ Encountered a syntax error while rendering template: check #{@offending_code_string}
254
+ MESSAGE
255
+ end
256
+
257
+ def annotated_source_code
258
+ @offending_code_string.split("\n").map.with_index(1) { |line, index|
259
+ indentation = " " * 4
260
+ "#{index}:#{indentation}#{line}"
261
+ }
262
+ end
263
+ end
264
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Template::Handlers
5
+ class Builder
6
+ class_attribute :default_format, default: :xml
7
+
8
+ def call(template, source)
9
+ require_engine
10
+ # the double assignment is to silence "assigned but unused variable" warnings
11
+ "xml = xml = ::Builder::XmlMarkup.new(indent: 2, target: output_buffer.raw);" \
12
+ "#{source};" \
13
+ "output_buffer.to_s"
14
+ end
15
+
16
+ private
17
+ def require_engine # :doc:
18
+ @required ||= begin
19
+ require "builder"
20
+ true
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erubi"
4
+
5
+ module ActionView
6
+ class Template
7
+ module Handlers
8
+ class ERB
9
+ class Erubi < ::Erubi::Engine
10
+ # :nodoc: all
11
+ def initialize(input, properties = {})
12
+ @newline_pending = 0
13
+
14
+ # Dup properties so that we don't modify argument
15
+ properties = Hash[properties]
16
+
17
+ properties[:bufvar] ||= "@output_buffer"
18
+ properties[:preamble] ||= ""
19
+ properties[:postamble] ||= "#{properties[:bufvar]}"
20
+
21
+ # Tell Eruby that whether template will be compiled with `frozen_string_literal: true`
22
+ properties[:freeze_template_literals] = !Template.frozen_string_literal
23
+
24
+ properties[:escapefunc] = ""
25
+
26
+ super
27
+ end
28
+
29
+ private
30
+ def add_text(text)
31
+ return if text.empty?
32
+
33
+ if text == "\n"
34
+ @newline_pending += 1
35
+ else
36
+ with_buffer do
37
+ src << ".safe_append='"
38
+ src << "\n" * @newline_pending if @newline_pending > 0
39
+ src << text.gsub(/['\\]/, '\\\\\&') << @text_end
40
+ end
41
+ @newline_pending = 0
42
+ end
43
+ end
44
+
45
+ BLOCK_EXPR = /((\s|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
46
+
47
+ def add_expression(indicator, code)
48
+ flush_newline_if_pending(src)
49
+
50
+ with_buffer do
51
+ if (indicator == "==") || @escape
52
+ src << ".safe_expr_append="
53
+ else
54
+ src << ".append="
55
+ end
56
+
57
+ if BLOCK_EXPR.match?(code)
58
+ src << " " << code
59
+ else
60
+ src << "(" << code << ")"
61
+ end
62
+ end
63
+ end
64
+
65
+ def add_code(code)
66
+ flush_newline_if_pending(src)
67
+ super
68
+ end
69
+
70
+ def add_postamble(_)
71
+ flush_newline_if_pending(src)
72
+ super
73
+ end
74
+
75
+ def flush_newline_if_pending(src)
76
+ if @newline_pending > 0
77
+ with_buffer { src << ".safe_append='#{"\n" * @newline_pending}" << @text_end }
78
+ @newline_pending = 0
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "strscan"
4
+ require "active_support/core_ext/erb/util"
5
+
6
+ module ActionView
7
+ class Template
8
+ module Handlers
9
+ class ERB
10
+ autoload :Erubi, "action_view/template/handlers/erb/erubi"
11
+
12
+ # Specify trim mode for the ERB compiler. Defaults to '-'.
13
+ # See ERB documentation for suitable values.
14
+ class_attribute :erb_trim_mode, default: "-"
15
+
16
+ # Default implementation used.
17
+ class_attribute :erb_implementation, default: Erubi
18
+
19
+ # Do not escape templates of these mime types.
20
+ class_attribute :escape_ignore_list, default: ["text/plain"]
21
+
22
+ # Strip trailing newlines from rendered output
23
+ class_attribute :strip_trailing_newlines, default: false
24
+
25
+ ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
26
+
27
+ LocationParsingError = Class.new(StandardError) # :nodoc:
28
+
29
+ def self.call(template, source)
30
+ new.call(template, source)
31
+ end
32
+
33
+ def supports_streaming?
34
+ true
35
+ end
36
+
37
+ def handles_encoding?
38
+ true
39
+ end
40
+
41
+ # Translate an error location returned by ErrorHighlight to the correct
42
+ # source location inside the template.
43
+ def translate_location(spot, backtrace_location, source)
44
+ # Tokenize the source line
45
+ tokens = ::ERB::Util.tokenize(source.lines[backtrace_location.lineno - 1])
46
+ new_first_column = find_offset(spot[:snippet], tokens, spot[:first_column])
47
+ lineno_delta = spot[:first_lineno] - backtrace_location.lineno
48
+ spot[:first_lineno] -= lineno_delta
49
+ spot[:last_lineno] -= lineno_delta
50
+
51
+ column_delta = spot[:first_column] - new_first_column
52
+ spot[:first_column] -= column_delta
53
+ spot[:last_column] -= column_delta
54
+ spot[:script_lines] = source.lines
55
+
56
+ spot
57
+ rescue NotImplementedError, LocationParsingError
58
+ nil
59
+ end
60
+
61
+ def call(template, source)
62
+ # First, convert to BINARY, so in case the encoding is
63
+ # wrong, we can still find an encoding tag
64
+ # (<%# encoding %>) inside the String using a regular
65
+ # expression
66
+ template_source = source.b
67
+
68
+ erb = template_source.gsub(ENCODING_TAG, "")
69
+ encoding = $2
70
+
71
+ erb.force_encoding valid_encoding(source.dup, encoding)
72
+
73
+ # Always make sure we return a String in the default_internal
74
+ erb.encode!
75
+
76
+ # Strip trailing newlines from the template if enabled
77
+ erb.chomp! if strip_trailing_newlines
78
+
79
+ options = {
80
+ escape: (self.class.escape_ignore_list.include? template.type),
81
+ trim: (self.class.erb_trim_mode == "-")
82
+ }
83
+
84
+ if ActionView::Base.annotate_rendered_view_with_filenames && template.format == :html
85
+ options[:preamble] = "@output_buffer.safe_append='<!-- BEGIN #{template.short_identifier} -->';"
86
+ options[:postamble] = "@output_buffer.safe_append='<!-- END #{template.short_identifier} -->';@output_buffer"
87
+ end
88
+
89
+ self.class.erb_implementation.new(erb, options).src
90
+ end
91
+
92
+ private
93
+ def valid_encoding(string, encoding)
94
+ # If a magic encoding comment was found, tag the
95
+ # String with this encoding. This is for a case
96
+ # where the original String was assumed to be,
97
+ # for instance, UTF-8, but a magic comment
98
+ # proved otherwise
99
+ string.force_encoding(encoding) if encoding
100
+
101
+ # If the String is valid, return the encoding we found
102
+ return string.encoding if string.valid_encoding?
103
+
104
+ # Otherwise, raise an exception
105
+ raise WrongEncodingError.new(string, string.encoding)
106
+ end
107
+
108
+ def find_offset(compiled, source_tokens, error_column)
109
+ compiled = StringScanner.new(compiled)
110
+
111
+ passed_tokens = []
112
+
113
+ while tok = source_tokens.shift
114
+ tok_name, str = *tok
115
+ case tok_name
116
+ when :TEXT
117
+ loop do
118
+ break if compiled.match?(str)
119
+ compiled.getch
120
+ end
121
+ raise LocationParsingError unless compiled.scan(str)
122
+ when :CODE
123
+ if compiled.pos > error_column
124
+ raise LocationParsingError, "We went too far"
125
+ end
126
+
127
+ if compiled.pos + str.bytesize >= error_column
128
+ offset = error_column - compiled.pos
129
+ return passed_tokens.map(&:last).join.bytesize + offset
130
+ else
131
+ unless compiled.scan(str)
132
+ raise LocationParsingError, "Couldn't find code snippet"
133
+ end
134
+ end
135
+ when :OPEN
136
+ next_tok = source_tokens.first.last
137
+ loop do
138
+ break if compiled.match?(next_tok)
139
+ compiled.getch
140
+ end
141
+ when :CLOSE
142
+ next_tok = source_tokens.first.last
143
+ loop do
144
+ break if compiled.match?(next_tok)
145
+ compiled.getch
146
+ end
147
+ else
148
+ raise LocationParsingError, "Not implemented: #{tok.first}"
149
+ end
150
+
151
+ passed_tokens << tok
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Template::Handlers
5
+ class Html < Raw
6
+ def call(template, source)
7
+ "ActionView::OutputBuffer.new #{super}"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Template::Handlers
5
+ class Raw
6
+ def call(template, source)
7
+ "#{source.inspect}.html_safe;"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView # :nodoc:
4
+ class Template # :nodoc:
5
+ # = Action View Template Handlers
6
+ module Handlers # :nodoc:
7
+ autoload :Raw, "action_view/template/handlers/raw"
8
+ autoload :ERB, "action_view/template/handlers/erb"
9
+ autoload :Html, "action_view/template/handlers/html"
10
+ autoload :Builder, "action_view/template/handlers/builder"
11
+
12
+ def self.extended(base)
13
+ base.register_default_template_handler :raw, Raw.new
14
+ base.register_template_handler :erb, ERB.new
15
+ base.register_template_handler :html, Html.new
16
+ base.register_template_handler :builder, Builder.new
17
+ base.register_template_handler :ruby, lambda { |_, source| source }
18
+ end
19
+
20
+ @@template_handlers = {}
21
+ @@default_template_handlers = nil
22
+
23
+ def self.extensions
24
+ @@template_extensions ||= @@template_handlers.keys
25
+ end
26
+
27
+ # Register an object that knows how to handle template files with the given
28
+ # extensions. This can be used to implement new template types.
29
+ # The handler must respond to +:call+, which will be passed the template
30
+ # and should return the rendered template as a String.
31
+ def register_template_handler(*extensions, handler)
32
+ raise(ArgumentError, "Extension is required") if extensions.empty?
33
+ extensions.each do |extension|
34
+ @@template_handlers[extension.to_sym] = handler
35
+ end
36
+ @@template_extensions = nil
37
+ end
38
+
39
+ # Opposite to register_template_handler.
40
+ def unregister_template_handler(*extensions)
41
+ extensions.each do |extension|
42
+ handler = @@template_handlers.delete extension.to_sym
43
+ @@default_template_handlers = nil if @@default_template_handlers == handler
44
+ end
45
+ @@template_extensions = nil
46
+ end
47
+
48
+ def template_handler_extensions
49
+ @@template_handlers.keys.map(&:to_s).sort
50
+ end
51
+
52
+ def registered_template_handler(extension)
53
+ extension && @@template_handlers[extension.to_sym]
54
+ end
55
+
56
+ def register_default_template_handler(extension, klass)
57
+ register_template_handler(extension, klass)
58
+ @@default_template_handlers = klass
59
+ end
60
+
61
+ def handler_for_extension(extension)
62
+ registered_template_handler(extension) || @@default_template_handlers
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView # :nodoc:
4
+ class Template # :nodoc:
5
+ # = Action View HTML Template
6
+ class HTML # :nodoc:
7
+ attr_reader :type
8
+
9
+ def initialize(string, type)
10
+ @string = string.to_s
11
+ @type = type
12
+ end
13
+
14
+ def identifier
15
+ "html template"
16
+ end
17
+
18
+ alias_method :inspect, :identifier
19
+
20
+ def to_str
21
+ ERB::Util.h(@string)
22
+ end
23
+
24
+ def render(*args)
25
+ to_str
26
+ end
27
+
28
+ def format
29
+ @type
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView # :nodoc:
4
+ class Template # :nodoc:
5
+ class Inline < Template # :nodoc:
6
+ # This finalizer is needed (and exactly with a proc inside another proc)
7
+ # otherwise templates leak in development.
8
+ Finalizer = proc do |method_name, mod| # :nodoc:
9
+ proc do
10
+ mod.module_eval do
11
+ remove_possible_method method_name
12
+ end
13
+ end
14
+ end
15
+
16
+ def compile(mod)
17
+ super
18
+ ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView # :nodoc:
4
+ class Template # :nodoc:
5
+ # = Action View RawFile Template
6
+ class RawFile # :nodoc:
7
+ attr_accessor :type, :format
8
+
9
+ def initialize(filename)
10
+ @filename = filename.to_s
11
+ extname = ::File.extname(filename).delete(".")
12
+ @type = Template::Types[extname] || Template::Types[:text]
13
+ @format = @type.symbol
14
+ end
15
+
16
+ def identifier
17
+ @filename
18
+ end
19
+
20
+ def render(*args)
21
+ ::File.read(@filename)
22
+ end
23
+ end
24
+ end
25
+ end