Class: ViewComponent::Base
- Inherits:
-
ActionView::Base
- Object
- ActionView::Base
- ViewComponent::Base
- Includes:
- ActiveSupport::Configurable, Previewable
- Defined in:
- lib/view_component/base.rb
Constant Summary collapse
- ViewContextCalledBeforeRenderError =
Class.new(StandardError)
Class Attribute Summary collapse
-
.source_location ⇒ Object
Returns the value of attribute source_location.
-
.virtual_path ⇒ Object
Returns the value of attribute virtual_path.
Class Method Summary collapse
-
.compile(raise_errors: false) ⇒ Object
Compile templates to instance methods, assuming they haven’t been compiled already.
- .compiled? ⇒ Boolean
- .format ⇒ Object
- .identifier ⇒ Object
- .inherited(child) ⇒ Object
-
.short_identifier ⇒ Object
Provide identifier for ActionView template annotations.
- .template_compiler ⇒ Object
-
.type ⇒ Object
we’ll eventually want to update this to support other types.
-
.validate_collection_parameter!(validate_default: false) ⇒ Object
Ensure the component initializer accepts the collection parameter.
-
.with_collection(collection, **args) ⇒ Object
Render a component collection.
-
.with_collection_parameter(param) ⇒ Object
Support overriding collection parameter name.
- .with_content_areas(*areas) ⇒ Object
Instance Method Summary collapse
- #before_render ⇒ Object
- #before_render_check ⇒ Object
- #controller ⇒ Object
-
#format ⇒ Object
For caching, such as #cache_if.
-
#helpers ⇒ Object
Provides a proxy to access helper methods from the context of the current controller.
-
#initialize ⇒ Base
constructor
A new instance of Base.
-
#render(options = {}, args = {}, &block) ⇒ Object
Re-use original view_context if we’re not rendering a component.
- #render? ⇒ Boolean
-
#render_in(view_context, &block) ⇒ Object
Entrypoint for rendering components.
-
#view_cache_dependencies ⇒ Object
For caching, such as #cache_if.
-
#virtual_path ⇒ Object
Exposes .virutal_path as an instance method.
-
#with(area, content = nil, &block) ⇒ Object
Assign the provided content to the content area accessor.
- #with_variant(variant) ⇒ Object
Constructor Details
#initialize ⇒ Base
Returns a new instance of Base.
97 |
# File 'lib/view_component/base.rb', line 97 def initialize(*); end |
Class Attribute Details
.source_location ⇒ Object
Returns the value of attribute source_location.
182 183 184 |
# File 'lib/view_component/base.rb', line 182 def source_location @source_location end |
.virtual_path ⇒ Object
Returns the value of attribute virtual_path.
182 183 184 |
# File 'lib/view_component/base.rb', line 182 def virtual_path @virtual_path end |
Class Method Details
.compile(raise_errors: false) ⇒ Object
Compile templates to instance methods, assuming they haven’t been compiled already.
Do as much work as possible in this step, as doing so reduces the amount of work done each time a component is rendered.
224 225 226 |
# File 'lib/view_component/base.rb', line 224 def compile(raise_errors: false) template_compiler.compile(raise_errors: raise_errors) end |
.compiled? ⇒ Boolean
216 217 218 |
# File 'lib/view_component/base.rb', line 216 def compiled? template_compiler.compiled? end |
.format ⇒ Object
237 238 239 |
# File 'lib/view_component/base.rb', line 237 def format :html end |
.identifier ⇒ Object
241 242 243 |
# File 'lib/view_component/base.rb', line 241 def identifier source_location end |
.inherited(child) ⇒ Object
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/view_component/base.rb', line 194 def inherited(child) # Compile so child will inherit compiled `call_*` template methods that # `compile` defines compile # If Rails application is loaded, add application url_helpers to the component context # we need to check this to use this gem as a dependency if defined?(Rails) && Rails.application child.include Rails.application.routes.url_helpers unless child < Rails.application.routes.url_helpers end # Derive the source location of the component Ruby file from the call stack. # We need to ignore `inherited` frames here as they indicate that `inherited` # has been re-defined by the consuming application, likely in ApplicationComponent. child.source_location = caller_locations(1, 10).reject { |l| l.label == "inherited" }[0].absolute_path # Removes the first part of the path and the extension. child.virtual_path = child.source_location.gsub(%r{(.*app/components)|(\.rb)}, "") super end |
.short_identifier ⇒ Object
Provide identifier for ActionView template annotations
190 191 192 |
# File 'lib/view_component/base.rb', line 190 def short_identifier @short_identifier ||= defined?(Rails.root) ? source_location.sub("#{Rails.root}/", "") : source_location end |
.template_compiler ⇒ Object
228 229 230 |
# File 'lib/view_component/base.rb', line 228 def template_compiler @_template_compiler ||= Compiler.new(self) end |
.type ⇒ Object
we’ll eventually want to update this to support other types
233 234 235 |
# File 'lib/view_component/base.rb', line 233 def type "text/html" end |
.validate_collection_parameter!(validate_default: false) ⇒ Object
Ensure the component initializer accepts the collection parameter. By default, we do not validate that the default parameter name is accepted, as support for collection rendering is optional.
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/view_component/base.rb', line 263 def validate_collection_parameter!(validate_default: false) parameter = validate_default ? collection_parameter : provided_collection_parameter return unless parameter return if initialize_parameters.map(&:last).include?(parameter) # If Ruby cannot parse the component class, then the initalize # parameters will be empty and ViewComponent will not be able to render # the component. if initialize_parameters.empty? raise ArgumentError.new( "#{self} initializer is empty or invalid." ) end raise ArgumentError.new( "#{self} initializer must accept " \ "`#{parameter}` collection parameter." ) end |
.with_collection(collection, **args) ⇒ Object
Render a component collection.
185 186 187 |
# File 'lib/view_component/base.rb', line 185 def with_collection(collection, **args) Collection.new(self, collection, **args) end |
.with_collection_parameter(param) ⇒ Object
Support overriding collection parameter name
254 255 256 |
# File 'lib/view_component/base.rb', line 254 def with_collection_parameter(param) @provided_collection_parameter = param end |
.with_content_areas(*areas) ⇒ Object
245 246 247 248 249 250 251 |
# File 'lib/view_component/base.rb', line 245 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 ⇒ Object
85 86 87 |
# File 'lib/view_component/base.rb', line 85 def before_render before_render_check end |
#before_render_check ⇒ Object
89 90 91 |
# File 'lib/view_component/base.rb', line 89 def before_render_check # noop end |
#controller ⇒ Object
112 113 114 115 |
# File 'lib/view_component/base.rb', line 112 def controller raise ViewContextCalledBeforeRenderError, "`controller` can only be called at render time." if view_context.nil? @controller ||= view_context.controller end |
#format ⇒ Object
For caching, such as #cache_if
134 135 136 137 138 139 |
# File 'lib/view_component/base.rb', line 134 def format # Ruby 2.6 throws a warning without checking `defined?`, 2.7 does not if defined?(@variant) @variant end end |
#helpers ⇒ Object
Provides a proxy to access helper methods from the context of the current controller
118 119 120 121 |
# File 'lib/view_component/base.rb', line 118 def helpers raise ViewContextCalledBeforeRenderError, "`helpers` can only be called at render time." if view_context.nil? @helpers ||= controller.view_context end |
#render(options = {}, args = {}, &block) ⇒ Object
Re-use original view_context if we’re not rendering a component.
This prevents an exception when rendering a partial inside of a component that has also been rendered outside of the component. This is due to the partials compiled template method existing in the parent ‘view_context`,
and not the component's `view_context`.
104 105 106 107 108 109 110 |
# File 'lib/view_component/base.rb', line 104 def render( = {}, args = {}, &block) if .is_a? ViewComponent::Base super else view_context.render(, args, &block) end end |
#render? ⇒ Boolean
93 94 95 |
# File 'lib/view_component/base.rb', line 93 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>
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 |
# File 'lib/view_component/base.rb', line 48 def render_in(view_context, &block) self.class.compile(raise_errors: true) @view_context = view_context @lookup_context ||= view_context.lookup_context # required for path helpers in older Rails versions @view_renderer ||= view_context.view_renderer # For content_for @view_flow ||= view_context.view_flow # For i18n @virtual_path ||= virtual_path # For template variants (+phone, +desktop, etc.) @variant ||= @lookup_context.variants.first # For caching, such as #cache_if @current_template = nil unless defined?(@current_template) old_current_template = @current_template @current_template = self # Assign captured content passed to component as a block to @content @content = view_context.capture(self, &block) if block_given? before_render if render? render_template_for(@variant) else "" end ensure @current_template = old_current_template end |
#view_cache_dependencies ⇒ Object
For caching, such as #cache_if
129 130 131 |
# File 'lib/view_component/base.rb', line 129 def view_cache_dependencies [] end |
#virtual_path ⇒ Object
Exposes .virutal_path as an instance method
124 125 126 |
# File 'lib/view_component/base.rb', line 124 def virtual_path self.class.virtual_path end |
#with(area, content = nil, &block) ⇒ Object
Assign the provided content to the content area accessor
142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/view_component/base.rb', line 142 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 |
#with_variant(variant) ⇒ Object
155 156 157 158 159 |
# File 'lib/view_component/base.rb', line 155 def with_variant(variant) @variant = variant self end |