Class: ViewComponent::Base
- Inherits:
-
ActionView::Base
- Object
- ActionView::Base
- ViewComponent::Base
- Includes:
- ActiveSupport::Configurable, Previewable
- Defined in:
- lib/view_component/base.rb
Direct Known Subclasses
Class Method Summary collapse
- .call_method_name(variant) ⇒ Object
-
.compile(raise_template_errors: false) ⇒ Object
Compile templates to instance methods, assuming they haven’t been compiled already.
- .compile! ⇒ Object
- .compiled? ⇒ Boolean
- .identifier ⇒ Object
- .inherited(child) ⇒ Object
- .source_location ⇒ Object
-
.type ⇒ Object
we’ll eventually want to update this to support other types.
- .variants ⇒ Object
- .with_content_areas(*areas) ⇒ Object
Instance Method Summary collapse
- #before_render_check ⇒ Object
- #controller ⇒ Object
-
#format ⇒ Object
:nodoc:.
-
#helpers ⇒ Object
Provides a proxy to access helper methods through.
-
#initialize ⇒ Base
constructor
A new instance of Base.
- #render(options = {}, args = {}, &block) ⇒ Object
- #render? ⇒ Boolean
-
#render_in(view_context, &block) ⇒ Object
Entrypoint for rendering components.
- #view_cache_dependencies ⇒ Object
-
#virtual_path ⇒ Object
Removes the first part of the path and the extension.
- #with(area, content = nil, &block) ⇒ Object
Constructor Details
#initialize ⇒ Base
Returns a new instance of Base.
74 |
# File 'lib/view_component/base.rb', line 74 def initialize(*); end |
Class Method Details
.call_method_name(variant) ⇒ Object
140 141 142 143 144 145 146 |
# File 'lib/view_component/base.rb', line 140 def call_method_name(variant) if variant.present? && variants.include?(variant) "call_#{variant}" else "call" end end |
.compile(raise_template_errors: false) ⇒ Object
Compile templates to instance methods, assuming they haven’t been compiled already. We could in theory do this on app boot, at least in production environments. Right now this just compiles the first time the component is rendered.
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/view_component/base.rb', line 169 def compile(raise_template_errors: false) return if compiled? if template_errors.present? raise ViewComponent::TemplateError.new(template_errors) if raise_template_errors return false end templates.each do |template| class_eval " def \#{call_method_name(template[:variant])}\n @output_buffer = ActionView::OutputBuffer.new\n \#{compiled_template(template[:path])}\n end\n RUBY\n end\n\n @compiled = true\nend\n", template[:path], -1 |
.compile! ⇒ Object
162 163 164 |
# File 'lib/view_component/base.rb', line 162 def compile! compile(raise_template_errors: true) end |
.compiled? ⇒ Boolean
158 159 160 |
# File 'lib/view_component/base.rb', line 158 def compiled? @compiled && ActionView::Base.cache_template_loading end |
.identifier ⇒ Object
198 199 200 |
# File 'lib/view_component/base.rb', line 198 def identifier source_location end |
.inherited(child) ⇒ Object
134 135 136 137 138 |
# File 'lib/view_component/base.rb', line 134 def inherited(child) child.include Rails.application.routes.url_helpers unless child < Rails.application.routes.url_helpers super end |
.source_location ⇒ Object
148 149 150 151 152 153 154 155 156 |
# File 'lib/view_component/base.rb', line 148 def source_location @source_location ||= begin # Require `#initialize` to be defined so that we can use `method#source_location` # to look up the filename of the component. initialize_method = instance_method(:initialize) initialize_method.source_location[0] if initialize_method.owner == self end end |
.type ⇒ Object
we’ll eventually want to update this to support other types
194 195 196 |
# File 'lib/view_component/base.rb', line 194 def type "text/html" end |
.variants ⇒ Object
189 190 191 |
# File 'lib/view_component/base.rb', line 189 def variants templates.map { |template| template[:variant] } end |
.with_content_areas(*areas) ⇒ Object
202 203 204 205 206 207 208 |
# File 'lib/view_component/base.rb', line 202 def with_content_areas(*areas) if areas.include?(:content) raise ArgumentError.new ":content is a reserved content area name. Please use another name, such as ':body'" end attr_reader *areas self.content_areas = areas end |
Instance Method Details
#before_render_check ⇒ Object
66 67 68 |
# File 'lib/view_component/base.rb', line 66 def before_render_check # noop end |
#controller ⇒ Object
84 85 86 |
# File 'lib/view_component/base.rb', line 84 def controller @controller ||= view_context.controller end |
#format ⇒ Object
:nodoc:
102 103 104 |
# File 'lib/view_component/base.rb', line 102 def format # :nodoc: @variant end |
#helpers ⇒ Object
Provides a proxy to access helper methods through
89 90 91 |
# File 'lib/view_component/base.rb', line 89 def helpers @helpers ||= view_context end |
#render(options = {}, args = {}, &block) ⇒ Object
76 77 78 79 80 81 82 |
# File 'lib/view_component/base.rb', line 76 def render( = {}, args = {}, &block) if .is_a?(String) || (.is_a?(Hash) && .has_key?(:partial)) view_context.render(, args, &block) else super end end |
#render? ⇒ Boolean
70 71 72 |
# File 'lib/view_component/base.rb', line 70 def render? true end |
#render_in(view_context, &block) ⇒ Object
Entrypoint for rendering components.
view_context: ActionView context from calling view block: optional block to be captured within the view context
returns HTML that has been escaped by the respective template handler
Example subclass:
app/components/my_component.rb: class MyComponent < ViewComponent::Base
def initialize(title:)
@title = title
end
end
app/components/my_component.html.erb <span title=“<%= @title %>”>Hello, <%= content %>!</span>
In use: <%= render MyComponent.new(title: “greeting”) do %>world<% end %> returns: <span title=“greeting”>Hello, world!</span>
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/view_component/base.rb', line 41 def render_in(view_context, &block) self.class.compile! @view_context = view_context @view_renderer ||= view_context.view_renderer @lookup_context ||= view_context.lookup_context @view_flow ||= view_context.view_flow @virtual_path ||= virtual_path @variant = @lookup_context.variants.first old_current_template = @current_template @current_template = self @content = view_context.capture(self, &block) if block_given? before_render_check if render? send(self.class.call_method_name(@variant)) else "" end ensure @current_template = old_current_template end |
#view_cache_dependencies ⇒ Object
98 99 100 |
# File 'lib/view_component/base.rb', line 98 def view_cache_dependencies [] end |
#virtual_path ⇒ Object
Removes the first part of the path and the extension.
94 95 96 |
# File 'lib/view_component/base.rb', line 94 def virtual_path self.class.source_location.gsub(%r{(.*app/components)|(\.rb)}, "") end |
#with(area, content = nil, &block) ⇒ Object
106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/view_component/base.rb', line 106 def with(area, content = nil, &block) unless content_areas.include?(area) raise ArgumentError.new "Unknown content_area '#{area}' - expected one of '#{content_areas}'" end if block_given? content = view_context.capture(&block) end instance_variable_set("@#{area}".to_sym, content) nil end |