Skip to content

refactor: convert to middleclass OOP architecture and improve code organization#56

Merged
zerochae merged 11 commits intomasterfrom
refactor/oop-structure-improvement
Sep 30, 2025
Merged

refactor: convert to middleclass OOP architecture and improve code organization#56
zerochae merged 11 commits intomasterfrom
refactor/oop-structure-improvement

Conversation

@zerochae
Copy link
Copy Markdown
Owner

@zerochae zerochae commented Sep 30, 2025

Summary

Complete architectural refactor converting the entire codebase to middleclass-based OOP structure with improved organization, cleaner naming conventions, and
better separation of concerns.

🎯 Main Goals

  1. ✅ Convert all classes to middleclass OOP pattern
  2. ✅ Remove "Manager" suffix throughout codebase
  3. ✅ Implement dependency injection for better testability
  4. ✅ Separate concerns with FrameworkRegistry
  5. ✅ Introduce Events singleton pattern
  6. ✅ Improve type annotations and LSP support

🏗️ Architecture Changes

File Reorganization

Before After
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
manager/EndpointManager.lua → core/Endpoint.lua
manager/CacheManager.lua → core/Cache.lua
manager/EventManager.lua → core/Events.lua (singleton)
manager/PickerManager.lua → core/PickerRegistry.lua
(new) → core/FrameworkRegistry.lua

New Directory Structure

  lua/endpoint/
  ├── core/
  │   ├── Cache.lua              ⭐ (was manager/CacheManager.lua)
  │   ├── Events.lua             ⭐ (was manager/EventManager.lua, now singleton)
  │   ├── Endpoint.lua           ⭐ (was manager/EndpointManager.lua)
  │   ├── FrameworkRegistry.lua  ⭐ (new, extracted from EndpointManager)
  │   ├── PickerRegistry.lua     ⭐ (was manager/PickerManager.lua)
  │   ├── Framework.lua          (base class)
  │   ├── Parser.lua             (base class)
  │   ├── Detector.lua           (integrated detector)
  │   ├── Picker.lua             (base class)
  │   ├── Highlighter.lua
  │   └── Themes.lua
  ├── frameworks/
  │   ├── spring.lua
  │   ├── rails.lua
  │   ├── nestjs.lua
  │   └── ... (10 frameworks)
  ├── parser/
  ├── pickers/
  ├── lib/
  │   └── middleclass.lua        ⭐ (OOP library)
  └── init.lua                   (public API)

📝 Detailed Changes

1. Middleclass OOP Conversion

Before (setmetatable pattern):

local CacheManager = {}
CacheManager.__index = CacheManager

function CacheManager.new()
  local self = setmetatable({}, CacheManager)
  self.cache = {}
  return self
end

After (middleclass):
local class = require "endpoint.lib.middleclass"
local Cache = class "Cache"

function Cache:initialize()
  self.cache = {}
end
  1. Manager Classes Renamed
Old Name New Name Location Change
EndpointManager Endpoint → core/Endpoint.lua
CacheManager Cache → core/Cache.lua
EventManager Events (singleton) → core/Events.lua
PickerManager PickerRegistry → core/PickerRegistry.lua
  1. Events Singleton Pattern
  Before:
  -- EndpointManager had its own event_manager instance
  function EndpointManager:initialize()
    self.event_manager = EventManager:new()
  end

  After:
  -- Events is a singleton accessible globally
  local Events = require "endpoint.core.Events"
  local events = Events.static.get_instance()

  -- Static methods and constants
  Events.static.EVENT_TYPES = {
    SCAN_STARTED = "scan_started",
    ENDPOINT_DISCOVERED = "endpoint_discovered",
    SCAN_COMPLETED = "scan_completed"
  }
  1. FrameworkRegistry Extraction
  Extracted from EndpointManager for single responsibility:

  local FrameworkRegistry = require "endpoint.core.FrameworkRegistry"

  local registry = FrameworkRegistry:new()
  registry:register(SpringFramework)
  registry:register(RailsFramework)

  local detected = registry:detect_all()

API:

  • register(framework) - Register a framework
  • unregister(name) - Remove a framework
  • get_all() - Get all frameworks
  • get_by_name(name) - Get specific framework
  • detect_all() - Auto-detect frameworks
  • clear() - Clear all frameworks
  1. Dependency Injection
  Endpoint now supports DI for testing:

  -- Production
  local endpoint = Endpoint:new()

  -- Testing with mocks
  local endpoint = Endpoint:new({
    framework_registry = mock_registry,
    cache = mock_cache,
    picker_registry = mock_picker_registry
  })
  1. Method Decomposition
  EndpointManager:find() was refactored:

  -- Before: One large find() method

  -- After: Smaller, focused methods
  function Endpoint:find(opts)
    self:_ensure_initialized()
    local endpoints = self:_resolve_endpoints(opts)
    self:_show_with_picker(endpoints, opts)
  end

  function Endpoint:_resolve_endpoints(opts)
    if self:_should_use_cache(opts.method) then
      return self.cache:get_endpoints(opts.method)
    end
    local endpoints = self:scan_all_endpoints(opts)
    self:_update_cache_if_enabled(endpoints, opts.method)
    return endpoints
  end
  1. Detector Integration
  Before:
  local detector = new_dependency_detector(required_deps, manifest_files)

  After:
  local Detector = require "endpoint.core.Detector"
  local detector = Detector:new(required_deps, manifest_files)
  1. Type System Improvements
  Enhanced type annotations:

  ---@class endpoint.Events.static
  ---@field get_instance fun(): endpoint.Events
  ---@field reset_instance fun()
  ---@field EVENT_TYPES table<string, string>
  ---@field [any] any  -- For extensibility

  ---@class endpoint.core.Endpoint : Class
  ---@field framework_registry endpoint.FrameworkRegistry
  ---@field cache endpoint.Cache
  ---@field picker_registry endpoint.PickerRegistry

  Legacy aliases for compatibility:
  ---@alias EventManager endpoint.Events
  ---@alias CacheManager endpoint.Cache
  ---@alias PickerManager endpoint.PickerRegistry

  🧪 Testing Improvements

  Integration Tests

  Updated for new architecture:

  -- Before
  local EndpointManager = require "endpoint.manager.EndpointManager"
  local manager = EndpointManager:new()
  local cache_manager = manager.cache_manager

  -- After
  local Endpoint = require "endpoint.core.Endpoint"
  local endpoint = Endpoint:new()
  local cache = endpoint.cache

Test Quality

  • ✅ Fixed mock function redeclaration warnings
  • ✅ Removed unused variables
  • ✅ Removed redundant assertion messages
  • ✅ Improved code formatting
  • ✅ All tests passing

📚 Documentation Updates

Updated doc/endpoint.txt

  • ✅ Architecture section now includes FrameworkRegistry
  • ✅ EndpointManager → Endpoint
  • ✅ Fixed API usage examples
  • ✅ Updated debug commands

Before:
Check framework detection:
lua print(vim.inspect(require("endpoint.manager.EndpointManager"):new():get_detected_frameworks()))

After:
Check framework detection:
lua print(vim.inspect(require("endpoint").detect_frameworks()))

📊 Statistics

45 files changed
2,259 insertions(+)
1,335 deletions(-)

Key Files Changed

  • Core classes: 11 files (complete refactor)
  • Framework implementations: 10 files (updated to new API)
  • Parser implementations: 10 files (updated to new API)
  • Picker implementations: 3 files (updated to new API)
  • Tests: 4 files (updated for new architecture)
  • Documentation: 2 files (updated)
  • Type definitions: 1 file (comprehensive update)

🔄 Migration Guide

For Plugin Users

No changes required! The public API remains the same:

  require("endpoint").setup({
    picker = { type = "telescope" },
    cache = { mode = "session" }
  })

  vim.cmd("Endpoint Get")

For Plugin Developers

Import path changes:

  -- Old
  local EndpointManager = require "endpoint.manager.EndpointManager"
  local CacheManager = require "endpoint.manager.CacheManager"
  local EventManager = require "endpoint.manager.EventManager"

  -- New
  local Endpoint = require "endpoint.core.Endpoint"
  local Cache = require "endpoint.core.Cache"
  local Events = require "endpoint.core.Events"

Event access:

  -- Old
  local event_manager = endpoint_manager.event_manager

  -- New
  local Events = require "endpoint.core.Events"
  local events = Events.static.get_instance()

✨ Benefits

Aspect Before After
Naming Verbose "Manager" everywhere Clean, intuitive names
Organization Mixed locations All core in core/
Testability Hard to mock Dependency injection
Type Safety Basic annotations Comprehensive types
LSP Support Some warnings Clean, no warnings
Maintainability Mixed concerns Single responsibility
Extensibility Coupled Loosely coupled

🎯 Commit History

952a3ba refactor: remove return type annotations from new() methods
45a7872 refactor: convert Detector and Manager classes to middleclass OOP
9f2a67e refactor: improve Manager architecture with EventBus and FrameworkRegistry
4dc0137 fix: remove duplicated get_event_manager method
dfbb601 refactor: restructure manager classes and fix type annotations
40d6a7e fix: update integration test to match refactored architecture
0b346fc refactor: improve test code quality and remove linter warnings
5e7b912 style: apply linter formatting and minor code improvements
4f2a786 refactor: rename EndpointManager to Endpoint and move to core/
5046dc4 refactor: remove remaining "manager" references from comments
cec9b97 docs: update documentation to reflect new architecture

✅ Checklist

  • All classes converted to middleclass
  • "Manager" suffix removed
  • Dependency injection implemented
  • FrameworkRegistry extracted
  • Events singleton pattern implemented
  • Type annotations updated
  • Tests updated and passing
  • Documentation updated
  • No breaking changes for users
  • Linter warnings fixed
  • Code formatting consistent

🚀 Future Work

This refactor sets the foundation for:

  • Easier plugin extension system
  • Better testing infrastructure
  • Custom framework additions
  • Advanced caching strategies
  • Plugin architecture patterns

📎 Related

This is a major architectural improvement with no breaking changes for end users. All existing configurations and commands work as before.

zerochae and others added 11 commits September 30, 2025 12:09
- Remove return type from TelescopePicker:new() in meta/types.lua
- Add return self to picker implementations (telescope, vim_ui_select, snacks)
- Fix LSP warnings while maintaining runtime compatibility
- Classic.lua's super.new() pattern doesn't require explicit returns but LSP expects consistency

Co-Authored-By: Claude <noreply@anthropic.com>
- Detector 클래스를 middleclass 기반으로 변경
  - new_dependency_detector 제거
  - dependency 검사 기능을 Detector 클래스 자체에 통합

- 모든 Manager 클래스를 middleclass로 변환
  - CacheManager: setmetatable → class('CacheManager')
  - EventManager: setmetatable → class('EventManager')
  - PickerManager: setmetatable → class('PickerManager')
  - EndpointManager: setmetatable → class('EndpointManager')

- telescope.lua의 pcall 타입 에러 수정
  - 함수 래핑으로 타입 안전성 확보

- Highlighter 타입 어노테이션 정리
  - 중복 제거, meta/types.lua로 통합

- meta/types.lua 업데이트
  - Detector 타입 정의 수정 (initialize 메서드 반영)
  - endpoint.entry에 end_column 필드 추가
…istry

제안 3 (최소 변경):
- EventBus 싱글톤 구현 (lua/endpoint/core/EventBus.lua)
  - EventManager를 전역에서 접근 가능한 싱글톤으로 변경
  - EndpointManager의 event_manager 인스턴스 제거

- EndpointManager 메서드 분해
  - find() 메서드를 더 작고 명확한 단위로 분리
  - _resolve_endpoints(): 캐시/스캔 로직 분리
  - _should_use_cache(): 캐시 사용 여부 판단
  - _update_cache_if_enabled(): 캐시 업데이트 로직

제안 2 (Facade + 독립 모듈):
- FrameworkRegistry 분리 (lua/endpoint/core/FrameworkRegistry.lua)
  - Framework 등록/관리 로직을 독립 클래스로 분리
  - 단일 책임 원칙 준수
  - 명확한 API: register, unregister, get_all, detect_all 등

- Dependency Injection 추가
  - EndpointManager:initialize(dependencies) 패턴 도입
  - 테스트 시 Mock 객체 주입 가능
  - framework_registry, cache_manager, picker_manager 주입 가능

개선 효과:
- 테스트 용이성 향상 (DI 패턴)
- 코드 가독성 개선 (메서드 분해)
- 관심사 분리 (FrameworkRegistry)
- EventManager의 독립성 확보 (싱글톤)
- Rename and move manager classes to improve organization
  - EndpointManager: manager/ -> root level
  - CacheManager -> Cache: manager/ -> core/
  - EventManager -> Events: manager/ -> core/ (with static singleton pattern)
  - PickerManager -> PickerRegistry: manager/ -> core/
- Remove EventBus in favor of direct Events singleton usage
- Fix type annotation issues
  - Add endpoint.Events.static class definition with [any] any for extensibility
  - Fix Cache._get_project_hash() gsub return value handling
  - Update Events to use static methods (get_instance, reset_instance, EVENT_TYPES)
- Update FrameworkRegistry and PickerRegistry type definitions
- Improve overall type safety and LSP support
- Update require path: endpoint.manager.EndpointManager → endpoint.EndpointManager
- Update cache reference: cache_manager → cache
- Remove redundant comments for cleaner test code
- Fix mock function pattern to avoid redeclaration warnings
- Remove unused content variable in spring_spec.lua
- Remove redundant assertion messages for cleaner code
- Improve code formatting for better readability
- Fix EOF newlines in multiple files
- Change plugin guard from numeric to boolean
- Improve code formatting for better consistency
- Move lua/endpoint/EndpointManager.lua -> lua/endpoint/core/Endpoint.lua
- Rename class from EndpointManager to Endpoint
- Update init.lua to use endpoint.core.Endpoint
- Update all type definitions in meta/types.lua
- Update integration tests to use new naming
- Remove "Manager" suffix for cleaner naming
- Update docstrings to use "endpoint" instead of "endpoint manager"
- Rename event_manager variable to events for consistency
- Update dotnet.lua comment to reference FrameworkRegistry
- Update EndpointManager references to Endpoint
- Add FrameworkRegistry to architecture description
- Fix outdated API usage examples
- Update troubleshooting debug commands
@zerochae zerochae merged commit 112d1fd into master Sep 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant