Skip to content

Instantly share code, notes, and snippets.

@andypike
Last active October 21, 2022 12:25
Show Gist options
  • Save andypike/82eb791e948171e6ad4e to your computer and use it in GitHub Desktop.
Save andypike/82eb791e948171e6ad4e to your computer and use it in GitHub Desktop.
Self registering classes that works within a Rails app without turning on eager_loading loading in development
# ./config/environments/development.rb
config.eager_load = false
# ./lib/registry.rb
module Registry
def self.included(base)
base.send(:extend, ClassMethods)
end
module ClassMethods
def strategies(options = {})
self.default = options.fetch(:default)
self.registry = {}
end
def register(strategy)
registry[strategy.identifier] = strategy
end
def strategy_for(identifier)
SelectiveEagerLoader.ensure_loaded
registry.fetch(identifier) { default }
end
private
attr_accessor :registry, :default
end
end
# ./lib/selective_eager_loader_base.rb
class SelectiveEagerLoaderBase
cattr_accessor :loaded
def self.ensure_loaded
return if already_loaded?
Rails.logger.info("Selectivly eager loading types")
files.each { |file| require_dependency(file) }
loaded!
end
def self.files
Dir.glob(
paths.map{ |path| Rails.root.join(path, "**/*.rb") }
)
end
def self.already_loaded?
loaded? || Rails.configuration.eager_load
end
def self.loaded?
loaded
end
def self.loaded!
self.loaded = true
end
end
# ./lib/self_registering.rb
module SelfRegistering
def self.included(base)
base.send(:extend, ClassMethods)
end
module ClassMethods
attr_accessor :identifier
def works_with(identifier, options = {})
self.identifier = identifier
options.fetch(:registry).register(self)
end
end
end
# ---------------------------------------------------------------------
# ./app/models/selective_eager_loader.rb
class SelectiveEagerLoader < SelectiveEagerLoaderBase
def self.paths
["app/models/tools"]
end
end
# ./app/models/toolbox.rb
class Toolbox
include Registry
strategies :default => Tools::BareHands
end
# ./app/models/tools/anvil.rb
module Tools
class Anvil
include SelfRegistering
works_with :metal, :registry => Toolbox
def build_something
puts "I'm an anvil and I can work with metal"
end
end
end
# ./app/models/tools/saw.rb
module Tools
class Saw
include SelfRegistering
works_with :wood, :registry => Toolbox
def build_something
puts "I'm a saw and I can work with wood"
end
end
end
# ./app/models/tools/bare_hands.rb
module Tools
class BareHands
def build_something
puts "I use my bare hands and work with anything else"
end
end
end
# ./app/controllers/tools_controller.rb
class ToolsController < ApplicationController
def index
tool = Toolbox.strategy_for(:wood)
puts tool.new.build_something
tool = Toolbox.strategy_for(:metal)
puts tool.new.build_something
tool = Toolbox.strategy_for(:fabric)
puts tool.new.build_something
end
end
# => I'm a saw and I can work with wood
# => I'm an anvil and I can work with metal
# => I use my bare hands and work with anything else
@sonya
Copy link

sonya commented Aug 11, 2017

Hi, I was trying to build something to register a set of classes in Rails when I came across this gist. I would like to use your code, but there doesn't seem to be a license attached. What is your policy on this?

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