generated from gopherdojo/template
-
Couldn't load subscription status.
- Fork 0
tsuchinaga / 課題2 #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Open
tsuchinaga / 課題2 #13
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
6 changes: 6 additions & 0 deletions
kadai2/tsuchinaga/.gitignore
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| ## for IntelliJ | ||
| .idea | ||
| *.iml | ||
|
|
||
| testdata/**/*.converted.* | ||
| cover.out |
100 changes: 100 additions & 0 deletions
kadai2/tsuchinaga/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| # 課題2 tsuchinaga | ||
|
|
||
| ## 課題 | ||
| スライドより転載 | ||
|
|
||
| * io.Readerとio.Writerについて調べてみよう | ||
| * 標準パッケージでどのように使われているか | ||
| * io.Readerとio.Writerがあることで | ||
| どういう利点があるのか具体例を挙げて考えてみる | ||
| * 1回目の課題のテストを作ってみて下さい | ||
| * テストのしやすさを考えてリファクタリングしてみる | ||
| * テストのカバレッジを取ってみる | ||
| * テーブル駆動テストを行う | ||
| * テストヘルパーを作ってみる | ||
|
|
||
|
|
||
| ### io.Readerとio.Writerについて調べてみよう | ||
|
|
||
| `io.Reader` と `io.Writer` を実装している代表的なのはやはり `os.File` だと思う。 | ||
|
|
||
| `os.File` には `os.Stdin` 、 `os.Stdout` 、 `os.Stderr` がある。 | ||
| これらはそれぞれ、標準入力、標準出力、標準エラーにあたり、誰もが入出力先にしたことがあると思う。 | ||
|
|
||
| 例えば、「出力先を標準出力からログファイルにかえたいなー」というときに、 | ||
| `io.Writer` がなければ新たに出力用の処理を用意して上げる必要があったり、 | ||
| 標準ライブラリが `io.Writer` を出力先に指定していなければ、標準ライブラリと同じことをするのに自前で用意する必要があったりする。 | ||
| さらに、「標準出力とログファイルの両方に出したいなー」みたいなときに、それぞれを同じように扱えるようにしていないと処理を作ることも難しい。 | ||
|
|
||
| 読み込みの代表例として、 `bufio.Scanner` をあげる。 | ||
|
|
||
| 競技プログラミングで大きな入力を受け取るときや、サイズの大きいファイルを読み込むときにお世話になる。 | ||
| ※(注記) 他にも1単語ずつじゃなくて、1行ずつ読むときとかにも重宝する | ||
|
|
||
| この `Scanner` を生成する関数である `NewScanner` は下記のようになっている。 | ||
| ```go | ||
| // NewScanner returns a new Scanner to read from r. | ||
| // The split function defaults to ScanLines. | ||
| func NewScanner(r io.Reader) *Scanner { | ||
| return &Scanner{ | ||
| r: r, | ||
| split: ScanLines, | ||
| maxTokenSize: MaxScanTokenSize, | ||
| } | ||
| } | ||
| ``` | ||
| 引数が `io.Reader` となっている。 | ||
| これは標準入力だろうが、ファイルだろうが、HTTPリクエストのBodyだろうが、なんでもかんでも同じように扱える。 | ||
|
|
||
| 同じような処理をいっぱい作らなくていい以外に、モックを作りやすいというメリットもある。(interfaceのメリットかも) | ||
|
|
||
| テストコードを書いていると、ここなんか適当なもので置き換えたいなーというのは頻繁にある。 | ||
| そんな時に構造体に依存していたりすると置き換えることができず、密結合が起きてしまい、テストしづらくなる。 | ||
| そこをinterfaceに依存するようにしていると、置き換えることが容易になる。 | ||
|
|
||
|
|
||
| ### 1回目の課題のテストを作ってみて下さい | ||
|
|
||
| リファクタリングの域をこえてるきもするけど、いつもやってるようにTODO出してテストを作っていく | ||
| 課題1の時よりも責務を意識してTODOを分ける | ||
|
|
||
| * [x] ディレクトリが指定でき、その配下のディレクトリとファイルを再帰的に探索し、変換前として指定されたフォーマットの画像ファイルを変換後として指定されたフォーマットの画像ファイルに変換する | ||
| * [x] コマンドのパラメータを指定する | ||
| * [x] ベースのディレクトリを指定する | ||
| * [x] 必須 | ||
| * [x] 変換前のファイルフォーマットを指定する | ||
| * [x] 任意 | ||
| * [x] デフォルトjpeg | ||
| * [x] 許容されるのはjpeg/png | ||
| * [x] 変換後のファイルフォーマットを指定する | ||
| * [x] 任意 | ||
| * [x] デフォルトpng | ||
| * [x] 許容されるのはjpeg/png | ||
| * [x] 変換前と同じフォーマットの指定は不可 | ||
| * [x] 指定されたディレクトリの配下のディレクトリとファイルの一覧を取得する | ||
| * [x] 配下にディレクトリがあればさらに読み込めるようにする | ||
| * [x] 指定されたパスがディレクトリでなければ中止 | ||
| * [x] ディレクトリかを判断する | ||
| * [x] ファイルの画像フォーマットを取得する | ||
| * [x] ファイルが画像なら画像フォーマットを取得する | ||
| * [x] ファイルが画像でなければフォーマットは取得できないので中止 | ||
| * [x] ファイルフォーマットを変換する | ||
|
|
||
|
|
||
| #### カバレッジ | ||
| ```bash | ||
| $ go tool cover -func=cover.out | ||
| github.com/gopherdojo/dojo8/kadai2/tsuchinaga/imgconv/imgconv.go:27: NewIMGConverter 100.0% | ||
| github.com/gopherdojo/dojo8/kadai2/tsuchinaga/imgconv/imgconv.go:42: Do 93.3% | ||
| github.com/gopherdojo/dojo8/kadai2/tsuchinaga/imgconv/imgconv.go:74: NewConverter 100.0% | ||
| github.com/gopherdojo/dojo8/kadai2/tsuchinaga/imgconv/imgconv.go:90: IsDir 100.0% | ||
| github.com/gopherdojo/dojo8/kadai2/tsuchinaga/imgconv/imgconv.go:99: GetIMGType 90.0% | ||
| github.com/gopherdojo/dojo8/kadai2/tsuchinaga/imgconv/imgconv.go:118: DirFileList 100.0% | ||
| github.com/gopherdojo/dojo8/kadai2/tsuchinaga/imgconv/imgconv.go:137: Convert 69.2% | ||
| github.com/gopherdojo/dojo8/kadai2/tsuchinaga/validation/validation.go:4: NewValidator 100.0% | ||
| github.com/gopherdojo/dojo8/kadai2/tsuchinaga/validation/validation.go:19: IsValidDir 100.0% | ||
| github.com/gopherdojo/dojo8/kadai2/tsuchinaga/validation/validation.go:26: IsValidFileType 100.0% | ||
| github.com/gopherdojo/dojo8/kadai2/tsuchinaga/validation/validation.go:31: IsValidSrc 100.0% | ||
| github.com/gopherdojo/dojo8/kadai2/tsuchinaga/validation/validation.go:36: IsValidDest 100.0% | ||
| total: (statements) 86.1% | ||
| ``` |
5 changes: 5 additions & 0 deletions
kadai2/tsuchinaga/go.mod
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| module github.com/gopherdojo/dojo8/kadai2/tsuchinaga | ||
|
|
||
| go 1.14 | ||
|
|
||
| require golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 |
2 changes: 2 additions & 0 deletions
kadai2/tsuchinaga/go.sum
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | ||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
181 changes: 181 additions & 0 deletions
kadai2/tsuchinaga/imgconv/imgconv.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,181 @@ | ||
| package imgconv | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "image" | ||
| "image/jpeg" | ||
| "image/png" | ||
| "io/ioutil" | ||
| "os" | ||
| "path" | ||
|
|
||
| "golang.org/x/xerrors" | ||
| ) | ||
|
|
||
| var ( | ||
| ReadDirError = xerrors.New("read dir error") | ||
| FileStatError = xerrors.New("file stat error") | ||
| OpenFileError = xerrors.New("open file error") | ||
| CloseFileError = xerrors.New("close file error") | ||
| NotImageError = xerrors.New("not image error") | ||
| ReadImageError = xerrors.New("read image error") | ||
| CreateDestinationFileError = xerrors.New("create destination file error") | ||
| EncodeImageError = xerrors.New("encode image error") | ||
| ) | ||
|
|
||
| // NewIMGConverter - 新しい画像変換の生成 | ||
| func NewIMGConverter() IMGConverter { | ||
| return &imgConverter{converter: NewConverter()} // converterを外からもらってもいいけど、使い方は決まってるので決め打ちで | ||
| } | ||
|
|
||
| // IMGConverter - 画像変換のユースケースのインターフェース | ||
| type IMGConverter interface { | ||
| Do(path, src, dest string) chan error | ||
| } | ||
|
|
||
| // imgConverter - 画像変換のユースケース | ||
| type imgConverter struct { | ||
| converter Converter | ||
| } | ||
|
|
||
| // Do - 指定されたディレクトリから再帰的にたどりながらファイルを変換する | ||
| func (c *imgConverter) Do(path, src, dest string) chan error { | ||
| ch := make(chan error) | ||
|
|
||
| go func() { | ||
| defer close(ch) | ||
|
|
||
| dirs, files, err := c.converter.DirFileList(path) | ||
| if err != nil { | ||
| ch <- err | ||
| return | ||
| } | ||
|
|
||
| // 非同期で再帰的にディレクトリをたどる | ||
| for _, dir := range dirs { | ||
| dch := c.Do(dir, src, dest) | ||
| for err := range dch { | ||
| ch <- err | ||
| } | ||
| } | ||
|
|
||
| // ファイルを変換する | ||
| for _, file := range files { | ||
| if err := c.converter.Convert(file, src, dest); err != nil { | ||
| ch <- err | ||
| } | ||
| } | ||
| }() | ||
|
|
||
| return ch | ||
| } | ||
|
|
||
| // NewConverter - 新しいConverterを生成する | ||
| func NewConverter() Converter { | ||
| return &converter{} | ||
| } | ||
|
|
||
| // converter - 変換処理とそれに付随する処理をもつサービスのインターフェース | ||
| type Converter interface { | ||
| IsDir(path string) (bool, error) | ||
| GetIMGType(path string) (_ string, err error) | ||
| DirFileList(filePath string) ([]string, []string, error) | ||
| Convert(filePath string, src string, dest string) (err error) | ||
| } | ||
|
|
||
| // converter - 変換処理とそれに付随する処理をもつサービス | ||
| type converter struct{} | ||
|
|
||
| // IsDir - pathがディレクトリかどうか | ||
| func (c converter) IsDir(path string) (bool, error) { | ||
| fi, err := os.Stat(path) | ||
| if err != nil { | ||
| return false, xerrors.Errorf("%+v: %w", err, FileStatError) | ||
| } | ||
| return fi.IsDir(), nil | ||
| } | ||
|
|
||
| // GetIMGType - 引数のファイルの画像フォーマットを取得する | ||
| func (c converter) GetIMGType(path string) (_ string, err error) { | ||
| f, err := os.Open(path) | ||
| if err != nil { // 開けない | ||
| return "", OpenFileError | ||
| } | ||
| defer func() { | ||
| if closeErr := f.Close(); closeErr != nil { | ||
| err = xerrors.Errorf("%+v: %w", closeErr, CloseFileError) | ||
| } | ||
| }() | ||
|
|
||
| _, format, err := image.DecodeConfig(f) | ||
| if err != nil { // 画像じゃない | ||
| return "", NotImageError | ||
| } | ||
| return format, nil | ||
| } | ||
|
|
||
| // DirFileList - 引数直下のディレクトリとファイルの配列を返す | ||
| func (c converter) DirFileList(filePath string) ([]string, []string, error) { | ||
| infos, err := ioutil.ReadDir(filePath) | ||
| if err != nil { | ||
| return nil, nil, xerrors.Errorf("%+v(filePath: %s): %w", err, filePath, ReadDirError) | ||
| } | ||
|
|
||
| dirs, files := make([]string, 0), make([]string, 0) | ||
| for _, info := range infos { | ||
| jp := path.Join(filePath, info.Name()) | ||
| if info.IsDir() { | ||
| dirs = append(dirs, jp) | ||
| } else { | ||
| files = append(files, jp) | ||
| } | ||
| } | ||
| return dirs, files, nil | ||
| } | ||
|
|
||
| // Convert - 画像変換 | ||
| func (c converter) Convert(filePath string, src string, dest string) (err error) { | ||
| if ft, err := c.GetIMGType(filePath); err != nil { | ||
| return err | ||
| } else if ft != src { | ||
| return nil | ||
| } | ||
|
|
||
| f, err := os.Open(filePath) | ||
| if err != nil { // 開けない | ||
| return xerrors.Errorf("%+v: %w", err, OpenFileError) | ||
| } | ||
| defer func() { | ||
| if closeErr := f.Close(); closeErr != nil { | ||
| err = xerrors.Errorf("%+v: %w", closeErr, CloseFileError) | ||
| } | ||
| }() | ||
|
|
||
| img, _, err := image.Decode(f) | ||
| if err != nil { | ||
| return xerrors.Errorf("%+v: %w", err, ReadImageError) | ||
| } | ||
|
|
||
| newFilePath := fmt.Sprintf("%s.%s.%s", filePath, "converted", dest) | ||
| o, err := os.Create(newFilePath) | ||
| if err != nil { | ||
| return xerrors.Errorf("%+v: %w", err, CreateDestinationFileError) | ||
| } | ||
| defer func() { | ||
| if closeErr := o.Close(); closeErr != nil { | ||
| err = xerrors.Errorf("%+v: %w", closeErr, CloseFileError) | ||
| } | ||
| }() | ||
|
|
||
| switch dest { | ||
| case "jpeg": | ||
| if err = jpeg.Encode(o, img, nil); err != nil { | ||
| return xerrors.Errorf("%+v: %w", err, EncodeImageError) | ||
| } | ||
| case "png": | ||
| if err = png.Encode(o, img); err != nil { | ||
| return xerrors.Errorf("%+v: %w", err, EncodeImageError) | ||
| } | ||
| } | ||
| return nil | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.