@@ -16,7 +16,7 @@ class Compiler
16
16
def initialize ( component_class )
17
17
@component_class = component_class
18
18
@redefinition_lock = Mutex . new
19
- @variants_rendering_templates = Set . new
19
+ @rendered_templates = Set . new
20
20
end
21
21
22
22
def compiled?
@@ -61,22 +61,22 @@ def call
61
61
62
62
component_class . silence_redefinition_of_method ( "render_template_for" )
63
63
component_class . class_eval <<-RUBY , __FILE__ , __LINE__ + 1
64
- def render_template_for(variant = nil)
64
+ def render_template_for(variant = nil, format = nil )
65
65
_call_#{ safe_class_name }
66
66
end
67
67
RUBY
68
68
end
69
69
else
70
70
templates . each do |template |
71
- method_name = call_method_name ( template [ :variant ] )
72
- @variants_rendering_templates << template [ :variant ]
71
+ method_name = call_method_name ( template [ :variant ] , template [ :format ] )
72
+ @rendered_templates << [ template [ :variant ] , template [ :format ] ]
73
73
74
74
redefinition_lock . synchronize do
75
75
component_class . silence_redefinition_of_method ( method_name )
76
76
# rubocop:disable Style/EvalWithLocation
77
77
component_class . class_eval <<-RUBY , template [ :path ] , 0
78
78
def #{ method_name }
79
- #{ compiled_template ( template [ :path ] ) }
79
+ #{ compiled_template ( template [ :path ] , template [ :format ] ) }
80
80
end
81
81
RUBY
82
82
# rubocop:enable Style/EvalWithLocation
@@ -97,37 +97,80 @@ def #{method_name}
97
97
CompileCache . register ( component_class )
98
98
end
99
99
100
- def renders_template_for_variant ?( variant )
101
- @variants_rendering_templates . include? ( variant )
100
+ def renders_template_for ?( variant , format )
101
+ @rendered_templates . include? ( [ variant , format ] )
102
102
end
103
103
104
104
private
105
105
106
106
attr_reader :component_class , :redefinition_lock
107
107
108
108
def define_render_template_for
109
- variant_elsifs = variants . compact . uniq . map do |variant |
110
- safe_name = "_call_variant_#{ normalized_variant_name ( variant ) } _#{ safe_class_name } "
109
+ branches = [ ]
110
+ default_method_name = "_call_#{ safe_class_name } "
111
+
112
+ templates . each do |template |
113
+ safe_name = +"_call"
114
+ variant_name = normalized_variant_name ( template [ :variant ] )
115
+ safe_name << "_#{ variant_name } " if variant_name . present?
116
+ safe_name << "_#{ template [ :format ] } " if template [ :format ] . present? && template [ :format ] != :html
117
+ safe_name << "_#{ safe_class_name } "
118
+
119
+ if safe_name == default_method_name
120
+ next
121
+ else
122
+ component_class . define_method (
123
+ safe_name ,
124
+ component_class . instance_method (
125
+ call_method_name ( template [ :variant ] , template [ :format ] )
126
+ )
127
+ )
128
+ end
129
+
130
+ format_conditional =
131
+ if template [ :format ] == :html
132
+ "(format == :html || format.nil?)"
133
+ else
134
+ "format == #{ template [ :format ] . inspect } "
135
+ end
136
+
137
+ variant_conditional =
138
+ if template [ :variant ] . nil?
139
+ "variant.nil?"
140
+ else
141
+ "variant&.to_sym == :'#{ template [ :variant ] } '"
142
+ end
143
+
144
+ branches << [ "#{ variant_conditional } && #{ format_conditional } " , safe_name ]
145
+ end
146
+
147
+ variants_from_inline_calls ( inline_calls ) . compact . uniq . each do |variant |
148
+ safe_name = "_call_#{ normalized_variant_name ( variant ) } _#{ safe_class_name } "
111
149
component_class . define_method ( safe_name , component_class . instance_method ( call_method_name ( variant ) ) )
112
150
113
- "elsif variant.to_sym == :'#{ variant } '\n #{ safe_name } "
114
- end . join ( "\n " )
151
+ branches << [ "variant&.to_sym == :'#{ variant } '" , safe_name ]
152
+ end
153
+
154
+ component_class . define_method ( :"#{ default_method_name } " , component_class . instance_method ( :call ) )
115
155
116
- component_class . define_method ( :"_call_#{ safe_class_name } " , component_class . instance_method ( :call ) )
156
+ # Just use default method name if no conditional branches or if there is a single
157
+ # conditional branch that just calls the default method_name
158
+ if branches . empty? || ( branches . length == 1 && branches [ 0 ] . last == default_method_name )
159
+ body = default_method_name
160
+ else
161
+ body = +""
117
162
118
- body = <<-RUBY
119
- if variant.nil?
120
- _call_#{ safe_class_name }
121
- #{ variant_elsifs }
122
- else
123
- _call_#{ safe_class_name }
163
+ branches . each do |conditional , method_body |
164
+ body << "#{ ( !body . present? ) ? "if" : "elsif" } #{ conditional } \n #{ method_body } \n "
124
165
end
125
- RUBY
166
+
167
+ body << "else\n #{ default_method_name } \n end"
168
+ end
126
169
127
170
redefinition_lock . synchronize do
128
171
component_class . silence_redefinition_of_method ( :render_template_for )
129
172
component_class . class_eval <<-RUBY , __FILE__ , __LINE__ + 1
130
- def render_template_for(variant = nil)
173
+ def render_template_for(variant = nil, format = nil )
131
174
#{ body }
132
175
end
133
176
RUBY
@@ -147,24 +190,16 @@ def template_errors
147
190
errors << "Couldn't find a template file or inline render method for #{ component_class } ."
148
191
end
149
192
150
- if templates . count { |template | template [ :variant ] . nil? } > 1
151
- errors <<
152
- "More than one template found for #{ component_class } . " \
153
- "There can only be one default template file per component."
154
- end
193
+ templates
194
+ . map { |template | [ template [ :variant ] , template [ :format ] ] }
195
+ . tally
196
+ . select { |_ , count | count > 1 }
197
+ . each do |tally |
198
+ variant , this_format = tally [ 0 ]
155
199
156
- invalid_variants =
157
- templates
158
- . group_by { |template | template [ :variant ] }
159
- . map { |variant , grouped | variant if grouped . length > 1 }
160
- . compact
161
- . sort
200
+ variant_string = " for variant `#{ variant } `" if variant . present?
162
201
163
- unless invalid_variants . empty?
164
- errors <<
165
- "More than one template found for #{ "variant" . pluralize ( invalid_variants . count ) } " \
166
- "#{ invalid_variants . map { |v | "'#{ v } '" } . to_sentence } in #{ component_class } . " \
167
- "There can only be one template file per variant."
202
+ errors << "More than one #{ this_format . upcase } template found#{ variant_string } for #{ component_class } . "
168
203
end
169
204
170
205
if templates . find { |template | template [ :variant ] . nil? } && inline_calls_defined_on_self . include? ( :call )
@@ -213,6 +248,7 @@ def templates
213
248
pieces = File . basename ( path ) . split ( "." )
214
249
memo << {
215
250
path : path ,
251
+ format : pieces [ 1 ..-2 ] . join ( "." ) . split ( "+" ) . first &.to_sym ,
216
252
variant : pieces [ 1 ..-2 ] . join ( "." ) . split ( "+" ) . second &.to_sym ,
217
253
handler : pieces . last
218
254
}
@@ -239,6 +275,10 @@ def inline_calls_defined_on_self
239
275
@inline_calls_defined_on_self ||= component_class . instance_methods ( false ) . grep ( /^call(_|$)/ )
240
276
end
241
277
278
+ def formats
279
+ @__vc_variants = ( templates . map { |template | template [ :format ] } ) . compact . uniq
280
+ end
281
+
242
282
def variants
243
283
@__vc_variants = (
244
284
templates . map { |template | template [ :variant ] } + variants_from_inline_calls ( inline_calls )
@@ -258,37 +298,54 @@ def compiled_inline_template(template)
258
298
compile_template ( template , handler )
259
299
end
260
300
261
- def compiled_template ( file_path )
301
+ def compiled_template ( file_path , format )
262
302
handler = ActionView ::Template . handler_for_extension ( File . extname ( file_path ) . delete ( "." ) )
263
303
template = File . read ( file_path )
264
304
265
- compile_template ( template , handler )
305
+ compile_template ( template , handler , file_path , format )
266
306
end
267
307
268
- def compile_template ( template , handler )
308
+ def compile_template ( template , handler , identifier = component_class . source_location , format = :html )
269
309
template . rstrip! if component_class . strip_trailing_whitespace?
270
310
311
+ short_identifier = defined? ( Rails . root ) ? identifier . sub ( "#{ Rails . root } /" , "" ) : identifier
312
+ type = ActionView ::Template ::Types [ format ]
313
+
271
314
if handler . method ( :call ) . parameters . length > 1
272
- handler . call ( component_class , template )
315
+ handler . call (
316
+ OpenStruct . new (
317
+ format : format ,
318
+ identifier : identifier ,
319
+ short_identifier : short_identifier ,
320
+ type : type
321
+ ) ,
322
+ template
323
+ )
273
324
# :nocov:
274
325
else
275
326
handler . call (
276
327
OpenStruct . new (
277
328
source : template ,
278
- identifier : component_class . identifier ,
279
- type : component_class . type
329
+ identifier : identifier ,
330
+ type : type
280
331
)
281
332
)
282
333
end
283
334
# :nocov:
284
335
end
285
336
286
- def call_method_name ( variant )
287
- if variant . present? && variants . include? ( variant )
288
- "call_#{ normalized_variant_name ( variant ) } "
289
- else
290
- "call"
337
+ def call_method_name ( variant , format = nil )
338
+ out = +"call"
339
+
340
+ if variant . present?
341
+ out << "_#{ normalized_variant_name ( variant ) } "
342
+ end
343
+
344
+ if format . present? && format != :html && formats . length > 1
345
+ out << "_#{ format } "
291
346
end
347
+
348
+ out
292
349
end
293
350
294
351
def normalized_variant_name ( variant )
0 commit comments