diff --git a/CHANGELOG.md b/CHANGELOG.md index 78da334..ff48b47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Added - Optional `namespace` parameter to `Magic::Lookup#for` for class lookups within a namespace. +- `Magic::Lookup#namespaces` to set default lookup namespaces. ## [0.1.0] — 2024-10-10 diff --git a/README.md b/README.md index f5c6249..6efbc3e 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,13 @@ Scope.for MyModel # => MyScope Scope.for MyModel, MyNamespace # => MyNamespace::MyScope ``` +Multiple default lookup namespaces may be set for the base class: + +```ruby +Scope.namespaces << MyNamespace # => [nil, MyNamespace] +Scope.for MyModel # => MyNamespace::MyScope +``` + > [!TIP] > Until a comprehensive documentation on all the use cases is released, the spec is recommended as further reading. > One can access it by running `rake` in the gem directory. diff --git a/lib/magic/lookup.rb b/lib/magic/lookup.rb index 5ecfee9..f7821b1 100644 --- a/lib/magic/lookup.rb +++ b/lib/magic/lookup.rb @@ -6,7 +6,8 @@ module Magic module Lookup - autoload :Error, 'magic/lookup/error' + autoload :Error, 'magic/lookup/error' + autoload :Namespaces, 'magic/lookup/namespaces' include Memery @@ -25,6 +26,8 @@ module Lookup .first end + prepend Namespaces + def name_for(object_class) = raise NotImplementedError def descendants diff --git a/lib/magic/lookup/namespaces.rb b/lib/magic/lookup/namespaces.rb new file mode 100644 index 0000000..ae08850 --- /dev/null +++ b/lib/magic/lookup/namespaces.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Magic + module Lookup + module Namespaces + attr_writer :namespaces + + def namespaces = @namespaces ||= [ nil ] + + def for object_class, *namespaces + return super unless namespaces.empty? + + self.namespaces + .reverse # recently added first + .lazy # optimization + .filter_map { super object_class, _1 } + .first + end + end + end +end diff --git a/spec/magic/lookup_spec.rb b/spec/magic/lookup_spec.rb index 5d7fbd5..f4545b4 100644 --- a/spec/magic/lookup_spec.rb +++ b/spec/magic/lookup_spec.rb @@ -73,10 +73,36 @@ def self.name_for(object_class) = "#{object_class}Scope" end describe 'namespaces' do + before { stub_const 'ArrayScope', Class.new(base_class) } before { stub_const 'Namespace::ArrayScope', Class.new(base_class) } its([Array, 'Namespace']) { is_expected.to be Namespace::ArrayScope } + context 'when set for the base class' do + before { base_class.namespaces << Namespace } + + its([Array ]) { is_expected.to be Namespace::ArrayScope } + its([Array, nil]) { is_expected.to be ArrayScope } + + it 'isn’t cached' do + expect(subject[Array]).to be Namespace::ArrayScope + base_class.namespaces.delete Namespace + expect(subject[Array]).to be ArrayScope + end + + context 'without matching decorators in the namespace' do + before { base_class.namespaces = [ 'OtherNamespace' ] } + + its([Array]) { is_expected.to be_nil } + + context 'with a fallback' do + before { base_class.namespaces = [ nil, 'OtherNamespace' ] } + + its([Array]) { is_expected.to be ArrayScope } + end + end + end + context 'when target class is namespaced' do context 'when matching class is of the same namespace' do before { stub_const 'Enumerator::LazyScope', Class.new(base_class) }