mk_semi_lattice 0.4.5 → 0.4.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0c82ca311d171778531ddf25c26e0d7b3228e358a5c77ee3308100ae788d8787
4
- data.tar.gz: 33590bb27ad318428025207187e6f3126d24fa6546fe886cf9d9393d8323d4f4
3
+ metadata.gz: ab93185225f98ff9decc294bcd22bcb730910405bce57f9e5b8abd9b98f0942e
4
+ data.tar.gz: 7a7ca43878ef13f9e32495b6a813da62ea551c423dd581b76aa9492ad0ab3be0
5
5
  SHA512:
6
- metadata.gz: 58608e9f2cba2911dc8787b87351a709e332a502f2160422dd94d686633a54d2c63cacfbc41a288c1df65acb5c316d16d4c249876df7f389aa3c6d8e5d5e2020
7
- data.tar.gz: ae3ccad4bf58d0f28ef50c080f702826bf3c9f03a763c079a8fa2fe4aa46c0c2395202b8aec5d35cc65a47df8eaf71a93f7fb353ef60c414ed6fba47d4ab6fa9
6
+ metadata.gz: 3260b0bc9e85d536d0ffc53231ffa20cb26cceaf790dceae083f2ff4c1cb2f5279473823eef61c69d92bc641e9a3df2d3416cde82faeef600951540611822cbf
7
+ data.tar.gz: 80b233d58003d4b7b39915bb7220852a5ebfe5fc267c0a6d2bfaaf1b66d8c4fe28c9eb049a8fe6f7f04e3ab54fc0fdc125993313efb77c779a24c2ce54cffa35
@@ -18,8 +18,12 @@
18
18
  ssh=secure shell=安全なremote shell接続:
19
19
  #path=path=パス,小径:
20
20
  full_path=full path=フルパス:
21
- abs_oath=abosolute path=絶対(absolute)パス:
21
+ abs_path=abosolute path=絶対(absolute)パス:
22
22
  rel_path=relative path=相対パス:
23
+ # etc:
24
+ fish=friendly interactive shell:
25
+ apt=advanced package tool:
26
+ sudo=super user do:
23
27
  #c-keys=controll-keys=fishコマンド編集keyバインド:
24
28
  c-a/ahead=行頭へ移動:
25
29
  c-e/end=行末へ移動:
@@ -12,5 +12,6 @@ IP=internet protocol:
12
12
  OS=operating system:
13
13
  pdf=portable document format:
14
14
  tar=tape archive:
15
+ wsl=windows subsystem fro linux:
15
16
 
16
17
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MkSemiLattice
4
- VERSION = "0.4.5"
4
+ VERSION = "0.4.6"
5
5
  end
Binary file
@@ -4,12 +4,13 @@ require 'fileutils'
4
4
  require 'optparse'
5
5
  require 'date'
6
6
 
7
+
7
8
  module StackOperations
8
9
  class OptionParserWrapper
9
10
  attr_reader :options, :args
10
11
 
11
12
  def initialize(argv)
12
- @options = { flatten: false, nest: false, dryrun: true, reverse: false }
13
+ @options = { flatten: false, nest: false, dryrun: true, reverse: false, empty: false, no_dir_move: false }
13
14
  @args = []
14
15
  OptionParser.new do |opts|
15
16
  opts.banner = "Usage: hc stack [options]"
@@ -27,13 +28,50 @@ module StackOperations
27
28
  end
28
29
  end
29
30
 
30
- class MkStack
31
+ class MkTree
32
+ def mk_dir_tree(dir, current_level = 1, max_level = 2, dirs_only: false)
33
+ tree = {}
34
+ return tree unless File.directory?(dir)
35
+
36
+ Dir.children(dir).sort.each do |entry|
37
+ path = File.join(dir, entry)
38
+ is_dir = File.directory?(path)
39
+ next if dirs_only && !is_dir
40
+
41
+ if is_dir
42
+ tree[entry] = current_level < max_level ? mk_dir_tree(path, current_level + 1, max_level, dirs_only: dirs_only) : {}
43
+ else
44
+ tree[entry] = nil
45
+ end
46
+ end
47
+ tree
48
+ end
49
+
50
+ def print_tree(tree, is_last_arr = [])
51
+ entries = tree.keys
52
+ entries.each_with_index do |entry, idx|
53
+ is_last = idx == entries.size - 1
54
+ prefix = ""
55
+ is_last_arr.each { |last| prefix += last ? " " : "│ " }
56
+ prefix += is_last ? "└── " : "├── "
57
+
58
+ is_dir = tree[entry].is_a?(Hash)
59
+ name = is_dir ? "#{entry}/" : entry
60
+ puts "#{prefix}#{name}"
61
+
62
+ if is_dir
63
+ print_tree(tree[entry], is_last_arr + [is_last])
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ class MkStack < MkTree
31
70
  def initialize(options, args)
32
71
  @options = options
33
72
  @root_name = args[0]
34
73
  @date = args[1] || Date.today.strftime('%y%m%d')
35
74
  end
36
-
37
75
  def ordinal(n)
38
76
  abs_n = n.to_i.abs
39
77
  if (11..13).include?(abs_n % 100)
@@ -47,7 +85,6 @@ module StackOperations
47
85
  end
48
86
  end
49
87
  end
50
-
51
88
  def pull_root_name(dir_name)
52
89
  if dir_name =~ /^_stack_(.+)_(\d{6})$/
53
90
  $1
@@ -55,7 +92,6 @@ module StackOperations
55
92
  dir_name
56
93
  end
57
94
  end
58
-
59
95
  def next_available_dir(root_name, date)
60
96
  n = 1
61
97
  if root_name =~ /^(\d+)(st|nd|rd|th)$/
@@ -82,218 +118,213 @@ module StackOperations
82
118
  dir = "_stack_#{base_name}_#{date}"
83
119
  [base_name, dir]
84
120
  end
85
-
86
121
  def find_max_ordinal
87
122
  candidates = Dir.glob("_stack_*_*").select { |d| d =~ /^_stack_(\d+)(st|nd|rd|th)_(\d{6})$/ }
88
- ordinals = candidates.map do |d|
89
- if d =~ /^_stack_(\d+)(st|nd|rd|th)_(\d{6})$/
90
- $1.to_i
91
- else
92
- nil
93
- end
94
- end.compact
123
+ ordinals = candidates.map { |d| d[/^_stack_(\d+)/, 1].to_i }.compact
95
124
  ordinals.any? ? ordinals.max : 0
96
125
  end
97
-
98
126
  def ensure_root_name
99
- if @root_name.nil? || @root_name.strip.empty?
127
+ if @root_name.nil? || @root_name.empty?
100
128
  max_num = find_max_ordinal
101
- @root_name = max_num > 0 ? ordinal(max_num) : "1st"
129
+ @root_name = max_num > 0 ? "#{max_num}th" : "1st"
102
130
  end
103
131
  end
132
+ def run
133
+ ensure_root_name
134
+ @root_name, dir = next_available_dir(@root_name, @date)
104
135
 
105
- def create_dir(dir)
106
- if @options[:dryrun]
107
- puts "[Dry Run] mkdir #{dir}"
108
- else
109
- Dir.mkdir(dir) unless Dir.exist?(dir)
110
- puts "Created directory: #{dir}"
136
+ # 1. 現在のツリーを取得
137
+ current_tree = mk_dir_tree(".", 1, 1)
138
+
139
+ # 2. 移動対象の決定
140
+ exclude = ['.', '..', dir, '.vscode', 'project.code-workspace', '.git']
141
+ all_entries = Dir.glob('*', File::FNM_DOTMATCH) - exclude
142
+ moved_entries = []
143
+
144
+ unless @options[:empty]
145
+ all_entries.each do |entry|
146
+ next if @options[:no_dir_move] && File.directory?(entry)
147
+ moved_entries << entry
148
+ end
111
149
  end
112
- end
113
-
114
- def move_entries(dir)
115
- exclude = ['.', '..', dir, '.vscode', 'project.code-workspace']
116
- entries = Dir.glob('*', File::FNM_DOTMATCH) - exclude
117
150
 
118
- if @options[:no_hidden]
119
- entries.reject! { |entry| entry.start_with?('.') }
151
+ # 3. 仮想ツリーの構築 (Hashの操作としてmvをシミュレート)
152
+ virtual_tree = {}
153
+ current_tree.each do |k, v|
154
+ virtual_tree[k] = v unless moved_entries.include?(k)
120
155
  end
121
-
122
- if @options[:empty]
123
- entries.select! { |entry| File.directory?(entry) && entry.start_with?('_stack') }
156
+
157
+ moved_tree = {}
158
+ moved_entries.each do |entry|
159
+ moved_tree[entry] = current_tree[entry]
124
160
  end
161
+ virtual_tree[dir] = moved_tree
162
+ virtual_tree = virtual_tree.sort.to_h
125
163
 
126
- entries.each do |entry|
127
- next if @options[:no_dir_move] && File.directory?(entry)
128
- if @options[:dryrun]
129
- puts "[Dry Run] mv #{entry} (to) #{dir}"
130
- else
131
- FileUtils.mv(entry, dir, force: true)
164
+ # 4. Before表示
165
+ puts "Before stack:"
166
+ puts "."
167
+ print_tree(current_tree)
168
+
169
+ # 5. 操作の表示 (Dry Run or Execute)
170
+ puts "\nOperations:"
171
+ prefix = @options[:dryrun] ? "[Dry Run] " : ""
172
+ puts "#{prefix}mkdir #{dir}"
173
+ if moved_entries.empty?
174
+ puts "#{prefix}mv nothing (to) #{dir}"
175
+ else
176
+ moved_entries.each do |entry|
177
+ puts "#{prefix}mv #{entry} (to) #{dir}"
132
178
  end
133
179
  end
134
180
 
135
- if @options[:dryrun]
136
- puts "[Dry Run] mv #{entries.empty? ? 'nothing' : entries.join(', ')} (to) #{dir}"
137
- else
138
- puts "Moved #{entries.empty? ? 'nothing' : entries.join(', ')} to #{dir}"
139
- end
140
- end
181
+ # 6. After表示
182
+ puts "\nAfter stack:"
183
+ puts "."
184
+ print_tree(virtual_tree)
141
185
 
142
- def run
143
- ensure_root_name
144
- @root_name, dir = next_available_dir(@root_name, @date)
145
- create_dir(dir)
146
- move_entries(dir)
186
+ # 7. 実行 (-e 指定時)
187
+ unless @options[:dryrun]
188
+ Dir.mkdir(dir) unless Dir.exist?(dir)
189
+ moved_entries.each do |entry|
190
+ FileUtils.mv(entry, dir)
191
+ end
192
+ puts "\nExecution completed."
193
+ end
147
194
  end
148
195
  end
149
196
 
150
- class MkFlatten
197
+ class MkFlatten < MkTree
151
198
  def initialize(options, args)
152
199
  @options = options
153
200
  @target_dir = args[0] || "."
154
201
  @target_dir = @target_dir.chomp("/")
155
202
  end
156
-
157
- # 指定ディレクトリ以下のtree構造を表示(ディレクトリのみ)
158
- def print_tree(dir, prefix = "", is_last_arr = [])
159
- puts "#{prefix}#{File.basename(dir)}"
160
- if File.directory?(dir)
161
- entries = Dir.children(dir).sort.select { |entry| File.directory?(File.join(dir, entry)) }
162
- entries.each_with_index do |entry, idx|
163
- path = File.join(dir, entry)
164
- is_last = idx == entries.size - 1
165
- # インデント生成
166
- new_prefix = ""
167
- is_last_arr.each { |last| new_prefix += last ? " " : "│ " }
168
- new_prefix += is_last ? "└── " : "├── "
169
- print_tree(path, new_prefix, is_last_arr + [is_last])
170
- end
171
- end
172
- end
173
-
174
- # flatten後のtree構造を表示(@target_dir直下に全ての_stack_*_*ディレクトリを並べる)
175
- def print_flattened_tree
176
- stacks = []
203
+ def build_virtual_tree
177
204
  search_dirs = Dir.glob(File.join(@target_dir, "**/_stack_*_*")).select { |d| File.directory?(d) }
178
- # ルート直下の_stack_*_*も含める
179
205
  root_stacks = Dir.glob(File.join(@target_dir, "_stack_*_*")).select { |d| File.directory?(d) }
180
206
  all_stacks = (root_stacks + search_dirs).uniq
181
- all_stacks.map! { |d| File.basename(d) }
182
- all_stacks.each do |d|
183
- puts "└── #{d}"
207
+
208
+ tree = {}
209
+ all_stacks.map { |d| File.basename(d) }.sort.each do |d|
210
+ tree[d] = {}
184
211
  end
212
+ tree
185
213
  end
186
-
187
- # flattenで移動するディレクトリのmvコマンドをdryrun表示
188
- def print_flatten_moves
189
- flatten_moves.each do |src, dest|
190
- puts "[Dry Run] mv '#{src}' '#{dest}'"
214
+ def flatten_moves
215
+ search_dirs = Dir.glob(File.join(@target_dir, "**/_stack_*_*")).select { |d| File.directory?(d) }
216
+ root_stacks = Dir.glob(File.join(@target_dir, "_stack_*_*")).select { |d| File.directory?(d) }
217
+ stacks = (root_stacks + search_dirs).uniq
218
+
219
+ # 深い階層から順に移動させるため、パスの深さで降順ソート
220
+ stacks.sort_by! { |d| -d.count('/') }
221
+
222
+ moves = []
223
+ stacks.each do |src|
224
+ dest = File.join(@target_dir, File.basename(src))
225
+ moves << [src, dest] unless src == dest
191
226
  end
227
+ moves
192
228
  end
229
+ def run
230
+ puts "Before flatten:"
231
+ puts @target_dir == "." ? "." : "#{File.basename(@target_dir)}/"
232
+ print_tree(mk_dir_tree(@target_dir, 1, 999, dirs_only: true))
193
233
 
194
- def flatten_moves
195
- stacks = Dir.glob(File.join(@target_dir, "**/_stack_*_*")).select { |d| File.directory?(d) }
196
- stacks.sort_by! { |d| -d.count(File::SEPARATOR) }
197
- stacks.map { |src| [src, File.join(".", File.basename(src))] }
198
- end
234
+ moves = flatten_moves
199
235
 
200
- def execute_flatten_moves
201
- flatten_moves.each do |src, dest|
202
- # mv元とmv先が同じ場合はスキップ
203
- if File.expand_path(src) == File.expand_path(dest)
204
- puts "[Skip] mv '#{src}' '#{dest}' (same path)"
205
- next
236
+ puts "\nOperations:"
237
+ prefix = @options[:dryrun] ? "[Dry Run] " : ""
238
+ if moves.empty?
239
+ puts "#{prefix}nothing to flatten"
240
+ else
241
+ moves.each do |src, dest|
242
+ puts "#{prefix}mv #{src} (to) #{dest}"
206
243
  end
207
- puts "mv '#{src}' '#{dest}'"
208
- FileUtils.mv(src, dest)
209
244
  end
210
- end
211
245
 
212
- def run
213
- puts "Before flatten:"
214
- puts "."
215
- print_tree(@target_dir, "", [])
216
246
  puts "\nAfter flatten:"
217
247
  puts "."
218
- print_flattened_tree
219
- puts "\nFlatten moves:"
220
- if @options[:dryrun]
221
- print_flatten_moves
222
- else
223
- execute_flatten_moves
248
+ print_tree(build_virtual_tree)
249
+
250
+ unless @options[:dryrun]
251
+ moves.each do |src, dest|
252
+ FileUtils.mv(src, dest)
253
+ end
254
+ puts "\nExecution completed."
224
255
  end
225
256
  end
226
257
  end
227
258
 
228
- class MkNest
259
+ class MkNest < MkTree
229
260
  def initialize(options, args)
230
261
  @options = options
231
262
  @target_dir = args[0] || "."
232
263
  end
233
-
234
- # yymmdd順に並べ替え(reverseオプションで順序切替)
235
264
  def sorted_stacks
236
- stacks = Dir.glob(File.join(@target_dir, "_stack_*_*"))
237
- .select { |d| File.directory?(d) && d =~ /_stack_.+_\d{6}$/ }
238
- .sort_by { |d| d[/_stack_.+_(\d{6})$/, 1].to_i }
239
- #.select { |d| File.directory?(d) }
240
- #.sort_by { |d| d[/^_stack_(\d+)(st|nd|rd|th)_/, 1].to_i }
265
+ stacks = Dir.glob(File.join(@target_dir, "_stack_*_*")).select { |d| File.directory?(d) }
266
+ stacks = stacks.sort_by { |d| d[/_stack_(\d+)(st|nd|rd|th)_/, 1].to_i }
267
+ # デフォルトはFILO(新しいものがルート: 7th, 6th, 5th...)
268
+ # -r オプションでFIFO(古いものがルート: 1st, 2nd, 3rd...)
241
269
  @options[:reverse] ? stacks : stacks.reverse
242
270
  end
243
-
244
- # ディレクトリのみtree表示
245
- def print_tree(dir, prefix = "", is_last_arr = [])
246
- puts "#{prefix}#{File.basename(dir)}"
247
- if File.directory?(dir)
248
- entries = Dir.children(dir).sort.select { |entry| File.directory?(File.join(dir, entry)) }
249
- entries.each_with_index do |entry, idx|
250
- path = File.join(dir, entry)
251
- is_last = idx == entries.size - 1
252
- # インデント生成
253
- new_prefix = ""
254
- is_last_arr.each { |last| new_prefix += last ? " " : "│ " }
255
- new_prefix += is_last ? "└── " : "├── "
256
- print_tree(path, new_prefix, is_last_arr + [is_last])
257
- end
258
- end
259
- end
260
-
261
- # nest後のtree構造を表示
262
- def print_nested_tree
263
- p stacks = sorted_stacks
264
- return if stacks.empty?
265
- puts "."
266
- stacks.each_with_index do |dir, idx|
267
- indent = " " * idx + "└── "
268
- puts "#{indent}#{File.basename(dir)}"
271
+ def build_virtual_tree
272
+ stacks = sorted_stacks
273
+ tree = {}
274
+ return tree if stacks.empty?
275
+
276
+ current = tree
277
+ stacks.each do |dir|
278
+ name = File.basename(dir)
279
+ current[name] = {}
280
+ current = current[name]
269
281
  end
282
+ tree
270
283
  end
271
-
272
- # nestのmvコマンド生成&実行/表示
273
284
  def nest_moves
274
285
  stacks = sorted_stacks
275
- # 子から親へ(パスが変わらないように逆順でmv)
276
- moves = stacks.each_cons(2).to_a.reverse
277
- moves.each do |parent, child|
286
+ moves = []
287
+ return moves if stacks.size < 2
288
+
289
+ # ENOENTエラーを防ぐため、一番奥(深い階層)に入るものから順に親へ移動する
290
+ # 例: stacks = [7th, 6th, 5th] の場合
291
+ # 1. 5th を 6th へ移動
292
+ # 2. 6th を 7th へ移動
293
+ (0...stacks.size - 1).to_a.reverse.each do |i|
294
+ parent = stacks[i]
295
+ child = stacks[i + 1]
278
296
  dest = File.join(parent, File.basename(child))
279
- if @options[:dryrun]
280
- puts "[Dry Run] mv '#{child}' '#{dest}'"
281
- else
282
- puts "mv '#{child}' '#{dest}'"
283
- FileUtils.mkdir_p(parent) unless Dir.exist?(parent)
284
- FileUtils.mv(child, dest)
285
- end
297
+ moves << [child, dest]
286
298
  end
299
+ moves
287
300
  end
288
-
289
301
  def run
290
302
  puts "Before nest:"
291
- puts "."
292
- print_tree(@target_dir, "", [])
303
+ puts @target_dir == "." ? "." : "#{File.basename(@target_dir)}/"
304
+ print_tree(mk_dir_tree(@target_dir, 1, 999, dirs_only: true))
305
+
306
+ moves = nest_moves
307
+
308
+ puts "\nOperations:"
309
+ prefix = @options[:dryrun] ? "[Dry Run] " : ""
310
+ if moves.empty?
311
+ puts "#{prefix}nothing to nest"
312
+ else
313
+ moves.each do |child, dest|
314
+ puts "#{prefix}mv #{child} (to) #{dest}"
315
+ end
316
+ end
317
+
293
318
  puts "\nAfter nest:"
294
- print_nested_tree
295
- puts "\nNest moves:"
296
- nest_moves
319
+ puts "."
320
+ print_tree(build_virtual_tree)
321
+
322
+ unless @options[:dryrun]
323
+ moves.each do |child, dest|
324
+ FileUtils.mv(child, dest)
325
+ end
326
+ puts "\nExecution completed."
327
+ end
297
328
  end
298
329
  end
299
330
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mk_semi_lattice
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.4.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shigeto R. Nishitani