# frozen_string_literal: true require 'legion/extensions/core' require 'legion/runner' module Legion module Extensions class << self def setup hook_extensions end def hook_extensions @timer_tasks = [] @loop_tasks = [] @once_tasks = [] @poll_tasks = [] @subscription_tasks = [] @actors = [] find_extensions load_extensions end def shutdown @subscription_tasks.each do |task| task[:threadpool].shutdown task[:threadpool].wait_for_termination(2) task[:threadpool].kill end @loop_tasks.each { |task| task[:running_class].cancel if task[:running_class].respond_to?(:cancel) } @once_tasks.each { |task| task[:running_class].cancel if task[:running_class].respond_to?(:cancel) } @timer_tasks.each { |task| task[:running_class].cancel if task[:running_class].respond_to?(:cancel) } @poll_tasks.each { |task| task[:running_class].cancel if task[:running_class].respond_to?(:cancel) } Legion::Logging.info 'Successfully shut down all actors' end def load_extensions @extensions ||= {} @loaded_extensions ||= [] @extensions.each do |extension, values| if values.key(:enabled) && !values[:enabled] Legion::Logging.info "Skipping #{extension} because it's disabled" next end if Legion::Settings[:extensions].key?(extension.to_sym) && Legion::Settings[:extensions][extension.to_sym].key?(:enabled) && !Legion::Settings[:extensions][extension.to_sym][:enabled] # rubocop:disable Layout/LineLength next end unless load_extension(extension, values) Legion::Logging.warn("#{extension} failed to load") next end @loaded_extensions.push(extension) end Legion::Logging.info "#{@extensions.count} extensions loaded with subscription:#{@subscription_tasks.count},every:#{@timer_tasks.count},poll:#{@poll_tasks.count},once:#{@once_tasks.count},loop:#{@loop_tasks.count}" end def load_extension(extension, values) return unless gem_load(values[:gem_name], extension) extension = Kernel.const_get(values[:extension_class]) has_logger = extension.respond_to?(:log) extension.autobuild require 'legion/transport/messages/lex_register' Legion::Transport::Messages::LexRegister.new(function: 'save', opts: extension.runners).publish if extension.respond_to?(:meta_actors) && extension.meta_actors.is_a?(Array) extension.meta_actors.each do |_key, actor| extension.log.debug("hooking meta actor: #{actor}") if has_logger hook_actor(**actor) end end extension.actors.each do |_key, actor| extension.log.debug("hooking literal actor: #{actor}") if has_logger hook_actor(**actor) end extension.log.info 'Loaded' rescue StandardError => e Legion::Logging.error e.message Legion::Logging.error e.backtrace false end def hook_actor(extension:, extension_name:, actor_class:, size: 1, **opts) size = 1 unless size.is_a? Integer extension_hash = { extension: extension, extension_name: extension_name, actor_class: actor_class, size: size, **opts } extension_hash[:running_class] = if actor_class.ancestors.include? Legion::Extensions::Actors::Subscription actor_class else actor_class.new end if actor_class.ancestors.include? Legion::Extensions::Actors::Every @timer_tasks.push(extension_hash) elsif actor_class.ancestors.include? Legion::Extensions::Actors::Once @once_tasks.push(extension_hash) elsif actor_class.ancestors.include? Legion::Extensions::Actors::Loop @loop_tasks.push(extension_hash) elsif actor_class.ancestors.include? Legion::Extensions::Actors::Poll @poll_tasks.push(extension_hash) elsif actor_class.ancestors.include? Legion::Extensions::Actors::Subscription extension_hash[:threadpool] = Concurrent::FixedThreadPool.new(100) size.times do extension_hash[:threadpool].post do actor_class.new.async.subscribe end end @subscription_tasks.push(extension_hash) else Legion::Logging.fatal 'did not match any actor classes' end end def gem_load(gem_name, name) gem_path = "#{Gem::Specification.find_by_name(gem_name).gem_dir}/lib/legion/extensions/#{name}" require gem_path true rescue LoadError => e Legion::Logging.error e.message Legion::Logging.error e.backtrace Legion::Logging.error "gem_path: #{gem_path}" unless gem_path.nil? false end def find_extensions @extensions ||= {} Gem::Specification.all_names.each do |gem| next unless gem[0..3] == 'lex-' lex = gem.split('-') @extensions[lex[1]] = { full_gem_name: gem, gem_name: "lex-#{lex[1]}", extension_name: lex[1], version: lex[2], extension_class: "Legion::Extensions::#{lex[1].capitalize}" } end end end end end