File: prelude.rb

package info (click to toggle)
mikutter 5.1.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,780 kB
  • sloc: ruby: 22,912; sh: 186; makefile: 21
file content (161 lines) | stat: -rw-r--r-- 3,839 bytes parent folder | download
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