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
end
The 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
end
Where 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
end
And 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
end
Then 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.