Skip to content

Instantly share code, notes, and snippets.

@BlakeWilliams
Last active July 22, 2020 14:00
Show Gist options
  • Save BlakeWilliams/29774478f6c2c359233d73032c9f0f48 to your computer and use it in GitHub Desktop.
Save BlakeWilliams/29774478f6c2c359233d73032c9f0f48 to your computer and use it in GitHub Desktop.
Component Embedded Ruby ideas

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

current_user example

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment