omg-actionview 8.0.0.alpha1

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 (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