From 76316a4022ad132604b731305af189d1dfe2ba34 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Sat, 14 Sep 2024 23:43:15 +0900 Subject: [PATCH 01/41] first commit From db6a6831746c6566406ff5698aa8895a24db9847 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Mon, 16 Sep 2024 15:32:17 +0900 Subject: [PATCH 02/41] add: Ls and Test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 一旦、最低限カレントディレクトリのファイルを返す実装とテストを追加 - bin/ls.rb - 実行ファイル - lib/ls_command - 実行コマンドからpathを受け取りFIleの配列を持つ - lib/ls_file - Fileの名前など情報を持つクラス - 上記のテストと、テストに必要なファイルを追加 --- 07.ls_object/bin/ls.rb | 10 ++++++++++ 07.ls_object/lib/ls_command.rb | 17 +++++++++++++++++ 07.ls_object/lib/ls_file.rb | 15 +++++++++++++++ 07.ls_object/test/ls_command_test.rb | 16 ++++++++++++++++ 07.ls_object/test/ls_file_test.rb | 11 +++++++++++ 07.ls_object/test/ls_test.rb | 10 ++++++++++ 07.ls_object/test/test_helper.rb | 4 ++++ 07.ls_object/test_dir/test.txt | 1 + 07.ls_object/test_dir2/test.txt | 1 + 07.ls_object/test_dir2/test_2.txt | 1 + 07.ls_object/test_dir3/.test | 0 07.ls_object/test_dir3/test.txt | 1 + 07.ls_object/test_dir3/test_2.txt | 1 + 07.ls_object/test_file.txt | 0 14 files changed, 88 insertions(+) create mode 100755 07.ls_object/bin/ls.rb create mode 100644 07.ls_object/lib/ls_command.rb create mode 100644 07.ls_object/lib/ls_file.rb create mode 100644 07.ls_object/test/ls_command_test.rb create mode 100644 07.ls_object/test/ls_file_test.rb create mode 100644 07.ls_object/test/ls_test.rb create mode 100644 07.ls_object/test/test_helper.rb create mode 100644 07.ls_object/test_dir/test.txt create mode 100644 07.ls_object/test_dir2/test.txt create mode 100644 07.ls_object/test_dir2/test_2.txt create mode 100644 07.ls_object/test_dir3/.test create mode 100644 07.ls_object/test_dir3/test.txt create mode 100644 07.ls_object/test_dir3/test_2.txt create mode 100644 07.ls_object/test_file.txt diff --git a/07.ls_object/bin/ls.rb b/07.ls_object/bin/ls.rb new file mode 100755 index 0000000000..10984c6386 --- /dev/null +++ b/07.ls_object/bin/ls.rb @@ -0,0 +1,10 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'optparse' +require_relative '../lib/ls_command' + +if __FILE__ == $PROGRAM_NAME + ls = LsCommand.new(".") + puts ls.display +end diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb new file mode 100644 index 0000000000..9e2ad67dbc --- /dev/null +++ b/07.ls_object/lib/ls_command.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true +require 'debug' +require 'pathname' +require_relative 'ls_file' + +class LsCommand + + def initialize(path) + @path = path + @ls_files = Dir.open(@path).children.each {|file| LsFile.new(file) }.sort + end + + def display + @ls_files.join(" ") + end +end diff --git a/07.ls_object/lib/ls_file.rb b/07.ls_object/lib/ls_file.rb new file mode 100644 index 0000000000..1534014b9c --- /dev/null +++ b/07.ls_object/lib/ls_file.rb @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true +require 'debug' + +class LsFile + attr_reader :name + + def initialize(file) + @name = file + end + + def file_format? + File.file?(arg) + end +end \ No newline at end of file diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb new file mode 100644 index 0000000000..3d585208c2 --- /dev/null +++ b/07.ls_object/test/ls_command_test.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require_relative 'test_helper' +require_relative '../lib/ls_command' + +class LsCommandTest < Minitest::Test + def test_display_a_file + lscommand = LsCommand.new('test_dir') + assert_equal 'test.txt', lscommand.display + end + + def test_display_two_files + lscommand = LsCommand.new('test_dir2') + assert_equal 'test.txt test_2.txt', lscommand.display + end +end diff --git a/07.ls_object/test/ls_file_test.rb b/07.ls_object/test/ls_file_test.rb new file mode 100644 index 0000000000..44083bd4ec --- /dev/null +++ b/07.ls_object/test/ls_file_test.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require_relative 'test_helper' +require_relative '../lib/ls_file' + +class LsFilePathTest < Minitest::Test + def test_name + file = LsFile.new('test.txt') + assert_equal 'test.txt', file.name + end +end diff --git a/07.ls_object/test/ls_test.rb b/07.ls_object/test/ls_test.rb new file mode 100644 index 0000000000..51a97b4caa --- /dev/null +++ b/07.ls_object/test/ls_test.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require_relative 'test_helper' +require_relative '../bin/ls' + +class LsTest < Minitest::Test + def test_run + assert_equal 'test', Ls.run + end +end diff --git a/07.ls_object/test/test_helper.rb b/07.ls_object/test/test_helper.rb new file mode 100644 index 0000000000..90d46e9bc6 --- /dev/null +++ b/07.ls_object/test/test_helper.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require 'minitest/pride' diff --git a/07.ls_object/test_dir/test.txt b/07.ls_object/test_dir/test.txt new file mode 100644 index 0000000000..910898e128 --- /dev/null +++ b/07.ls_object/test_dir/test.txt @@ -0,0 +1 @@ +testです \ No newline at end of file diff --git a/07.ls_object/test_dir2/test.txt b/07.ls_object/test_dir2/test.txt new file mode 100644 index 0000000000..910898e128 --- /dev/null +++ b/07.ls_object/test_dir2/test.txt @@ -0,0 +1 @@ +testです \ No newline at end of file diff --git a/07.ls_object/test_dir2/test_2.txt b/07.ls_object/test_dir2/test_2.txt new file mode 100644 index 0000000000..910898e128 --- /dev/null +++ b/07.ls_object/test_dir2/test_2.txt @@ -0,0 +1 @@ +testです \ No newline at end of file diff --git a/07.ls_object/test_dir3/.test b/07.ls_object/test_dir3/.test new file mode 100644 index 0000000000..e69de29bb2 diff --git a/07.ls_object/test_dir3/test.txt b/07.ls_object/test_dir3/test.txt new file mode 100644 index 0000000000..910898e128 --- /dev/null +++ b/07.ls_object/test_dir3/test.txt @@ -0,0 +1 @@ +testです \ No newline at end of file diff --git a/07.ls_object/test_dir3/test_2.txt b/07.ls_object/test_dir3/test_2.txt new file mode 100644 index 0000000000..910898e128 --- /dev/null +++ b/07.ls_object/test_dir3/test_2.txt @@ -0,0 +1 @@ +testです \ No newline at end of file diff --git a/07.ls_object/test_file.txt b/07.ls_object/test_file.txt new file mode 100644 index 0000000000..e69de29bb2 From e33b543f8497d0f0e9b277b51f364828350836e8 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Mon, 16 Sep 2024 19:41:44 +0900 Subject: [PATCH 03/41] add: a and r options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 必要要件であるaオプションとrオプションの実装追加 - テストを追加 - 各オプションの分岐をプライベートメソッドに切り出す --- 07.ls_object/bin/ls.rb | 9 +++++++- 07.ls_object/lib/ls_command.rb | 31 +++++++++++++++++++++++----- 07.ls_object/test/ls_command_test.rb | 23 +++++++++++++++++++++ 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/07.ls_object/bin/ls.rb b/07.ls_object/bin/ls.rb index 10984c6386..af708b76c3 100755 --- a/07.ls_object/bin/ls.rb +++ b/07.ls_object/bin/ls.rb @@ -5,6 +5,13 @@ require_relative '../lib/ls_command' if __FILE__ == $PROGRAM_NAME - ls = LsCommand.new(".") + opt = OptionParser.new + + params = { dot_match: false } + opt.on('-a') { |v| params[:dot_match] = v } + opt.on('-r') { |v| params[:reverse] = v } + opt.parse!(ARGV) + + ls = LsCommand.new('.', **params) puts ls.display end diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index 9e2ad67dbc..b36a41f276 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -1,17 +1,38 @@ -#!/usr/bin/env ruby # frozen_string_literal: true + require 'debug' require 'pathname' require_relative 'ls_file' class LsCommand - - def initialize(path) + def initialize(path, dot_match: false, reverse: false) @path = path - @ls_files = Dir.open(@path).children.each {|file| LsFile.new(file) }.sort + @dot_match = dot_match + @reverse = reverse + @ls_files = Dir.open(@path).entries.map { |file| LsFile.new(file) } + @matched_files = dot_match_files + @sorted_ls_files = sort_files end def display - @ls_files.join(" ") + @sorted_ls_files.map(&:name).join(' ') + end + + private + + def dot_match_files + if @dot_match + @ls_files + else + @ls_files.filter { |file| !/^\./.match?(file.name) } + end + end + + def sort_files + if @reverse + @matched_files.sort_by{|file| file.name }.reverse + else + @matched_files.sort_by{|file| file.name } + end end end diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb index 3d585208c2..eeebc21bc2 100644 --- a/07.ls_object/test/ls_command_test.rb +++ b/07.ls_object/test/ls_command_test.rb @@ -13,4 +13,27 @@ def test_display_two_files lscommand = LsCommand.new('test_dir2') assert_equal 'test.txt test_2.txt', lscommand.display end + + def test_display_dot_file + lscommand = LsCommand.new('test_dir3') + assert_equal 'dir test.txt test_2.txt', lscommand.display + end + + def test_display_match_dot_file + params = { dot_match: true } + lscommand = LsCommand.new('test_dir3', **params) + assert_equal '. .. .test dir test.txt test_2.txt', lscommand.display + end + + def test_display_reverse_sort_file + params = { reverse: true, dot_match: false } + lscommand = LsCommand.new('test_dir3', **params) + assert_equal 'test_2.txt test.txt dir', lscommand.display + end + + def test_display_dot_match_and_reverse_sort_file + params = { reverse: true, dot_match: true } + lscommand = LsCommand.new('test_dir3', **params) + assert_equal 'test_2.txt test.txt dir .test .. .', lscommand.display + end end From 8df64dd546fd0f7c8572857d9cdaa2df038ecc20 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Mon, 16 Sep 2024 22:41:35 +0900 Subject: [PATCH 04/41] fix: class name ls_file to file_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LsFileという命名は限定的すぎるので、FileDataに変更する --- 07.ls_object/lib/{ls_file.rb => file_data.rb} | 9 +++------ 07.ls_object/lib/ls_command.rb | 12 ++++++------ .../test/{ls_file_test.rb => file_data_test.rb} | 4 ++-- 3 files changed, 11 insertions(+), 14 deletions(-) rename 07.ls_object/lib/{ls_file.rb => file_data.rb} (68%) mode change 100644 => 100755 rename 07.ls_object/test/{ls_file_test.rb => file_data_test.rb} (69%) diff --git a/07.ls_object/lib/ls_file.rb b/07.ls_object/lib/file_data.rb old mode 100644 new mode 100755 similarity index 68% rename from 07.ls_object/lib/ls_file.rb rename to 07.ls_object/lib/file_data.rb index 1534014b9c..8b2fe380f7 --- a/07.ls_object/lib/ls_file.rb +++ b/07.ls_object/lib/file_data.rb @@ -1,15 +1,12 @@ #!/usr/bin/env ruby # frozen_string_literal: true + require 'debug' -class LsFile +class FileData attr_reader :name def initialize(file) @name = file end - - def file_format? - File.file?(arg) - end -end \ No newline at end of file +end diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index b36a41f276..5917957991 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -2,29 +2,29 @@ require 'debug' require 'pathname' -require_relative 'ls_file' +require_relative 'file_data' class LsCommand def initialize(path, dot_match: false, reverse: false) @path = path @dot_match = dot_match @reverse = reverse - @ls_files = Dir.open(@path).entries.map { |file| LsFile.new(file) } + @file_data = Dir.open(@path).entries.map { |file| FileData.new(file) } @matched_files = dot_match_files - @sorted_ls_files = sort_files + @sorted_file_data = sort_files end def display - @sorted_ls_files.map(&:name).join(' ') + @sorted_file_data.map(&:name).join(' ') end private def dot_match_files if @dot_match - @ls_files + @file_data else - @ls_files.filter { |file| !/^\./.match?(file.name) } + @file_data.filter { |file| !/^\./.match?(file.name) } end end diff --git a/07.ls_object/test/ls_file_test.rb b/07.ls_object/test/file_data_test.rb similarity index 69% rename from 07.ls_object/test/ls_file_test.rb rename to 07.ls_object/test/file_data_test.rb index 44083bd4ec..9a83c86cc3 100644 --- a/07.ls_object/test/ls_file_test.rb +++ b/07.ls_object/test/file_data_test.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true require_relative 'test_helper' -require_relative '../lib/ls_file' +require_relative '../lib/file_data' class LsFilePathTest < Minitest::Test def test_name - file = LsFile.new('test.txt') + file = FileData.new('test.txt') assert_equal 'test.txt', file.name end end From 31006e3d251d95581ab1663f1ced081d512c202c Mon Sep 17 00:00:00 2001 From: yokomaru Date: Wed, 18 Sep 2024 12:25:53 +0900 Subject: [PATCH 05/41] add: l option and test --- 07.ls_object/bin/ls.rb | 2 + 07.ls_object/lib/file_data.rb | 86 +++++++++++++++++++++++++++- 07.ls_object/lib/ls_command.rb | 13 ++++- 07.ls_object/test/ls_command_test.rb | 30 ++++++++++ 4 files changed, 126 insertions(+), 5 deletions(-) diff --git a/07.ls_object/bin/ls.rb b/07.ls_object/bin/ls.rb index af708b76c3..cd157ecc30 100755 --- a/07.ls_object/bin/ls.rb +++ b/07.ls_object/bin/ls.rb @@ -10,8 +10,10 @@ params = { dot_match: false } opt.on('-a') { |v| params[:dot_match] = v } opt.on('-r') { |v| params[:reverse] = v } + opt.on('-l') { |v| params[:long_format] = v } opt.parse!(ARGV) ls = LsCommand.new('.', **params) + puts ls.display end diff --git a/07.ls_object/lib/file_data.rb b/07.ls_object/lib/file_data.rb index 8b2fe380f7..dd0f5b2607 100755 --- a/07.ls_object/lib/file_data.rb +++ b/07.ls_object/lib/file_data.rb @@ -1,12 +1,94 @@ #!/usr/bin/env ruby # frozen_string_literal: true +require 'etc' require 'debug' class FileData - attr_reader :name + SPECIAL_PERMISSION_INDEX = 2 + OWNER_PERMISSION_INDEX = 3 + GROUP_PERMISSION_INDEX = 4 + OTHER_PERMISSION_INDEX = 5 - def initialize(file) + FILE_TYPE = { + '01' => 'p', + '02' => 'c', + '04' => 'd', + '06' => 'b', + '10' => '-', + '12' => 'l', + '14' => 's' + }.freeze + + PERMISSION_TYPE = { + '0' => '---', + '1' => '--x', + '2' => '-w-', + '3' => '-wx', + '4' => 'r--', + '5' => 'r-x', + '6' => 'rw-', + '7' => 'rwx' + }.freeze + + SPECIAL_PERMISSION_TYPE = { + '0' => '-', + '1' => 't', + '2' => 's', + '4' => 's' + }.freeze + + TARGET_SPECIAL_PERMISSION = { + OWNER_PERMISSION_INDEX => '2', + GROUP_PERMISSION_INDEX => '4', + OTHER_PERMISSION_INDEX => '1' + }.freeze + + attr_reader :name, :file_status + + def initialize(file, path) @name = file + @path = path + @file_status = generate_file_status + @max_bitesize = {} + end + + def generate_file_status + status = File::Stat.new(File.absolute_path(@name, @path)) + { + filemode: filemode(status), + hardlink_nums: status.nlink.to_s, + owner_name: Etc.getpwuid(status.uid).name, + group_name: Etc.getgrgid(status.gid).name, + bytesize: status.size.to_s, + latest_modify_datetime: status.mtime.strftime('%_m %e %H:%M'), + filename: @name, + blocks: status.blocks + } + end + + # リファクタする + def display_file_status() + @file_status.reject { |key| key == :blocks }.map do |key, value| # blocksは表示には使用しないため表示の配列から除く + value + end.join(' ') + end + + # リファクタする + def filemode(status) + mode = status.mode.to_s(8).rjust(6, '0') + [OWNER_PERMISSION_INDEX, GROUP_PERMISSION_INDEX, OTHER_PERMISSION_INDEX].map do |index| + convert_permission(mode[index], mode[SPECIAL_PERMISSION_INDEX], TARGET_SPECIAL_PERMISSION[index]) + end.unshift(FILE_TYPE[mode[0, SPECIAL_PERMISSION_INDEX]]).join + end + + # リファクタする + def convert_permission(permission, special_permission, target_special_permission) + permission_type = PERMISSION_TYPE[permission] + return permission_type if special_permission != target_special_permission + + special_permission_type = SPECIAL_PERMISSION_TYPE[special_permission] + special_permission_type = special_permission_type.upcase if permission.to_i.even? + [permission_type.chop, special_permission_type].join end end diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index 5917957991..6118da63ab 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -5,17 +5,24 @@ require_relative 'file_data' class LsCommand - def initialize(path, dot_match: false, reverse: false) + def initialize(path, dot_match: false, reverse: false, long_format: false) @path = path @dot_match = dot_match @reverse = reverse - @file_data = Dir.open(@path).entries.map { |file| FileData.new(file) } + @long_format = long_format + @file_data = Dir.open(@path).entries.map { |file| FileData.new(file, @path) } @matched_files = dot_match_files @sorted_file_data = sort_files end def display - @sorted_file_data.map(&:name).join(' ') + if @long_format + ["total #{@sorted_file_data.sum { |status| status.file_status[:blocks] }}"] + .concat(@sorted_file_data.map(&:display_file_status)) + .join("\n") + else + @sorted_file_data.map(&:name).join(' ') + end end private diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb index eeebc21bc2..fd6aaae384 100644 --- a/07.ls_object/test/ls_command_test.rb +++ b/07.ls_object/test/ls_command_test.rb @@ -36,4 +36,34 @@ def test_display_dot_match_and_reverse_sort_file lscommand = LsCommand.new('test_dir3', **params) assert_equal 'test_2.txt test.txt dir .test .. .', lscommand.display end + + def test_display_long_format_file + params = { long_format: true } + lscommand = LsCommand.new('test_dir', **params) + expected = "total 8\n-rw-r--r-- 1 suzukiyouko staff 10 9 15 14:34 test.txt" + assert_equal expected, lscommand.display + end + + def test_display_long_format_and_all_file + params = { long_format: true , dot_match: true } + lscommand = LsCommand.new('test_dir2', **params) + expected = "total 16 +drwxr-xr-x 4 suzukiyouko staff 128 9 15 15:30 . +drwxr-xr-x 10 suzukiyouko staff 320 9 16 15:14 .. +-rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test.txt +-rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test_2.txt" + assert_equal expected, lscommand.display + end + + + def test_display_long_format_and_all_and_reverse_file + params = { reverse: true, long_format: true , dot_match: true } + lscommand = LsCommand.new('test_dir2', **params) + expected = "total 16 +-rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test_2.txt +-rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test.txt +drwxr-xr-x 10 suzukiyouko staff 320 9 16 15:14 .. +drwxr-xr-x 4 suzukiyouko staff 128 9 15 15:30 ." + assert_equal expected, lscommand.display + end end From 9f1202dd8d60691eaf66bc08f215c35789c4cb8d Mon Sep 17 00:00:00 2001 From: yokomaru Date: Wed, 18 Sep 2024 18:17:41 +0900 Subject: [PATCH 06/41] fix: remove FileStatus from FileData MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fileクラスが大きくなりすぎるため、FileStatus情報を別クラスとして切り出す --- 07.ls_object/lib/file_data.rb | 82 ++------------------------- 07.ls_object/lib/file_status.rb | 98 +++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 77 deletions(-) create mode 100644 07.ls_object/lib/file_status.rb diff --git a/07.ls_object/lib/file_data.rb b/07.ls_object/lib/file_data.rb index dd0f5b2607..0af89317b6 100755 --- a/07.ls_object/lib/file_data.rb +++ b/07.ls_object/lib/file_data.rb @@ -3,92 +3,20 @@ require 'etc' require 'debug' +require_relative 'file_status' class FileData - SPECIAL_PERMISSION_INDEX = 2 - OWNER_PERMISSION_INDEX = 3 - GROUP_PERMISSION_INDEX = 4 - OTHER_PERMISSION_INDEX = 5 - - FILE_TYPE = { - '01' => 'p', - '02' => 'c', - '04' => 'd', - '06' => 'b', - '10' => '-', - '12' => 'l', - '14' => 's' - }.freeze - - PERMISSION_TYPE = { - '0' => '---', - '1' => '--x', - '2' => '-w-', - '3' => '-wx', - '4' => 'r--', - '5' => 'r-x', - '6' => 'rw-', - '7' => 'rwx' - }.freeze - - SPECIAL_PERMISSION_TYPE = { - '0' => '-', - '1' => 't', - '2' => 's', - '4' => 's' - }.freeze - - TARGET_SPECIAL_PERMISSION = { - OWNER_PERMISSION_INDEX => '2', - GROUP_PERMISSION_INDEX => '4', - OTHER_PERMISSION_INDEX => '1' - }.freeze - attr_reader :name, :file_status - def initialize(file, path) - @name = file + def initialize(file_name, path) + @name = file_name @path = path - @file_status = generate_file_status - @max_bitesize = {} - end - - def generate_file_status - status = File::Stat.new(File.absolute_path(@name, @path)) - { - filemode: filemode(status), - hardlink_nums: status.nlink.to_s, - owner_name: Etc.getpwuid(status.uid).name, - group_name: Etc.getgrgid(status.gid).name, - bytesize: status.size.to_s, - latest_modify_datetime: status.mtime.strftime('%_m %e %H:%M'), - filename: @name, - blocks: status.blocks - } + @file_status = FileStatus.new(@name, @path).build_file_status end - # リファクタする - def display_file_status() + def display_file_status @file_status.reject { |key| key == :blocks }.map do |key, value| # blocksは表示には使用しないため表示の配列から除く value end.join(' ') end - - # リファクタする - def filemode(status) - mode = status.mode.to_s(8).rjust(6, '0') - [OWNER_PERMISSION_INDEX, GROUP_PERMISSION_INDEX, OTHER_PERMISSION_INDEX].map do |index| - convert_permission(mode[index], mode[SPECIAL_PERMISSION_INDEX], TARGET_SPECIAL_PERMISSION[index]) - end.unshift(FILE_TYPE[mode[0, SPECIAL_PERMISSION_INDEX]]).join - end - - # リファクタする - def convert_permission(permission, special_permission, target_special_permission) - permission_type = PERMISSION_TYPE[permission] - return permission_type if special_permission != target_special_permission - - special_permission_type = SPECIAL_PERMISSION_TYPE[special_permission] - special_permission_type = special_permission_type.upcase if permission.to_i.even? - [permission_type.chop, special_permission_type].join - end end diff --git a/07.ls_object/lib/file_status.rb b/07.ls_object/lib/file_status.rb new file mode 100644 index 0000000000..e28b3658aa --- /dev/null +++ b/07.ls_object/lib/file_status.rb @@ -0,0 +1,98 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'etc' +require 'debug' + +class FileStatus + SPECIAL_PERMISSION_INDEX = 2 + OWNER_PERMISSION_INDEX = 3 + GROUP_PERMISSION_INDEX = 4 + OTHER_PERMISSION_INDEX = 5 + + FILE_TYPE = { + '01' => 'p', + '02' => 'c', + '04' => 'd', + '06' => 'b', + '10' => '-', + '12' => 'l', + '14' => 's' + }.freeze + + PERMISSION_TYPE = { + '0' => '---', + '1' => '--x', + '2' => '-w-', + '3' => '-wx', + '4' => 'r--', + '5' => 'r-x', + '6' => 'rw-', + '7' => 'rwx' + }.freeze + + SPECIAL_PERMISSION_TYPE = { + '0' => '-', + '1' => 't', + '2' => 's', + '4' => 's' + }.freeze + + TARGET_SPECIAL_PERMISSION = { + OWNER_PERMISSION_INDEX => '2', + GROUP_PERMISSION_INDEX => '4', + OTHER_PERMISSION_INDEX => '1' + }.freeze + + attr_reader :name, :file_status + + def initialize(file_name, path) + @file_name = file_name + @status = File::Stat.new(File.absolute_path(file_name, path)) + @filemode = filemode(@status) + @hardlink_nums = @status.nlink + @owner_name = Etc.getpwuid(@status.uid).name + @group_name = Etc.getgrgid(@status.gid).name + @bytesize = @status.size + @latest_modify_datetime = @status.mtime.strftime('%_m %e %H:%M') + @blocks = @status.blocks + end + + def build_file_status + { + filemode: @filemode, + hardlink_nums: @hardlink_nums, + owner_name: @owner_name, + group_name: @group_name, + bytesize: @bytesize, + latest_modify_datetime: @latest_modify_datetime, + filename: @file_name, + blocks: @blocks + } + end + + private + # リファクタする + def filemode(status) + mode = status.mode.to_s(8).rjust(6, '0') + [OWNER_PERMISSION_INDEX, GROUP_PERMISSION_INDEX, OTHER_PERMISSION_INDEX].map do |index| + convert_permission(mode[index], + mode[SPECIAL_PERMISSION_INDEX], + TARGET_SPECIAL_PERMISSION[index]) + end.unshift(FILE_TYPE[ + mode[0, SPECIAL_PERMISSION_INDEX]]) + .join + end + + # リファクタする + def convert_permission(permission, special_permission, target_special_permission) + permission_type = PERMISSION_TYPE[permission] + return permission_type if special_permission != target_special_permission + + special_permission_type = SPECIAL_PERMISSION_TYPE[special_permission] + + special_permission_type = special_permission_type.upcase if permission.to_i.even? + + [permission_type.chop, special_permission_type].join + end +end From 0a23a16917bc07688025c69e679a3c2f23124f6d Mon Sep 17 00:00:00 2001 From: yokomaru Date: Wed, 18 Sep 2024 18:18:16 +0900 Subject: [PATCH 07/41] fix: display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `-l`オプションをdisplayから分ける --- 07.ls_object/lib/ls_command.rb | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index 6118da63ab..9766389563 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -10,23 +10,28 @@ def initialize(path, dot_match: false, reverse: false, long_format: false) @dot_match = dot_match @reverse = reverse @long_format = long_format - @file_data = Dir.open(@path).entries.map { |file| FileData.new(file, @path) } + @file_data = Dir.open(@path).entries.map { |name| FileData.new(name, @path) } @matched_files = dot_match_files - @sorted_file_data = sort_files + @sorted_files = sort_files + @formated_files = format_files end def display + @formated_files + end + + private + + def format_files if @long_format - ["total #{@sorted_file_data.sum { |status| status.file_status[:blocks] }}"] - .concat(@sorted_file_data.map(&:display_file_status)) + ["total #{@sorted_files.sum { |status| status.file_status[:blocks] }}"] + .concat(@sorted_files.map(&:display_file_status)) .join("\n") else - @sorted_file_data.map(&:name).join(' ') + @sorted_files.map(&:name).join(' ') end end - private - def dot_match_files if @dot_match @file_data From 52a7e6b13c9f913902531a89b91bb98bfb99814d Mon Sep 17 00:00:00 2001 From: yokomaru Date: Wed, 18 Sep 2024 19:20:31 +0900 Subject: [PATCH 08/41] =?UTF-8?q?add:=20=E4=B8=80=E6=97=A6command=E3=83=91?= =?UTF-8?q?=E3=82=BF=E3=83=BC=E3=83=B3=E3=81=A7=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 07.ls_object/bin/ls.rb | 2 +- 07.ls_object/lib/ls_command.rb | 13 +++++++++++++ 07.ls_object/lib/option/composit_option.rb | 13 +++++++++++++ 07.ls_object/lib/option/format_option.rb | 18 ++++++++++++++++++ 07.ls_object/lib/option/match_option.rb | 16 ++++++++++++++++ 07.ls_object/lib/option/option.rb | 8 ++++++++ 07.ls_object/lib/option/sort_option.rb | 15 +++++++++++++++ 7 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 07.ls_object/lib/option/composit_option.rb create mode 100644 07.ls_object/lib/option/format_option.rb create mode 100644 07.ls_object/lib/option/match_option.rb create mode 100644 07.ls_object/lib/option/option.rb create mode 100644 07.ls_object/lib/option/sort_option.rb diff --git a/07.ls_object/bin/ls.rb b/07.ls_object/bin/ls.rb index cd157ecc30..61dd237a56 100755 --- a/07.ls_object/bin/ls.rb +++ b/07.ls_object/bin/ls.rb @@ -14,6 +14,6 @@ opt.parse!(ARGV) ls = LsCommand.new('.', **params) - + ls.build_files puts ls.display end diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index 9766389563..78e1058b0a 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -3,6 +3,11 @@ require 'debug' require 'pathname' require_relative 'file_data' +require_relative 'option/option' +require_relative 'option/composit_option' +require_relative 'option/match_option' +require_relative 'option/format_option' +require_relative 'option/sort_option' class LsCommand def initialize(path, dot_match: false, reverse: false, long_format: false) @@ -11,6 +16,7 @@ def initialize(path, dot_match: false, reverse: false, long_format: false) @reverse = reverse @long_format = long_format @file_data = Dir.open(@path).entries.map { |name| FileData.new(name, @path) } + @command_list = CompositeOption.new @matched_files = dot_match_files @sorted_files = sort_files @formated_files = format_files @@ -20,6 +26,13 @@ def display @formated_files end + def build_files + @command_list.add_option(MatchOption.new(@file_data, @dot_match)) + @command_list.add_option(SortOption.new(@file_data, @reverse)) + @command_list.add_option(FormatOption.new(@file_data, @long_format)) + p @command_list.execute + end + private def format_files diff --git a/07.ls_object/lib/option/composit_option.rb b/07.ls_object/lib/option/composit_option.rb new file mode 100644 index 0000000000..e7c956ed3a --- /dev/null +++ b/07.ls_object/lib/option/composit_option.rb @@ -0,0 +1,13 @@ +class CompositeOption < Option + def initialize + @options = [] + end + + def add_option(option) + @options << option + end + + def execute + @options.each { |option| option.execute } + end +end \ No newline at end of file diff --git a/07.ls_object/lib/option/format_option.rb b/07.ls_object/lib/option/format_option.rb new file mode 100644 index 0000000000..b47b29f928 --- /dev/null +++ b/07.ls_object/lib/option/format_option.rb @@ -0,0 +1,18 @@ +# コマンドのインターフェース +class FormatOption < Option + attr_reader :description + def initialize(file_data, long_format) + @long_format = long_format + @file_data = file_data + end + + def execute + if @long_format + ["total #{@file_data.sum { |status| status.file_status[:blocks] }}"] + .concat(@file_data.map(&:display_file_status)) + .join("\n") + else + @file_data.map(&:name).join(' ') + end + end +end \ No newline at end of file diff --git a/07.ls_object/lib/option/match_option.rb b/07.ls_object/lib/option/match_option.rb new file mode 100644 index 0000000000..74a088139c --- /dev/null +++ b/07.ls_object/lib/option/match_option.rb @@ -0,0 +1,16 @@ +# コマンドのインターフェース +class MatchOption < Option + + def initialize(file_data, dot_match) + @dot_match = dot_match + @file_data = file_data + end + + def execute + if @dot_match + @file_data + else + @file_data.filter { |file| !/^\./.match?(file.name) } + end + end +end \ No newline at end of file diff --git a/07.ls_object/lib/option/option.rb b/07.ls_object/lib/option/option.rb new file mode 100644 index 0000000000..fed68ae52f --- /dev/null +++ b/07.ls_object/lib/option/option.rb @@ -0,0 +1,8 @@ +# コマンドのインターフェース +class Option + def initialize() + end + + def execute + end +end \ No newline at end of file diff --git a/07.ls_object/lib/option/sort_option.rb b/07.ls_object/lib/option/sort_option.rb new file mode 100644 index 0000000000..c400226f90 --- /dev/null +++ b/07.ls_object/lib/option/sort_option.rb @@ -0,0 +1,15 @@ +# コマンドのインターフェース +class SortOption < Option + def initialize(file_data, reverse) + @reverse = reverse + @file_data = file_data + end + + def execute + if @reverse + @file_data.sort_by{|file| file.name }.reverse + else + @file_data.sort_by{|file| file.name } + end + end +end \ No newline at end of file From 56381d81398adaa9602097d1a8daaa2f93af8317 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Wed, 18 Sep 2024 21:34:02 +0900 Subject: [PATCH 09/41] =?UTF-8?q?add:=20options=20-=20Option=E3=82=92?= =?UTF-8?q?=E7=B6=99=E6=89=BF=E3=81=97=E3=81=9F=E4=BB=A5=E4=B8=8B=E3=82=AF?= =?UTF-8?q?=E3=83=A9=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0=20=20=20=20=20-?= =?UTF-8?q?=20FormatOption=20=20=20=20=20=20=20=20=20-=20`-l`=E3=82=AA?= =?UTF-8?q?=E3=83=97=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AB=E8=A9=B2=E5=BD=93?= =?UTF-8?q?=20=20=20=20=20-=20MatchOption=20=20=20=20=20=20=20=20=20-=20`-?= =?UTF-8?q?a`=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AB?= =?UTF-8?q?=E8=A9=B2=E5=BD=93=20=20=20=20=20-=20SortOption=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20-=20`-r`=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=81=AB=E8=A9=B2=E5=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 07.ls_object/lib/option/composit_option.rb | 13 ------------- 07.ls_object/lib/option/format_option.rb | 18 ++++++++---------- 07.ls_object/lib/option/match_option.rb | 14 ++++---------- 07.ls_object/lib/option/option.rb | 5 +++-- 07.ls_object/lib/option/sort_option.rb | 13 ++++--------- 5 files changed, 19 insertions(+), 44 deletions(-) delete mode 100644 07.ls_object/lib/option/composit_option.rb diff --git a/07.ls_object/lib/option/composit_option.rb b/07.ls_object/lib/option/composit_option.rb deleted file mode 100644 index e7c956ed3a..0000000000 --- a/07.ls_object/lib/option/composit_option.rb +++ /dev/null @@ -1,13 +0,0 @@ -class CompositeOption < Option - def initialize - @options = [] - end - - def add_option(option) - @options << option - end - - def execute - @options.each { |option| option.execute } - end -end \ No newline at end of file diff --git a/07.ls_object/lib/option/format_option.rb b/07.ls_object/lib/option/format_option.rb index b47b29f928..48ad72f90e 100644 --- a/07.ls_object/lib/option/format_option.rb +++ b/07.ls_object/lib/option/format_option.rb @@ -1,18 +1,16 @@ -# コマンドのインターフェース +require_relative 'option' class FormatOption < Option - attr_reader :description - def initialize(file_data, long_format) - @long_format = long_format - @file_data = file_data + + def initialize(file, option, total_block) + super(file, option) + @total_block = total_block end def execute - if @long_format - ["total #{@file_data.sum { |status| status.file_status[:blocks] }}"] - .concat(@file_data.map(&:display_file_status)) - .join("\n") + if @option + ["total #{@total_block}"].concat(@files.map(&:display_file_status)).join("\n") else - @file_data.map(&:name).join(' ') + @files.map(&:name).join(' ') end end end \ No newline at end of file diff --git a/07.ls_object/lib/option/match_option.rb b/07.ls_object/lib/option/match_option.rb index 74a088139c..3dfd1bf326 100644 --- a/07.ls_object/lib/option/match_option.rb +++ b/07.ls_object/lib/option/match_option.rb @@ -1,16 +1,10 @@ -# コマンドのインターフェース +require_relative 'option' class MatchOption < Option - - def initialize(file_data, dot_match) - @dot_match = dot_match - @file_data = file_data - end - def execute - if @dot_match - @file_data + if @option + @files else - @file_data.filter { |file| !/^\./.match?(file.name) } + @files.filter { |file| !/^\./.match?(file.name) } end end end \ No newline at end of file diff --git a/07.ls_object/lib/option/option.rb b/07.ls_object/lib/option/option.rb index fed68ae52f..93ce5ba8d4 100644 --- a/07.ls_object/lib/option/option.rb +++ b/07.ls_object/lib/option/option.rb @@ -1,6 +1,7 @@ -# コマンドのインターフェース class Option - def initialize() + def initialize(files, option) + @files = files + @option = option end def execute diff --git a/07.ls_object/lib/option/sort_option.rb b/07.ls_object/lib/option/sort_option.rb index c400226f90..30501fcb27 100644 --- a/07.ls_object/lib/option/sort_option.rb +++ b/07.ls_object/lib/option/sort_option.rb @@ -1,15 +1,10 @@ -# コマンドのインターフェース +require_relative 'option' class SortOption < Option - def initialize(file_data, reverse) - @reverse = reverse - @file_data = file_data - end - def execute - if @reverse - @file_data.sort_by{|file| file.name }.reverse + if @option + @files.sort_by{|file| file.name }.reverse else - @file_data.sort_by{|file| file.name } + @files.sort_by{|file| file.name } end end end \ No newline at end of file From e9a7794ef7a8ce482cbe7db9aa94773c76431354 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Wed, 18 Sep 2024 21:36:09 +0900 Subject: [PATCH 10/41] fix: FileStatus and move directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FileStatusをシンプルに実装 - ディレクトリを移動 --- 07.ls_object/lib/{ => file}/file_data.rb | 0 07.ls_object/lib/file/file_status.rb | 58 ++++++++++++++ 07.ls_object/lib/file_status.rb | 98 ------------------------ 3 files changed, 58 insertions(+), 98 deletions(-) rename 07.ls_object/lib/{ => file}/file_data.rb (100%) create mode 100644 07.ls_object/lib/file/file_status.rb delete mode 100644 07.ls_object/lib/file_status.rb diff --git a/07.ls_object/lib/file_data.rb b/07.ls_object/lib/file/file_data.rb similarity index 100% rename from 07.ls_object/lib/file_data.rb rename to 07.ls_object/lib/file/file_data.rb diff --git a/07.ls_object/lib/file/file_status.rb b/07.ls_object/lib/file/file_status.rb new file mode 100644 index 0000000000..ba0742f7fe --- /dev/null +++ b/07.ls_object/lib/file/file_status.rb @@ -0,0 +1,58 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'etc' +require 'debug' +require 'pathname' + +class FileStatus + MODE_TABLE = { + '0' => '---', + '1' => '--x', + '2' => '-w-', + '3' => '-wx', + '4' => 'r--', + '5' => 'r-x', + '6' => 'rw-', + '7' => 'rwx' + }.freeze + + attr_reader :file_name, :file_status + + def initialize(file_name, path) + @file_name = file_name + @pathname = Pathname(File.absolute_path(@file_name, path)) + @status = File::Stat.new(@pathname) + @file_type = format_file_type + @file_mode = format_file_mode + @hardlink_nums = @status.nlink + @owner_name = Etc.getpwuid(@status.uid).name + @group_name = Etc.getgrgid(@status.gid).name + @bytesize = @status.size + @latest_modify_datetime = @status.mtime.strftime('%_m %e %H:%M') + @blocks = @status.blocks + end + + def build_file_status + { + type_and_mode: "#{@file_type}#{@file_mode}", + hardlink_nums: @hardlink_nums, + owner_name: @owner_name, + group_name: @group_name, + bytesize: @bytesize, + latest_modify_datetime: @latest_modify_datetime, + filename: @file_name, + blocks: @blocks + } + end + + private + + def format_file_type + @pathname.directory? ? 'd' : '-' + end + + def format_file_mode + @pathname.stat.mode.to_s(8)[-3..-1].gsub(/./, MODE_TABLE) + end +end diff --git a/07.ls_object/lib/file_status.rb b/07.ls_object/lib/file_status.rb deleted file mode 100644 index e28b3658aa..0000000000 --- a/07.ls_object/lib/file_status.rb +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require 'etc' -require 'debug' - -class FileStatus - SPECIAL_PERMISSION_INDEX = 2 - OWNER_PERMISSION_INDEX = 3 - GROUP_PERMISSION_INDEX = 4 - OTHER_PERMISSION_INDEX = 5 - - FILE_TYPE = { - '01' => 'p', - '02' => 'c', - '04' => 'd', - '06' => 'b', - '10' => '-', - '12' => 'l', - '14' => 's' - }.freeze - - PERMISSION_TYPE = { - '0' => '---', - '1' => '--x', - '2' => '-w-', - '3' => '-wx', - '4' => 'r--', - '5' => 'r-x', - '6' => 'rw-', - '7' => 'rwx' - }.freeze - - SPECIAL_PERMISSION_TYPE = { - '0' => '-', - '1' => 't', - '2' => 's', - '4' => 's' - }.freeze - - TARGET_SPECIAL_PERMISSION = { - OWNER_PERMISSION_INDEX => '2', - GROUP_PERMISSION_INDEX => '4', - OTHER_PERMISSION_INDEX => '1' - }.freeze - - attr_reader :name, :file_status - - def initialize(file_name, path) - @file_name = file_name - @status = File::Stat.new(File.absolute_path(file_name, path)) - @filemode = filemode(@status) - @hardlink_nums = @status.nlink - @owner_name = Etc.getpwuid(@status.uid).name - @group_name = Etc.getgrgid(@status.gid).name - @bytesize = @status.size - @latest_modify_datetime = @status.mtime.strftime('%_m %e %H:%M') - @blocks = @status.blocks - end - - def build_file_status - { - filemode: @filemode, - hardlink_nums: @hardlink_nums, - owner_name: @owner_name, - group_name: @group_name, - bytesize: @bytesize, - latest_modify_datetime: @latest_modify_datetime, - filename: @file_name, - blocks: @blocks - } - end - - private - # リファクタする - def filemode(status) - mode = status.mode.to_s(8).rjust(6, '0') - [OWNER_PERMISSION_INDEX, GROUP_PERMISSION_INDEX, OTHER_PERMISSION_INDEX].map do |index| - convert_permission(mode[index], - mode[SPECIAL_PERMISSION_INDEX], - TARGET_SPECIAL_PERMISSION[index]) - end.unshift(FILE_TYPE[ - mode[0, SPECIAL_PERMISSION_INDEX]]) - .join - end - - # リファクタする - def convert_permission(permission, special_permission, target_special_permission) - permission_type = PERMISSION_TYPE[permission] - return permission_type if special_permission != target_special_permission - - special_permission_type = SPECIAL_PERMISSION_TYPE[special_permission] - - special_permission_type = special_permission_type.upcase if permission.to_i.even? - - [permission_type.chop, special_permission_type].join - end -end From 65df83283444c8cd23fcc00b3c372c97fb86c42f Mon Sep 17 00:00:00 2001 From: yokomaru Date: Wed, 18 Sep 2024 21:37:19 +0900 Subject: [PATCH 11/41] fix: ls.rb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 不要なメソッドを削除 - Optionを切り分けたため、分岐を削除 --- 07.ls_object/bin/ls.rb | 1 - 07.ls_object/lib/ls_command.rb | 49 +++++++--------------------------- 2 files changed, 10 insertions(+), 40 deletions(-) diff --git a/07.ls_object/bin/ls.rb b/07.ls_object/bin/ls.rb index 61dd237a56..43daedeca7 100755 --- a/07.ls_object/bin/ls.rb +++ b/07.ls_object/bin/ls.rb @@ -14,6 +14,5 @@ opt.parse!(ARGV) ls = LsCommand.new('.', **params) - ls.build_files puts ls.display end diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index 78e1058b0a..262e74b011 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -1,10 +1,7 @@ # frozen_string_literal: true require 'debug' -require 'pathname' -require_relative 'file_data' -require_relative 'option/option' -require_relative 'option/composit_option' +require_relative 'file/file_data' require_relative 'option/match_option' require_relative 'option/format_option' require_relative 'option/sort_option' @@ -15,49 +12,23 @@ def initialize(path, dot_match: false, reverse: false, long_format: false) @dot_match = dot_match @reverse = reverse @long_format = long_format - @file_data = Dir.open(@path).entries.map { |name| FileData.new(name, @path) } - @command_list = CompositeOption.new - @matched_files = dot_match_files - @sorted_files = sort_files - @formated_files = format_files + @files = build_files + @total_block = sum_blocks end def display - @formated_files - end - - def build_files - @command_list.add_option(MatchOption.new(@file_data, @dot_match)) - @command_list.add_option(SortOption.new(@file_data, @reverse)) - @command_list.add_option(FormatOption.new(@file_data, @long_format)) - p @command_list.execute + FormatOption.new(@files, @long_format, @total_block ).execute end private - def format_files - if @long_format - ["total #{@sorted_files.sum { |status| status.file_status[:blocks] }}"] - .concat(@sorted_files.map(&:display_file_status)) - .join("\n") - else - @sorted_files.map(&:name).join(' ') - end - end - - def dot_match_files - if @dot_match - @file_data - else - @file_data.filter { |file| !/^\./.match?(file.name) } - end + def build_files + files = Dir.open(@path).entries.map { |name| FileData.new(name, @path) } + matched_files = MatchOption.new(files, @dot_match).execute + sorted_files = SortOption.new(matched_files, @reverse).execute end - def sort_files - if @reverse - @matched_files.sort_by{|file| file.name }.reverse - else - @matched_files.sort_by{|file| file.name } - end + def sum_blocks + @files.sum { |status| status.file_status[:blocks] } end end From 99c65ae346ab5e8fa80393554fdbb6c2b96a896d Mon Sep 17 00:00:00 2001 From: yokomaru Date: Wed, 18 Sep 2024 21:37:35 +0900 Subject: [PATCH 12/41] fix: test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - テスト内の変数名を修正 --- 07.ls_object/test/ls_command_test.rb | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb index fd6aaae384..171cc8abf2 100644 --- a/07.ls_object/test/ls_command_test.rb +++ b/07.ls_object/test/ls_command_test.rb @@ -5,65 +5,65 @@ class LsCommandTest < Minitest::Test def test_display_a_file - lscommand = LsCommand.new('test_dir') - assert_equal 'test.txt', lscommand.display + ls_command = LsCommand.new('test_dir') + assert_equal 'test.txt', ls_command.display end def test_display_two_files - lscommand = LsCommand.new('test_dir2') - assert_equal 'test.txt test_2.txt', lscommand.display + ls_command = LsCommand.new('test_dir2') + assert_equal 'test.txt test_2.txt', ls_command.display end def test_display_dot_file - lscommand = LsCommand.new('test_dir3') - assert_equal 'dir test.txt test_2.txt', lscommand.display + ls_command = LsCommand.new('test_dir3') + assert_equal 'dir test.txt test_2.txt', ls_command.display end def test_display_match_dot_file params = { dot_match: true } - lscommand = LsCommand.new('test_dir3', **params) - assert_equal '. .. .test dir test.txt test_2.txt', lscommand.display + ls_command = LsCommand.new('test_dir3', **params) + assert_equal '. .. .test dir test.txt test_2.txt', ls_command.display end def test_display_reverse_sort_file params = { reverse: true, dot_match: false } - lscommand = LsCommand.new('test_dir3', **params) - assert_equal 'test_2.txt test.txt dir', lscommand.display + ls_command = LsCommand.new('test_dir3', **params) + assert_equal 'test_2.txt test.txt dir', ls_command.display end def test_display_dot_match_and_reverse_sort_file params = { reverse: true, dot_match: true } - lscommand = LsCommand.new('test_dir3', **params) - assert_equal 'test_2.txt test.txt dir .test .. .', lscommand.display + ls_command = LsCommand.new('test_dir3', **params) + assert_equal 'test_2.txt test.txt dir .test .. .', ls_command.display end def test_display_long_format_file params = { long_format: true } - lscommand = LsCommand.new('test_dir', **params) + ls_command = LsCommand.new('test_dir', **params) expected = "total 8\n-rw-r--r-- 1 suzukiyouko staff 10 9 15 14:34 test.txt" - assert_equal expected, lscommand.display + assert_equal expected, ls_command.display end def test_display_long_format_and_all_file params = { long_format: true , dot_match: true } - lscommand = LsCommand.new('test_dir2', **params) + ls_command = LsCommand.new('test_dir2', **params) expected = "total 16 drwxr-xr-x 4 suzukiyouko staff 128 9 15 15:30 . drwxr-xr-x 10 suzukiyouko staff 320 9 16 15:14 .. -rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test.txt -rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test_2.txt" - assert_equal expected, lscommand.display + assert_equal expected, ls_command.display end def test_display_long_format_and_all_and_reverse_file params = { reverse: true, long_format: true , dot_match: true } - lscommand = LsCommand.new('test_dir2', **params) + ls_command = LsCommand.new('test_dir2', **params) expected = "total 16 -rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test_2.txt -rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test.txt drwxr-xr-x 10 suzukiyouko staff 320 9 16 15:14 .. drwxr-xr-x 4 suzukiyouko staff 128 9 15 15:30 ." - assert_equal expected, lscommand.display + assert_equal expected, ls_command.display end end From 1f242e414a1f0baea9e47428c180264f66fb3dae Mon Sep 17 00:00:00 2001 From: yokomaru Date: Wed, 18 Sep 2024 21:39:00 +0900 Subject: [PATCH 13/41] fix: apply rubocop error check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - rubocopで指摘された箇所をまとめて修正 --- 07.ls_object/lib/file/file_data.rb | 2 +- 07.ls_object/lib/file/file_status.rb | 2 +- 07.ls_object/lib/ls_command.rb | 4 ++-- 07.ls_object/lib/option/format_option.rb | 5 +++-- 07.ls_object/lib/option/match_option.rb | 4 +++- 07.ls_object/lib/option/option.rb | 7 ++++--- 07.ls_object/lib/option/sort_option.rb | 8 +++++--- 07.ls_object/test/ls_command_test.rb | 5 ++--- 8 files changed, 21 insertions(+), 16 deletions(-) mode change 100644 => 100755 07.ls_object/lib/file/file_status.rb diff --git a/07.ls_object/lib/file/file_data.rb b/07.ls_object/lib/file/file_data.rb index 0af89317b6..00103aab01 100755 --- a/07.ls_object/lib/file/file_data.rb +++ b/07.ls_object/lib/file/file_data.rb @@ -15,7 +15,7 @@ def initialize(file_name, path) end def display_file_status - @file_status.reject { |key| key == :blocks }.map do |key, value| # blocksは表示には使用しないため表示の配列から除く + @file_status.reject { |key| key == :blocks }.map do |_key, value| # blocksは表示には使用しないため表示の配列から除く value end.join(' ') end diff --git a/07.ls_object/lib/file/file_status.rb b/07.ls_object/lib/file/file_status.rb old mode 100644 new mode 100755 index ba0742f7fe..e2e3b9eeb9 --- a/07.ls_object/lib/file/file_status.rb +++ b/07.ls_object/lib/file/file_status.rb @@ -53,6 +53,6 @@ def format_file_type end def format_file_mode - @pathname.stat.mode.to_s(8)[-3..-1].gsub(/./, MODE_TABLE) + @pathname.stat.mode.to_s(8)[-3..].gsub(/./, MODE_TABLE) end end diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index 262e74b011..b6bfad2438 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -17,7 +17,7 @@ def initialize(path, dot_match: false, reverse: false, long_format: false) end def display - FormatOption.new(@files, @long_format, @total_block ).execute + FormatOption.new(@files, @long_format, @total_block).execute end private @@ -25,7 +25,7 @@ def display def build_files files = Dir.open(@path).entries.map { |name| FileData.new(name, @path) } matched_files = MatchOption.new(files, @dot_match).execute - sorted_files = SortOption.new(matched_files, @reverse).execute + SortOption.new(matched_files, @reverse).execute end def sum_blocks diff --git a/07.ls_object/lib/option/format_option.rb b/07.ls_object/lib/option/format_option.rb index 48ad72f90e..5d87abcddf 100644 --- a/07.ls_object/lib/option/format_option.rb +++ b/07.ls_object/lib/option/format_option.rb @@ -1,6 +1,7 @@ +# frozen_string_literal: true + require_relative 'option' class FormatOption < Option - def initialize(file, option, total_block) super(file, option) @total_block = total_block @@ -13,4 +14,4 @@ def execute @files.map(&:name).join(' ') end end -end \ No newline at end of file +end diff --git a/07.ls_object/lib/option/match_option.rb b/07.ls_object/lib/option/match_option.rb index 3dfd1bf326..33c958dff6 100644 --- a/07.ls_object/lib/option/match_option.rb +++ b/07.ls_object/lib/option/match_option.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'option' class MatchOption < Option def execute @@ -7,4 +9,4 @@ def execute @files.filter { |file| !/^\./.match?(file.name) } end end -end \ No newline at end of file +end diff --git a/07.ls_object/lib/option/option.rb b/07.ls_object/lib/option/option.rb index 93ce5ba8d4..df1fe316b7 100644 --- a/07.ls_object/lib/option/option.rb +++ b/07.ls_object/lib/option/option.rb @@ -1,9 +1,10 @@ +# frozen_string_literal: true + class Option def initialize(files, option) @files = files @option = option end - def execute - end -end \ No newline at end of file + def execute; end +end diff --git a/07.ls_object/lib/option/sort_option.rb b/07.ls_object/lib/option/sort_option.rb index 30501fcb27..cedd4f59ea 100644 --- a/07.ls_object/lib/option/sort_option.rb +++ b/07.ls_object/lib/option/sort_option.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require_relative 'option' class SortOption < Option def execute if @option - @files.sort_by{|file| file.name }.reverse + @files.sort_by(&:name).reverse else - @files.sort_by{|file| file.name } + @files.sort_by(&:name) end end -end \ No newline at end of file +end diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb index 171cc8abf2..b6b8e099ee 100644 --- a/07.ls_object/test/ls_command_test.rb +++ b/07.ls_object/test/ls_command_test.rb @@ -45,7 +45,7 @@ def test_display_long_format_file end def test_display_long_format_and_all_file - params = { long_format: true , dot_match: true } + params = { long_format: true, dot_match: true } ls_command = LsCommand.new('test_dir2', **params) expected = "total 16 drwxr-xr-x 4 suzukiyouko staff 128 9 15 15:30 . @@ -55,9 +55,8 @@ def test_display_long_format_and_all_file assert_equal expected, ls_command.display end - def test_display_long_format_and_all_and_reverse_file - params = { reverse: true, long_format: true , dot_match: true } + params = { reverse: true, long_format: true, dot_match: true } ls_command = LsCommand.new('test_dir2', **params) expected = "total 16 -rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test_2.txt From cec0f056f4a61394a8c0e016a47c33cd5bc130e2 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Wed, 18 Sep 2024 23:20:35 +0900 Subject: [PATCH 14/41] fix: execute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - if文を三項演算子へ変更 --- 07.ls_object/lib/option/match_option.rb | 6 +----- 07.ls_object/lib/option/sort_option.rb | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/07.ls_object/lib/option/match_option.rb b/07.ls_object/lib/option/match_option.rb index 33c958dff6..ef9e251ec4 100644 --- a/07.ls_object/lib/option/match_option.rb +++ b/07.ls_object/lib/option/match_option.rb @@ -3,10 +3,6 @@ require_relative 'option' class MatchOption < Option def execute - if @option - @files - else - @files.filter { |file| !/^\./.match?(file.name) } - end + @option ? @files : @files.filter { |file| !/^\./.match?(file.name) } end end diff --git a/07.ls_object/lib/option/sort_option.rb b/07.ls_object/lib/option/sort_option.rb index cedd4f59ea..1e42771012 100644 --- a/07.ls_object/lib/option/sort_option.rb +++ b/07.ls_object/lib/option/sort_option.rb @@ -3,10 +3,6 @@ require_relative 'option' class SortOption < Option def execute - if @option - @files.sort_by(&:name).reverse - else - @files.sort_by(&:name) - end + @option ? @files.sort_by(&:name).reverse : @files.sort_by(&:name) end end From 3905060207a02bed2a5e1db6e65eb1538566c6b6 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Wed, 18 Sep 2024 23:21:14 +0900 Subject: [PATCH 15/41] add: width MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 実行時のターミナルの幅を取得する --- 07.ls_object/bin/ls.rb | 4 +++- 07.ls_object/lib/ls_command.rb | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/07.ls_object/bin/ls.rb b/07.ls_object/bin/ls.rb index 43daedeca7..bbfb789d98 100755 --- a/07.ls_object/bin/ls.rb +++ b/07.ls_object/bin/ls.rb @@ -12,7 +12,9 @@ opt.on('-r') { |v| params[:reverse] = v } opt.on('-l') { |v| params[:long_format] = v } opt.parse!(ARGV) + path = ARGV[0] || '.' + width = IO.console.winsize[1] - ls = LsCommand.new('.', **params) + ls = LsCommand.new(path, width:, **params) puts ls.display end diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index b6bfad2438..3db7ae4794 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -7,8 +7,9 @@ require_relative 'option/sort_option' class LsCommand - def initialize(path, dot_match: false, reverse: false, long_format: false) + def initialize(path, width: 80, dot_match: false, reverse: false, long_format: false) @path = path + @width = width @dot_match = dot_match @reverse = reverse @long_format = long_format @@ -17,7 +18,7 @@ def initialize(path, dot_match: false, reverse: false, long_format: false) end def display - FormatOption.new(@files, @long_format, @total_block).execute + FormatOption.new(@files, @long_format, @total_block, @width).execute end private From 66d2082d6f5799f1640868ef20275b3f4529c51e Mon Sep 17 00:00:00 2001 From: yokomaru Date: Wed, 18 Sep 2024 23:22:31 +0900 Subject: [PATCH 16/41] fix: format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - shortとlongで処理を分ける - shortの場合はターミナルの幅と文字数で列数を決定する - longは特定の項目だけ最大値を取得し幅を表示する - 併せてテストを修正 --- 07.ls_object/lib/file/file_data.rb | 6 --- 07.ls_object/lib/file/file_status.rb | 4 +- 07.ls_object/lib/option/format_option.rb | 68 ++++++++++++++++++++++-- 07.ls_object/test/ls_command_test.rb | 43 ++++++++------- 4 files changed, 91 insertions(+), 30 deletions(-) diff --git a/07.ls_object/lib/file/file_data.rb b/07.ls_object/lib/file/file_data.rb index 00103aab01..2e465daf6a 100755 --- a/07.ls_object/lib/file/file_data.rb +++ b/07.ls_object/lib/file/file_data.rb @@ -13,10 +13,4 @@ def initialize(file_name, path) @path = path @file_status = FileStatus.new(@name, @path).build_file_status end - - def display_file_status - @file_status.reject { |key| key == :blocks }.map do |_key, value| # blocksは表示には使用しないため表示の配列から除く - value - end.join(' ') - end end diff --git a/07.ls_object/lib/file/file_status.rb b/07.ls_object/lib/file/file_status.rb index e2e3b9eeb9..08d4d08c6e 100755 --- a/07.ls_object/lib/file/file_status.rb +++ b/07.ls_object/lib/file/file_status.rb @@ -25,10 +25,10 @@ def initialize(file_name, path) @status = File::Stat.new(@pathname) @file_type = format_file_type @file_mode = format_file_mode - @hardlink_nums = @status.nlink + @hardlink_nums = @status.nlink.to_s @owner_name = Etc.getpwuid(@status.uid).name @group_name = Etc.getgrgid(@status.gid).name - @bytesize = @status.size + @bytesize = @status.size.to_s @latest_modify_datetime = @status.mtime.strftime('%_m %e %H:%M') @blocks = @status.blocks end diff --git a/07.ls_object/lib/option/format_option.rb b/07.ls_object/lib/option/format_option.rb index 5d87abcddf..fab73bdd14 100644 --- a/07.ls_object/lib/option/format_option.rb +++ b/07.ls_object/lib/option/format_option.rb @@ -1,17 +1,77 @@ # frozen_string_literal: true require_relative 'option' +require 'pathname' class FormatOption < Option - def initialize(file, option, total_block) - super(file, option) + def initialize(files, option, total_block, width) + super(files, option) @total_block = total_block + @width = width end def execute if @option - ["total #{@total_block}"].concat(@files.map(&:display_file_status)).join("\n") + ls_long else - @files.map(&:name).join(' ') + ls_short end end + + private + + def ls_short + max_file_path_count = @files.map { |f| File.basename(f.name).size }.max + col_count = @width / (max_file_path_count + 1) + row_count = col_count.zero? ? @files.count : (@files.count.to_f / col_count).ceil + transposed_file_paths = safe_transpose(@files.each_slice(row_count).to_a) + format_table(transposed_file_paths, max_file_path_count) + end + + def safe_transpose(nested_file_names) + nested_file_names[0].zip(*nested_file_names[1..]) + end + + def format_table(file_paths, max_file_path_count) + file_paths.map do |row_files| + render_short_format_row(row_files, max_file_path_count) + end.join("\n") + end + + def render_short_format_row(row_files, max_file_path_count) + row_files.map do |file_path| + basename = file_path ? File.basename(file_path.name) : '' + basename.ljust(max_file_path_count + 1) + end.join.rstrip + end + + def ls_long + total = "total #{@total_block}" + body = render_long_format_body + [total, *body].join("\n") + end + + def render_long_format_body + max_sizes = %i[hardlink_nums owner_name group_name bytesize].map do |key| + find_max_size(key) + end + @files.map do |data| + format_row(data.file_status, *max_sizes) + end + end + + def find_max_size(key) + @files.map { |data| data.file_status[key].size }.max + end + + def format_row(data, max_nlink, max_user, max_group, max_size) + [ + data[:type_and_mode], + " #{data[:hardlink_nums].rjust(max_nlink)}", + " #{data[:owner_name].ljust(max_user)}", + " #{data[:group_name].ljust(max_group)}", + " #{data[:bytesize].rjust(max_size)}", + " #{data[:latest_modify_datetime]}", + " #{data[:filename]}" + ].join + end end diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb index b6b8e099ee..ac667ac79b 100644 --- a/07.ls_object/test/ls_command_test.rb +++ b/07.ls_object/test/ls_command_test.rb @@ -11,58 +11,65 @@ def test_display_a_file def test_display_two_files ls_command = LsCommand.new('test_dir2') - assert_equal 'test.txt test_2.txt', ls_command.display + assert_equal 'test.txt test_2.txt', ls_command.display end def test_display_dot_file ls_command = LsCommand.new('test_dir3') - assert_equal 'dir test.txt test_2.txt', ls_command.display + assert_equal 'dir test.txt test_2.txt', ls_command.display end def test_display_match_dot_file params = { dot_match: true } ls_command = LsCommand.new('test_dir3', **params) - assert_equal '. .. .test dir test.txt test_2.txt', ls_command.display + assert_equal '. .. .test dir test.txt test_2.txt', ls_command.display end def test_display_reverse_sort_file params = { reverse: true, dot_match: false } ls_command = LsCommand.new('test_dir3', **params) - assert_equal 'test_2.txt test.txt dir', ls_command.display + assert_equal 'test_2.txt test.txt dir', ls_command.display end def test_display_dot_match_and_reverse_sort_file params = { reverse: true, dot_match: true } ls_command = LsCommand.new('test_dir3', **params) - assert_equal 'test_2.txt test.txt dir .test .. .', ls_command.display + assert_equal 'test_2.txt test.txt dir .test .. .', ls_command.display end def test_display_long_format_file params = { long_format: true } ls_command = LsCommand.new('test_dir', **params) - expected = "total 8\n-rw-r--r-- 1 suzukiyouko staff 10 9 15 14:34 test.txt" + expected = <<~LS_RESULT.chomp + total 8 + -rw-r--r-- 1 suzukiyouko staff 10 9 15 14:34 test.txt + LS_RESULT assert_equal expected, ls_command.display end - def test_display_long_format_and_all_file + def test_display_long_format_and_dot_match_file params = { long_format: true, dot_match: true } ls_command = LsCommand.new('test_dir2', **params) - expected = "total 16 -drwxr-xr-x 4 suzukiyouko staff 128 9 15 15:30 . -drwxr-xr-x 10 suzukiyouko staff 320 9 16 15:14 .. --rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test.txt --rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test_2.txt" + expected = <<~LS_RESULT.chomp + total 16 + drwxr-xr-x 4 suzukiyouko staff 128 9 15 15:30 . + drwxr-xr-x 10 suzukiyouko staff 320 9 16 15:14 .. + -rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test.txt + -rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test_2.txt + LS_RESULT assert_equal expected, ls_command.display end - def test_display_long_format_and_all_and_reverse_file + def test_display_long_format_and_dot_match_and_reverse_file params = { reverse: true, long_format: true, dot_match: true } ls_command = LsCommand.new('test_dir2', **params) - expected = "total 16 --rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test_2.txt --rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test.txt -drwxr-xr-x 10 suzukiyouko staff 320 9 16 15:14 .. -drwxr-xr-x 4 suzukiyouko staff 128 9 15 15:30 ." + expected = <<~LS_RESULT.chomp + total 16 + -rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test_2.txt + -rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test.txt + drwxr-xr-x 10 suzukiyouko staff 320 9 16 15:14 .. + drwxr-xr-x 4 suzukiyouko staff 128 9 15 15:30 . + LS_RESULT assert_equal expected, ls_command.display end end From bd46011ce78e0ca8dfcd3a727d061d136fbfadd8 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Thu, 19 Sep 2024 10:20:21 +0900 Subject: [PATCH 17/41] add: format class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - formatoptionの責務が大きいので、出力のフォーマットに関するクラスを切り出し、判断だけするようにした - Format - 抽象クラス - LongFormat - `-l`適用時のトータルブロック、ファイルステータスの情報を生成 - ShortFormat - 通常時のファイル名一覧の情報の列と行の加工と生成 --- 07.ls_object/lib/format/format.rb | 9 ++++ 07.ls_object/lib/format/long_format.rb | 45 ++++++++++++++++ 07.ls_object/lib/format/short_format.rb | 43 +++++++++++++++ 07.ls_object/lib/option/format_option.rb | 66 ++---------------------- 4 files changed, 102 insertions(+), 61 deletions(-) create mode 100644 07.ls_object/lib/format/format.rb create mode 100644 07.ls_object/lib/format/long_format.rb create mode 100644 07.ls_object/lib/format/short_format.rb diff --git a/07.ls_object/lib/format/format.rb b/07.ls_object/lib/format/format.rb new file mode 100644 index 0000000000..92e3ba10e0 --- /dev/null +++ b/07.ls_object/lib/format/format.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class Format + def initialize(files) + @files = files + end + + def render; end +end diff --git a/07.ls_object/lib/format/long_format.rb b/07.ls_object/lib/format/long_format.rb new file mode 100644 index 0000000000..96b67ca895 --- /dev/null +++ b/07.ls_object/lib/format/long_format.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require_relative 'format' + +class LongFormat < Format + def initialize(files, total_block) + super(files) + @total_block = total_block + @max_sizes = build_max_sizes + @long_format_data = build_long_format + end + + def render + total = "total #{@total_block}" + [total, *@long_format_data].join("\n") + end + + private + + def build_long_format + @files.map { |file| format_row(file.file_status, *@max_sizes) } + end + + def build_max_sizes + %i[hardlink_nums owner_name group_name bytesize].map do |key| + find_max_size(key) + end + end + + def find_max_size(key) + @files.map { |file| file.file_status[key].size }.max + end + + def format_row(data, max_nlink, max_user, max_group, max_size) + [ + data[:type_and_mode], + " #{data[:hardlink_nums].rjust(max_nlink)}", + " #{data[:owner_name].ljust(max_user)}", + " #{data[:group_name].ljust(max_group)}", + " #{data[:bytesize].rjust(max_size)}", + " #{data[:latest_modify_datetime]}", + " #{data[:filename]}" + ].join + end +end diff --git a/07.ls_object/lib/format/short_format.rb b/07.ls_object/lib/format/short_format.rb new file mode 100644 index 0000000000..ee08d20cda --- /dev/null +++ b/07.ls_object/lib/format/short_format.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require_relative 'format' + +class ShortFormat < Format + def initialize(files, width) + super(files) + @width = width + @max_file_name = @files.map { |f| File.basename(f.name).size }.max + @col_count = calculate_col_count + @row_count = calculate_row_count + end + + def render + format_table(safe_transpose) + end + + private + + def calculate_row_count + @col_count.zero? ? @files.count : (@files.count.to_f / @col_count).ceil + end + + def calculate_col_count + @width / (@max_file_name + 1) + end + + def safe_transpose + nested_file_names = @files.each_slice(@row_count).to_a + nested_file_names[0].zip(*nested_file_names[1..]) + end + + def format_table(file_paths) + file_paths.map { |row_files| render_short_format_row(row_files) }.join("\n") + end + + def render_short_format_row(row_files) + row_files.map do |file_path| + basename = file_path ? File.basename(file_path.name) : '' + basename.ljust(@max_file_name + 1) + end.join.rstrip + end +end diff --git a/07.ls_object/lib/option/format_option.rb b/07.ls_object/lib/option/format_option.rb index fab73bdd14..427836508a 100644 --- a/07.ls_object/lib/option/format_option.rb +++ b/07.ls_object/lib/option/format_option.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true require_relative 'option' -require 'pathname' +require_relative '../format/long_format' +require_relative '../format/short_format' + class FormatOption < Option def initialize(files, option, total_block, width) super(files, option) @@ -11,67 +13,9 @@ def initialize(files, option, total_block, width) def execute if @option - ls_long + LongFormat.new(@files, @total_block).render else - ls_short - end - end - - private - - def ls_short - max_file_path_count = @files.map { |f| File.basename(f.name).size }.max - col_count = @width / (max_file_path_count + 1) - row_count = col_count.zero? ? @files.count : (@files.count.to_f / col_count).ceil - transposed_file_paths = safe_transpose(@files.each_slice(row_count).to_a) - format_table(transposed_file_paths, max_file_path_count) - end - - def safe_transpose(nested_file_names) - nested_file_names[0].zip(*nested_file_names[1..]) - end - - def format_table(file_paths, max_file_path_count) - file_paths.map do |row_files| - render_short_format_row(row_files, max_file_path_count) - end.join("\n") - end - - def render_short_format_row(row_files, max_file_path_count) - row_files.map do |file_path| - basename = file_path ? File.basename(file_path.name) : '' - basename.ljust(max_file_path_count + 1) - end.join.rstrip - end - - def ls_long - total = "total #{@total_block}" - body = render_long_format_body - [total, *body].join("\n") - end - - def render_long_format_body - max_sizes = %i[hardlink_nums owner_name group_name bytesize].map do |key| - find_max_size(key) + ShortFormat.new(@files, @width).render end - @files.map do |data| - format_row(data.file_status, *max_sizes) - end - end - - def find_max_size(key) - @files.map { |data| data.file_status[key].size }.max - end - - def format_row(data, max_nlink, max_user, max_group, max_size) - [ - data[:type_and_mode], - " #{data[:hardlink_nums].rjust(max_nlink)}", - " #{data[:owner_name].ljust(max_user)}", - " #{data[:group_name].ljust(max_group)}", - " #{data[:bytesize].rjust(max_size)}", - " #{data[:latest_modify_datetime]}", - " #{data[:filename]}" - ].join end end From 522334ec8552562f17d688fe4c2c41d477be129a Mon Sep 17 00:00:00 2001 From: yokomaru Date: Thu, 19 Sep 2024 10:51:55 +0900 Subject: [PATCH 18/41] fix: delete require and test and line break MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 不要なrequireの記述を削除 不要なテストを削除 不要な改行を削除 --- 07.ls_object/lib/file/file_data.rb | 1 - 07.ls_object/lib/file/file_status.rb | 1 - 07.ls_object/lib/option/match_option.rb | 1 + 07.ls_object/lib/option/sort_option.rb | 1 + 07.ls_object/test/file_data_test.rb | 11 ----------- 07.ls_object/test/ls_test.rb | 10 ---------- 6 files changed, 2 insertions(+), 23 deletions(-) delete mode 100644 07.ls_object/test/file_data_test.rb delete mode 100644 07.ls_object/test/ls_test.rb diff --git a/07.ls_object/lib/file/file_data.rb b/07.ls_object/lib/file/file_data.rb index 2e465daf6a..7ab561aa1e 100755 --- a/07.ls_object/lib/file/file_data.rb +++ b/07.ls_object/lib/file/file_data.rb @@ -2,7 +2,6 @@ # frozen_string_literal: true require 'etc' -require 'debug' require_relative 'file_status' class FileData diff --git a/07.ls_object/lib/file/file_status.rb b/07.ls_object/lib/file/file_status.rb index 08d4d08c6e..4d4df5d0fb 100755 --- a/07.ls_object/lib/file/file_status.rb +++ b/07.ls_object/lib/file/file_status.rb @@ -2,7 +2,6 @@ # frozen_string_literal: true require 'etc' -require 'debug' require 'pathname' class FileStatus diff --git a/07.ls_object/lib/option/match_option.rb b/07.ls_object/lib/option/match_option.rb index ef9e251ec4..e452fed8fc 100644 --- a/07.ls_object/lib/option/match_option.rb +++ b/07.ls_object/lib/option/match_option.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative 'option' + class MatchOption < Option def execute @option ? @files : @files.filter { |file| !/^\./.match?(file.name) } diff --git a/07.ls_object/lib/option/sort_option.rb b/07.ls_object/lib/option/sort_option.rb index 1e42771012..822305fa13 100644 --- a/07.ls_object/lib/option/sort_option.rb +++ b/07.ls_object/lib/option/sort_option.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative 'option' + class SortOption < Option def execute @option ? @files.sort_by(&:name).reverse : @files.sort_by(&:name) diff --git a/07.ls_object/test/file_data_test.rb b/07.ls_object/test/file_data_test.rb deleted file mode 100644 index 9a83c86cc3..0000000000 --- a/07.ls_object/test/file_data_test.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -require_relative 'test_helper' -require_relative '../lib/file_data' - -class LsFilePathTest < Minitest::Test - def test_name - file = FileData.new('test.txt') - assert_equal 'test.txt', file.name - end -end diff --git a/07.ls_object/test/ls_test.rb b/07.ls_object/test/ls_test.rb deleted file mode 100644 index 51a97b4caa..0000000000 --- a/07.ls_object/test/ls_test.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -require_relative 'test_helper' -require_relative '../bin/ls' - -class LsTest < Minitest::Test - def test_run - assert_equal 'test', Ls.run - end -end From f25684b0eb801adfaf34bd45da1e0cdcbae61b20 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Thu, 19 Sep 2024 10:53:34 +0900 Subject: [PATCH 19/41] fix: move total_block to long_format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LongFormatでのみ使用するため、LsCommandクラスにあったtotal_blockをLongFormatクラスに移動する --- 07.ls_object/lib/format/long_format.rb | 12 ++++++++---- 07.ls_object/lib/format/short_format.rb | 8 ++++---- 07.ls_object/lib/ls_command.rb | 7 ++----- 07.ls_object/lib/option/format_option.rb | 5 ++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/07.ls_object/lib/format/long_format.rb b/07.ls_object/lib/format/long_format.rb index 96b67ca895..31022b93b5 100644 --- a/07.ls_object/lib/format/long_format.rb +++ b/07.ls_object/lib/format/long_format.rb @@ -3,11 +3,11 @@ require_relative 'format' class LongFormat < Format - def initialize(files, total_block) + def initialize(files) super(files) - @total_block = total_block @max_sizes = build_max_sizes - @long_format_data = build_long_format + @total_block = sum_blocks + @long_format_data = build_long_format_data end def render @@ -17,7 +17,7 @@ def render private - def build_long_format + def build_long_format_data @files.map { |file| format_row(file.file_status, *@max_sizes) } end @@ -42,4 +42,8 @@ def format_row(data, max_nlink, max_user, max_group, max_size) " #{data[:filename]}" ].join end + + def sum_blocks + @files.sum { |status| status.file_status[:blocks] } + end end diff --git a/07.ls_object/lib/format/short_format.rb b/07.ls_object/lib/format/short_format.rb index ee08d20cda..f53869b4b1 100644 --- a/07.ls_object/lib/format/short_format.rb +++ b/07.ls_object/lib/format/short_format.rb @@ -17,14 +17,14 @@ def render private - def calculate_row_count - @col_count.zero? ? @files.count : (@files.count.to_f / @col_count).ceil - end - def calculate_col_count @width / (@max_file_name + 1) end + def calculate_row_count + @col_count.zero? ? @files.count : (@files.count.to_f / @col_count).ceil + end + def safe_transpose nested_file_names = @files.each_slice(@row_count).to_a nested_file_names[0].zip(*nested_file_names[1..]) diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index 3db7ae4794..a53d49ffb6 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -14,11 +14,10 @@ def initialize(path, width: 80, dot_match: false, reverse: false, long_format: f @reverse = reverse @long_format = long_format @files = build_files - @total_block = sum_blocks end def display - FormatOption.new(@files, @long_format, @total_block, @width).execute + FormatOption.new(@files, @long_format, @width).execute end private @@ -29,7 +28,5 @@ def build_files SortOption.new(matched_files, @reverse).execute end - def sum_blocks - @files.sum { |status| status.file_status[:blocks] } - end + end diff --git a/07.ls_object/lib/option/format_option.rb b/07.ls_object/lib/option/format_option.rb index 427836508a..7d950dcc17 100644 --- a/07.ls_object/lib/option/format_option.rb +++ b/07.ls_object/lib/option/format_option.rb @@ -5,15 +5,14 @@ require_relative '../format/short_format' class FormatOption < Option - def initialize(files, option, total_block, width) + def initialize(files, option, width) super(files, option) - @total_block = total_block @width = width end def execute if @option - LongFormat.new(@files, @total_block).render + LongFormat.new(@files).render else ShortFormat.new(@files, @width).render end From 981a9f4e803d53ee39ec0c6086474431d1d3623d Mon Sep 17 00:00:00 2001 From: yokomaru Date: Thu, 19 Sep 2024 10:54:34 +0900 Subject: [PATCH 20/41] delete: unnecessary attr_reader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit delete: unnecessary attr_reader - 外部クラスから呼ばれないため file_name, :file_statusを削除 --- 07.ls_object/lib/file/file_status.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/07.ls_object/lib/file/file_status.rb b/07.ls_object/lib/file/file_status.rb index 4d4df5d0fb..f7599e878e 100755 --- a/07.ls_object/lib/file/file_status.rb +++ b/07.ls_object/lib/file/file_status.rb @@ -16,8 +16,6 @@ class FileStatus '7' => 'rwx' }.freeze - attr_reader :file_name, :file_status - def initialize(file_name, path) @file_name = file_name @pathname = Pathname(File.absolute_path(@file_name, path)) From 48b911e5ecb69ef6e2231d56fca7872e6cacd767 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Thu, 19 Sep 2024 18:52:51 +0900 Subject: [PATCH 21/41] fix: delete line break MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 不要な改行を削除 --- 07.ls_object/lib/ls_command.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index a53d49ffb6..980f8cf6fa 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -27,6 +27,4 @@ def build_files matched_files = MatchOption.new(files, @dot_match).execute SortOption.new(matched_files, @reverse).execute end - - end From 7540509f749714089b471412f39fecf21f963806 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Thu, 19 Sep 2024 18:54:39 +0900 Subject: [PATCH 22/41] fix: move build_file_status to file_data from file_status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - インスタンス変数が重複していたり、情報が重複していたためfilestatusのハッシュを作成する処理をfile_dataに持たせる - file statusには純粋にfile statusの情報を持たせるようにする --- 07.ls_object/lib/file/file_data.rb | 23 ++++++++++++++--- 07.ls_object/lib/file/file_status.rb | 37 +++++++++------------------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/07.ls_object/lib/file/file_data.rb b/07.ls_object/lib/file/file_data.rb index 7ab561aa1e..292010b90a 100755 --- a/07.ls_object/lib/file/file_data.rb +++ b/07.ls_object/lib/file/file_data.rb @@ -7,9 +7,24 @@ class FileData attr_reader :name, :file_status - def initialize(file_name, path) - @name = file_name - @path = path - @file_status = FileStatus.new(@name, @path).build_file_status + def initialize(name, path) + @name = name + @full_path = Pathname(File.absolute_path(@name, path)) + @file_status = build_file_status(FileStatus.new(@full_path)) + end + + private + + def build_file_status(file_status) + { + type_and_mode: "#{file_status.file_type}#{file_status.file_mode}", + hardlink_nums: file_status.hardlink_nums.to_s, + owner_name: file_status.owner_name, + group_name: file_status.group_name, + bytesize: file_status.bytesize.to_s, + latest_modify_datetime: file_status.latest_modify_datetime, + filename: @name, + blocks: file_status.blocks + } end end diff --git a/07.ls_object/lib/file/file_status.rb b/07.ls_object/lib/file/file_status.rb index f7599e878e..26f4c37d9c 100755 --- a/07.ls_object/lib/file/file_status.rb +++ b/07.ls_object/lib/file/file_status.rb @@ -16,40 +16,27 @@ class FileStatus '7' => 'rwx' }.freeze - def initialize(file_name, path) - @file_name = file_name - @pathname = Pathname(File.absolute_path(@file_name, path)) - @status = File::Stat.new(@pathname) - @file_type = format_file_type - @file_mode = format_file_mode - @hardlink_nums = @status.nlink.to_s + attr_reader :file_type, :file_mode, :hardlink_nums, :owner_name, :group_name, :bytesize, :latest_modify_datetime, :blocks + + def initialize(path) + @status = File::Stat.new(path) + @file_type = format_file_type(path) + @file_mode = format_file_mode(path) + @hardlink_nums = @status.nlink @owner_name = Etc.getpwuid(@status.uid).name @group_name = Etc.getgrgid(@status.gid).name - @bytesize = @status.size.to_s + @bytesize = @status.size @latest_modify_datetime = @status.mtime.strftime('%_m %e %H:%M') @blocks = @status.blocks end - def build_file_status - { - type_and_mode: "#{@file_type}#{@file_mode}", - hardlink_nums: @hardlink_nums, - owner_name: @owner_name, - group_name: @group_name, - bytesize: @bytesize, - latest_modify_datetime: @latest_modify_datetime, - filename: @file_name, - blocks: @blocks - } - end - private - def format_file_type - @pathname.directory? ? 'd' : '-' + def format_file_type(path_name) + path_name.directory? ? 'd' : '-' end - def format_file_mode - @pathname.stat.mode.to_s(8)[-3..].gsub(/./, MODE_TABLE) + def format_file_mode(path_name) + path_name.stat.mode.to_s(8)[-3..].gsub(/./, MODE_TABLE) end end From 94d606d47e03fb3ac016c4aabc6c90b78e38f7a2 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Thu, 19 Sep 2024 18:55:35 +0900 Subject: [PATCH 23/41] update: argment name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 変数名をよりわかりやすく --- 07.ls_object/lib/format/long_format.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/07.ls_object/lib/format/long_format.rb b/07.ls_object/lib/format/long_format.rb index 31022b93b5..9be1f2f317 100644 --- a/07.ls_object/lib/format/long_format.rb +++ b/07.ls_object/lib/format/long_format.rb @@ -31,19 +31,19 @@ def find_max_size(key) @files.map { |file| file.file_status[key].size }.max end - def format_row(data, max_nlink, max_user, max_group, max_size) + def format_row(status, max_nlink, max_user, max_group, max_size) [ - data[:type_and_mode], - " #{data[:hardlink_nums].rjust(max_nlink)}", - " #{data[:owner_name].ljust(max_user)}", - " #{data[:group_name].ljust(max_group)}", - " #{data[:bytesize].rjust(max_size)}", - " #{data[:latest_modify_datetime]}", - " #{data[:filename]}" + status[:type_and_mode], + " #{status[:hardlink_nums].rjust(max_nlink)}", + " #{status[:owner_name].ljust(max_user)}", + " #{status[:group_name].ljust(max_group)}", + " #{status[:bytesize].rjust(max_size)}", + " #{status[:latest_modify_datetime]}", + " #{status[:filename]}" ].join end def sum_blocks - @files.sum { |status| status.file_status[:blocks] } + @files.sum { |file| file.file_status[:blocks] } end end From 89c86705e06120dbbe1870e2df3f9828f5a3b6c3 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Thu, 19 Sep 2024 18:56:54 +0900 Subject: [PATCH 24/41] fix: render MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - renderがメソッドを呼ぶだけのメソッドになっていたので処理を移す --- 07.ls_object/lib/format/short_format.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/07.ls_object/lib/format/short_format.rb b/07.ls_object/lib/format/short_format.rb index f53869b4b1..c7eead1ac0 100644 --- a/07.ls_object/lib/format/short_format.rb +++ b/07.ls_object/lib/format/short_format.rb @@ -12,7 +12,7 @@ def initialize(files, width) end def render - format_table(safe_transpose) + safe_transpose.map { |row_files| render_short_format_row(row_files) }.join("\n") end private @@ -30,13 +30,9 @@ def safe_transpose nested_file_names[0].zip(*nested_file_names[1..]) end - def format_table(file_paths) - file_paths.map { |row_files| render_short_format_row(row_files) }.join("\n") - end - def render_short_format_row(row_files) - row_files.map do |file_path| - basename = file_path ? File.basename(file_path.name) : '' + row_files.map do |files| + basename = files ? File.basename(files.name) : '' basename.ljust(@max_file_name + 1) end.join.rstrip end From 8bd1de4f60117a85e9eb80062dd9ac7aaaafba9a Mon Sep 17 00:00:00 2001 From: yokomaru Date: Thu, 19 Sep 2024 18:57:50 +0900 Subject: [PATCH 25/41] update: test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - widthのテストを追加 - テストファイルを保持するフォルダの名前を変更+移動 --- 07.ls_object/test/ls_command_test.rb | 88 ++++++++++++++----- .../test_include_dir_and_dot_files}/.test | 0 .../test_include_dir_and_dot_files}/test.txt | 0 .../test_2.txt | 0 .../test_dir/test_one_file}/test.txt | 0 07.ls_object/test_dir3/test.txt | 1 - 07.ls_object/test_dir3/test_2.txt | 1 - 7 files changed, 64 insertions(+), 26 deletions(-) rename 07.ls_object/{test_dir3 => test/test_dir/test_include_dir_and_dot_files}/.test (100%) rename 07.ls_object/{test_dir => test/test_dir/test_include_dir_and_dot_files}/test.txt (100%) rename 07.ls_object/{test_dir2 => test/test_dir/test_include_dir_and_dot_files}/test_2.txt (100%) rename 07.ls_object/{test_dir2 => test/test_dir/test_one_file}/test.txt (100%) delete mode 100644 07.ls_object/test_dir3/test.txt delete mode 100644 07.ls_object/test_dir3/test_2.txt diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb index ac667ac79b..1c7d2f4f4e 100644 --- a/07.ls_object/test/ls_command_test.rb +++ b/07.ls_object/test/ls_command_test.rb @@ -5,41 +5,36 @@ class LsCommandTest < Minitest::Test def test_display_a_file - ls_command = LsCommand.new('test_dir') + ls_command = LsCommand.new('test/test_dir/test_one_file') assert_equal 'test.txt', ls_command.display end - def test_display_two_files - ls_command = LsCommand.new('test_dir2') - assert_equal 'test.txt test_2.txt', ls_command.display - end - - def test_display_dot_file - ls_command = LsCommand.new('test_dir3') + def test_display_files + ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files') assert_equal 'dir test.txt test_2.txt', ls_command.display end - def test_display_match_dot_file + def test_display_match_dot_files params = { dot_match: true } - ls_command = LsCommand.new('test_dir3', **params) + ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', **params) assert_equal '. .. .test dir test.txt test_2.txt', ls_command.display end - def test_display_reverse_sort_file + def test_display_reverse_sort_files params = { reverse: true, dot_match: false } - ls_command = LsCommand.new('test_dir3', **params) + ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', **params) assert_equal 'test_2.txt test.txt dir', ls_command.display end def test_display_dot_match_and_reverse_sort_file params = { reverse: true, dot_match: true } - ls_command = LsCommand.new('test_dir3', **params) + ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', **params) assert_equal 'test_2.txt test.txt dir .test .. .', ls_command.display end def test_display_long_format_file params = { long_format: true } - ls_command = LsCommand.new('test_dir', **params) + ls_command = LsCommand.new('test/test_dir/test_one_file', **params) expected = <<~LS_RESULT.chomp total 8 -rw-r--r-- 1 suzukiyouko staff 10 9 15 14:34 test.txt @@ -49,26 +44,71 @@ def test_display_long_format_file def test_display_long_format_and_dot_match_file params = { long_format: true, dot_match: true } - ls_command = LsCommand.new('test_dir2', **params) + ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', **params) expected = <<~LS_RESULT.chomp total 16 - drwxr-xr-x 4 suzukiyouko staff 128 9 15 15:30 . - drwxr-xr-x 10 suzukiyouko staff 320 9 16 15:14 .. - -rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test.txt - -rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test_2.txt + drwxr-xr-x 6 suzukiyouko staff 192 9 16 15:18 . + drwxr-xr-x 5 suzukiyouko staff 160 9 19 18:20 .. + -rw-r--r-- 1 suzukiyouko staff 0 9 16 15:14 .test + drwxr-xr-x 2 suzukiyouko staff 64 9 16 15:18 dir + -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test.txt + -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test_2.txt LS_RESULT assert_equal expected, ls_command.display end def test_display_long_format_and_dot_match_and_reverse_file params = { reverse: true, long_format: true, dot_match: true } - ls_command = LsCommand.new('test_dir2', **params) + ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', **params) expected = <<~LS_RESULT.chomp total 16 - -rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test_2.txt - -rw-r--r-- 1 suzukiyouko staff 10 9 15 15:30 test.txt - drwxr-xr-x 10 suzukiyouko staff 320 9 16 15:14 .. - drwxr-xr-x 4 suzukiyouko staff 128 9 15 15:30 . + -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test_2.txt + -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test.txt + drwxr-xr-x 2 suzukiyouko staff 64 9 16 15:18 dir + -rw-r--r-- 1 suzukiyouko staff 0 9 16 15:14 .test + drwxr-xr-x 5 suzukiyouko staff 160 9 19 18:20 .. + drwxr-xr-x 6 suzukiyouko staff 192 9 16 15:18 . + LS_RESULT + assert_equal expected, ls_command.display + end + + def test_display_width_eighty + params = { dot_match: true } + ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', **params) + assert_equal '. .. .test dir test.txt test_2.txt', ls_command.display + end + + def test_display_width_fourty + params = { dot_match: true } + ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', width: 40, **params) + expected = <<~LS_RESULT.chomp + . .test test.txt + .. dir test_2.txt + LS_RESULT + assert_equal expected, ls_command.display + end + + def test_display_width_thirty + params = { dot_match: true } + ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', width: 30, **params) + expected = <<~LS_RESULT.chomp + . dir + .. test.txt + .test test_2.txt + LS_RESULT + assert_equal expected, ls_command.display + end + + def test_display_width_twenty + params = { dot_match: true } + ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', width: 20, **params) + expected = <<~LS_RESULT.chomp + . + .. + .test + dir + test.txt + test_2.txt LS_RESULT assert_equal expected, ls_command.display end diff --git a/07.ls_object/test_dir3/.test b/07.ls_object/test/test_dir/test_include_dir_and_dot_files/.test similarity index 100% rename from 07.ls_object/test_dir3/.test rename to 07.ls_object/test/test_dir/test_include_dir_and_dot_files/.test diff --git a/07.ls_object/test_dir/test.txt b/07.ls_object/test/test_dir/test_include_dir_and_dot_files/test.txt similarity index 100% rename from 07.ls_object/test_dir/test.txt rename to 07.ls_object/test/test_dir/test_include_dir_and_dot_files/test.txt diff --git a/07.ls_object/test_dir2/test_2.txt b/07.ls_object/test/test_dir/test_include_dir_and_dot_files/test_2.txt similarity index 100% rename from 07.ls_object/test_dir2/test_2.txt rename to 07.ls_object/test/test_dir/test_include_dir_and_dot_files/test_2.txt diff --git a/07.ls_object/test_dir2/test.txt b/07.ls_object/test/test_dir/test_one_file/test.txt similarity index 100% rename from 07.ls_object/test_dir2/test.txt rename to 07.ls_object/test/test_dir/test_one_file/test.txt diff --git a/07.ls_object/test_dir3/test.txt b/07.ls_object/test_dir3/test.txt deleted file mode 100644 index 910898e128..0000000000 --- a/07.ls_object/test_dir3/test.txt +++ /dev/null @@ -1 +0,0 @@ -testです \ No newline at end of file diff --git a/07.ls_object/test_dir3/test_2.txt b/07.ls_object/test_dir3/test_2.txt deleted file mode 100644 index 910898e128..0000000000 --- a/07.ls_object/test_dir3/test_2.txt +++ /dev/null @@ -1 +0,0 @@ -testです \ No newline at end of file From f2c5f567752ea98961f5d3b70fbe7a74a6144bab Mon Sep 17 00:00:00 2001 From: yokomaru Date: Thu, 19 Sep 2024 19:00:07 +0900 Subject: [PATCH 26/41] fix: require MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 不要な組み込みクラスを削除 - 使用しているクラスに移動 --- 07.ls_object/lib/file/file_data.rb | 2 +- 07.ls_object/lib/file/file_status.rb | 1 - 07.ls_object/lib/ls_command.rb | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/07.ls_object/lib/file/file_data.rb b/07.ls_object/lib/file/file_data.rb index 292010b90a..ac69c92272 100755 --- a/07.ls_object/lib/file/file_data.rb +++ b/07.ls_object/lib/file/file_data.rb @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require 'etc' +require 'pathname' require_relative 'file_status' class FileData diff --git a/07.ls_object/lib/file/file_status.rb b/07.ls_object/lib/file/file_status.rb index 26f4c37d9c..ade9e11533 100755 --- a/07.ls_object/lib/file/file_status.rb +++ b/07.ls_object/lib/file/file_status.rb @@ -2,7 +2,6 @@ # frozen_string_literal: true require 'etc' -require 'pathname' class FileStatus MODE_TABLE = { diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index 980f8cf6fa..71d968dce0 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'debug' require_relative 'file/file_data' require_relative 'option/match_option' require_relative 'option/format_option' From 589fa086ef2ad349aa645de7257ece557cdb976a Mon Sep 17 00:00:00 2001 From: yokomaru Date: Thu, 19 Sep 2024 19:20:44 +0900 Subject: [PATCH 27/41] add: require 'io/console' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 誤って削除してしまっていたため、`require 'io/console'`を追加 --- 07.ls_object/bin/ls.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/07.ls_object/bin/ls.rb b/07.ls_object/bin/ls.rb index bbfb789d98..4f4782ea1c 100755 --- a/07.ls_object/bin/ls.rb +++ b/07.ls_object/bin/ls.rb @@ -1,6 +1,7 @@ #!/usr/bin/env ruby # frozen_string_literal: true +require 'io/console' require 'optparse' require_relative '../lib/ls_command' From 534db113c12772d6c006e9be07569355e9e020c2 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Thu, 19 Sep 2024 19:40:53 +0900 Subject: [PATCH 28/41] add: instance variable short_format_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引数としてshort_format_dataを持っていなかったので持たせ、renderでjoinして表示するように修正 --- 07.ls_object/lib/format/short_format.rb | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/07.ls_object/lib/format/short_format.rb b/07.ls_object/lib/format/short_format.rb index c7eead1ac0..e3d86e886e 100644 --- a/07.ls_object/lib/format/short_format.rb +++ b/07.ls_object/lib/format/short_format.rb @@ -6,17 +6,29 @@ class ShortFormat < Format def initialize(files, width) super(files) @width = width - @max_file_name = @files.map { |f| File.basename(f.name).size }.max + @max_file_name = @files.map { |file| File.basename(file.name).size }.max @col_count = calculate_col_count @row_count = calculate_row_count + @short_format_data = build_short_format_data end def render - safe_transpose.map { |row_files| render_short_format_row(row_files) }.join("\n") + @short_format_data.join("\n") end private + def build_short_format_data + safe_transpose.map { |row_files| build_short_format_row(row_files) } + end + + def build_short_format_row(row_files) + row_files.map do |files| + basename = files ? File.basename(files.name) : '' + basename.ljust(@max_file_name + 1) + end.join.rstrip + end + def calculate_col_count @width / (@max_file_name + 1) end @@ -29,11 +41,4 @@ def safe_transpose nested_file_names = @files.each_slice(@row_count).to_a nested_file_names[0].zip(*nested_file_names[1..]) end - - def render_short_format_row(row_files) - row_files.map do |files| - basename = files ? File.basename(files.name) : '' - basename.ljust(@max_file_name + 1) - end.join.rstrip - end end From 6490225ace5f3d10fa13914e4166d58bddfe61ea Mon Sep 17 00:00:00 2001 From: yokomaru Date: Fri, 20 Sep 2024 09:35:16 +0900 Subject: [PATCH 29/41] fix: variable name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - pathで統一しているためpathnameになっているところを修正 - 単数のfileを扱う処理がfilesになっていたのでfileに修正 --- 07.ls_object/lib/file/file_status.rb | 8 ++++---- 07.ls_object/lib/format/short_format.rb | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/07.ls_object/lib/file/file_status.rb b/07.ls_object/lib/file/file_status.rb index ade9e11533..e6db8716bd 100755 --- a/07.ls_object/lib/file/file_status.rb +++ b/07.ls_object/lib/file/file_status.rb @@ -31,11 +31,11 @@ def initialize(path) private - def format_file_type(path_name) - path_name.directory? ? 'd' : '-' + def format_file_type(path) + path.directory? ? 'd' : '-' end - def format_file_mode(path_name) - path_name.stat.mode.to_s(8)[-3..].gsub(/./, MODE_TABLE) + def format_file_mode(path) + path.stat.mode.to_s(8)[-3..].gsub(/./, MODE_TABLE) end end diff --git a/07.ls_object/lib/format/short_format.rb b/07.ls_object/lib/format/short_format.rb index e3d86e886e..d0d5172b95 100644 --- a/07.ls_object/lib/format/short_format.rb +++ b/07.ls_object/lib/format/short_format.rb @@ -23,8 +23,8 @@ def build_short_format_data end def build_short_format_row(row_files) - row_files.map do |files| - basename = files ? File.basename(files.name) : '' + row_files.map do |file| + basename = file ? File.basename(file.name) : '' basename.ljust(@max_file_name + 1) end.join.rstrip end From 998c7210f43e29b9b15378e43e14bdaa4ac0dbd3 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Fri, 20 Sep 2024 09:36:53 +0900 Subject: [PATCH 30/41] fix: longformat test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - user情報など端末によって異なるため、実際のlsを実行しその結果と比較するように修正する --- 07.ls_object/test/ls_command_test.rb | 53 +++++++++++++++------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb index 1c7d2f4f4e..b470d5a8bd 100644 --- a/07.ls_object/test/ls_command_test.rb +++ b/07.ls_object/test/ls_command_test.rb @@ -33,42 +33,45 @@ def test_display_dot_match_and_reverse_sort_file end def test_display_long_format_file + # Output example + # total 8 + # -rw-r--r-- 1 suzukiyouko staff 10 9 15 14:34 test.txt + path = 'test/test_dir/test_one_file' params = { long_format: true } - ls_command = LsCommand.new('test/test_dir/test_one_file', **params) - expected = <<~LS_RESULT.chomp - total 8 - -rw-r--r-- 1 suzukiyouko staff 10 9 15 14:34 test.txt - LS_RESULT + expected = `ls -l #{path}`.chomp + ls_command = LsCommand.new(path, **params) assert_equal expected, ls_command.display end def test_display_long_format_and_dot_match_file + # Output example + # total 16 + # drwxr-xr-x 6 suzukiyouko staff 192 9 16 15:18 . + # drwxr-xr-x 5 suzukiyouko staff 160 9 19 18:20 .. + # -rw-r--r-- 1 suzukiyouko staff 0 9 16 15:14 .test + # drwxr-xr-x 2 suzukiyouko staff 64 9 16 15:18 dir + # -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test.txt + # -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test_2.txt + path = 'test/test_dir/test_include_dir_and_dot_files' params = { long_format: true, dot_match: true } - ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', **params) - expected = <<~LS_RESULT.chomp - total 16 - drwxr-xr-x 6 suzukiyouko staff 192 9 16 15:18 . - drwxr-xr-x 5 suzukiyouko staff 160 9 19 18:20 .. - -rw-r--r-- 1 suzukiyouko staff 0 9 16 15:14 .test - drwxr-xr-x 2 suzukiyouko staff 64 9 16 15:18 dir - -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test.txt - -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test_2.txt - LS_RESULT + expected = `ls -al #{path}`.chomp + ls_command = LsCommand.new(path, **params) assert_equal expected, ls_command.display end def test_display_long_format_and_dot_match_and_reverse_file + # Output example + # total 16 + # -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test_2.txt + # -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test.txt + # drwxr-xr-x 2 suzukiyouko staff 64 9 16 15:18 dir + # -rw-r--r-- 1 suzukiyouko staff 0 9 16 15:14 .test + # drwxr-xr-x 5 suzukiyouko staff 160 9 19 18:20 .. + # drwxr-xr-x 6 suzukiyouko staff 192 9 16 15:18 . params = { reverse: true, long_format: true, dot_match: true } - ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', **params) - expected = <<~LS_RESULT.chomp - total 16 - -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test_2.txt - -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test.txt - drwxr-xr-x 2 suzukiyouko staff 64 9 16 15:18 dir - -rw-r--r-- 1 suzukiyouko staff 0 9 16 15:14 .test - drwxr-xr-x 5 suzukiyouko staff 160 9 19 18:20 .. - drwxr-xr-x 6 suzukiyouko staff 192 9 16 15:18 . - LS_RESULT + path = 'test/test_dir/test_include_dir_and_dot_files' + expected = `ls -arl #{path}`.chomp + ls_command = LsCommand.new(path, **params) assert_equal expected, ls_command.display end From ea4d0557d88202d8f1ef5fe514e18a259446efaf Mon Sep 17 00:00:00 2001 From: yokomaru Date: Mon, 30 Sep 2024 11:03:07 +0900 Subject: [PATCH 31/41] delete: Option classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - オブジェクトとしてオプションがファイルを操作するのは違和感があるので、クラス自体を削除しLsCommadnクラス内でパラメーターで分岐して処理を行う --- 07.ls_object/lib/ls_command.rb | 11 +++++------ 07.ls_object/lib/option/format_option.rb | 20 -------------------- 07.ls_object/lib/option/match_option.rb | 9 --------- 07.ls_object/lib/option/option.rb | 10 ---------- 07.ls_object/lib/option/sort_option.rb | 9 --------- 5 files changed, 5 insertions(+), 54 deletions(-) delete mode 100644 07.ls_object/lib/option/format_option.rb delete mode 100644 07.ls_object/lib/option/match_option.rb delete mode 100644 07.ls_object/lib/option/option.rb delete mode 100644 07.ls_object/lib/option/sort_option.rb diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index 71d968dce0..8bf89fb492 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -1,9 +1,8 @@ # frozen_string_literal: true require_relative 'file/file_data' -require_relative 'option/match_option' -require_relative 'option/format_option' -require_relative 'option/sort_option' +require_relative 'format/long_format' +require_relative 'format/short_format' class LsCommand def initialize(path, width: 80, dot_match: false, reverse: false, long_format: false) @@ -16,14 +15,14 @@ def initialize(path, width: 80, dot_match: false, reverse: false, long_format: f end def display - FormatOption.new(@files, @long_format, @width).execute + @long_format ? LongFormat.new(@files).render : ShortFormatter.new(@files, @width).render end private def build_files files = Dir.open(@path).entries.map { |name| FileData.new(name, @path) } - matched_files = MatchOption.new(files, @dot_match).execute - SortOption.new(matched_files, @reverse).execute + matched_files = @dot_match ? files : files.filter { |file| !/^\./.match?(file.name) } + @reverse ? matched_files.sort_by(&:name).reverse : matched_files.sort_by(&:name) end end diff --git a/07.ls_object/lib/option/format_option.rb b/07.ls_object/lib/option/format_option.rb deleted file mode 100644 index 7d950dcc17..0000000000 --- a/07.ls_object/lib/option/format_option.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -require_relative 'option' -require_relative '../format/long_format' -require_relative '../format/short_format' - -class FormatOption < Option - def initialize(files, option, width) - super(files, option) - @width = width - end - - def execute - if @option - LongFormat.new(@files).render - else - ShortFormat.new(@files, @width).render - end - end -end diff --git a/07.ls_object/lib/option/match_option.rb b/07.ls_object/lib/option/match_option.rb deleted file mode 100644 index e452fed8fc..0000000000 --- a/07.ls_object/lib/option/match_option.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -require_relative 'option' - -class MatchOption < Option - def execute - @option ? @files : @files.filter { |file| !/^\./.match?(file.name) } - end -end diff --git a/07.ls_object/lib/option/option.rb b/07.ls_object/lib/option/option.rb deleted file mode 100644 index df1fe316b7..0000000000 --- a/07.ls_object/lib/option/option.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -class Option - def initialize(files, option) - @files = files - @option = option - end - - def execute; end -end diff --git a/07.ls_object/lib/option/sort_option.rb b/07.ls_object/lib/option/sort_option.rb deleted file mode 100644 index 822305fa13..0000000000 --- a/07.ls_object/lib/option/sort_option.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -require_relative 'option' - -class SortOption < Option - def execute - @option ? @files.sort_by(&:name).reverse : @files.sort_by(&:name) - end -end From ddaeb3cd9168298e5f72daee25495835a6a8d97e Mon Sep 17 00:00:00 2001 From: yokomaru Date: Mon, 30 Sep 2024 11:09:51 +0900 Subject: [PATCH 32/41] fix: rename Format to Formatter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FormatクラスはFormatそのものではなく、ファイルのフォーマットを整えるクラスのためFormatterという名称に修正&継承しているクラスも統一する - Formatterクラスでrender処理するのは想定外の動きになってしまうのでメソッド名を formatに変更 --- 07.ls_object/lib/format/{format.rb => formatter.rb} | 2 +- .../lib/format/{long_format.rb => long_formatter.rb} | 6 +++--- .../lib/format/{short_format.rb => short_formatter.rb} | 6 +++--- 07.ls_object/lib/ls_command.rb | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) rename 07.ls_object/lib/format/{format.rb => formatter.rb} (86%) rename 07.ls_object/lib/format/{long_format.rb => long_formatter.rb} (93%) rename 07.ls_object/lib/format/{short_format.rb => short_formatter.rb} (92%) diff --git a/07.ls_object/lib/format/format.rb b/07.ls_object/lib/format/formatter.rb similarity index 86% rename from 07.ls_object/lib/format/format.rb rename to 07.ls_object/lib/format/formatter.rb index 92e3ba10e0..9de0048cbf 100644 --- a/07.ls_object/lib/format/format.rb +++ b/07.ls_object/lib/format/formatter.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Format +class Formatter def initialize(files) @files = files end diff --git a/07.ls_object/lib/format/long_format.rb b/07.ls_object/lib/format/long_formatter.rb similarity index 93% rename from 07.ls_object/lib/format/long_format.rb rename to 07.ls_object/lib/format/long_formatter.rb index 9be1f2f317..8db8a97bba 100644 --- a/07.ls_object/lib/format/long_format.rb +++ b/07.ls_object/lib/format/long_formatter.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative 'format' +require_relative 'formatter' -class LongFormat < Format +class LongFormatter < Formatter def initialize(files) super(files) @max_sizes = build_max_sizes @@ -10,7 +10,7 @@ def initialize(files) @long_format_data = build_long_format_data end - def render + def format total = "total #{@total_block}" [total, *@long_format_data].join("\n") end diff --git a/07.ls_object/lib/format/short_format.rb b/07.ls_object/lib/format/short_formatter.rb similarity index 92% rename from 07.ls_object/lib/format/short_format.rb rename to 07.ls_object/lib/format/short_formatter.rb index d0d5172b95..2119ac87aa 100644 --- a/07.ls_object/lib/format/short_format.rb +++ b/07.ls_object/lib/format/short_formatter.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require_relative 'format' +require_relative 'formatter' -class ShortFormat < Format +class ShortFormatter < Formatter def initialize(files, width) super(files) @width = width @@ -12,7 +12,7 @@ def initialize(files, width) @short_format_data = build_short_format_data end - def render + def format @short_format_data.join("\n") end diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index 8bf89fb492..50b0ce1dc9 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true require_relative 'file/file_data' -require_relative 'format/long_format' -require_relative 'format/short_format' +require_relative 'format/long_formatter' +require_relative 'format/short_formatter' class LsCommand def initialize(path, width: 80, dot_match: false, reverse: false, long_format: false) @@ -15,7 +15,7 @@ def initialize(path, width: 80, dot_match: false, reverse: false, long_format: f end def display - @long_format ? LongFormat.new(@files).render : ShortFormatter.new(@files, @width).render + @long_format ? LongFormatter.new(@files).format : ShortFormatter.new(@files, @width).format end private From a99e31fcff81f8dadef5d129d3835894dc3dad5a Mon Sep 17 00:00:00 2001 From: yokomaru Date: Mon, 30 Sep 2024 11:13:03 +0900 Subject: [PATCH 33/41] fix: integrate FileData and FileStatus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FileDataとFileStatusは1対1なのでクラスを分ける必要がないため、FileStatusを削除しFileDataにステータス関連の情報を生成する処理を統合する --- 07.ls_object/lib/file/file_data.rb | 39 +++++++++++++++++++------- 07.ls_object/lib/file/file_status.rb | 41 ---------------------------- 2 files changed, 29 insertions(+), 51 deletions(-) delete mode 100755 07.ls_object/lib/file/file_status.rb diff --git a/07.ls_object/lib/file/file_data.rb b/07.ls_object/lib/file/file_data.rb index ac69c92272..87c0755fc2 100755 --- a/07.ls_object/lib/file/file_data.rb +++ b/07.ls_object/lib/file/file_data.rb @@ -1,30 +1,49 @@ #!/usr/bin/env ruby # frozen_string_literal: true +require 'etc' require 'pathname' -require_relative 'file_status' class FileData attr_reader :name, :file_status + MODE_TABLE = { + '0' => '---', + '1' => '--x', + '2' => '-w-', + '3' => '-wx', + '4' => 'r--', + '5' => 'r-x', + '6' => 'rw-', + '7' => 'rwx' + }.freeze + def initialize(name, path) @name = name @full_path = Pathname(File.absolute_path(@name, path)) - @file_status = build_file_status(FileStatus.new(@full_path)) + @file_status = build_file_status(File::Stat.new(@full_path)) end private - def build_file_status(file_status) + def build_file_status(status) { - type_and_mode: "#{file_status.file_type}#{file_status.file_mode}", - hardlink_nums: file_status.hardlink_nums.to_s, - owner_name: file_status.owner_name, - group_name: file_status.group_name, - bytesize: file_status.bytesize.to_s, - latest_modify_datetime: file_status.latest_modify_datetime, + type_and_mode: "#{format_file_type}#{format_file_mode}", + hardlink_nums: status.nlink.to_s, + owner_name: Etc.getpwuid(status.uid).name, + group_name: Etc.getgrgid(status.gid).name, + bytesize: status.size.to_s, + latest_modify_datetime: status.mtime.strftime('%_m %e %H:%M'), filename: @name, - blocks: file_status.blocks + blocks: status.blocks } end + + def format_file_type + @full_path.directory? ? 'd' : '-' + end + + def format_file_mode + @full_path.stat.mode.to_s(8)[-3..].gsub(/./, MODE_TABLE) + end end diff --git a/07.ls_object/lib/file/file_status.rb b/07.ls_object/lib/file/file_status.rb deleted file mode 100755 index e6db8716bd..0000000000 --- a/07.ls_object/lib/file/file_status.rb +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require 'etc' - -class FileStatus - MODE_TABLE = { - '0' => '---', - '1' => '--x', - '2' => '-w-', - '3' => '-wx', - '4' => 'r--', - '5' => 'r-x', - '6' => 'rw-', - '7' => 'rwx' - }.freeze - - attr_reader :file_type, :file_mode, :hardlink_nums, :owner_name, :group_name, :bytesize, :latest_modify_datetime, :blocks - - def initialize(path) - @status = File::Stat.new(path) - @file_type = format_file_type(path) - @file_mode = format_file_mode(path) - @hardlink_nums = @status.nlink - @owner_name = Etc.getpwuid(@status.uid).name - @group_name = Etc.getgrgid(@status.gid).name - @bytesize = @status.size - @latest_modify_datetime = @status.mtime.strftime('%_m %e %H:%M') - @blocks = @status.blocks - end - - private - - def format_file_type(path) - path.directory? ? 'd' : '-' - end - - def format_file_mode(path) - path.stat.mode.to_s(8)[-3..].gsub(/./, MODE_TABLE) - end -end From 433e8ec3cd2abafaf1edeb812b4fa482ec48209b Mon Sep 17 00:00:00 2001 From: yokomaru Date: Mon, 30 Sep 2024 11:37:58 +0900 Subject: [PATCH 34/41] delete: test_file.txt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用していないテスト用のテキストファイルの削除 --- 07.ls_object/test_file.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 07.ls_object/test_file.txt diff --git a/07.ls_object/test_file.txt b/07.ls_object/test_file.txt deleted file mode 100644 index e69de29bb2..0000000000 From ed156587c79748cc46463642150735e862c83c9e Mon Sep 17 00:00:00 2001 From: yokomaru Date: Tue, 1 Oct 2024 22:39:35 +0900 Subject: [PATCH 35/41] fix: rename method name at Formatter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - renderをformatに修正(他クラスは修正ずみ) --- 07.ls_object/lib/format/formatter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/07.ls_object/lib/format/formatter.rb b/07.ls_object/lib/format/formatter.rb index 9de0048cbf..9a2579f38c 100644 --- a/07.ls_object/lib/format/formatter.rb +++ b/07.ls_object/lib/format/formatter.rb @@ -5,5 +5,5 @@ def initialize(files) @files = files end - def render; end + def format; end end From 645681ba61ab4061b554a48a2cb69f8dea5a8f6c Mon Sep 17 00:00:00 2001 From: yokomaru Date: Tue, 1 Oct 2024 22:40:58 +0900 Subject: [PATCH 36/41] fix: rename method name at LsCommand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - displayというメソッド名だが、描画は別クラスで行なっており、LsCommandではフォーマット済み文字列を出力するメソッドのため、formatted_outputに修正 --- 07.ls_object/bin/ls.rb | 2 +- 07.ls_object/lib/ls_command.rb | 2 +- 07.ls_object/test/ls_command_test.rb | 24 ++++++++++++------------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/07.ls_object/bin/ls.rb b/07.ls_object/bin/ls.rb index 4f4782ea1c..87e7cc6fa2 100755 --- a/07.ls_object/bin/ls.rb +++ b/07.ls_object/bin/ls.rb @@ -17,5 +17,5 @@ width = IO.console.winsize[1] ls = LsCommand.new(path, width:, **params) - puts ls.display + puts ls.formatted_output end diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index 50b0ce1dc9..50452b8265 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -14,7 +14,7 @@ def initialize(path, width: 80, dot_match: false, reverse: false, long_format: f @files = build_files end - def display + def formatted_output @long_format ? LongFormatter.new(@files).format : ShortFormatter.new(@files, @width).format end diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb index b470d5a8bd..dc345f464e 100644 --- a/07.ls_object/test/ls_command_test.rb +++ b/07.ls_object/test/ls_command_test.rb @@ -6,30 +6,30 @@ class LsCommandTest < Minitest::Test def test_display_a_file ls_command = LsCommand.new('test/test_dir/test_one_file') - assert_equal 'test.txt', ls_command.display + assert_equal 'test.txt', ls_command.formatted_output end def test_display_files ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files') - assert_equal 'dir test.txt test_2.txt', ls_command.display + assert_equal 'dir test.txt test_2.txt', ls_command.formatted_output end def test_display_match_dot_files params = { dot_match: true } ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', **params) - assert_equal '. .. .test dir test.txt test_2.txt', ls_command.display + assert_equal '. .. .test dir test.txt test_2.txt', ls_command.formatted_output end def test_display_reverse_sort_files params = { reverse: true, dot_match: false } ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', **params) - assert_equal 'test_2.txt test.txt dir', ls_command.display + assert_equal 'test_2.txt test.txt dir', ls_command.formatted_output end def test_display_dot_match_and_reverse_sort_file params = { reverse: true, dot_match: true } ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', **params) - assert_equal 'test_2.txt test.txt dir .test .. .', ls_command.display + assert_equal 'test_2.txt test.txt dir .test .. .', ls_command.formatted_output end def test_display_long_format_file @@ -40,7 +40,7 @@ def test_display_long_format_file params = { long_format: true } expected = `ls -l #{path}`.chomp ls_command = LsCommand.new(path, **params) - assert_equal expected, ls_command.display + assert_equal expected, ls_command.formatted_output end def test_display_long_format_and_dot_match_file @@ -56,7 +56,7 @@ def test_display_long_format_and_dot_match_file params = { long_format: true, dot_match: true } expected = `ls -al #{path}`.chomp ls_command = LsCommand.new(path, **params) - assert_equal expected, ls_command.display + assert_equal expected, ls_command.formatted_output end def test_display_long_format_and_dot_match_and_reverse_file @@ -72,13 +72,13 @@ def test_display_long_format_and_dot_match_and_reverse_file path = 'test/test_dir/test_include_dir_and_dot_files' expected = `ls -arl #{path}`.chomp ls_command = LsCommand.new(path, **params) - assert_equal expected, ls_command.display + assert_equal expected, ls_command.formatted_output end def test_display_width_eighty params = { dot_match: true } ls_command = LsCommand.new('test/test_dir/test_include_dir_and_dot_files', **params) - assert_equal '. .. .test dir test.txt test_2.txt', ls_command.display + assert_equal '. .. .test dir test.txt test_2.txt', ls_command.formatted_output end def test_display_width_fourty @@ -88,7 +88,7 @@ def test_display_width_fourty . .test test.txt .. dir test_2.txt LS_RESULT - assert_equal expected, ls_command.display + assert_equal expected, ls_command.formatted_output end def test_display_width_thirty @@ -99,7 +99,7 @@ def test_display_width_thirty .. test.txt .test test_2.txt LS_RESULT - assert_equal expected, ls_command.display + assert_equal expected, ls_command.formatted_output end def test_display_width_twenty @@ -113,6 +113,6 @@ def test_display_width_twenty test.txt test_2.txt LS_RESULT - assert_equal expected, ls_command.display + assert_equal expected, ls_command.formatted_output end end From b1254f5113c25e892e4f65447e7feacd6290413b Mon Sep 17 00:00:00 2001 From: yokomaru Date: Tue, 8 Oct 2024 09:01:31 +0900 Subject: [PATCH 37/41] add: special permission MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 特殊権限に関する処理とテストを追加 --- 07.ls_object/lib/file/file_data.rb | 49 ++++++++++++++++--- 07.ls_object/lib/format/long_formatter.rb | 2 +- 07.ls_object/test/ls_command_test.rb | 22 +++++++++ .../set_group_id_file.txt | 0 .../set_group_id_file_not_x_permission.txt | 0 .../set_user_id_file.txt | 0 .../set_user_id_file_not_x_permission.txt | 0 .../sticky_bit_file.txt | 0 .../sticky_bit_file_not_x_permission.txt | 0 9 files changed, 66 insertions(+), 7 deletions(-) create mode 100755 07.ls_object/test/test_dir/special_permission_dir/set_group_id_file.txt create mode 100755 07.ls_object/test/test_dir/special_permission_dir/set_group_id_file_not_x_permission.txt create mode 100755 07.ls_object/test/test_dir/special_permission_dir/set_user_id_file.txt create mode 100644 07.ls_object/test/test_dir/special_permission_dir/set_user_id_file_not_x_permission.txt create mode 100755 07.ls_object/test/test_dir/special_permission_dir/sticky_bit_file.txt create mode 100755 07.ls_object/test/test_dir/special_permission_dir/sticky_bit_file_not_x_permission.txt diff --git a/07.ls_object/lib/file/file_data.rb b/07.ls_object/lib/file/file_data.rb index 87c0755fc2..9ba8b89b3d 100755 --- a/07.ls_object/lib/file/file_data.rb +++ b/07.ls_object/lib/file/file_data.rb @@ -7,7 +7,22 @@ class FileData attr_reader :name, :file_status - MODE_TABLE = { + SPECIAL_PERMISSION_INDEX = 2 + OWNER_PERMISSION_INDEX = 3 + GROUP_PERMISSION_INDEX = 4 + OTHER_PERMISSION_INDEX = 5 + + FILE_TYPE = { + '01' => 'p', + '02' => 'c', + '04' => 'd', + '06' => 'b', + '10' => '-', + '12' => 'l', + '14' => 's' + }.freeze + + PERMISSION_TYPE = { '0' => '---', '1' => '--x', '2' => '-w-', @@ -18,6 +33,19 @@ class FileData '7' => 'rwx' }.freeze + SPECIAL_PERMISSION_TYPE = { + '0' => '-', + '1' => 't', + '2' => 's', + '4' => 's' + }.freeze + + TARGET_SPECIAL_PERMISSION = { + OWNER_PERMISSION_INDEX => '4', + GROUP_PERMISSION_INDEX => '2', + OTHER_PERMISSION_INDEX => '1' + }.freeze + def initialize(name, path) @name = name @full_path = Pathname(File.absolute_path(@name, path)) @@ -28,7 +56,7 @@ def initialize(name, path) def build_file_status(status) { - type_and_mode: "#{format_file_type}#{format_file_mode}", + filemode: filemode(status), hardlink_nums: status.nlink.to_s, owner_name: Etc.getpwuid(status.uid).name, group_name: Etc.getgrgid(status.gid).name, @@ -39,11 +67,20 @@ def build_file_status(status) } end - def format_file_type - @full_path.directory? ? 'd' : '-' + def filemode(status) + mode = status.mode.to_s(8).rjust(6, '0') + [OWNER_PERMISSION_INDEX, GROUP_PERMISSION_INDEX, OTHER_PERMISSION_INDEX].map do |index| + convert_permission(mode[index], mode[SPECIAL_PERMISSION_INDEX], TARGET_SPECIAL_PERMISSION[index]) + end.unshift(FILE_TYPE[mode[0, SPECIAL_PERMISSION_INDEX]]).join end - def format_file_mode - @full_path.stat.mode.to_s(8)[-3..].gsub(/./, MODE_TABLE) + def convert_permission(permission, special_permission, target_special_permission) + permission_type = PERMISSION_TYPE[permission] + + return permission_type if special_permission != target_special_permission + + special_permission_type = SPECIAL_PERMISSION_TYPE[special_permission] + special_permission_type = special_permission_type.upcase if permission.to_i.even? + [permission_type.chop, special_permission_type].join end end diff --git a/07.ls_object/lib/format/long_formatter.rb b/07.ls_object/lib/format/long_formatter.rb index 8db8a97bba..92ff18052e 100644 --- a/07.ls_object/lib/format/long_formatter.rb +++ b/07.ls_object/lib/format/long_formatter.rb @@ -33,7 +33,7 @@ def find_max_size(key) def format_row(status, max_nlink, max_user, max_group, max_size) [ - status[:type_and_mode], + status[:filemode], " #{status[:hardlink_nums].rjust(max_nlink)}", " #{status[:owner_name].ljust(max_user)}", " #{status[:group_name].ljust(max_group)}", diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb index dc345f464e..8919fd5f2a 100644 --- a/07.ls_object/test/ls_command_test.rb +++ b/07.ls_object/test/ls_command_test.rb @@ -115,4 +115,26 @@ def test_display_width_twenty LS_RESULT assert_equal expected, ls_command.formatted_output end + + def test_display_special_permission + # Output example + # total 0 + # -rwxr-sr-x 1 username staff 0 10 7 21:46 set_group_id_file.txt + # -rwxr-Sr-x 1 username staff 0 10 7 22:05 set_group_id_file_not_x_permission.txt + # -rwsr-xr-x 1 username staff 0 10 7 21:46 set_user_id_file.txt + # -r-Sr-xr-x 1 username staff 0 10 7 22:05 set_user_id_file_not_x_permission.txt + # -rwxr-xr-t 1 username staff 0 10 7 21:46 sticky_bit_file.txt + # -rwxr-xr-T 1 username staff 0 10 7 21:51 sticky_bit_file_not_x_permission.txt + params = { long_format: true } + path = 'test/test_dir/special_permission_dir' + `chmod 2755 #{path}/set_group_id_file.txt` # group permissionに実行権限xと特殊権限を付与 + `chmod 2745 #{path}/set_group_id_file_not_x_permission.txt` # group permissionの実行権限xを外し特殊権限を付与 + `chmod 4755 #{path}/set_user_id_file.txt` # owner permissionに実行権限xと特殊権限を付与 + `chmod 4455 #{path}/set_user_id_file_not_x_permission.txt` # owner permissionの実行権限xを外し特殊権限を付与 + `chmod 1755 #{path}/sticky_bit_file.txt` # other permissionに実行権限xと特殊権限を付与 + `chmod 1754 #{path}/sticky_bit_file_not_x_permission.txt` # other permissionの実行権限xを外し特殊権限を付与 + expected = `ls -l #{path}`.chomp + ls_command = LsCommand.new(path, **params) + assert_equal expected, ls_command.formatted_output + end end diff --git a/07.ls_object/test/test_dir/special_permission_dir/set_group_id_file.txt b/07.ls_object/test/test_dir/special_permission_dir/set_group_id_file.txt new file mode 100755 index 0000000000..e69de29bb2 diff --git a/07.ls_object/test/test_dir/special_permission_dir/set_group_id_file_not_x_permission.txt b/07.ls_object/test/test_dir/special_permission_dir/set_group_id_file_not_x_permission.txt new file mode 100755 index 0000000000..e69de29bb2 diff --git a/07.ls_object/test/test_dir/special_permission_dir/set_user_id_file.txt b/07.ls_object/test/test_dir/special_permission_dir/set_user_id_file.txt new file mode 100755 index 0000000000..e69de29bb2 diff --git a/07.ls_object/test/test_dir/special_permission_dir/set_user_id_file_not_x_permission.txt b/07.ls_object/test/test_dir/special_permission_dir/set_user_id_file_not_x_permission.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/07.ls_object/test/test_dir/special_permission_dir/sticky_bit_file.txt b/07.ls_object/test/test_dir/special_permission_dir/sticky_bit_file.txt new file mode 100755 index 0000000000..e69de29bb2 diff --git a/07.ls_object/test/test_dir/special_permission_dir/sticky_bit_file_not_x_permission.txt b/07.ls_object/test/test_dir/special_permission_dir/sticky_bit_file_not_x_permission.txt new file mode 100755 index 0000000000..e69de29bb2 From 71ece3bed6edfdfc71dee562cec6a378b86a2d2b Mon Sep 17 00:00:00 2001 From: yokomaru Date: Tue, 8 Oct 2024 09:32:59 +0900 Subject: [PATCH 38/41] add: filetype MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 様々なfiletypeに対応する実装とテストを追加 --- 07.ls_object/lib/file/file_data.rb | 31 +++++++----- 07.ls_object/lib/format/long_formatter.rb | 9 ++-- 07.ls_object/lib/ls_command.rb | 16 +++++-- 07.ls_object/test/ls_command_test.rb | 47 +++++++++++++++++++ .../test/test_dir/file_type_dir/test_file.txt | 0 .../test/test_dir/file_type_dir/test_link | 1 + .../test/test_dir/link_dir/link_test.txt | 1 + 7 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 07.ls_object/test/test_dir/file_type_dir/test_file.txt create mode 120000 07.ls_object/test/test_dir/file_type_dir/test_link create mode 100644 07.ls_object/test/test_dir/link_dir/link_test.txt diff --git a/07.ls_object/lib/file/file_data.rb b/07.ls_object/lib/file/file_data.rb index 9ba8b89b3d..6b3787a8b3 100755 --- a/07.ls_object/lib/file/file_data.rb +++ b/07.ls_object/lib/file/file_data.rb @@ -49,31 +49,25 @@ class FileData def initialize(name, path) @name = name @full_path = Pathname(File.absolute_path(@name, path)) - @file_status = build_file_status(File::Stat.new(@full_path)) + @file_type = File.ftype(@full_path) + @file_status = build_file_status(File.lstat(@full_path)) end private def build_file_status(status) { - filemode: filemode(status), + type_and_mode: type_and_mode(status), hardlink_nums: status.nlink.to_s, owner_name: Etc.getpwuid(status.uid).name, group_name: Etc.getgrgid(status.gid).name, - bytesize: status.size.to_s, + bytesize: generate_rdev_or_bytesize(status), latest_modify_datetime: status.mtime.strftime('%_m %e %H:%M'), - filename: @name, + filename: generate_file_name(status), blocks: status.blocks } end - def filemode(status) - mode = status.mode.to_s(8).rjust(6, '0') - [OWNER_PERMISSION_INDEX, GROUP_PERMISSION_INDEX, OTHER_PERMISSION_INDEX].map do |index| - convert_permission(mode[index], mode[SPECIAL_PERMISSION_INDEX], TARGET_SPECIAL_PERMISSION[index]) - end.unshift(FILE_TYPE[mode[0, SPECIAL_PERMISSION_INDEX]]).join - end - def convert_permission(permission, special_permission, target_special_permission) permission_type = PERMISSION_TYPE[permission] @@ -83,4 +77,19 @@ def convert_permission(permission, special_permission, target_special_permission special_permission_type = special_permission_type.upcase if permission.to_i.even? [permission_type.chop, special_permission_type].join end + + def generate_file_name(status) + status.symlink? ? "#{@name} -> #{File.readlink(@full_path)}" : @name + end + + def generate_rdev_or_bytesize(status) + %w[characterSpecial blockSpecial].include?(@file_type) ? format('%#01x', status.rdev.to_s(10)) : status.size.to_s + end + + def type_and_mode(status) + mode = status.mode.to_s(8).rjust(6, '0') + [OWNER_PERMISSION_INDEX, GROUP_PERMISSION_INDEX, OTHER_PERMISSION_INDEX].map do |index| + convert_permission(mode[index], mode[SPECIAL_PERMISSION_INDEX], TARGET_SPECIAL_PERMISSION[index]) + end.unshift(FILE_TYPE[mode[0, SPECIAL_PERMISSION_INDEX]]).join + end end diff --git a/07.ls_object/lib/format/long_formatter.rb b/07.ls_object/lib/format/long_formatter.rb index 92ff18052e..67b9790371 100644 --- a/07.ls_object/lib/format/long_formatter.rb +++ b/07.ls_object/lib/format/long_formatter.rb @@ -3,16 +3,17 @@ require_relative 'formatter' class LongFormatter < Formatter - def initialize(files) + def initialize(files, path) super(files) + @path = path @max_sizes = build_max_sizes @total_block = sum_blocks @long_format_data = build_long_format_data end def format - total = "total #{@total_block}" - [total, *@long_format_data].join("\n") + total = "total #{@total_block}" if File.directory?(@path) || @files.size > 1 + [total, *@long_format_data].compact.join("\n") end private @@ -33,7 +34,7 @@ def find_max_size(key) def format_row(status, max_nlink, max_user, max_group, max_size) [ - status[:filemode], + status[:type_and_mode], " #{status[:hardlink_nums].rjust(max_nlink)}", " #{status[:owner_name].ljust(max_user)}", " #{status[:group_name].ljust(max_group)}", diff --git a/07.ls_object/lib/ls_command.rb b/07.ls_object/lib/ls_command.rb index 50452b8265..6ad6e0940e 100644 --- a/07.ls_object/lib/ls_command.rb +++ b/07.ls_object/lib/ls_command.rb @@ -11,18 +11,26 @@ def initialize(path, width: 80, dot_match: false, reverse: false, long_format: f @dot_match = dot_match @reverse = reverse @long_format = long_format - @files = build_files + @files = build_dot_match_and_sorted_files end def formatted_output - @long_format ? LongFormatter.new(@files).format : ShortFormatter.new(@files, @width).format + @long_format ? LongFormatter.new(@files, @path).format : ShortFormatter.new(@files, @width).format end private - def build_files - files = Dir.open(@path).entries.map { |name| FileData.new(name, @path) } + def build_dot_match_and_sorted_files + files = generate_files matched_files = @dot_match ? files : files.filter { |file| !/^\./.match?(file.name) } @reverse ? matched_files.sort_by(&:name).reverse : matched_files.sort_by(&:name) end + + def generate_files + if File.directory?(@path) + Dir.open(@path).entries.map { |name| FileData.new(name, @path) } + else + [FileData.new(@path, File.dirname(@path))] + end + end end diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb index 8919fd5f2a..37e9be43bb 100644 --- a/07.ls_object/test/ls_command_test.rb +++ b/07.ls_object/test/ls_command_test.rb @@ -137,4 +137,51 @@ def test_display_special_permission ls_command = LsCommand.new(path, **params) assert_equal expected, ls_command.formatted_output end + + def test_display_some_file_types + # Output example + # total 0 + # drwxr-xr-x 2 suzukiyouko staff 64 10 7 23:12 test_dir + # -rw-r--r-- 1 suzukiyouko staff 0 10 7 23:07 test_file.txt + # lrwxr-xr-x 1 suzukiyouko staff 102 10 7 22:59 test_link -> ../07.ls_object/test/test_dir/link_dir/link_test.txt # フルパスのため省略 + # prw-r--r-- 1 suzukiyouko staff 0 10 8 01:17 testfile + params = { long_format: true } + path = 'test/test_dir/file_type_dir' + # パイプファイルの作成 + `mkfifo test/test_dir/file_type_dir/test_fifo_file` unless File.exist?('test/test_dir/file_type_dir/test_fifo_file') + expected = `ls -l #{path}`.chomp + ls_command = LsCommand.new(path, **params) + assert_equal expected, ls_command.formatted_output + end + + def test_display_block_device_file_type + # Output example + # brw-r----- 1 root operator 0x1000004 9 8 09:43 /dev/disk1 + params = { long_format: true } + path = '/dev/disk1' + expected = `ls -l #{path}`.chomp + ls_command = LsCommand.new(path, **params) + assert_equal expected, ls_command.formatted_output + end + + def test_display_character_device_file_file_type + # Output example + # total 0 + # crw-rw-rw- 1 root wheel 0x3000002 10 8 01:26 /dev/null + params = { long_format: true } + path = '/dev/null' + expected = `ls -l #{path}`.chomp + ls_command = LsCommand.new(path, **params) + assert_equal expected, ls_command.formatted_output + end + + def test_display_local_domain_sockets_file_type + # Output example + # srw------- 1 root daemon 0 9 8 09:43 /var/run/vpncontrol.sock + params = { long_format: true } + path = '/var/run/vpncontrol.sock' + expected = `ls -l #{path}`.chomp + ls_command = LsCommand.new(path, **params) + assert_equal expected, ls_command.formatted_output + end end diff --git a/07.ls_object/test/test_dir/file_type_dir/test_file.txt b/07.ls_object/test/test_dir/file_type_dir/test_file.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/07.ls_object/test/test_dir/file_type_dir/test_link b/07.ls_object/test/test_dir/file_type_dir/test_link new file mode 120000 index 0000000000..bcc6714554 --- /dev/null +++ b/07.ls_object/test/test_dir/file_type_dir/test_link @@ -0,0 +1 @@ +/Users/suzukiyouko/dev/fjold-bootcamp/ruby-practices/07.ls_object/test/test_dir/link_dir/link_test.txt \ No newline at end of file diff --git a/07.ls_object/test/test_dir/link_dir/link_test.txt b/07.ls_object/test/test_dir/link_dir/link_test.txt new file mode 100644 index 0000000000..4b0a21d6fc --- /dev/null +++ b/07.ls_object/test/test_dir/link_dir/link_test.txt @@ -0,0 +1 @@ +testtest \ No newline at end of file From 1dfdf57580e192586a7ed04fcb733f7234712c61 Mon Sep 17 00:00:00 2001 From: yokomaru Date: Tue, 8 Oct 2024 09:58:22 +0900 Subject: [PATCH 39/41] fix: test symbolic link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - フルパス指定だったSymbolicLinkを相対パスに修正 --- 07.ls_object/test/ls_command_test.rb | 2 ++ 07.ls_object/test/test_dir/file_type_dir/link_test | 1 + 07.ls_object/test/test_dir/file_type_dir/test_link | 1 - 3 files changed, 3 insertions(+), 1 deletion(-) create mode 120000 07.ls_object/test/test_dir/file_type_dir/link_test delete mode 120000 07.ls_object/test/test_dir/file_type_dir/test_link diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb index 37e9be43bb..994e8b92d8 100644 --- a/07.ls_object/test/ls_command_test.rb +++ b/07.ls_object/test/ls_command_test.rb @@ -149,6 +149,8 @@ def test_display_some_file_types path = 'test/test_dir/file_type_dir' # パイプファイルの作成 `mkfifo test/test_dir/file_type_dir/test_fifo_file` unless File.exist?('test/test_dir/file_type_dir/test_fifo_file') + # シンボリックリンクの作成 + `ln -s /test/test_dir/link_dir/link_test.txt test/test_dir/file_type_dir/link_test` unless File.exist?('test/test_dir/file_type_dir/link_test') expected = `ls -l #{path}`.chomp ls_command = LsCommand.new(path, **params) assert_equal expected, ls_command.formatted_output diff --git a/07.ls_object/test/test_dir/file_type_dir/link_test b/07.ls_object/test/test_dir/file_type_dir/link_test new file mode 120000 index 0000000000..4fb2b3f711 --- /dev/null +++ b/07.ls_object/test/test_dir/file_type_dir/link_test @@ -0,0 +1 @@ +/test/test_dir/link_dir/link_test.txt \ No newline at end of file diff --git a/07.ls_object/test/test_dir/file_type_dir/test_link b/07.ls_object/test/test_dir/file_type_dir/test_link deleted file mode 120000 index bcc6714554..0000000000 --- a/07.ls_object/test/test_dir/file_type_dir/test_link +++ /dev/null @@ -1 +0,0 @@ -/Users/suzukiyouko/dev/fjold-bootcamp/ruby-practices/07.ls_object/test/test_dir/link_dir/link_test.txt \ No newline at end of file From 10f613bd792e065c1a7f2e1c70acd3ec653184cb Mon Sep 17 00:00:00 2001 From: yokomaru Date: Tue, 8 Oct 2024 10:02:02 +0900 Subject: [PATCH 40/41] fix: make symbolic link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - フルパス指定じゃないとうまくリンクされないと思って都度シンボリックリンクを作成するようにしていたが、相対パスでシンボリックリンクを作れたので削除 --- 07.ls_object/test/ls_command_test.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb index 994e8b92d8..37e9be43bb 100644 --- a/07.ls_object/test/ls_command_test.rb +++ b/07.ls_object/test/ls_command_test.rb @@ -149,8 +149,6 @@ def test_display_some_file_types path = 'test/test_dir/file_type_dir' # パイプファイルの作成 `mkfifo test/test_dir/file_type_dir/test_fifo_file` unless File.exist?('test/test_dir/file_type_dir/test_fifo_file') - # シンボリックリンクの作成 - `ln -s /test/test_dir/link_dir/link_test.txt test/test_dir/file_type_dir/link_test` unless File.exist?('test/test_dir/file_type_dir/link_test') expected = `ls -l #{path}`.chomp ls_command = LsCommand.new(path, **params) assert_equal expected, ls_command.formatted_output From 16634c13a8c2b6019db781bc878eaaded8ca234b Mon Sep 17 00:00:00 2001 From: yokomaru Date: Thu, 17 Oct 2024 21:59:56 +0900 Subject: [PATCH 41/41] fix: special permission MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 特殊権限の処理を、各ビットに1が立っていたら設定するように修正 - 特殊権限を兼ねるテストの追加 - テストのユーザ名をマスクする - generateがついているメソッド名を修正 - type_and_modeメソッドをtypeとmodeに分ける --- 07.ls_object/lib/file/file_data.rb | 91 ++++++++----------- 07.ls_object/lib/format/long_formatter.rb | 2 +- 07.ls_object/test/ls_command_test.rb | 38 ++++---- ...and_group_id_and_sticky_bit_permission.txt | 0 .../set_user_id_and_group_id_permission.txt | 0 5 files changed, 61 insertions(+), 70 deletions(-) create mode 100644 07.ls_object/test/test_dir/special_permission_dir/set_user_id_and_group_id_and_sticky_bit_permission.txt create mode 100755 07.ls_object/test/test_dir/special_permission_dir/set_user_id_and_group_id_permission.txt diff --git a/07.ls_object/lib/file/file_data.rb b/07.ls_object/lib/file/file_data.rb index 6b3787a8b3..851757fdd9 100755 --- a/07.ls_object/lib/file/file_data.rb +++ b/07.ls_object/lib/file/file_data.rb @@ -7,43 +7,34 @@ class FileData attr_reader :name, :file_status - SPECIAL_PERMISSION_INDEX = 2 - OWNER_PERMISSION_INDEX = 3 - GROUP_PERMISSION_INDEX = 4 - OTHER_PERMISSION_INDEX = 5 + PERMISSION_START_POSITION = 7 + PERMISSION_END_POSITION = 9 FILE_TYPE = { - '01' => 'p', - '02' => 'c', - '04' => 'd', - '06' => 'b', - '10' => '-', - '12' => 'l', - '14' => 's' + 'fifo' => 'p', + 'characterSpecial' => 'c', + 'directory' => 'd', + 'blockSpecial' => 'b', + 'file' => '-', + 'link' => 'l', + 'socket' => 's' }.freeze - PERMISSION_TYPE = { - '0' => '---', - '1' => '--x', - '2' => '-w-', - '3' => '-wx', - '4' => 'r--', - '5' => 'r-x', - '6' => 'rw-', - '7' => 'rwx' - }.freeze - - SPECIAL_PERMISSION_TYPE = { - '0' => '-', - '1' => 't', - '2' => 's', - '4' => 's' + SPECIAL_PERMISSION = { + 0 => 's', + 1 => 's', + 2 => 't' }.freeze - TARGET_SPECIAL_PERMISSION = { - OWNER_PERMISSION_INDEX => '4', - GROUP_PERMISSION_INDEX => '2', - OTHER_PERMISSION_INDEX => '1' + PERMISSION_TYPE = { + '000' => '---', + '001' => '--x', + '010' => '-w-', + '011' => '-wx', + '100' => 'r--', + '101' => 'r-x', + '110' => 'rw-', + '111' => 'rwx' }.freeze def initialize(name, path) @@ -57,39 +48,35 @@ def initialize(name, path) def build_file_status(status) { - type_and_mode: type_and_mode(status), + type: FILE_TYPE[status.ftype], + mode: mode(status), hardlink_nums: status.nlink.to_s, owner_name: Etc.getpwuid(status.uid).name, group_name: Etc.getgrgid(status.gid).name, - bytesize: generate_rdev_or_bytesize(status), + bytesize: rdev_or_bytesize(status), latest_modify_datetime: status.mtime.strftime('%_m %e %H:%M'), - filename: generate_file_name(status), + filename: file_name(status), blocks: status.blocks } end - def convert_permission(permission, special_permission, target_special_permission) - permission_type = PERMISSION_TYPE[permission] - - return permission_type if special_permission != target_special_permission - - special_permission_type = SPECIAL_PERMISSION_TYPE[special_permission] - special_permission_type = special_permission_type.upcase if permission.to_i.even? - [permission_type.chop, special_permission_type].join - end - - def generate_file_name(status) + def file_name(status) status.symlink? ? "#{@name} -> #{File.readlink(@full_path)}" : @name end - def generate_rdev_or_bytesize(status) - %w[characterSpecial blockSpecial].include?(@file_type) ? format('%#01x', status.rdev.to_s(10)) : status.size.to_s + def mode(status) + mode_binary_numbers = status.mode.to_s(2).rjust(16, '0') + # SUID、SGID、STICKEYBITの順番で特殊権限をチェック + mode_binary_numbers[4..6].each_char.with_index.map do |special_permission, i| + range_start = PERMISSION_START_POSITION + (3 * i) + range_end = PERMISSION_END_POSITION + (3 * i) + permission = PERMISSION_TYPE[mode_binary_numbers[range_start..range_end]].dup + permission[2] = permission[2] == 'x' ? SPECIAL_PERMISSION[i] : SPECIAL_PERMISSION[i].upcase if special_permission == '1' + permission + end.join end - def type_and_mode(status) - mode = status.mode.to_s(8).rjust(6, '0') - [OWNER_PERMISSION_INDEX, GROUP_PERMISSION_INDEX, OTHER_PERMISSION_INDEX].map do |index| - convert_permission(mode[index], mode[SPECIAL_PERMISSION_INDEX], TARGET_SPECIAL_PERMISSION[index]) - end.unshift(FILE_TYPE[mode[0, SPECIAL_PERMISSION_INDEX]]).join + def rdev_or_bytesize(status) + %w[characterSpecial blockSpecial].include?(@file_type) ? format('%#01x', status.rdev.to_s(10)) : status.size.to_s end end diff --git a/07.ls_object/lib/format/long_formatter.rb b/07.ls_object/lib/format/long_formatter.rb index 67b9790371..3a0d1b9ffe 100644 --- a/07.ls_object/lib/format/long_formatter.rb +++ b/07.ls_object/lib/format/long_formatter.rb @@ -34,7 +34,7 @@ def find_max_size(key) def format_row(status, max_nlink, max_user, max_group, max_size) [ - status[:type_and_mode], + "#{status[:type]}#{status[:mode]}", " #{status[:hardlink_nums].rjust(max_nlink)}", " #{status[:owner_name].ljust(max_user)}", " #{status[:group_name].ljust(max_group)}", diff --git a/07.ls_object/test/ls_command_test.rb b/07.ls_object/test/ls_command_test.rb index 37e9be43bb..1d4033f054 100644 --- a/07.ls_object/test/ls_command_test.rb +++ b/07.ls_object/test/ls_command_test.rb @@ -35,7 +35,7 @@ def test_display_dot_match_and_reverse_sort_file def test_display_long_format_file # Output example # total 8 - # -rw-r--r-- 1 suzukiyouko staff 10 9 15 14:34 test.txt + # -rw-r--r-- 1 username staff 10 9 15 14:34 test.txt path = 'test/test_dir/test_one_file' params = { long_format: true } expected = `ls -l #{path}`.chomp @@ -46,12 +46,12 @@ def test_display_long_format_file def test_display_long_format_and_dot_match_file # Output example # total 16 - # drwxr-xr-x 6 suzukiyouko staff 192 9 16 15:18 . - # drwxr-xr-x 5 suzukiyouko staff 160 9 19 18:20 .. - # -rw-r--r-- 1 suzukiyouko staff 0 9 16 15:14 .test - # drwxr-xr-x 2 suzukiyouko staff 64 9 16 15:18 dir - # -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test.txt - # -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test_2.txt + # drwxr-xr-x 6 username staff 192 9 16 15:18 . + # drwxr-xr-x 5 username staff 160 9 19 18:20 .. + # -rw-r--r-- 1 username staff 0 9 16 15:14 .test + # drwxr-xr-x 2 username staff 64 9 16 15:18 dir + # -rw-r--r-- 1 username staff 10 9 16 15:14 test.txt + # -rw-r--r-- 1 username staff 10 9 16 15:14 test_2.txt path = 'test/test_dir/test_include_dir_and_dot_files' params = { long_format: true, dot_match: true } expected = `ls -al #{path}`.chomp @@ -62,12 +62,12 @@ def test_display_long_format_and_dot_match_file def test_display_long_format_and_dot_match_and_reverse_file # Output example # total 16 - # -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test_2.txt - # -rw-r--r-- 1 suzukiyouko staff 10 9 16 15:14 test.txt - # drwxr-xr-x 2 suzukiyouko staff 64 9 16 15:18 dir - # -rw-r--r-- 1 suzukiyouko staff 0 9 16 15:14 .test - # drwxr-xr-x 5 suzukiyouko staff 160 9 19 18:20 .. - # drwxr-xr-x 6 suzukiyouko staff 192 9 16 15:18 . + # -rw-r--r-- 1 username staff 10 9 16 15:14 test_2.txt + # -rw-r--r-- 1 username staff 10 9 16 15:14 test.txt + # drwxr-xr-x 2 username staff 64 9 16 15:18 dir + # -rw-r--r-- 1 username staff 0 9 16 15:14 .test + # drwxr-xr-x 5 username staff 160 9 19 18:20 .. + # drwxr-xr-x 6 username staff 192 9 16 15:18 . params = { reverse: true, long_format: true, dot_match: true } path = 'test/test_dir/test_include_dir_and_dot_files' expected = `ls -arl #{path}`.chomp @@ -121,6 +121,8 @@ def test_display_special_permission # total 0 # -rwxr-sr-x 1 username staff 0 10 7 21:46 set_group_id_file.txt # -rwxr-Sr-x 1 username staff 0 10 7 22:05 set_group_id_file_not_x_permission.txt + # -r-Sr-Sr-T 1 username staff 0 10 17 00:18 set_user_id_and_group_id_and_sticky_bit_permission.txt + # -rwsr-sr-x 1 username staff 0 10 17 00:17 set_user_id_and_group_id_permission.txt # -rwsr-xr-x 1 username staff 0 10 7 21:46 set_user_id_file.txt # -r-Sr-xr-x 1 username staff 0 10 7 22:05 set_user_id_file_not_x_permission.txt # -rwxr-xr-t 1 username staff 0 10 7 21:46 sticky_bit_file.txt @@ -133,6 +135,8 @@ def test_display_special_permission `chmod 4455 #{path}/set_user_id_file_not_x_permission.txt` # owner permissionの実行権限xを外し特殊権限を付与 `chmod 1755 #{path}/sticky_bit_file.txt` # other permissionに実行権限xと特殊権限を付与 `chmod 1754 #{path}/sticky_bit_file_not_x_permission.txt` # other permissionの実行権限xを外し特殊権限を付与 + `chmod 6755 test/test_dir/special_permission_dir/set_user_id_and_group_id_permission.txt` + `chmod 7444 test/test_dir/special_permission_dir/set_user_id_and_group_id_and_sticky_bit_permission.txt` expected = `ls -l #{path}`.chomp ls_command = LsCommand.new(path, **params) assert_equal expected, ls_command.formatted_output @@ -141,10 +145,10 @@ def test_display_special_permission def test_display_some_file_types # Output example # total 0 - # drwxr-xr-x 2 suzukiyouko staff 64 10 7 23:12 test_dir - # -rw-r--r-- 1 suzukiyouko staff 0 10 7 23:07 test_file.txt - # lrwxr-xr-x 1 suzukiyouko staff 102 10 7 22:59 test_link -> ../07.ls_object/test/test_dir/link_dir/link_test.txt # フルパスのため省略 - # prw-r--r-- 1 suzukiyouko staff 0 10 8 01:17 testfile + # drwxr-xr-x 2 username staff 64 10 7 23:12 test_dir + # -rw-r--r-- 1 username staff 0 10 7 23:07 test_file.txt + # lrwxr-xr-x 1 username staff 102 10 7 22:59 test_link -> ../07.ls_object/test/test_dir/link_dir/link_test.txt # フルパスのため省略 + # prw-r--r-- 1 username staff 0 10 8 01:17 testfile params = { long_format: true } path = 'test/test_dir/file_type_dir' # パイプファイルの作成 diff --git a/07.ls_object/test/test_dir/special_permission_dir/set_user_id_and_group_id_and_sticky_bit_permission.txt b/07.ls_object/test/test_dir/special_permission_dir/set_user_id_and_group_id_and_sticky_bit_permission.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/07.ls_object/test/test_dir/special_permission_dir/set_user_id_and_group_id_permission.txt b/07.ls_object/test/test_dir/special_permission_dir/set_user_id_and_group_id_permission.txt new file mode 100755 index 0000000000..e69de29bb2