1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
# frozen_string_literal: true
# プラグインでCLIコマンドを作成するためのDSLと、実行環境のセットアップ
module Prelude
class NameError < RuntimeError; end
module DSL
def namespace(name, &)
@namespaces ||= {}
if toplevel? && Prelude.plugin_context[:slug].to_s != name.to_s
raise NameError, '最上位のnamespaceはプラグインのslugと同じ名前にする必要があります'
end
(@namespaces[name] ||= Namespace.new(self, name)).instance_eval(&)
end
def command(name, description: nil, &block)
@commands_dict ||= {}
if !name || name.empty?
raise NameError, 'command name cannot be empty'
end
if toplevel? && Prelude.plugin_context[:slug].to_s != name.to_s
raise NameError, 'namespaceに含まれないcommandはプラグインのslugと同じ名前にする必要があります'
end
if @commands_dict[name]
warn "already defined command `#{[full_name, name].compact.join(':')}`"
end
@commands_dict[name] = Command.new(self, name, action: block, description:)
end
def commands
@commands ||= commands_nocached
end
def commands_nocached
[@namespaces&.values&.map(&:commands), @commands_dict&.values].compact.flatten.freeze
end
end
class Namespace
include DSL
attr_reader :parent, :name
def initialize(parent, name)
@parent = parent
@name = name
end
def full_name
[parent.full_name, name].compact.join(':')
end
private
def toplevel?
false
end
end
class Command
attr_reader :parent, :name, :description
def initialize(parent, name, action:, description: nil)
@parent = parent
@name = name
@action = action
@description = description
@plugin = Prelude.plugin_context
end
def full_name
[parent.full_name, name].compact.join(':')
end
def execute
ExecutionScope.new(full_name, @plugin).instance_eval(&@action)
end
end
class ExecutionScope
attr_reader :name, :spec
def initialize(name, spec)
@name = name
@spec = spec
end
def load_plugin(*slugs)
if slugs.empty?
Miquire::Plugin.load(spec)
else
slugs.each(&Miquire::Plugin.method(:load))
end
end
end
class << self
include DSL
attr_accessor :plugin_context
def full_name
nil
end
def load_by_slug(slug)
Prelude.plugin_context = spec = Miquire::Plugin.get_spec_by_slug(slug)
return unless spec
Array(spec[:prelude]).each do |file|
require File.join(spec[:path], file)
end
ensure
Prelude.plugin_context = nil
end
def load_all
Miquire::Plugin.each_spec do |spec|
Prelude.plugin_context = spec
Array(spec[:prelude]).each do |file|
require File.join(spec[:path], file)
end
ensure
Prelude.plugin_context = nil
end
end
def execute!(command_name)
require_relative '../utils'
require 'boot/check_config_permission'
load_by_slug(command_name.split(':', 2).first.to_sym)
cmd = Prelude.commands.find { |c| c.full_name == command_name }
if cmd
require 'fileutils'
require 'lib/diva_hacks'
require 'lib/lazy'
require 'lib/reserver'
require 'lib/timelimitedqueue'
require 'lib/uithreadonly'
require 'lib/weakstorage'
require 'userconfig'
cmd.execute
else
file = File.join(__dir__, "shell/#{command_name}.rb")
if FileTest.exist?(file)
require file
else
puts "no such command: #{command_name}"
end
end
exit
end
private
def toplevel?
true
end
end
end
|