Class: Lich::Common::FrontendLocator

Inherits:
Object
  • Object
show all
Defined in:
documented/common/frontend_locator.rb

Overview

Resolves installed frontend executables from the shared Frontend catalog. It has no GTK dependency and is safe for GUI, CLI, and --no-gtk startup.

Defined Under Namespace

Classes: Resolution

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(platform_key: Frontend.platform_key, environment: ENV, logger: nil, application_roots: ['/Applications', '~/Applications'], wine: (Wine) ? Wine : nil)) ⇒ FrontendLocator

Returns a new instance of FrontendLocator.

Parameters:

  • platform_key (Symbol) (defaults to: Frontend.platform_key)

    canonical host classification

  • environment (Hash) (defaults to: ENV)

    environment used for path expansion/PATH

  • logger (#call, nil) (defaults to: nil)

    receives handled discovery warning strings

  • application_roots (Array<String>) (defaults to: ['/Applications', '~/Applications'])

    macOS application directories

  • wine (Module, nil) (defaults to: (Wine) ? Wine : nil))

    injected Wine integration provider



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'documented/common/frontend_locator.rb', line 95

def initialize(
  platform_key: Frontend.platform_key,
  environment: ENV,
  logger: nil,
  application_roots: ['/Applications', '~/Applications'],
  wine: (defined?(Wine) ? Wine : nil)
)
  @platform_key = Frontend.validate_platform_key!(platform_key)
  @environment = environment
  @logger = logger || method(:default_log)
  @application_roots = application_roots
  @wine = wine
  @cache = {}
  # Always acquire the cache mutex before the application-index mutex
  # when both are needed. resolve(refresh: true) relies on this order.
  @cache_mutex = Mutex.new
  @application_index = nil
  @application_index_mutex = Mutex.new
end

Class Method Details

.available(gui_selectable: nil, refresh: false) ⇒ Array<Resolution>

Returns resolutions for installed catalog entries.

Parameters:

  • gui_selectable (Boolean, nil) (defaults to: nil)

    catalog presentation filter

  • refresh (Boolean) (defaults to: false)

    clear cached results before discovery

Returns:



42
43
44
# File 'documented/common/frontend_locator.rb', line 42

def available(gui_selectable: nil, refresh: false)
  default.available(gui_selectable: gui_selectable, refresh: refresh)
end

.compatibility_location(frontend_id) ⇒ String?

Backward-compatible directory result used by Lich.seek.

Parameters:

  • frontend_id (String, Symbol)

Returns:



76
77
78
79
80
81
# File 'documented/common/frontend_locator.rb', line 76

def compatibility_location(frontend_id)
  resolution = resolve(frontend_id)
  resolution && File.dirname(resolution.executable_path)
rescue ArgumentError
  nil
end

.launchable?(frontend_id, refresh: false) ⇒ Boolean

Returns whether a frontend has a native launcher on this platform and its required executable is installed. Unlike selectable?, this does not apply graphical presentation metadata.

Parameters:

  • frontend_id (String, Symbol)

    registered frontend identifier

  • refresh (Boolean) (defaults to: false)

    bypass the process-local discovery cache

Returns:

  • (Boolean)


63
64
65
# File 'documented/common/frontend_locator.rb', line 63

def launchable?(frontend_id, refresh: false)
  default.launchable?(frontend_id, refresh: refresh)
end

.refresh!void

This method returns an undefined value.

Clears process-local discovery results. No settings are persisted.



69
70
71
# File 'documented/common/frontend_locator.rb', line 69

def refresh!
  default.refresh!
end

.resolve(frontend_id, override: nil, refresh: false) ⇒ Resolution?

Resolves a known frontend to a launchable executable.

Parameters:

  • frontend_id (String, Symbol)

    registered frontend identifier

  • override (String, nil) (defaults to: nil)

    explicit executable path for this call

  • refresh (Boolean) (defaults to: false)

    bypass the process-local discovery cache

Returns:

  • (Resolution, nil)

    nil when the frontend is not installed

Raises:

  • (ArgumentError)

    for blank/unknown identifiers or invalid overrides



33
34
35
# File 'documented/common/frontend_locator.rb', line 33

def resolve(frontend_id, override: nil, refresh: false)
  default.resolve(frontend_id, override: override, refresh: refresh)
end

.selectable?(frontend_id, refresh: false) ⇒ Boolean

Returns whether a frontend is both installed and supported by the graphical launcher on this platform.

Parameters:

  • frontend_id (String, Symbol)

    registered frontend identifier

  • refresh (Boolean) (defaults to: false)

    bypass the process-local discovery cache

Returns:

  • (Boolean)


52
53
54
# File 'documented/common/frontend_locator.rb', line 52

def selectable?(frontend_id, refresh: false)
  default.selectable?(frontend_id, refresh: refresh)
end

Instance Method Details

#available(gui_selectable: nil, refresh: false) ⇒ Array<Resolution>

Returns an array of resolved frontends for all installed catalog entries.

When gui_selectable: true, filters results to only frontends marked as graphical and supported on the current platform. When nil or false, returns all installed frontends.

Examples:

locator.available(gui_selectable: true)  # => [Resolution(...stormfront), Resolution(...genie)]
locator.available(refresh: true)  # => fresh discovery, bypassing cache

Parameters:

  • gui_selectable (Boolean, nil) (defaults to: nil)

    filter to graphical frontends supported on this platform

  • refresh (Boolean) (defaults to: false)

    clear the process-local discovery cache before returning results

Returns:

  • (Array<Resolution>)

    resolutions for installed frontends; empty array if none found



156
157
158
159
160
161
162
163
# File 'documented/common/frontend_locator.rb', line 156

def available(gui_selectable: nil, refresh: false)
  refresh! if refresh
  definitions = Frontend.definitions(gui_selectable: gui_selectable)
  definitions = definitions.select { |definition| gui_platform_supported?(definition) } if gui_selectable
  definitions.filter_map do |definition|
    resolve(definition[:id])
  end
end

#launchable?(frontend_id, refresh: false) ⇒ Boolean

Returns whether a frontend has a native launcher on this platform and its executable is installed.

Unlike selectable?, this does not apply graphical presentation metadata. It checks: whether the frontend's launcher_adapter is supported on the current platform (environment, avalon, simutronics, or embedded), and whether the frontend itself is installed. Embedded launchers always return true (they do not require an external executable).

Examples:

locator.launchable?(:stormfront)  # => true (if installed with native launcher support)
locator.launchable?(:invalid)  # => false

Parameters:

  • frontend_id (String, Symbol)

    registered frontend identifier

  • refresh (Boolean) (defaults to: false)

    bypass the process-local discovery cache

Returns:

  • (Boolean)

    true if a native launcher exists on this platform and the frontend is installed



198
199
200
201
202
203
204
# File 'documented/common/frontend_locator.rb', line 198

def launchable?(frontend_id, refresh: false)
  definition = Frontend.definition_for(frontend_id)
  return false unless native_launcher_supported?(definition)
  return true if definition.dig(:metadata, :launcher_adapter) == :embedded

  !resolve(definition[:id], refresh: refresh).nil?
end

#refresh!void

This method returns an undefined value.

Clears the process-local discovery cache and application bundle index.

All subsequent resolve/available/selectable?/launchable? calls will perform fresh discovery. No settings or state outside the current process are affected.



212
213
214
215
216
# File 'documented/common/frontend_locator.rb', line 212

def refresh!
  @cache_mutex.synchronize { @cache.clear }
  @application_index_mutex.synchronize { @application_index = nil }
  nil
end

#resolve(frontend_id, override: nil, refresh: false) ⇒ Resolution?

Resolves a registered frontend to its executable path, with optional override and cache control.

Queries the process-local discovery cache by default. Use refresh: true to bypass the cache and rediscover. An explicit override path is resolved immediately without caching.

Examples:

resolution = locator.resolve(:stormfront)
resolution&.executable_path  # => "/usr/local/bin/stormfront"
locator.resolve(:genie, override: "/home/user/genie-custom")  # => Resolution with custom path

Parameters:

  • frontend_id (String, Symbol)

    registered frontend identifier (e.g., :stormfront, :genie)

  • override (String, nil) (defaults to: nil)

    explicit filesystem path to use instead of discovery

  • refresh (Boolean) (defaults to: false)

    bypass and clear the process-local discovery cache for this frontend

Returns:

  • (Resolution, nil)

    a Resolution struct with frontend_id, executable_path, and source; nil if not installed

Raises:

  • (ArgumentError)

    if frontend_id is blank or unknown, or if override path is not executable



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'documented/common/frontend_locator.rb', line 129

def resolve(frontend_id, override: nil, refresh: false)
  definition = Frontend.definition_for(frontend_id)
  return resolve_override(definition, override) unless override.nil?

  @cache_mutex.synchronize do
    if refresh
      @cache.delete(definition[:id])
      @application_index_mutex.synchronize { @application_index = nil }
    end
    return @cache[definition[:id]] if @cache.key?(definition[:id])
  end

  discovered = discover(definition)
  @cache_mutex.synchronize { @cache[definition[:id]] = discovered }
end

#selectable?(frontend_id, refresh: false) ⇒ Boolean

Returns whether a frontend is installed and marked for graphical launcher presentation on this platform.

Combines two checks: the frontend must have gui_selectable: true in its catalog metadata, and its platform must be in the gui_platforms list (or gui_platforms must be nil, meaning all platforms). Additionally, the frontend must be installed (discoverable via the normal resolution pipeline).

Examples:

locator.selectable?(:stormfront)  # => true (if installed and gui_selectable)
locator.selectable?(:invalid)  # => false

Parameters:

  • frontend_id (String, Symbol)

    registered frontend identifier

  • refresh (Boolean) (defaults to: false)

    bypass the process-local discovery cache

Returns:

  • (Boolean)

    true if both installed and graphically selectable; false otherwise



177
178
179
180
181
182
183
# File 'documented/common/frontend_locator.rb', line 177

def selectable?(frontend_id, refresh: false)
  definition = Frontend.definition_for(frontend_id)
  return false unless definition.dig(:metadata, :gui_selectable)
  return false unless gui_platform_supported?(definition)

  !resolve(definition[:id], refresh: refresh).nil?
end