forked from primer/view_components
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathacts_as_component.rb
More file actions
135 lines (109 loc) · 3.93 KB
/
Copy pathacts_as_component.rb
File metadata and controls
135 lines (109 loc) · 3.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# frozen_string_literal: true
require "pathname"
module Primer
module Forms
# :nodoc:
module ActsAsComponent
# :nodoc:
module InstanceMethods
delegate :render, :content_tag, :output_buffer, :capture, to: :@view_context
def render_in(view_context, **_kwargs, &block)
@view_context = view_context
before_render
perform_render(&block)
end
# :nocov:
def perform_render(&_block)
raise NotImplementedError, "subclasses must implement ##{__method__}."
end
# :nocov:
def before_render; end
# :nocov:
def set_original_view_context(view_context)
@view_context = view_context
end
# rubocop:enable Naming/AccessorMethodName
# :nocov:
end
def self.extended(base)
base.include(InstanceMethods)
end
TemplateGlob = Struct.new(:glob_pattern, :method_name, :on_compile_callback)
TemplateParams = Struct.new(:source, :identifier, :type, :format, keyword_init: true)
def renders_templates(glob_pattern, method_name = nil, &block)
template_globs << TemplateGlob.new(glob_pattern, method_name, block)
end
alias renders_template renders_templates
def compile!
# always recompile in dev
return if defined?(@compiled) && @compiled && !Rails.env.development?
template_globs.each do |template_glob|
compile_templates_in(template_glob)
end
@compiled = true
end
def template_root_path
return @template_root_path if defined?(@template_root_path)
form_path = Utils.const_source_location(self.name)
@template_root_path =
if form_path
File.join(File.dirname(form_path), self.name.demodulize.underscore)
end
end
def base_template_path
return @base_template_path if defined?(@base_template_path)
base_path = Utils.const_source_location(self.name)
if base_path
@base_template_path = File.dirname(base_path)
end
end
private
def template_globs
@template_globs ||= []
end
def compile_templates_in(template_glob)
pattern = template_glob.glob_pattern
pattern = pattern.gsub("%{base_template_path}", base_template_path) if base_template_path
pattern =
if Pathname(pattern).absolute?
pattern
else
# skip compilation for anonymous form classes, as in tests
return unless template_root_path
File.join(template_root_path, pattern)
end
template_paths = Dir.glob(pattern)
raise "Cannot compile multiple templates with the same method name." if template_paths.size > 1 && template_glob.method_name
template_paths.each do |template_path|
method_name = template_glob.method_name
method_name ||= "render_#{File.basename(template_path).chomp('.html.erb')}"
define_template_method(template_path, method_name)
template_glob&.on_compile_callback&.call(template_path)
end
end
def define_template_method(template_path, method_name)
class_eval <<-RUBY, template_path, 0
private def #{method_name}
capture { #{compile_template(template_path)} }
end
RUBY
# rubocop:enable Style/EvalWithLocation
# rubocop:enable Style/DocumentDynamicEvalDefinition
end
def compile_template(path)
handler = ActionView::Template.handler_for_extension("erb")
template = File.read(path)
template_params = TemplateParams.new({
source: template,
identifier: __FILE__,
type: "text/html",
format: "text/html"
})
# change @output_buffer ivar to output_buffer method call
BufferRewriter.rewrite(
handler.call(template_params, template)
)
end
end
end
end