File: miquire_plugin.rb

package info (click to toggle)
mikutter 4.1.3%2Bdfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 9,260 kB
  • sloc: ruby: 20,126; sh: 183; makefile: 19
file content (170 lines) | stat: -rw-r--r-- 5,924 bytes parent folder | download | duplicates (3)
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
162
163
164
165
166
167
168
169
170
# -*- coding: utf-8 -*-
require 'miquire'
require 'plugin'
require 'miquire_to_spec'

# プラグインのロードに関すること
module Miquire::Plugin
  class << self
    using Miquire::ToSpec
    include Enumerable

    # ロードパスの配列を返す。
    # ロードパスに追加したい場合は、以下のようにすればいい
    #
    #  Miquire::Plugin.loadpath << 'pathA' << 'pathB'
    def loadpath
      @loadpath ||= [] end

    # プラグインのファイル名(フルパス)で繰り返す。
    def each(&block)
      iterated = Set.new
      detected = []
      loadpath.reverse.each { |path|
        Dir[File.join(File.expand_path(path), '*')].each { |file|
          if FileTest.directory?(file) and FileTest.exist?(File.join(file, File.basename(file))+'.rb')
            file = File.join(file, File.basename(file))+'.rb'
          elsif not file.end_with?('.rb'.freeze)
            next end
          plugin_name = File.basename(file, '.rb')
          if not iterated.include? plugin_name
            iterated << plugin_name
            detected << file end } }
      detected.sort_by{ |a|
        [:bundle == get_kind(a) ? 0 : 1, a]
      }.each(&block) end

    def each_spec
      each{ |path|
        spec = get_spec path
        yield spec if spec } end

    def to_hash
      result = {}
      each_spec{ |spec|
        result[spec[:slug].to_sym] = spec }
      result end

    # 受け取ったパスにあるプラグインのスラッグを返す
    # ==== Args
    # [path] パス(String)
    # ==== Return
    # プラグインスラッグ(Symbol)
    def get_slug(path)
      type_strict path => String
      spec = get_spec(path)
      if spec
        spec[:slug]
      else
        File.basename(path, ".rb").to_sym end end

    # specファイルがあればそれを返す
    # ==== Args
    # [path] パス(String)
    # ==== Return
    # specファイルの内容か、存在しなければnil
    def get_spec(path)
      type_strict path => String
      plugin_dir = FileTest.directory?(path) ? path : File.dirname(path)
      spec_filename = File.join(plugin_dir, ".mikutter.yml")
      deprecated_spec = false
      unless FileTest.exist? spec_filename
        spec_filename = File.join(plugin_dir, "spec")
        deprecated_spec = true end
      if FileTest.exist? spec_filename
        YAML.load_file(spec_filename).symbolize
          .merge(kind: get_kind(path),
                 path: plugin_dir,
                 deprecated_spec: deprecated_spec)
      elsif FileTest.exist? path
        { slug: File.basename(path, ".rb").to_sym,
          kind: get_kind(path),
          path: plugin_dir,
          deprecated_spec: false } end end

    def get_spec_by_slug(slug)
      type_strict slug => Symbol
      to_hash[slug] end

    # プラグインがthirdpartyかbundleかを返す
    def get_kind(path)
      type_strict path => String
      if Environment::PLUGIN_PATH.any?(&path.method(:start_with?))
        :bundle
      else
        :thirdparty end end

    def load_all
      each_spec do |spec|
        load spec
      rescue Miquire::LoadError => e
        ::Plugin.call(:modify_activity,
                      kind: "system",
                      title: "#{spec[:slug]} load failed",
                      date: Time.new,
                      exception: e,
                      description: e.to_s)
      end
    end

    def satisfy_mikutter_version?(spec)
      if defined?(spec[:depends][:mikutter]) and spec[:depends][:mikutter]
        version = Environment::Version.new(*(spec[:depends][:mikutter].split(".").map(&:to_i) + ([0]*4))[0...4])
        if Environment::VERSION < version
          raise Miquire::LoadError, "plugin #{spec[:slug]}: #{Environment::NAME} version too old (#{spec[:depends][:mikutter]} required, but #{Environment::NAME} version is #{Environment::VERSION})"
          return false end end
      true
    end

    def depended_plugins(_spec, recursive: false)
      spec = _spec.to_spec
      unless spec
        error "spec #{_spec.inspect}"
        return false
      end
      if spec.dig(:depends, :plugin)
        if recursive
          Array(spec.dig(:depends, :plugin)).map { |s| Array(s).first.to_sym }.flat_map { |s|
            depended_plugins(s, recursive: recursive).map { |d| d[:slug].to_sym }
          }.uniq.map { |d| d.to_spec }
        else
          Array(spec.dig(:depends, :plugin)).map do |s|
            Array(s).first.to_sym&.to_spec
          end
        end
      else
        []
      end
    end

    def load(_spec)
      return false unless _spec
      spec = _spec.to_spec
      return false unless spec
      return true if ::Plugin.instance_exist?(spec[:slug])
      return false unless satisfy_mikutter_version?(spec)

      atomic do
        depended_plugins(spec).each do |depend|
          raise Miquire::LoadError, "plugin #{spec[:slug].inspect} was not loaded because dependent plugin #{depend.inspect} was not loaded." unless load(depend)
        rescue Miquire::LoadError => err
          raise Miquire::LoadError, "plugin #{spec[:slug].inspect} was not loaded because dependent plugin was not loaded. previous error is:\n#{err.to_s}"
        end

        notice "plugin loaded: " + File.join(spec[:path], "#{spec[:slug]}.rb")
        ::Plugin.create(spec[:slug].to_sym) do
          self.spec = spec end
        Kernel.load File.join(spec[:path], "#{spec[:slug]}.rb")
        if spec[:deprecated_spec]
          title = "#{spec[:slug]}: specファイルは非推奨になりました。"
          Plugin.call(:modify_activity,
                      { plugin: spec[:slug],
                        kind: "error",
                        title: title,
                        date: Time.now,
                        spec: spec,
                        description: "#{title}\n代わりに.mikutter.ymlを使ってください。"}) end
        true end
    end
  end
end