diff --git a/08.ls_object/base_view.rb b/08.ls_object/base_view.rb new file mode 100644 index 0000000000..2caa9fb542 --- /dev/null +++ b/08.ls_object/base_view.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class BaseView + def initialize(file_names) + @file_names = file_names + @file_infos = file_names.map do |file_name| + File.lstat(file_name) + end + end + + def render + raise NotImplementedError, 'renderはサブクラスで定義する' + end +end diff --git a/08.ls_object/column_view.rb b/08.ls_object/column_view.rb new file mode 100644 index 0000000000..32673699f6 --- /dev/null +++ b/08.ls_object/column_view.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require_relative 'base_view' + +class ColumnView < BaseView + def initialize(file_names, columns = 3) + super(file_names) + @columns = columns + end + + def render + max_len = @file_names.map(&:length).max + rows = display_rows(@file_names) + + rows.times do |row_index| + @columns.times do + break if row_index > @file_names.size + + file = @file_names[row_index] + print file.ljust(max_len).concat("\t") unless file.nil? + row_index += rows + end + print "\n" + end + end + + def display_rows(list) + (list.size.to_r / @columns.to_r).to_f.ceil + end +end diff --git a/08.ls_object/list_view.rb b/08.ls_object/list_view.rb new file mode 100644 index 0000000000..42995b0ab6 --- /dev/null +++ b/08.ls_object/list_view.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require 'etc' +require_relative 'base_view' + +class ListView < BaseView + FILE_TYPE_STR = { + 'file' => '-', + 'blockSpecial' => 'b', + 'characterSpecial' => 'c', + 'directory' => 'd', + 'link' => 'l', + 'fifo' => 'p', + 'socket' => 's', + 'unknown' => '?' + }.freeze + + PERMISSION_STR = { + 0 => '-', + 4 => 'r', + 2 => 'w', + 1 => 'x' + }.freeze + + def render + display_file_infos = display_file_info + row_max_len = row_max_lenght(display_file_infos) + puts "total #{display_file_infos.map { |v| v[:blocks] }.sum}" + display_file_infos.each do |file| + print file[:type].rjust(row_max_len[:type]) + print "#{file[:permission].rjust(row_max_len[:permission])} " + print "#{file[:hard_link].rjust(row_max_len[:hard_link])} " + print "#{file[:owner].ljust(row_max_len[:owner])} " + print "#{file[:group].ljust(row_max_len[:group])} " + print "#{file[:file_size].to_s.rjust(row_max_len[:file_size])} " + print "#{file[:timestamp].rjust(row_max_len[:timestamp])} " + print "#{file[:file_name]} " + print " -> #{File.readlink(file[:file_name])}" if file[:file_name] == 'l' + print "\n" + end + end + + def display_file_info + @file_names.map do |file_name| + stat = File.lstat(file_name) + display = { + file_name: file_name, + type: FILE_TYPE_STR[stat.ftype], + permission: print_permission(stat.mode, PERMISSION_STR), + hard_link: stat.nlink.to_s, + owner: Etc.getpwuid(stat.uid).name, + group: Etc.getgrgid(stat.gid).name, + file_size: stat.size, + timestamp: stat.mtime.strftime('%_m %_d %H:%M'), + blocks: stat.blocks + } + display + end + end + + def row_max_lenght(display_file_infos) + max_len = Hash.new(0) + display_file_infos.each do |item| + item.each do |key, value| + max_len[key] = [max_len[key], value.to_s.length].max + end + end + max_len + end + + def print_permission(mode, permission_str) + # 0100744 -> 774 + mode.to_s(8)[-3, 3].split('').map do |v| + 2.downto(0).map { |count| permission_str[v.to_i & (1 << count)] }.join + end.join + end +end diff --git a/08.ls_object/ls.rb b/08.ls_object/ls.rb new file mode 100755 index 0000000000..6315c21483 --- /dev/null +++ b/08.ls_object/ls.rb @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require_relative 'option' +require_relative 'ls_command' + +options = Option.new(ARGV) +LsCommand.new.run(options) diff --git a/08.ls_object/ls_command.rb b/08.ls_object/ls_command.rb new file mode 100644 index 0000000000..13bb19c132 --- /dev/null +++ b/08.ls_object/ls_command.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require_relative 'list_view' +require_relative 'column_view' + +class LsCommand + def run(options) + file_names = target_files(options) + file_names.reverse! if options.reverse_order + + view = options.list_mode ? ListView.new(file_names) : ColumnView.new(file_names, 3) + view.render + end + + def target_files(options) + flag = 0 + flag = File::FNM_DOTMATCH if options.include_dotfiles + + Dir.glob('*', flag) + end +end diff --git a/08.ls_object/option.rb b/08.ls_object/option.rb new file mode 100644 index 0000000000..eda910f68f --- /dev/null +++ b/08.ls_object/option.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class Option + attr_reader :include_dotfiles, :reverse_order, :list_mode + + def initialize(argv) + @include_dotfiles, @reverse_order, @list_mode = %w[a r l].map do |opt| + argv.any? { |v| v.start_with?('-') && v.include?(opt) } + end + end +end