Class: Kabosu::DictManager

Inherits:
Object
  • Object
show all
Defined in:
lib/kabosu/dict_manager.rb

Defined Under Namespace

Classes: DictNotFound, DownloadError

Constant Summary collapse

EDITIONS =
%w[small core full].freeze
EDITION_PRIORITY =
%w[full core small].freeze
GITHUB_REPO =
"WorksApplications/SudachiDict"
GITHUB_API =
"https://api.github.com"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dir: self.class.default_dir) ⇒ DictManager

Returns a new instance of DictManager.



22
23
24
# File 'lib/kabosu/dict_manager.rb', line 22

def initialize(dir: self.class.default_dir)
  @dir = dir
end

Instance Attribute Details

#dirObject (readonly)

Returns the value of attribute dir.



26
27
28
# File 'lib/kabosu/dict_manager.rb', line 26

def dir
  @dir
end

Class Method Details

.default_dirObject

Default storage directory: ~/.kabosu/dict/



18
19
20
# File 'lib/kabosu/dict_manager.rb', line 18

def self.default_dir
  File.join(Dir.home, ".kabosu", "dict")
end

Instance Method Details

#available_versionsObject

List available versions from GitHub releases.



148
149
150
151
152
# File 'lib/kabosu/dict_manager.rb', line 148

def available_versions
  uri = URI("#{GITHUB_API}/repos/#{GITHUB_REPO}/releases")
  response = http_get(uri, headers: { "Accept" => "application/json" })
  JSON.parse(response.body).map { |r| r["tag_name"].sub(/\Av/, "") }
end

#find(edition: nil) ⇒ Object

Find the best available dictionary path. Prefers: latest version, then full > core > small.

Raises:



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/kabosu/dict_manager.rb', line 88

def find(edition: nil)
  candidates = installed
  raise DictNotFound, "No dictionaries installed. Run: rake kabosu:install" if candidates.empty?

  if edition
    edition = validate_edition(edition)
    match = candidates.find { |d| d[:edition] == edition }
    raise DictNotFound, "No #{edition} dictionary installed" unless match
    return match[:path]
  end

  # Group by version (already sorted newest-first), pick best edition
  by_version = candidates.group_by { |d| d[:version] }
  latest_version_dicts = by_version.values.first

  best = EDITION_PRIORITY.each do |ed|
    found = latest_version_dicts.find { |d| d[:edition] == ed }
    break found if found
  end

  best.is_a?(Hash) ? best[:path] : latest_version_dicts.first[:path]
end

#install(edition = "core", version: nil) ⇒ Object

Download and extract a dictionary edition.

manager.install("small")
manager.install("core", version: "20260116")


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
# File 'lib/kabosu/dict_manager.rb', line 35

def install(edition = "core", version: nil)
  edition = validate_edition(edition)
  version ||= latest_version

  dest_dir = File.join(@dir, "sudachi-dictionary-#{version}")
  dic_path = File.join(dest_dir, "system_#{edition}.dic")

  if File.exist?(dic_path)
    $stderr.puts "Already installed: #{dic_path}"
    return dic_path
  end

  url = release_asset_url(version, edition)
  zip_path = File.join(@dir, "sudachi-dictionary-#{version}-#{edition}.zip")

  FileUtils.mkdir_p(@dir)
  download(url, zip_path)
  extract(zip_path, @dir)
  FileUtils.rm_f(zip_path)

  unless File.exist?(dic_path)
    raise DownloadError, "Expected #{dic_path} after extraction, but file not found"
  end

  $stderr.puts "Installed: #{dic_path}"
  dic_path
end

#installedObject

List all installed dictionaries. Returns an array of hashes: { version:, edition:, path: }



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/kabosu/dict_manager.rb', line 67

def installed
  results = []
  return results unless Dir.exist?(@dir)

  Dir.glob(File.join(@dir, "sudachi-dictionary-*")).sort.reverse.each do |version_dir|
    next unless File.directory?(version_dir)

    version = File.basename(version_dir).sub("sudachi-dictionary-", "")
    EDITIONS.each do |edition|
      dic = File.join(version_dir, "system_#{edition}.dic")
      next unless File.exist?(dic)

      results << { version: version, edition: edition, path: dic }
    end
  end

  results
end

#latest_versionObject

Fetch the latest release tag from GitHub.



138
139
140
141
142
143
144
145
# File 'lib/kabosu/dict_manager.rb', line 138

def latest_version
  uri = URI("#{GITHUB_API}/repos/#{GITHUB_REPO}/releases/latest")
  response = http_get(uri, headers: { "Accept" => "application/json" })
  data = JSON.parse(response.body)
  tag = data["tag_name"]
  # Tags are like "v20260116" — strip the "v" prefix
  tag.sub(/\Av/, "")
end

#remove(edition: nil, version: nil) ⇒ Object

Remove a specific dictionary edition, or an entire version.

Raises:



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/kabosu/dict_manager.rb', line 114

def remove(edition: nil, version: nil)
  targets = installed
  targets = targets.select { |d| d[:version] == version } if version
  targets = targets.select { |d| d[:edition] == edition } if edition

  raise DictNotFound, "No matching dictionary found" if targets.empty?

  targets.each do |d|
    FileUtils.rm_f(d[:path])
    $stderr.puts "Removed: #{d[:path]}"

    # Clean up empty version directories
    version_dir = File.dirname(d[:path])
    dics_remaining = Dir.glob(File.join(version_dir, "system_*.dic"))
    if dics_remaining.empty?
      FileUtils.rm_rf(version_dir)
      $stderr.puts "Removed empty directory: #{version_dir}"
    end
  end
end