From 920ba4f7dd360af75b296352ac63a2dc548d9107 Mon Sep 17 00:00:00 2001 From: Nishisi Date: Wed, 31 Jul 2019 13:26:15 +0900 Subject: [PATCH 1/4] first commit --- kadai2/dobuzora/.gitignore | 2 + kadai2/dobuzora/README.md | 39 +++++++++++ kadai2/dobuzora/cmd/j2p/main.go | 9 +++ kadai2/dobuzora/go.mod | 3 + kadai2/dobuzora/go.sum | 1 + kadai2/dobuzora/internal/cli/cli.go | 75 +++++++++++++++++++++ kadai2/dobuzora/internal/convert/convert.go | 48 +++++++++++++ 7 files changed, 177 insertions(+) create mode 100644 kadai2/dobuzora/.gitignore create mode 100644 kadai2/dobuzora/README.md create mode 100644 kadai2/dobuzora/cmd/j2p/main.go create mode 100644 kadai2/dobuzora/go.mod create mode 100644 kadai2/dobuzora/go.sum create mode 100644 kadai2/dobuzora/internal/cli/cli.go create mode 100644 kadai2/dobuzora/internal/convert/convert.go diff --git a/kadai2/dobuzora/.gitignore b/kadai2/dobuzora/.gitignore new file mode 100644 index 0000000..f1093d8 --- /dev/null +++ b/kadai2/dobuzora/.gitignore @@ -0,0 +1,2 @@ +_testdata +_bin diff --git a/kadai2/dobuzora/README.md b/kadai2/dobuzora/README.md new file mode 100644 index 0000000..0bdb168 --- /dev/null +++ b/kadai2/dobuzora/README.md @@ -0,0 +1,39 @@ +# 課題 1 + +## 画像変換コマンドを作ろう + +### 仕様 + +- [x] ディレクトリを指定する +- [x] 指定したディレクトリ以下の JPG ファイルを PNG に変換 +- [x] ディレクトリ以下は再帰的に処理する +- [ ] 変換前と変換後の画像形式に指定できる(オプション) + +### 要件 + +- [x] main パッケージと分離する +- [x] 自作パッケージと標準パッケージと準標準パッケージのみを使う +- [x] ユーザ定義型を使ってみる +- [x] GoDoc を生成してみる + +## Install + +``` +export GOBIN=`pwd`/_bin +$ go install github.com/gopherdojo/dojo6/kadai1/cmd/j2p +$ _bin/cmd +``` + +## How to use + +``` +./_bin/j2p DIRPATH +``` + +## 回答 + +最初に、ユーザ定義型を作らずに実装しました。これは狙ったわけではなく私の設計では活用場面が浮かばなかったためです。しかし、要件にはユーザ定義型を使うとのことなので、`convert`パッケージに`ConvertFile`という変換前と変換後のパスを含むファイル名の文字列を持つ型を定義して変換を行うようにしました。 + +一番つまずいた点は動作確認を終えて`main`パッケージから分離する際に、突然動かなくなってしまったことです。これは、`main`パッケージに含まれていた`_ "image/jpeg"`が分離する際に消えてしまったためでした。delve で潜ってもわからず、時間経過でなくなっていることに気が付きました。分離する前に一度コミットしておけば、比較してすぐにわかったので経験不足でした。 + +ディレクトリの構成は、tenntenn さんが okinawa.go で行ってくだささった勉強会の資料及びリポジトリを参考にしました。 diff --git a/kadai2/dobuzora/cmd/j2p/main.go b/kadai2/dobuzora/cmd/j2p/main.go new file mode 100644 index 0000000..f75114f --- /dev/null +++ b/kadai2/dobuzora/cmd/j2p/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/gopherdojo/dojo6/kadai1/dobuzora/internal/cli" +) + +func main() { + cli.Do() +} diff --git a/kadai2/dobuzora/go.mod b/kadai2/dobuzora/go.mod new file mode 100644 index 0000000..d708235 --- /dev/null +++ b/kadai2/dobuzora/go.mod @@ -0,0 +1,3 @@ +module github.com/gopherdojo/dojo6/kadai1/dobuzora + +go 1.12 diff --git a/kadai2/dobuzora/go.sum b/kadai2/dobuzora/go.sum new file mode 100644 index 0000000..f0ec61a --- /dev/null +++ b/kadai2/dobuzora/go.sum @@ -0,0 +1 @@ +github.com/gopherdojo/dojo6 v0.0.0-20190710155631-1b40d3406c2f h1:UOg/ZpRwvokKADOjfCukjqQXYHb7HOdVSgZGABZc2BQ= diff --git a/kadai2/dobuzora/internal/cli/cli.go b/kadai2/dobuzora/internal/cli/cli.go new file mode 100644 index 0000000..ad51330 --- /dev/null +++ b/kadai2/dobuzora/internal/cli/cli.go @@ -0,0 +1,75 @@ +/* +cli はコマンドライン引数を処理します。 +*/ +package cli + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + cnv "github.com/gopherdojo/dojo6/kadai1/dobuzora/internal/convert" +) + +// isDir は ディレクトリかどうかを判断します +func isDir(dirName string) bool { + dInfo, err := os.Stat(dirName) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + return dInfo.IsDir() +} + +// ファイル名から拡張子を取ったファイル名を返します +func getFileNameWithoutExt(path string) string { + // Fixed with a nice method given by mattn-san + return filepath.Base(path[:len(path)-len(filepath.Ext(path))]) +} + +// imageConvert はディレクトリ名を受け取り、そのディレクトリ内のファイルを変換します +func imageConvert(dirName string) []cnv.ConvertFile { + var fileList []cnv.ConvertFile + + files, err := ioutil.ReadDir(dirName) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + // + for _, file := range files { + path := filepath.Join(dirName, file.Name()) + if isDir(path) { + flist := imageConvert(path) + fileList = append(fileList, flist...) + } else { + ext := filepath.Ext(path) + if ext == ".jpeg" || ext == ".jpg" { + newPath := filepath.Dir(path) + "/" + getFileNameWithoutExt(path) + ".png" + fileList = append(fileList, cnv.ConvertFile{Old: path, New: newPath}) + } + } + } + return fileList +} + +// Do は ファイルを走査し、convertパッケージを用いてJPEをPNG形式に変換します +func Do() { + if len(os.Args) != 2 { + fmt.Fprintln(os.Stderr, "Wrong number of arguments") + os.Exit(1) + } + rootDir := os.Args[1] + if !isDir(rootDir) { + fmt.Fprintln(os.Stderr, "Not a directory") + os.Exit(1) + } + + hoge := imageConvert(rootDir) + for _, v := range hoge { + v.ConvertToPng() + os.Remove(v.Old) + } +} diff --git a/kadai2/dobuzora/internal/convert/convert.go b/kadai2/dobuzora/internal/convert/convert.go new file mode 100644 index 0000000..2ce2dd2 --- /dev/null +++ b/kadai2/dobuzora/internal/convert/convert.go @@ -0,0 +1,48 @@ +/* +JPEG形式の画像をPNG形式に変換するパッケージです。 +*/ +package convert + +import ( + "fmt" + "image" + _ "image/jpeg" + "image/png" + "os" +) + +// 変換する前と後のファイル名を持った構造体です。 +type ConvertFile struct { + Old string // 変換前のファイル名 + New string // 変換後のファイル名 +} + +// ConvertToPng は ファイルのパスを受け取り、JPGならPNG形式に画像を変換します +func (cnv *ConvertFile) ConvertToPng() error { + file, err := os.Open(cnv.Old) + if err != nil { + fmt.Printf("Can not open : %v ", err) + return err + } + defer file.Close() + + img, format, err := image.Decode(file) + if err != nil { + fmt.Printf("Can not Decode : %v ", err) + return err + } + if format != "jpeg" { + return nil + } + + out, err := os.Create(cnv.New) + defer out.Close() + + err = png.Encode(out, img) + if err != nil { + fmt.Println(err) + return err + } + + return nil +} From e358a58073adc5d06a97e6ac3cf021c09dc709f0 Mon Sep 17 00:00:00 2001 From: Nishisi Date: Wed, 31 Jul 2019 17:51:48 +0900 Subject: [PATCH 2/4] Update README and add test --- kadai2/dobuzora/README.md | 47 ++++++++++++++---------- kadai2/dobuzora/cmd/j2p/main.go | 2 +- kadai2/dobuzora/go.mod | 2 +- kadai2/dobuzora/internal/cli/cli.go | 2 +- kadai2/dobuzora/internal/cli/cli_test.go | 21 +++++++++++ 5 files changed, 52 insertions(+), 22 deletions(-) create mode 100644 kadai2/dobuzora/internal/cli/cli_test.go diff --git a/kadai2/dobuzora/README.md b/kadai2/dobuzora/README.md index 0bdb168..daee7f6 100644 --- a/kadai2/dobuzora/README.md +++ b/kadai2/dobuzora/README.md @@ -1,26 +1,41 @@ -# 課題 1 +# 課題 2 -## 画像変換コマンドを作ろう +## io.Readerとio.Writer +io.Readerとio.Writerについて調べてみよう -### 仕様 +### 標準パッケージではどのように使われているか +私がよく使ったのは`func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)`です。 +第一引数が`io.Writer`となっているため、条件を満たしているものは何でも引数に指定できるため頻繁に使っています。 +`io.Writer`は`Write(p []byte) (n int, err error)`メソッドを持っていればよい。 +このため、`func (f *File) Write(b []byte) (n int, err error)`を持つ`os.File`オブジェクトを指定すればファイルに出力することができる。 -- [x] ディレクトリを指定する -- [x] 指定したディレクトリ以下の JPG ファイルを PNG に変換 -- [x] ディレクトリ以下は再帰的に処理する -- [ ] 変換前と変換後の画像形式に指定できる(オプション) +io.Readerは競技プログラミングでお世話になります。 +読み込み高速化のために`bufio.NewScanner`を頻繁に使いますが、これは`func NewScanner(r io.Reader) *Scanner`です。 +僕は`bufio.NewScanner(os.Stdin)`をよく使いますが、`os.Stdin`は、`Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")`です。 +NewFile関数について調べると`func NewFile(fd uintptr, name string) *File`であり、Fileオブジェクトを返します。 +Fileオブジェクトは`func (f *File) Read(b []byte) (n int, err error)`であるため、`io.Reader`を満たします。 +普段は、`os.Stdin`しか使いませんが、別の入力を読み込む時は`buio.NewScanner`の引数を`io.Reader`を満たしているものに変えれば同様に読み込めます。 -### 要件 +### io.Readerとio.Writerがあることでどういう利点があるのかを具体的に挙げて考えてみる。 -- [x] main パッケージと分離する -- [x] 自作パッケージと標準パッケージと準標準パッケージのみを使う -- [x] ユーザ定義型を使ってみる -- [x] GoDoc を生成してみる +上記でも、触れた通り書き込むこと、読み込むことに関して抽象化されていることによりプログラマーがほとんど意識を割く必要がない点に大きな利点があると考えます。 + +## テストを書いてみよう +1回目の宿題のテストを作ってみてください + +### テストのしやすさを考えてリファクタリングしてみる + +### テストのカバレッジを取ってみる + +### テーブル駆動テストを行う + +### テストヘルパーを作ってみる ## Install ``` export GOBIN=`pwd`/_bin -$ go install github.com/gopherdojo/dojo6/kadai1/cmd/j2p +$ go install github.com/gopherdojo/dojo6/kadai2/dobuzora/cmd/j2p $ _bin/cmd ``` @@ -31,9 +46,3 @@ $ _bin/cmd ``` ## 回答 - -最初に、ユーザ定義型を作らずに実装しました。これは狙ったわけではなく私の設計では活用場面が浮かばなかったためです。しかし、要件にはユーザ定義型を使うとのことなので、`convert`パッケージに`ConvertFile`という変換前と変換後のパスを含むファイル名の文字列を持つ型を定義して変換を行うようにしました。 - -一番つまずいた点は動作確認を終えて`main`パッケージから分離する際に、突然動かなくなってしまったことです。これは、`main`パッケージに含まれていた`_ "image/jpeg"`が分離する際に消えてしまったためでした。delve で潜ってもわからず、時間経過でなくなっていることに気が付きました。分離する前に一度コミットしておけば、比較してすぐにわかったので経験不足でした。 - -ディレクトリの構成は、tenntenn さんが okinawa.go で行ってくだささった勉強会の資料及びリポジトリを参考にしました。 diff --git a/kadai2/dobuzora/cmd/j2p/main.go b/kadai2/dobuzora/cmd/j2p/main.go index f75114f..dcae40f 100644 --- a/kadai2/dobuzora/cmd/j2p/main.go +++ b/kadai2/dobuzora/cmd/j2p/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/gopherdojo/dojo6/kadai1/dobuzora/internal/cli" + "github.com/gopherdojo/dojo6/kadai2/dobuzora/internal/cli" ) func main() { diff --git a/kadai2/dobuzora/go.mod b/kadai2/dobuzora/go.mod index d708235..709e5eb 100644 --- a/kadai2/dobuzora/go.mod +++ b/kadai2/dobuzora/go.mod @@ -1,3 +1,3 @@ -module github.com/gopherdojo/dojo6/kadai1/dobuzora +module github.com/gopherdojo/dojo6/kadai2/dobuzora go 1.12 diff --git a/kadai2/dobuzora/internal/cli/cli.go b/kadai2/dobuzora/internal/cli/cli.go index ad51330..1a408ba 100644 --- a/kadai2/dobuzora/internal/cli/cli.go +++ b/kadai2/dobuzora/internal/cli/cli.go @@ -9,7 +9,7 @@ import ( "os" "path/filepath" - cnv "github.com/gopherdojo/dojo6/kadai1/dobuzora/internal/convert" + cnv "github.com/gopherdojo/dojo6/kadai2/dobuzora/internal/convert" ) // isDir は ディレクトリかどうかを判断します diff --git a/kadai2/dobuzora/internal/cli/cli_test.go b/kadai2/dobuzora/internal/cli/cli_test.go new file mode 100644 index 0000000..56ee421 --- /dev/null +++ b/kadai2/dobuzora/internal/cli/cli_test.go @@ -0,0 +1,21 @@ +package cli + +import "testing" + +var inputtests = []struct { + in string + expected string +}{ + {"ninja.png", "ninja"}, + {"ninja.jpeg", "ninja"}, + {".jpg", ""}, +} + +func TestgetFileNameWithoutExt(t *testing.T) { + for _, c := range inputtests { + actual := getFileNameWithoutExt(c.in) + if actual != c.expected { + t.Errorf("getFileNameWithoutExt(%q) == %q, expect %q", c.in, actual, c.expected) + } + } +} From 28c393c59d075597cdbb83a883668b11990671fa Mon Sep 17 00:00:00 2001 From: Nishisi Date: Wed, 31 Jul 2019 19:15:19 +0900 Subject: [PATCH 3/4] modfied cli_test.go --- kadai2/dobuzora/internal/cli/cli_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kadai2/dobuzora/internal/cli/cli_test.go b/kadai2/dobuzora/internal/cli/cli_test.go index 56ee421..647621b 100644 --- a/kadai2/dobuzora/internal/cli/cli_test.go +++ b/kadai2/dobuzora/internal/cli/cli_test.go @@ -8,10 +8,10 @@ var inputtests = []struct { }{ {"ninja.png", "ninja"}, {"ninja.jpeg", "ninja"}, - {".jpg", ""}, + {"n.jpg", "n"}, } -func TestgetFileNameWithoutExt(t *testing.T) { +func TestGetFileNameWithoutExt(t *testing.T) { for _, c := range inputtests { actual := getFileNameWithoutExt(c.in) if actual != c.expected { From 291f908637c3a6096cea0e64331e038ec590d1b2 Mon Sep 17 00:00:00 2001 From: Nishisi Date: Wed, 31 Jul 2019 22:09:32 +0900 Subject: [PATCH 4/4] add export_test.go --- kadai2/dobuzora/internal/cli/cli_test.go | 10 +++++++--- kadai2/dobuzora/internal/cli/export_test.go | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 kadai2/dobuzora/internal/cli/export_test.go diff --git a/kadai2/dobuzora/internal/cli/cli_test.go b/kadai2/dobuzora/internal/cli/cli_test.go index 647621b..34beb4c 100644 --- a/kadai2/dobuzora/internal/cli/cli_test.go +++ b/kadai2/dobuzora/internal/cli/cli_test.go @@ -1,6 +1,10 @@ -package cli +package cli_test -import "testing" +import ( + "testing" + + "github.com/gopherdojo/dojo6/kadai2/dobuzora/internal/cli" +) var inputtests = []struct { in string @@ -13,7 +17,7 @@ var inputtests = []struct { func TestGetFileNameWithoutExt(t *testing.T) { for _, c := range inputtests { - actual := getFileNameWithoutExt(c.in) + actual := cli.GetFileNameWithoutExt(c.in) if actual != c.expected { t.Errorf("getFileNameWithoutExt(%q) == %q, expect %q", c.in, actual, c.expected) } diff --git a/kadai2/dobuzora/internal/cli/export_test.go b/kadai2/dobuzora/internal/cli/export_test.go new file mode 100644 index 0000000..5210566 --- /dev/null +++ b/kadai2/dobuzora/internal/cli/export_test.go @@ -0,0 +1,3 @@ +package cli + +var GetFileNameWithoutExt = getFileNameWithoutExt