1
1
# frozen_string_literal: true
2
2
3
+ require "active_support/code_generator"
3
4
require "active_support/core_ext/enumerable"
4
5
require "active_support/core_ext/string/output_safety"
6
+ require "active_support/core_ext/string/inflections"
5
7
require "set"
6
8
require "action_view/helpers/capture_helper"
7
9
require "action_view/helpers/output_safety_helper"
@@ -46,8 +48,166 @@ class TagBuilder # :nodoc:
46
48
include CaptureHelper
47
49
include OutputSafetyHelper
48
50
49
- HTML_VOID_ELEMENTS = %i( area base br col embed hr img input keygen link meta param source track wbr ) . to_set
50
- SVG_SELF_CLOSING_ELEMENTS = %i( animate animateMotion animateTransform circle ellipse line path polygon polyline rect set stop use view ) . to_set
51
+ def self . define_element ( name , code_generator :, method_name : name . to_s . underscore )
52
+ code_generator . define_cached_method ( method_name , namespace : :tag_builder ) do |batch |
53
+ batch . push ( <<~RUBY ) unless instance_methods . include? ( method_name . to_sym )
54
+ def #{ method_name } (content = nil, escape: true, **options, &block)
55
+ tag_string(#{ name . inspect } , content, escape: escape, **options, &block)
56
+ end
57
+ RUBY
58
+ end
59
+ end
60
+
61
+ def self . define_void_element ( name , code_generator :, method_name : name . to_s . underscore , self_closing : false )
62
+ code_generator . define_cached_method ( method_name , namespace : :tag_builder ) do |batch |
63
+ batch . push ( <<~RUBY )
64
+ def #{ method_name } (content = nil, escape: true, **options, &block)
65
+ if content || block
66
+ tag_string(#{ name . inspect } , content, escape: escape, **options, &block)
67
+ else
68
+ void_tag_string(#{ name . inspect } , options, escape, #{ self_closing } )
69
+ end
70
+ end
71
+ RUBY
72
+ end
73
+ end
74
+
75
+ def self . define_self_closing_element ( name , **options )
76
+ define_void_element ( name , self_closing : true , **options )
77
+ end
78
+
79
+ ActiveSupport ::CodeGenerator . batch ( self , __FILE__ , __LINE__ ) do |code_generator |
80
+ define_void_element :area , code_generator : code_generator
81
+ define_void_element :base , code_generator : code_generator
82
+ define_void_element :br , code_generator : code_generator
83
+ define_void_element :col , code_generator : code_generator
84
+ define_void_element :embed , code_generator : code_generator
85
+ define_void_element :hr , code_generator : code_generator
86
+ define_void_element :img , code_generator : code_generator
87
+ define_void_element :input , code_generator : code_generator
88
+ define_void_element :keygen , code_generator : code_generator
89
+ define_void_element :link , code_generator : code_generator
90
+ define_void_element :meta , code_generator : code_generator
91
+ define_void_element :source , code_generator : code_generator
92
+ define_void_element :track , code_generator : code_generator
93
+ define_void_element :wbr , code_generator : code_generator
94
+
95
+ define_self_closing_element :animate , code_generator : code_generator
96
+ define_self_closing_element :animateMotion , code_generator : code_generator
97
+ define_self_closing_element :animateTransform , code_generator : code_generator
98
+ define_self_closing_element :circle , code_generator : code_generator
99
+ define_self_closing_element :ellipse , code_generator : code_generator
100
+ define_self_closing_element :line , code_generator : code_generator
101
+ define_self_closing_element :path , code_generator : code_generator
102
+ define_self_closing_element :polygon , code_generator : code_generator
103
+ define_self_closing_element :polyline , code_generator : code_generator
104
+ define_self_closing_element :rect , code_generator : code_generator
105
+ define_self_closing_element :set , code_generator : code_generator
106
+ define_self_closing_element :stop , code_generator : code_generator
107
+ define_self_closing_element :use , code_generator : code_generator
108
+ define_self_closing_element :view , code_generator : code_generator
109
+
110
+ define_element :a , code_generator : code_generator
111
+ define_element :abbr , code_generator : code_generator
112
+ define_element :address , code_generator : code_generator
113
+ define_element :article , code_generator : code_generator
114
+ define_element :aside , code_generator : code_generator
115
+ define_element :audio , code_generator : code_generator
116
+ define_element :b , code_generator : code_generator
117
+ define_element :bdi , code_generator : code_generator
118
+ define_element :bdo , code_generator : code_generator
119
+ define_element :blockquote , code_generator : code_generator
120
+ define_element :body , code_generator : code_generator
121
+ define_element :button , code_generator : code_generator
122
+ define_element :canvas , code_generator : code_generator
123
+ define_element :caption , code_generator : code_generator
124
+ define_element :cite , code_generator : code_generator
125
+ define_element :code , code_generator : code_generator
126
+ define_element :colgroup , code_generator : code_generator
127
+ define_element :data , code_generator : code_generator
128
+ define_element :datalist , code_generator : code_generator
129
+ define_element :dd , code_generator : code_generator
130
+ define_element :del , code_generator : code_generator
131
+ define_element :details , code_generator : code_generator
132
+ define_element :dfn , code_generator : code_generator
133
+ define_element :dialog , code_generator : code_generator
134
+ define_element :div , code_generator : code_generator
135
+ define_element :dl , code_generator : code_generator
136
+ define_element :dt , code_generator : code_generator
137
+ define_element :em , code_generator : code_generator
138
+ define_element :fieldset , code_generator : code_generator
139
+ define_element :figcaption , code_generator : code_generator
140
+ define_element :figure , code_generator : code_generator
141
+ define_element :footer , code_generator : code_generator
142
+ define_element :form , code_generator : code_generator
143
+ define_element :h1 , code_generator : code_generator
144
+ define_element :h2 , code_generator : code_generator
145
+ define_element :h3 , code_generator : code_generator
146
+ define_element :h4 , code_generator : code_generator
147
+ define_element :h5 , code_generator : code_generator
148
+ define_element :h6 , code_generator : code_generator
149
+ define_element :head , code_generator : code_generator
150
+ define_element :header , code_generator : code_generator
151
+ define_element :hgroup , code_generator : code_generator
152
+ define_element :html , code_generator : code_generator
153
+ define_element :i , code_generator : code_generator
154
+ define_element :iframe , code_generator : code_generator
155
+ define_element :ins , code_generator : code_generator
156
+ define_element :kbd , code_generator : code_generator
157
+ define_element :label , code_generator : code_generator
158
+ define_element :legend , code_generator : code_generator
159
+ define_element :li , code_generator : code_generator
160
+ define_element :main , code_generator : code_generator
161
+ define_element :map , code_generator : code_generator
162
+ define_element :mark , code_generator : code_generator
163
+ define_element :menu , code_generator : code_generator
164
+ define_element :meter , code_generator : code_generator
165
+ define_element :nav , code_generator : code_generator
166
+ define_element :noscript , code_generator : code_generator
167
+ define_element :object , code_generator : code_generator
168
+ define_element :ol , code_generator : code_generator
169
+ define_element :optgroup , code_generator : code_generator
170
+ define_element :option , code_generator : code_generator
171
+ define_element :output , code_generator : code_generator
172
+ define_element :p , code_generator : code_generator
173
+ define_element :picture , code_generator : code_generator
174
+ define_element :portal , code_generator : code_generator
175
+ define_element :pre , code_generator : code_generator
176
+ define_element :progress , code_generator : code_generator
177
+ define_element :q , code_generator : code_generator
178
+ define_element :rp , code_generator : code_generator
179
+ define_element :rt , code_generator : code_generator
180
+ define_element :ruby , code_generator : code_generator
181
+ define_element :s , code_generator : code_generator
182
+ define_element :samp , code_generator : code_generator
183
+ define_element :script , code_generator : code_generator
184
+ define_element :search , code_generator : code_generator
185
+ define_element :section , code_generator : code_generator
186
+ define_element :select , code_generator : code_generator
187
+ define_element :slot , code_generator : code_generator
188
+ define_element :small , code_generator : code_generator
189
+ define_element :span , code_generator : code_generator
190
+ define_element :strong , code_generator : code_generator
191
+ define_element :style , code_generator : code_generator
192
+ define_element :sub , code_generator : code_generator
193
+ define_element :summary , code_generator : code_generator
194
+ define_element :sup , code_generator : code_generator
195
+ define_element :table , code_generator : code_generator
196
+ define_element :tbody , code_generator : code_generator
197
+ define_element :td , code_generator : code_generator
198
+ define_element :template , code_generator : code_generator
199
+ define_element :textarea , code_generator : code_generator
200
+ define_element :tfoot , code_generator : code_generator
201
+ define_element :th , code_generator : code_generator
202
+ define_element :thead , code_generator : code_generator
203
+ define_element :time , code_generator : code_generator
204
+ define_element :title , code_generator : code_generator
205
+ define_element :tr , code_generator : code_generator
206
+ define_element :u , code_generator : code_generator
207
+ define_element :ul , code_generator : code_generator
208
+ define_element :var , code_generator : code_generator
209
+ define_element :video , code_generator : code_generator
210
+ end
51
211
52
212
def initialize ( view_context )
53
213
@view_context = view_context
@@ -62,24 +222,19 @@ def attributes(attributes)
62
222
tag_options ( attributes . to_h ) . to_s . strip . html_safe
63
223
end
64
224
65
- def p ( *arguments , **options , &block )
66
- tag_string ( :p , *arguments , **options , &block )
225
+ def tag_string ( name , content = nil , escape : true , **options , &block )
226
+ content = @view_context . capture ( self , &block ) if block
227
+
228
+ content_tag_string ( name , content , options , escape )
67
229
end
68
230
69
- def tag_string ( name , content = nil , escape : true , **options , &block )
70
- content = @view_context . capture ( self , &block ) if block_given?
71
- self_closing = SVG_SELF_CLOSING_ELEMENTS . include? ( name )
72
- if ( HTML_VOID_ELEMENTS . include? ( name ) || self_closing ) && content . nil?
73
- "<#{ name . to_s . dasherize } #{ tag_options ( options , escape ) } #{ self_closing ? " />" : ">" } " . html_safe
74
- else
75
- content_tag_string ( name . to_s . dasherize , content || "" , options , escape )
76
- end
231
+ def void_tag_string ( name , options , escape = true , self_closing = false )
232
+ "<#{ name } #{ tag_options ( options , escape ) } #{ self_closing ? " />" : ">" } " . html_safe
77
233
end
78
234
79
235
def content_tag_string ( name , content , options , escape = true )
80
236
tag_options = tag_options ( options , escape ) if options
81
237
82
- TagHelper . ensure_valid_html5_tag_name ( name )
83
238
if escape
84
239
content = ERB ::Util . unwrapped_html_escape ( content )
85
240
end
@@ -163,7 +318,11 @@ def respond_to_missing?(*args)
163
318
end
164
319
165
320
def method_missing ( called , *args , **options , &block )
166
- tag_string ( called , *args , **options , &block )
321
+ name = called . to_s . dasherize
322
+
323
+ TagHelper . ensure_valid_html5_tag_name ( name )
324
+
325
+ tag_string ( name , *args , **options , &block )
167
326
end
168
327
end
169
328
@@ -343,6 +502,8 @@ def tag(name = nil, options = nil, open = false, escape = true)
343
502
# <% end -%>
344
503
# # => <div class="strong">Hello world!</div>
345
504
def content_tag ( name , content_or_options_with_block = nil , options = nil , escape = true , &block )
505
+ ensure_valid_html5_tag_name ( name )
506
+
346
507
if block_given?
347
508
options = content_or_options_with_block if content_or_options_with_block . is_a? ( Hash )
348
509
tag_builder . content_tag_string ( name , capture ( &block ) , options , escape )
0 commit comments