diff --git a/07.ls_object/basic_format.rb b/07.ls_object/basic_format.rb new file mode 100644 index 0000000000..02d007f368 --- /dev/null +++ b/07.ls_object/basic_format.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require_relative 'file_info' + +class BasicFormat + def initialize(file_paths) + @files = file_paths.map { |file_path| FileInfo.new(file_path) } + end + + def format_lines + NotImplementedError + end +end diff --git a/07.ls_object/detailed_format.rb b/07.ls_object/detailed_format.rb new file mode 100644 index 0000000000..b48c4dfd04 --- /dev/null +++ b/07.ls_object/detailed_format.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require_relative 'basic_format' + +class DetailedFormat < BasicFormat + def format_lines + @files.map do |file| + file.full_info_format(max_lengths) + end + end + + def calculate_total_blocks + @files.sum(&:blocks) + end + + private + + def max_lengths + max_lengths = {} + max_lengths[:size] = @files.map { |file| file.size.to_s.length }.max + max_lengths[:nlink] = @files.map { |file| file.link_num.to_s.length }.max + max_lengths[:user] = @files.map { |file| file.user.length }.max + max_lengths[:group] = @files.map { |file| file.group.length }.max + + max_lengths + end +end diff --git a/07.ls_object/file_info.rb b/07.ls_object/file_info.rb new file mode 100644 index 0000000000..d78e813228 --- /dev/null +++ b/07.ls_object/file_info.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require 'etc' + +PERMISSIONS = { '0' => '---', '1' => '--x', '2' => '-w-', '3' => '-wx', + '4' => 'r--', '5' => 'r-x', '6' => 'rw-', '7' => 'rwx' }.freeze + +class FileInfo + def initialize(file_path) + @file_path = file_path + end + + def base_name + File.basename(@file_path) + end + + def type + file_lstat = File.lstat(@file_path) + if file_lstat.directory? + 'd' + elsif file_lstat.symlink? + 'l' + else + '-' + end + end + + def mode + file_stat.mode.to_s(8).slice(-3, 3).chars.to_a.map { |n| PERMISSIONS[n] }.join + end + + def link_num + file_stat.nlink + end + + def size + file_stat.size + end + + def user + Etc.getpwuid(file_stat.uid).name + end + + def group + Etc.getgrgid(file_stat.gid).name + end + + def last_access_time + file_stat.mtime.strftime('%_m %_d %H:%M') + end + + def symlink? + mode_num = File.lstat(@file_path).mode.to_s(8) + mode_num[0, 2] == '12' + end + + def blocks + file_stat.blocks + end + + def full_info_format(max_lengths) + "#{type}#{mode} "\ + "#{link_num.to_s.rjust(max_lengths[:nlink])} "\ + "#{user.rjust(max_lengths[:user])} #{group.to_s.rjust(max_lengths[:group])} "\ + "#{size.to_s.rjust(max_lengths[:size])} "\ + "#{last_access_time} #{base_name}"\ + "#{" -> #{File.readlink(@file_path)}" if symlink?}" + end + + private + + def file_stat + File::Stat.new(@file_path) + end +end diff --git a/07.ls_object/ls.rb b/07.ls_object/ls.rb new file mode 100644 index 0000000000..594e1348eb --- /dev/null +++ b/07.ls_object/ls.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require_relative 'ls_command' +require 'optparse' + +opt = OptionParser.new +options = {} +opt.on('-a') { |v| options[:a] = v } +opt.on('-r') { |v| options[:r] = v } +opt.on('-l') { |v| options[:l] = v } +opt.parse!(ARGV) + +path = ARGV[0] || '.' +LsCommand.new(path, options).display diff --git a/07.ls_object/ls_command.rb b/07.ls_object/ls_command.rb new file mode 100644 index 0000000000..447b6f22ea --- /dev/null +++ b/07.ls_object/ls_command.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require_relative 'short_format' +require_relative 'detailed_format' + +class LsCommand + def initialize(path, options) + @path = path + @options = options + end + + def display + puts "total #{formatter.calculate_total_blocks}" if @options[:l] + puts formatter.format_lines + end + + private + + def file_paths + file_pattern = File.join(@path, '*') + file_paths = @options[:a] ? Dir.glob(file_pattern, File::FNM_DOTMATCH) : Dir.glob(file_pattern) + @options[:r] ? file_paths.reverse : file_paths + end + + def formatter + @options[:l] ? DetailedFormat.new(file_paths) : ShortFormat.new(file_paths) + end +end diff --git a/07.ls_object/short_format.rb b/07.ls_object/short_format.rb new file mode 100644 index 0000000000..d7912d2e34 --- /dev/null +++ b/07.ls_object/short_format.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require_relative 'basic_format' + +class ShortFormat < BasicFormat + COLUMN_NUM = 3 + + def format_lines + files_grouped_into_lines.map do |line| + line.map do |file| + file_name = file.base_name + file_name.ljust(max_length) + end.join(' ') + end + end + + private + + def files_grouped_into_lines + total_line_count = (@files.size.to_f / COLUMN_NUM).ceil + files_sliced = @files.each_slice(total_line_count).to_a + + total_line_count.times.map do |index| + files_sliced.map { |files| files[index] }.compact + end + end + + def max_length + @files.map { |file| file.base_name.size }.max + end +end