Skip to content

オブジェクト指向版lsコマンドの実装#14

Open
kutimiti1234 wants to merge 71 commits intomainfrom
my-ls_object
Open

オブジェクト指向版lsコマンドの実装#14
kutimiti1234 wants to merge 71 commits intomainfrom
my-ls_object

Conversation

@kutimiti1234
Copy link
Owner

lsコマンド実装したもの

  • larオプション

実装しなかったもの

  • ファイルの列ごとの幅調整(すべてのファイルの名前の中で最も文字数の大きなものを幅として設定した)

require 'io/console'
require_relative 'ls'

LS_SHORT_WIDTH = IO.console.winsize[1]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これは何の値を取って LS_SHORT_WIDTHとはどういう意味でしょうか?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらについてですが、画面のサイズを取得してshort_formatで出力する際の最大幅を取得しています

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど、shortフォーマット用ということですね。
どちらかというと、「何に使うか?」よりも「何が入っているか?」の方がより気になるところなので画面のサイズという意味の変数の方が良いでしょう。

また、この値は環境によって違うので定数ではなく変数の方が良いですね。


private

def parse_paths_and_options
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

メソッドはシンプルにするのが原則なので、必要が無ければ分けましょう。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらについて1点質問です。
parse_optionsとparse_pathsに分ける場合、parse_options⇒parse_pathsの順番で実行されない場合挙動がおかしくなってしまう認識です(オプションあり、引数無しの場合に何も表示されないなど)。
この場合でもメソッドを分割したほうがよろしかったでしょうか

Copy link

@u1tnk u1tnk Sep 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確かにそうですね。ここはメソッドにしてない人が多かったのでいつも気にしておらず、長過ぎるメソッド名を見て指摘した感じですね。

理想としては parse_pathsでオプションにあたるものを無視するようにするあたりですが、今の状況で順番が大事である旨をコメントしておくぐらいで良いと思います。

あとオススメするわけじゃないですが parse_input ぐらいの名前だったらスルーしてしまったかもしれません 😓
長い名前で and を付けてると 「このメソッド分けられそう!」って思っちゃうので。

paths, options = parse_paths_and_options

@command = if options[:long_format]
LsLong.new(paths, options)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LongとShortは出力の形式が違うだけなのでLsCommand自体を継承した二つに分けるのはちょっとおかしいですね。

class FileEntriesList
include EntriesList

attr_accessor :entries
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

このaccessorは使って無いように見えますがどうでしょうか?

@@ -0,0 +1,27 @@
# frozen_string_literal: true

module EntriesList
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

名前が Entriesの時点で rubyの慣習上、複数系は単数名のクラスの配列もしくは配列を保持するクラスになるので、Listは不要です。

require_relative 'entries_list'

class DirEntry
include EntriesList
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DirEntryと FileEntriesList は両方とも file_entriesを持っていますが、どのように使い分けるのでしょうか?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらに関してですが、lsコマンドで引数にファイルを直接指定した場合と、ディレクトリを指定した場合でそれぞれ異なる挙動を取っていたため、ファイルを直接指定した場合のものをまとめて扱うクラスをFilleEntriesListとして、ディレクトリを扱うものをDirEntryとしました。

例:
image

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど!
「任意のパスを指定できる」仕様は現状必須では無いはず
( https://bootcamp.fjord.jp/pages/ls-command で確認しました)
なのでいったん外してしまいましょう。

そのまま無しでもプラクティス的には良いですし、やるにしてもいったん外してオブジェクト指向的に整理してから追加した方が早そうですし。

'directory' => 'd',
'file' => '-',
'link' => 'l'
}.tap { |h| h.default = '-' }.freeze
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この defaultのところは何をしてるのでしょうか?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらに指摘いただいたのですが、3通り以外のファイルタイプに対応しておらず、3通り以外のものを指定されたときに'-'を表示するように設定いたしました。
#11 (comment)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらせっかくなので下記の記事を参考に他のファイルタイプについても対応いたしました。
https://zenn.dev/universato/articles/20201202-z-mode

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど。了解です。

# File::statのブロックサイズの単位は512bytesであるから変換する
block_size = stats.blocks * (512 / BLOCK_SIZE.to_f)

{ type_and_mode: mode, nlink:, user:, group:, size:, mtime:, name:,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こうやって計算結果をhashに詰めて外部から参照できるんであればこのhashを作るメソッドと変わりません。

このFileEntryクラスに modeは? userは?と聞いて返ってくるようにしましょう。


require 'etc'

FILE_TYPE_LOOKUP = {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

解決するための変数名ですが、LOOKUPだとちょっと違和感があります、意味的にはFOR_LOOKUPならわかるけど、ちょっと冗長だし、 FILE_TYPESとかSHORT_FILE_TYPESとかかなと思います。


def initialize(path)
@path = path
@stats = File.lstat(@path)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

細かいけど、 メソッドもstatだし statsだと配列かな?って思っちゃうので @stat の方が良いかと思います。

@@ -0,0 +1,62 @@
# frozen_string_literal: true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

オブジェクトっぽくなりましたね! 👍

require 'optparse'
require_relative 'ls_command'

class Ls
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

このクラスはちょっと冗長かと思います。LsCommandかmainでやれば良いと思います。

def initialize(paths, options)
@paths = paths
@options = options
@format = @options[:long_format] ? LsLong.new : LsShort.new
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatだと フォーマットの名前が入っていそうです。 formatするオブジェクトを入れるなら formatter の方が良いですね。
LsLong/LsShort も Formatterを最後に付けた方が良いですね。またついですが、 Lsは冗長なのでLongFormatter/ShortFormatterで良いと思います。


require 'io/console'
class LsShort
def initialize(window_width = IO.console.winsize[1])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

デフォルト値を外から渡すケースは無いと思うので、 引数を無くして、initialize内で設定すれば良いと思います。

def run
output = @directories.map do |dir_entry|
header = "#{dir_entry.path}:" if @directories.count > 1
body = @format.run(dir_entry).to_s
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatするオブジェクトが runして 戻ってきたものを to_s する必要は無いはずです。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants