diff options
Diffstat (limited to 'activemodel/lib/active_model/type/registry.rb')
-rw-r--r-- | activemodel/lib/active_model/type/registry.rb | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/activemodel/lib/active_model/type/registry.rb b/activemodel/lib/active_model/type/registry.rb new file mode 100644 index 0000000000..8070a4bd38 --- /dev/null +++ b/activemodel/lib/active_model/type/registry.rb @@ -0,0 +1,151 @@ +module ActiveModel + # :stopdoc: + module Type + class Registry + def initialize + @registrations = [] + end + + def register(type_name, klass = nil, **options, &block) + block ||= proc { |_, *args| klass.new(*args) } + registrations << registration_klass.new(type_name, block, **options) + end + + def lookup(symbol, *args) + registration = registrations + .select { |r| r.matches?(symbol, *args) } + .max + + if registration + registration.call(self, symbol, *args) + else + raise ArgumentError, "Unknown type #{symbol.inspect}" + end + end + + def add_modifier(options, klass, **args) + registrations << decoration_registration_klass.new(options, klass, **args) + end + + protected + + attr_reader :registrations + + private + + def registration_klass + Registration + end + + def decoration_registration_klass + DecorationRegistration + end + end + + class Registration + def initialize(name, block, override: nil) + @name = name + @block = block + @override = override + end + + def call(_registry, *args, **kwargs) + if kwargs.any? # https://bugs.ruby-lang.org/issues/10856 + block.call(*args, **kwargs) + else + block.call(*args) + end + end + + def matches?(type_name, *args, **kwargs) + type_name == name# && matches_adapter?(**kwargs) + end + + def <=>(other) + # if conflicts_with?(other) + # raise TypeConflictError.new("Type #{name} was registered for all + # adapters, but shadows a native type with + # the same name for #{other.adapter}".squish) + # end + priority <=> other.priority + end + + protected + + attr_reader :name, :block, :override + + def priority + result = 0 + # if adapter + # result |= 1 + # end + if override + result |= 2 + end + result + end + + # def priority_except_adapter + # priority & 0b111111100 + # end + + private + + # def matches_adapter?(adapter: nil, **) + # (self.adapter.nil? || adapter == self.adapter) + # end + + # def conflicts_with?(other) + # same_priority_except_adapter?(other) && + # has_adapter_conflict?(other) + # end + + # def same_priority_except_adapter?(other) + # priority_except_adapter == other.priority_except_adapter + # end + + # def has_adapter_conflict?(other) + # (override.nil? && other.adapter) || + # (adapter && other.override.nil?) + # end + end + + class DecorationRegistration < Registration + def initialize(options, klass) + @options = options + @klass = klass + # @adapter = adapter + end + + def call(registry, *args, **kwargs) + subtype = registry.lookup(*args, **kwargs.except(*options.keys)) + klass.new(subtype) + end + + def matches?(*args, **kwargs) + matches_options?(**kwargs) + end + + def priority + super | 4 + end + + protected + + attr_reader :options, :klass + + private + + def matches_options?(**kwargs) + options.all? do |key, value| + kwargs[key] == value + end + end + end + end + + class TypeConflictError < StandardError + end + + # :startdoc: +end |