Introduce a context concept that provides anything extra that may be passed to a component:
A component that supports context might be implemented like:
class MyComponent
def context=(context)
@context = context
end
def render_in
if @context.render_components?
""
else
"Hello + world"
end
end
endThe crb compiler could build in respond_to?(:context=) checks (or check real time once, removing the runtime cost) and pass the context that was given to the compiled template call.
e.g.
ComponentEmbeddedRuby::Renderer.new(
"<MyComponent></MyComponent>",
).to_s(context: OpenStruct.new(render_components?: true))This gives components flexibility by giving components instantiated by the compiled template access to data provided at runtime.
Places where this may become useful:
- Providing helpers like
current_user - Rails integration with
i18n
Let's say we want to provide current_user to components in Rails apps, with context that could look something (somewhat naively) like this:
class ConnectedComponent
def context=(context)
@_context = context
end
def current_user
@_context.current_user
end
endWhere the passed in context could look like:
class MyContext
def initialize(session)
@session = session
end
def current_user
@current_user ||= User.find_by(@session[:user_id])
end
endAnd we'd create a component that inherits from ConnectedComponent
class WelcomeComponent < ConnectedComponent
def initialize(greeting: "hello")
@greeting = greeting
end
def render_in
if current_user.present?
"#{@greeting} #{current_user.name}!"
else
"#{@greeting} stranger!"
end
end
endThen the rendering call could look something like:
ComponentEmbeddedRuby::Renderer.new('<WelcomeComponent greeting="Hey,"></WelcomeComponent>
', context: MyContext.new(rails_session))Now every component rendered in that component tree (that implements context=) would have access to a single context instance that provides a current_user method.