narou 3.5.1 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +3 -3
- data/.rubocop.yml +1 -1
- data/ChangeLog.md +37 -0
- data/Gemfile.lock +74 -71
- data/README.md +4 -4
- data/lib/command/browser.rb +1 -1
- data/lib/command/diff.rb +2 -2
- data/lib/command/folder.rb +6 -1
- data/lib/command/setting.rb +2 -2
- data/lib/command/update/general_lastup_updater.rb +2 -1
- data/lib/device.rb +6 -7
- data/lib/downloader.rb +10 -3
- data/lib/helper.rb +3 -3
- data/lib/inventory.rb +2 -2
- data/lib/loadconverter.rb +3 -4
- data/lib/mailer.rb +1 -1
- data/lib/narou.rb +276 -270
- data/lib/novelconverter.rb +2 -2
- data/lib/sitesetting.rb +1 -1
- data/lib/template.rb +1 -1
- data/lib/version.rb +1 -1
- data/lib/web/appserver.rb +8 -5
- data/narou.gemspec +13 -10
- data/template/converter.rb.erb +1 -8
- metadata +78 -30
data/lib/helper.rb
CHANGED
@@ -350,10 +350,10 @@ module Helper
|
|
350
350
|
# 長過ぎるファイルパスを詰める
|
351
351
|
# ファイル名部分のみを詰める。拡張子は維持する
|
352
352
|
#
|
353
|
-
def truncate_path(path, limit = Inventory.load["filename-length-limit"])
|
353
|
+
def truncate_path(path, limit = Inventory.load["filename-length-limit"], extname: nil)
|
354
354
|
limit ||= FILENAME_LENGTH_LIMIT
|
355
355
|
dirname = File.dirname(path)
|
356
|
-
extname
|
356
|
+
extname ||= File.extname(path)
|
357
357
|
basename = File.basename(path, extname)
|
358
358
|
if basename.length > limit
|
359
359
|
basename = basename[0...limit]
|
@@ -375,7 +375,7 @@ module Helper
|
|
375
375
|
#
|
376
376
|
def erb_copy(src, dst, _binding)
|
377
377
|
data = File.read(src, mode: "r:BOM|UTF-8")
|
378
|
-
result = ERB.new(data,
|
378
|
+
result = ERB.new(data, trim_mode: "-").result(_binding)
|
379
379
|
File.write(dst, result)
|
380
380
|
end
|
381
381
|
|
data/lib/inventory.rb
CHANGED
@@ -44,13 +44,13 @@ module Inventory
|
|
44
44
|
return unless File.exist?(@inventory_file_path)
|
45
45
|
self.merge!(Helper::CacheLoader.memo(@inventory_file_path) { |yaml|
|
46
46
|
begin
|
47
|
-
YAML.
|
47
|
+
YAML.unsafe_load(yaml)
|
48
48
|
rescue Psych::SyntaxError
|
49
49
|
unless restore(@inventory_file_path)
|
50
50
|
error "#{@inventory_file_path} が壊れてるっぽい"
|
51
51
|
raise
|
52
52
|
end
|
53
|
-
YAML.
|
53
|
+
YAML.unsafe_load_file(@inventory_file_path)
|
54
54
|
end
|
55
55
|
})
|
56
56
|
end
|
data/lib/loadconverter.rb
CHANGED
@@ -10,7 +10,7 @@ BlankConverter = Class.new(ConverterBase) {}
|
|
10
10
|
|
11
11
|
$latest_converter = nil
|
12
12
|
|
13
|
-
def converter(
|
13
|
+
def converter(_title = nil, &block)
|
14
14
|
$latest_converter = Class.new(ConverterBase, &block)
|
15
15
|
end
|
16
16
|
|
@@ -58,7 +58,7 @@ end
|
|
58
58
|
# buffer
|
59
59
|
# end
|
60
60
|
# end
|
61
|
-
def load_converter(
|
61
|
+
def load_converter(archive_path)
|
62
62
|
converter_path = File.join(archive_path, "converter.rb")
|
63
63
|
if File.exist?(converter_path)
|
64
64
|
$latest_converter = nil
|
@@ -71,9 +71,8 @@ def load_converter(title, archive_path)
|
|
71
71
|
if conv
|
72
72
|
return conv
|
73
73
|
else
|
74
|
-
title_for_converter = (title =~ /.txt\z/ ? title : File.basename(archive_path))
|
75
74
|
error "converter.rbは見つかりましたが、`converter'で登録されていないようです。" +
|
76
|
-
"変換処理は converter
|
75
|
+
"変換処理は converter do ... end として登録する必要があります"
|
77
76
|
return BlankConverter
|
78
77
|
end
|
79
78
|
end
|
data/lib/mailer.rb
CHANGED
@@ -23,7 +23,7 @@ class Mailer
|
|
23
23
|
this.clear
|
24
24
|
setting_file_path = File.join(Narou.root_dir, SETTING_FILE)
|
25
25
|
if File.exist?(setting_file_path)
|
26
|
-
options = YAML.
|
26
|
+
options = YAML.unsafe_load_file(setting_file_path)
|
27
27
|
unless options.delete(:complete)
|
28
28
|
raise SettingUncompleteError, "設定ファイルの書き換えが終了していないようです。\n" +
|
29
29
|
"設定ファイルは #{setting_file_path} にあります"
|
data/lib/narou.rb
CHANGED
@@ -38,330 +38,336 @@ module Narou
|
|
38
38
|
|
39
39
|
extend Mixin::Locker
|
40
40
|
|
41
|
-
|
42
|
-
|
41
|
+
class << self
|
42
|
+
extend Memoist
|
43
43
|
|
44
|
-
|
44
|
+
@@is_web = false
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
def last_commit_year
|
47
|
+
2021
|
48
|
+
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
50
|
+
def root_dir
|
51
|
+
root = nil
|
52
|
+
path = Dir.pwd
|
53
|
+
drive_letter = ""
|
54
|
+
if Helper.os_windows?
|
55
|
+
path.encode!(Encoding::UTF_8)
|
56
|
+
path.gsub!(/^[a-z]:/i, "")
|
57
|
+
drive_letter = $&
|
58
|
+
end
|
59
|
+
while path != ""
|
60
|
+
if File.directory?("#{drive_letter}#{path}/#{LOCAL_SETTING_DIR_NAME}")
|
61
|
+
root = drive_letter + path
|
62
|
+
break
|
63
|
+
end
|
64
|
+
path.gsub!(%r!/[^/]*$!, "")
|
63
65
|
end
|
64
|
-
|
66
|
+
Pathname(root) if root
|
65
67
|
end
|
66
|
-
|
67
|
-
end
|
68
|
-
memoize :root_dir
|
68
|
+
memoize :root_dir
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
70
|
+
def local_setting_dir
|
71
|
+
root_dir&.join(LOCAL_SETTING_DIR_NAME)
|
72
|
+
end
|
73
|
+
memoize :local_setting_dir
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
def global_setting_dir
|
76
|
+
if root_dir
|
77
|
+
dir = root_dir.join(GLOBAL_SETTING_DIR_NAME)
|
78
|
+
return dir if dir.directory?
|
79
|
+
end
|
80
|
+
dir = Pathname(GLOBAL_SETTING_DIR_NAME).expand_path("~")
|
81
|
+
dir.mkdir unless dir.exist?
|
82
|
+
dir
|
79
83
|
end
|
80
|
-
|
81
|
-
dir.mkdir unless dir.exist?
|
82
|
-
dir
|
83
|
-
end
|
84
|
-
memoize :global_setting_dir
|
84
|
+
memoize :global_setting_dir
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
86
|
+
def script_dir
|
87
|
+
Pathname(__dir__).join("..").expand_path
|
88
|
+
end
|
89
|
+
memoize :script_dir
|
90
90
|
|
91
|
-
|
92
|
-
|
93
|
-
|
91
|
+
def log_dir
|
92
|
+
root_dir&.join(LOG_DIR)
|
93
|
+
end
|
94
94
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
95
|
+
def preset_dir
|
96
|
+
script_dir&.join(PRESET_DIR)
|
97
|
+
end
|
98
|
+
memoize :preset_dir
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
|
100
|
+
def already_init?
|
101
|
+
root_dir.present?
|
102
|
+
end
|
103
103
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
104
|
+
def init
|
105
|
+
return nil if already_init?
|
106
|
+
FileUtils.mkdir(LOCAL_SETTING_DIR_NAME)
|
107
|
+
puts "#{LOCAL_SETTING_DIR_NAME}/ を作成しました"
|
108
|
+
Database.init
|
109
|
+
end
|
110
110
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
111
|
+
#
|
112
|
+
# target が alias だった場合はIDに変換する
|
113
|
+
#
|
114
|
+
# 全てのtarget照合系はこのメソッドを通過するので、ここで小文字にしてしまう
|
115
|
+
#
|
116
|
+
def alias_to_id(target)
|
117
|
+
aliases = Inventory.load("alias")
|
118
|
+
if aliases[target]
|
119
|
+
return aliases[target]
|
120
|
+
end
|
121
|
+
target.is_a?(Numeric) ? target : target.downcase
|
122
|
+
end
|
123
123
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
124
|
+
def novel_frozen?(target)
|
125
|
+
id = Downloader.get_id_by_target(target) or return false
|
126
|
+
Inventory.load("freeze").include?(id)
|
127
|
+
end
|
128
128
|
|
129
|
-
|
130
|
-
|
131
|
-
|
129
|
+
def create_aozoraepub3_jar_path(*paths)
|
130
|
+
Pathname(File.expand_path(File.join(*paths, AOZORAEPUB3_JAR_NAME)))
|
131
|
+
end
|
132
132
|
|
133
|
-
|
134
|
-
|
135
|
-
|
133
|
+
def aozoraepub3_directory?(path)
|
134
|
+
create_aozoraepub3_jar_path(path).exist?
|
135
|
+
end
|
136
136
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
137
|
+
def parse_replace_txt(text)
|
138
|
+
pattern = []
|
139
|
+
text.each_line do |line|
|
140
|
+
line.sub!(/[\r\n]+\z/, "")
|
141
|
+
next if line[0] == ";" # コメント記号
|
142
|
+
pair = line.split("\t", 2)
|
143
|
+
if pair.length == 2 && pair[0]
|
144
|
+
pattern << pair
|
145
|
+
end
|
145
146
|
end
|
147
|
+
pattern
|
146
148
|
end
|
147
|
-
pattern
|
148
|
-
end
|
149
149
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
150
|
+
def write_replace_txt(path, pairs)
|
151
|
+
buffer = pairs.each_with_object("\t").map(&:join).join("\n")
|
152
|
+
File.write(path, buffer)
|
153
|
+
end
|
154
154
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
155
|
+
def load_global_replace_pattern
|
156
|
+
path = root_dir.join(GLOBAL_REPLACE_NAME)
|
157
|
+
pairs =
|
158
|
+
if path.exist?
|
159
|
+
Helper::CacheLoader.memo(path) do |text|
|
160
|
+
parse_replace_txt(text)
|
161
|
+
end
|
162
|
+
else
|
163
|
+
[]
|
161
164
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
@@global_replace_pattern_pairs = pairs
|
166
|
-
pairs
|
167
|
-
end
|
165
|
+
@@global_replace_pattern_pairs = pairs
|
166
|
+
pairs
|
167
|
+
end
|
168
168
|
|
169
|
-
|
170
|
-
|
171
|
-
|
169
|
+
def global_replace_pattern
|
170
|
+
@@global_replace_pattern_pairs ||= load_global_replace_pattern
|
171
|
+
end
|
172
172
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
173
|
+
def save_global_replace_pattern
|
174
|
+
path = root_dir.join(GLOBAL_REPLACE_NAME)
|
175
|
+
write_replace_txt(path, @@global_replace_pattern_pairs)
|
176
|
+
end
|
177
177
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
178
|
+
#
|
179
|
+
# AozoraEpub3 の実行ファイル(.jar)のフルパス取得
|
180
|
+
# 検索順序
|
181
|
+
# 1. グローバルセッティング (global_setting aozoraepub3dir)
|
182
|
+
# 2. 小説保存ディレクトリ(Narou.root_dir) 直下の AozoraEpub3
|
183
|
+
# 3. スクリプト保存ディレクトリ(Narou.script_dir) 直下の AozoraEpub3
|
184
|
+
#
|
185
|
+
def aozoraepub3_path
|
186
|
+
global_setting_aozora_path = Inventory.load("global_setting", :global)["aozoraepub3dir"]
|
187
|
+
if global_setting_aozora_path
|
188
|
+
aozora_jar_path = create_aozoraepub3_jar_path(global_setting_aozora_path)
|
189
|
+
if aozora_jar_path.exist?
|
190
|
+
return aozora_jar_path
|
191
|
+
end
|
191
192
|
end
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
return aozora_jar_path if aozora_jar_path.exist?
|
196
|
-
end
|
197
|
-
nil
|
198
|
-
end
|
199
|
-
memoize :aozoraepub3_path
|
200
|
-
|
201
|
-
#
|
202
|
-
# 書籍ファイル名を生成する
|
203
|
-
# convert.filename-to-ncode を設定している場合に novel_data に ncode、domain を
|
204
|
-
# 設定しない場合は id カラムが必須
|
205
|
-
#
|
206
|
-
def create_novel_filename(novel_data, ext = "")
|
207
|
-
filename_to_ncode = Inventory.load("local_setting")["convert.filename-to-ncode"]
|
208
|
-
novel_setting =
|
209
|
-
if novel_data["id"]
|
210
|
-
NovelSetting.load(novel_data["id"])
|
211
|
-
else
|
212
|
-
OpenStruct.new
|
193
|
+
[Narou.root_dir, Narou.script_dir].each do |dir|
|
194
|
+
aozora_jar_path = create_aozoraepub3_jar_path(dir, AOZORAEPUB3_DIR)
|
195
|
+
return aozora_jar_path if aozora_jar_path.exist?
|
213
196
|
end
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
197
|
+
nil
|
198
|
+
end
|
199
|
+
memoize :aozoraepub3_path
|
200
|
+
|
201
|
+
#
|
202
|
+
# 書籍ファイル名を生成する
|
203
|
+
# convert.filename-to-ncode を設定している場合に novel_data に ncode、domain を
|
204
|
+
# 設定しない場合は id カラムが必須
|
205
|
+
#
|
206
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
207
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
208
|
+
def create_novel_filename(novel_data, ext = "")
|
209
|
+
filename_to_ncode = Inventory.load("local_setting")["convert.filename-to-ncode"]
|
210
|
+
novel_setting =
|
211
|
+
if novel_data["id"]
|
212
|
+
NovelSetting.load(novel_data["id"])
|
213
|
+
else
|
214
|
+
OpenStruct.new
|
215
|
+
end
|
216
|
+
if novel_setting.output_filename.present?
|
217
|
+
%!#{novel_setting.output_filename}#{ext}!
|
218
|
+
elsif filename_to_ncode
|
219
|
+
ncode = novel_data["ncode"]
|
220
|
+
domain = novel_data["domain"]
|
221
|
+
if !ncode || !domain
|
222
|
+
id = novel_data["id"]
|
223
|
+
unless id
|
224
|
+
raise ArgumentError, %!novel_data["id"] を設定して下さい!
|
225
|
+
end
|
226
|
+
site_setting = Downloader.get_sitesetting_by_target(id)
|
227
|
+
ncode = site_setting["ncode"]
|
228
|
+
domain = site_setting["domain"]
|
222
229
|
end
|
223
|
-
|
224
|
-
ncode
|
225
|
-
|
230
|
+
serialized_domain = domain.to_s.gsub(".", "_")
|
231
|
+
%!#{serialized_domain}_#{ncode}#{ext}!
|
232
|
+
else
|
233
|
+
author = Helper.replace_filename_special_chars(
|
234
|
+
novel_setting.novel_author.presence || novel_data["author"],
|
235
|
+
true
|
236
|
+
)
|
237
|
+
title = Helper.replace_filename_special_chars(
|
238
|
+
novel_setting.novel_title.presence || novel_data["title"],
|
239
|
+
true
|
240
|
+
)
|
241
|
+
filename = "[#{author}] #{title}#{ext}"
|
242
|
+
length_limit = Inventory.load["ebook-filename-length-limit"]
|
243
|
+
length_limit ? Helper.truncate_path(filename, length_limit, extname: ext) : filename
|
226
244
|
end
|
227
|
-
serialized_domain = domain.to_s.gsub(".", "_")
|
228
|
-
%!#{serialized_domain}_#{ncode}#{ext}!
|
229
|
-
else
|
230
|
-
author = Helper.replace_filename_special_chars(
|
231
|
-
novel_setting.novel_author.presence || novel_data["author"],
|
232
|
-
true
|
233
|
-
)
|
234
|
-
title = Helper.replace_filename_special_chars(
|
235
|
-
novel_setting.novel_title.presence || novel_data["title"],
|
236
|
-
true
|
237
|
-
)
|
238
|
-
filename = "[#{author}] #{title}#{ext}"
|
239
|
-
length_limit = Inventory.load["ebook-filename-length-limit"]
|
240
|
-
length_limit ? Helper.truncate_path(filename, length_limit) : filename
|
241
245
|
end
|
242
|
-
|
246
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
247
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
243
248
|
|
244
|
-
|
245
|
-
|
246
|
-
|
249
|
+
def get_mobi_paths(target)
|
250
|
+
get_ebook_file_paths(target, ".mobi")
|
251
|
+
end
|
247
252
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
dir = Downloader.get_novel_data_dir_by_target(target)
|
252
|
-
fname = create_novel_filename(data, ext)
|
253
|
-
base = File.basename(fname, ext)
|
254
|
-
get_ebook_file_paths_from_components(dir, base, ext)
|
255
|
-
end
|
253
|
+
def get_ebook_file_paths(target, ext)
|
254
|
+
data = Downloader.get_data_by_target(target)
|
255
|
+
return nil unless data
|
256
256
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
paths.push(Pathname(path))
|
262
|
-
index += 1
|
257
|
+
dir = Downloader.get_novel_data_dir_by_target(target)
|
258
|
+
fname = create_novel_filename(data, ext)
|
259
|
+
base = File.basename(fname, ext)
|
260
|
+
get_ebook_file_paths_from_components(dir, base, ext)
|
263
261
|
end
|
264
|
-
paths
|
265
|
-
end
|
266
262
|
|
267
|
-
|
268
|
-
|
269
|
-
|
263
|
+
def get_ebook_file_paths_from_components(dir, base, ext)
|
264
|
+
paths = [File.join(dir, "#{base}#{ext}")]
|
265
|
+
index = 2
|
266
|
+
while File.exist?(path = File.join(dir, "#{base}_#{index}#{ext}"))
|
267
|
+
paths.push(Pathname(path))
|
268
|
+
index += 1
|
269
|
+
end
|
270
|
+
paths
|
271
|
+
end
|
272
|
+
|
273
|
+
def misc_dir
|
274
|
+
root_dir.join(MISC_DIR)
|
275
|
+
end
|
270
276
|
|
271
|
-
|
277
|
+
require_relative "device"
|
272
278
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
279
|
+
def get_device(device_name = nil)
|
280
|
+
device_name ||= Inventory.load("local_setting")["device"]
|
281
|
+
if device_name && Device.exists?(device_name)
|
282
|
+
return Device.create(device_name)
|
283
|
+
end
|
284
|
+
nil
|
277
285
|
end
|
278
|
-
nil
|
279
|
-
end
|
280
286
|
|
281
|
-
|
282
|
-
|
283
|
-
|
287
|
+
def web=(bool)
|
288
|
+
@@is_web = bool
|
289
|
+
end
|
284
290
|
|
285
|
-
|
286
|
-
|
287
|
-
|
291
|
+
def web?
|
292
|
+
@@is_web
|
293
|
+
end
|
288
294
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
295
|
+
def update_sort_key_summaries(left_space = 28)
|
296
|
+
summaries = { "KEY" => " 対象" }.merge(UPDATE_SORT_KEYS)
|
297
|
+
key_max_width = summaries.keys.max_by(&:length).length
|
298
|
+
summaries.map do |(key, summary)|
|
299
|
+
"#{" " * left_space}| #{key.center(key_max_width)} | #{summary}"
|
300
|
+
end.join("\n")
|
301
|
+
end
|
296
302
|
|
297
|
-
|
298
|
-
|
299
|
-
|
303
|
+
def theme
|
304
|
+
Inventory.load("local_setting")["webui.theme"]
|
305
|
+
end
|
300
306
|
|
301
|
-
|
302
|
-
|
303
|
-
|
307
|
+
def get_theme_dir(name = nil)
|
308
|
+
Pathname(File.join([script_dir, "lib/web/public/theme", name].compact))
|
309
|
+
end
|
304
310
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
311
|
+
def theme_names
|
312
|
+
Dir.glob(get_theme_dir("*")).map do |path|
|
313
|
+
name = File.basename(path)
|
314
|
+
name == "fonts" ? nil : name
|
315
|
+
end.compact
|
316
|
+
end
|
317
|
+
memoize :theme_names
|
312
318
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
319
|
+
def economy?(mode)
|
320
|
+
eco_modes = Inventory.load("local_setting")["economy"].to_s.split(",").map(&:strip)
|
321
|
+
eco_modes.include?(mode)
|
322
|
+
end
|
317
323
|
|
318
|
-
|
319
|
-
|
320
|
-
|
324
|
+
def novel_type_text(type)
|
325
|
+
type == 2 ? "短編" : "連載"
|
326
|
+
end
|
321
327
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
328
|
+
#
|
329
|
+
# Narou.rb gem の最新バージョン番号を取得する
|
330
|
+
#
|
331
|
+
# rubygems公式APIによる取得は、WindowsでのSSL証明書問題で取得出来ない
|
332
|
+
# 環境があるため、gemコマンド経由で取得する
|
333
|
+
#
|
334
|
+
def latest_version
|
335
|
+
response = `gem search ^narou$`.split("\n")
|
336
|
+
if response.last =~ /\Anarou \(([0-9.]+).*?\)\z/
|
337
|
+
$1
|
338
|
+
end
|
332
339
|
end
|
333
|
-
end
|
334
340
|
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
341
|
+
def commit_version
|
342
|
+
cv_path = File.expand_path("commitversion", script_dir)
|
343
|
+
File.read(cv_path) if File.exist?(cv_path)
|
344
|
+
end
|
345
|
+
memoize :commit_version
|
340
346
|
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
347
|
+
def kindlegen_path
|
348
|
+
postfix = Helper.os_windows? ? ".exe" : ""
|
349
|
+
aozoraepub3_path.dirname.join("kindlegen#{postfix}")
|
350
|
+
end
|
351
|
+
memoize :kindlegen_path
|
346
352
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
353
|
+
def line_height(default: LINE_HEIGHT_DEFAULT)
|
354
|
+
global_setting = Inventory.load("global_setting", :global)
|
355
|
+
global_setting["line-height"] || default
|
356
|
+
end
|
351
357
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
358
|
+
def concurrency_enabled?
|
359
|
+
$stdout != $stdout2
|
360
|
+
end
|
361
|
+
memoize :concurrency_enabled?
|
356
362
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
363
|
+
# 同時実行が有効ならキューに積んで、無効なら普通に実行する
|
364
|
+
def concurrency_call(&block)
|
365
|
+
if concurrency_enabled?
|
366
|
+
Worker.push(&block)
|
367
|
+
EXIT_SUCCESS
|
368
|
+
else
|
369
|
+
block.call
|
370
|
+
end
|
364
371
|
end
|
365
372
|
end
|
366
|
-
end
|
367
373
|
end
|