@@ -77,7 +77,7 @@ def render_in(view_context, &block)
7777 before_render
7878
7979 if render?
80- send ( self . class . call_method_name ( @variant ) )
80+ render_template_for ( @variant )
8181 else
8282 ""
8383 end
@@ -208,80 +208,20 @@ def inherited(child)
208208 super
209209 end
210210
211- def call_method_name ( variant )
212- if variant . present? && variants . include? ( variant )
213- "call_#{ variant } "
214- else
215- "call"
216- end
217- end
218-
219211 def compiled?
220- CompileCache . compiled? ( self )
212+ template_compiler . compiled?
221213 end
222214
223215 # Compile templates to instance methods, assuming they haven't been compiled already.
224216 #
225217 # Do as much work as possible in this step, as doing so reduces the amount
226218 # of work done each time a component is rendered.
227219 def compile ( raise_errors : false )
228- return if compiled?
229-
230- if template_errors . present?
231- raise ViewComponent ::TemplateError . new ( template_errors ) if raise_errors
232- return false
233- end
234-
235- if instance_methods ( false ) . include? ( :before_render_check )
236- ActiveSupport ::Deprecation . warn (
237- "`before_render_check` will be removed in v3.0.0. Use `before_render` instead."
238- )
239- end
240-
241- # Remove any existing singleton methods,
242- # as Ruby warns when redefining a method.
243- remove_possible_singleton_method ( :variants )
244- remove_possible_singleton_method ( :collection_parameter )
245- remove_possible_singleton_method ( :collection_counter_parameter )
246- remove_possible_singleton_method ( :counter_argument_present? )
247-
248- define_singleton_method ( :variants ) do
249- templates . map { |template | template [ :variant ] } + variants_from_inline_calls ( inline_calls )
250- end
251-
252- define_singleton_method ( :collection_parameter ) do
253- if provided_collection_parameter
254- provided_collection_parameter
255- else
256- name . demodulize . underscore . chomp ( "_component" ) . to_sym
257- end
258- end
259-
260- define_singleton_method ( :collection_counter_parameter ) do
261- "#{ collection_parameter } _counter" . to_sym
262- end
263-
264- define_singleton_method ( :counter_argument_present? ) do
265- instance_method ( :initialize ) . parameters . map ( &:second ) . include? ( collection_counter_parameter )
266- end
267-
268- validate_collection_parameter! if raise_errors
269-
270- templates . each do |template |
271- # Remove existing compiled template methods,
272- # as Ruby warns when redefining a method.
273- method_name = call_method_name ( template [ :variant ] )
274- undef_method ( method_name . to_sym ) if instance_methods . include? ( method_name . to_sym )
275-
276- class_eval <<-RUBY , template [ :path ] , -1
277- def #{ method_name }
278- @output_buffer = ActionView::OutputBuffer.new
279- #{ compiled_template ( template [ :path ] ) }
280- end
281- RUBY
282- end
220+ template_compiler . compile ( raise_errors : raise_errors )
221+ end
283222
284- CompileCache . register self
223+ def template_compiler
224+ @_template_compiler ||= Compiler . new ( self )
285225 end
286226
287227 # we'll eventually want to update this to support other types
@@ -346,111 +286,6 @@ def provided_collection_parameter
346286 @provided_collection_parameter ||= nil
347287 end
348288
349- def compiled_template ( file_path )
350- handler = ActionView ::Template . handler_for_extension ( File . extname ( file_path ) . gsub ( "." , "" ) )
351- template = File . read ( file_path )
352-
353- if handler . method ( :call ) . parameters . length > 1
354- handler . call ( self , template )
355- else
356- handler . call ( OpenStruct . new ( source : template , identifier : identifier , type : type ) )
357- end
358- end
359-
360- def inline_calls
361- @inline_calls ||=
362- begin
363- # Fetch only ViewComponent ancestor classes to limit the scope of
364- # finding inline calls
365- view_component_ancestors =
366- ancestors . take_while { |ancestor | ancestor != ViewComponent ::Base } - included_modules
367-
368- view_component_ancestors . flat_map { |ancestor | ancestor . instance_methods ( false ) . grep ( /^call/ ) } . uniq
369- end
370- end
371-
372- def inline_calls_defined_on_self
373- @inline_calls_defined_on_self ||= instance_methods ( false ) . grep ( /^call/ )
374- end
375-
376- def matching_views_in_source_location
377- return [ ] unless source_location
378-
379- location_without_extension = source_location . chomp ( File . extname ( source_location ) )
380-
381- extensions = ActionView ::Template . template_handler_extensions . join ( "," )
382-
383- # view files in the same directory as the component
384- sidecar_files = Dir [ "#{ location_without_extension } .*{#{ extensions } }" ]
385-
386- # view files in a directory named like the component
387- directory = File . dirname ( source_location )
388- filename = File . basename ( source_location , ".rb" )
389- component_name = name . demodulize . underscore
390-
391- sidecar_directory_files = Dir [ "#{ directory } /#{ component_name } /#{ filename } .*{#{ extensions } }" ]
392-
393- ( sidecar_files - [ source_location ] + sidecar_directory_files )
394- end
395-
396- def templates
397- @templates ||=
398- matching_views_in_source_location . each_with_object ( [ ] ) do |path , memo |
399- pieces = File . basename ( path ) . split ( "." )
400-
401- memo << {
402- path : path ,
403- variant : pieces . second . split ( "+" ) . second &.to_sym ,
404- handler : pieces . last
405- }
406- end
407- end
408-
409- def template_errors
410- @template_errors ||=
411- begin
412- errors = [ ]
413-
414- if ( templates + inline_calls ) . empty?
415- errors << "Could not find a template file or inline render method for #{ self } ."
416- end
417-
418- if templates . count { |template | template [ :variant ] . nil? } > 1
419- errors << "More than one template found for #{ self } . There can only be one default template file per component."
420- end
421-
422- invalid_variants = templates
423- . group_by { |template | template [ :variant ] }
424- . map { |variant , grouped | variant if grouped . length > 1 }
425- . compact
426- . sort
427-
428- unless invalid_variants . empty?
429- errors << "More than one template found for #{ 'variant' . pluralize ( invalid_variants . count ) } #{ invalid_variants . map { |v | "'#{ v } '" } . to_sentence } in #{ self } . There can only be one template file per variant."
430- end
431-
432- if templates . find { |template | template [ :variant ] . nil? } && inline_calls_defined_on_self . include? ( :call )
433- errors << "Template file and inline render method found for #{ self } . There can only be a template file or inline render method per component."
434- end
435-
436- duplicate_template_file_and_inline_variant_calls =
437- templates . pluck ( :variant ) & variants_from_inline_calls ( inline_calls_defined_on_self )
438-
439- unless duplicate_template_file_and_inline_variant_calls . empty?
440- count = duplicate_template_file_and_inline_variant_calls . count
441-
442- errors << "Template #{ 'file' . pluralize ( count ) } and inline render #{ 'method' . pluralize ( count ) } found for #{ 'variant' . pluralize ( count ) } #{ duplicate_template_file_and_inline_variant_calls . map { |v | "'#{ v } '" } . to_sentence } in #{ self } . There can only be a template file or inline render method per variant."
443- end
444-
445- errors
446- end
447- end
448-
449- def variants_from_inline_calls ( calls )
450- calls . reject { |call | call == :call } . map do |variant_call |
451- variant_call . to_s . sub ( "call_" , "" ) . to_sym
452- end
453- end
454289 end
455290
456291 ActiveSupport . run_load_hooks ( :view_component , self )
0 commit comments