Skip to content

Instantly share code, notes, and snippets.

@dhnaranjo
Created October 19, 2022 02:18
Show Gist options
  • Save dhnaranjo/3decca0782374aaa7f2200d69bd710ee to your computer and use it in GitHub Desktop.
Save dhnaranjo/3decca0782374aaa7f2200d69bd710ee to your computer and use it in GitHub Desktop.
Tryinna work out a cleaner way to access Phlex components
# frozen_string_literal: true
require "singleton"
require "bundler/inline"
gemfile do
gem "phlex"
end
class ComponentRegistry
include Singleton
def initialize
@scopes = {}
end
def register(component)
parts = component.name.split("::")
scopes = parts.map.with_index { |_, index| parts[0, index].join("::") }
component_names = parts.map.with_index { |_, index| parts[index, parts.size].join("_") }
scopes.each_with_index do |scope, index|
@scopes[scope] ||= RegistryScope.new
component_names[0..index].each do |component_name|
@scopes[scope].component_method(component_name, component)
end
end
end
def fetch_scopes(context)
parts = context.name.split("::")
parts.size
.times
.map { |n| parts.first(parts.size - n) }
.map { |path_parts| path_parts.join("::") }
.filter_map { |path| @scopes[path] }
end
end
class RegistryScope
def with_caller(caller)
@caller = caller
yield
end
def component_method(component_name, component)
define_singleton_method(component_name.to_sym) do |*args, **kwargs, &block|
@caller.render(component.new(*args, **kwargs), &block)
end
end
end
module RegisterableComponent
def self.included(base)
ComponentRegistry.instance.register(base)
end
end
module ComponentAccess
def c
ComponentRegistry.instance
.fetch_scopes(self.class)
.find do |scope|
scope.with_caller(self) { yield_self }
rescue NoMethodError
next
end
end
end
module One
module Two
module Three
module Four
class Component < Phlex::View
include RegisterableComponent
def initialize(param)
@param = param
end
def template(&block)
p do
span { @param }
whitespace
span(&block)
end
end
end
end
end
end
end
module One
module Two
module TwoPointFive
class View < Phlex::View
include ComponentAccess
def template
c.One_Two_Three_Four_Component("Deep paths") { "work fine" }
c.Three_Four_Component("Shallow ones") { "also good" }
begin
c.Component("Not this one") { "nope" }
rescue
nil
end
end
end
end
end
end
puts One::Two::TwoPointFive::View.new.call
# <p><span>Deep paths</span> <span>work fine</span></p><p><span>Shallow ones</span> <span>also good</span></p>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment