Posted on 10 Comments

2019年 振り返り

2019年の抱負から今年を振り返ってみます。子供らが遊んでいる中で書いているのでかなり雑です。あとできれいにします。

Google Cloud Platformを使う

2018年後半から2019年前半にかけて副業でFirebaseでスマホアプリのサーバサイドをやりました。感じたことはGCPめっちゃいいということ。メインで利用したのはCloudFirestoreとCloud Functionsでしたが他にも色々できるのですごい進化を感じました。

数学をやる

2019年前半に中学レベルの数学までを復習しました。やってみて思ったことは業務で必要にならない限りは継続が難しいことでした。

宅建をとる

以下で書いたので割愛

http://3.115.145.106.xip.io/archives/1629

体力をつける

会社でランニング部があり今年は多少ですが走ることができました。また、久しぶりに筋トレも再開することができました。2020年はこれらの活動を増やして継続していきたいです。

やらなくてよかったこと

何年かやってみてやらなくてよいことが見えてきました。というより実務で必要にならない限り習得が難しいです。重要だしやりたいけどサブでやるには継続が難しく思ったような成長曲線が描けません。逆に英語のリーディングは日常で使うのでそっちに極振りしています。

  • 機会学習
  • 数学
  • 英語のスピーキング

アウトプット

  • ブログ:22記事
  • QIITA:2記事

合計24記事ということで月2記事のペースは維持できました。特別気にしていませんが月間PVも約1万と少しずつ伸びてきていていい感じ。

まとめ

ここ数年はテックリード、スクラムマスター、マネージメント業務が多く業務で開発をする機会が減っていましたが、今年の後半は最高の会社・メンバー・プロダクト・技術スタックでいろんなことがとても充実した1年になりました。

  • VueJS
  • TypeScript
  • Golang
  • AWS
  • Micro Services
  • コーチング
Posted on

[Golang] Goを始めたらまずはioパッケージを知るべし

Goを書いているとio.writerio.readerを扱うケースが頻繁に出てきますが、これはioパッケージが多くの他のパッケージのインターフェースになっているからなのでioパッケージを知っておくことで開発が楽になります。

参考書籍

本書はGoio.Writerio.Readerからはじまりシステムの深いところまで丁寧に説明されているのでとてもおすすめです。ioパッケージから始まっているのも納得です。Webで無料で見れますし書籍版、PDF版で購入も可能です。

Web版

https://ascii.jp/elem/000/001/235/1235262/

書籍版

ioパッケージのインターフェース一覧

ioパッケージのインターフェース一覧です。Goのインターフェースの実装は、明示的にインターフェースを明示的にimplementsせず、インターフェースを満たしていたらimplementsしていることになります。

  • Reader(インターフェース)
  • Writer(インターフェース)
  • Seeker(インターフェース)
  • Closer(インターフェース)
  • ReadWriter(複合インターフェース)
  • ReadCloser(複合インターフェース)
  • WriteCloser(複合インターフェース)
  • ReadSeeker(複合インターフェース)
  • WriteSeeker(複合インターフェース)
  • ReadWriteCloser(複合インターフェース)
  • ReadWriteSeeker(複合インターフェース)
  • ReaderFrom(インターフェース)
  • WriterTo(インターフェース)
  • ReaderAt(インターフェース)
  • WriterAt(インターフェース)
  • ByteReader(インターフェース)
  • ByteScanner(インターフェース)
  • ByteWriter(インターフェース)
  • RuneScanner(インターフェース)
  • StringWriter(インターフェース)

複合インターフェース表

Goではインターフェースにインターフェースを食わせることができ、ioパッケージで作られている複合インターフェースは以下となります。

インターフェースio.Readerio.Writerio.Seekerio.Closer
io.ReadWriter  
io.ReadSeeker  
io.ReadCloser  
io.WriteSeeker  
io.WriteCloser  
io.ReadWriteSeeker 
io.ReadWriteCloser 

インターフェースを満たしている一覧を確認する方法

以下のコマンドを叩くと対象のインターフェースを満たしているものの一覧が確認できます。

$ GOPATH=/ godoc -http ":6060" -analysis type
## 実行後以下のURLにアクセスすると`io`パッケージが確認できる
http://localhost:6060/pkg/io/

Readerのimplements一覧

テストも含んでいますが沢山あります

  • pointer type *archive/tar.Reader implements Reader
  • pointer type *archive/tar.regFileReader implements Reader
  • pointer type *archive/tar.sparseFileReader implements Reader
  • pointer type *archive/tar.testFile implements Reader
  • pointer type *archive/zip.checksumReader implements Reader
  • pointer type *archive/zip.pooledFlateReader implements Reader
  • pointer type *bufio.Reader implements Reader
  • pointer type *bufio_test.StringReader implements Reader
  • pointer type *bufio_test.emptyThenNonEmptyReader implements Reader
  • pointer type *bufio_test.errorThenGoodReader implements Reader
  • pointer type *bufio_test.negativeReader implements Reader
  • pointer type *bufio_test.rot13Reader implements Reader
  • pointer type *bufio_test.scriptedReader implements Reader
  • pointer type *bufio_test.slowReader implements Reader
  • pointer type *bufio_test.testReader implements Reader
  • pointer type *bytes.Buffer implements Reader
  • pointer type *bytes.Reader implements Reader
  • pointer type *bytes_test.negativeReader implements Reader
  • pointer type *cmd/compile/internal/gc.importReader implements Reader
  • pointer type *cmd/compile/internal/gc.intWriter implements Reader
  • pointer type *cmd/go/internal/modfile.printer implements Reader
  • pointer type *cmd/pack.FakeFile implements Reader
  • pointer type *cmd/vendor/golang.org/x/crypto/ssh/terminal.MockTerminal implements Reader
  • pointer type *compress/bzip2.reader implements Reader
  • pointer type *compress/flate.decompressor implements Reader
  • pointer type *compress/flate.sparseReader implements Reader
  • pointer type *compress/flate.syncBuffer implements Reader
  • pointer type *compress/gzip.Reader implements Reader
  • pointer type *compress/lzw.decoder implements Reader
  • pointer type *compress/zlib.reader implements Reader
  • pointer type *crypto/ecdsa.zr implements Reader
  • pointer type *crypto/rand.devReader implements Reader
  • pointer type *crypto/rand.reader implements Reader
  • pointer type *crypto/rand_test.countingReader implements Reader
  • pointer type *crypto/tls.Conn implements Reader
  • pointer type *crypto/tls.atLeastReader implements Reader
  • pointer type *crypto/tls.recordingConn implements Reader
  • pointer type *debug/elf.readSeekerFromReader implements Reader
  • pointer type *encoding/ascii85.decoder implements Reader
  • pointer type *encoding/base32.badReader implements Reader
  • pointer type *encoding/base32.decoder implements Reader
  • pointer type *encoding/base32.newlineFilteringReader implements Reader
  • pointer type *encoding/base64.decoder implements Reader
  • pointer type *encoding/base64.faultInjectReader implements Reader
  • pointer type *encoding/base64.newlineFilteringReader implements Reader
  • pointer type *encoding/binary.byteSliceReader implements Reader
  • pointer type *encoding/csv.nTimes implements Reader
  • pointer type *encoding/gob.benchmarkBuf implements Reader
  • pointer type *encoding/gob.decBuffer implements Reader
  • pointer type *encoding/hex.decoder implements Reader
  • pointer type *encoding/json.encodeState implements Reader
  • pointer type *encoding/xml.downCaser implements Reader
  • pointer type *fmt.ss implements Reader
  • pointer type *fmt.stringReader implements Reader
  • pointer type *fmt_test.eofCounter implements Reader
  • pointer type *go/types_test.gen implements Reader
  • pointer type *image/gif.blockReader implements Reader
  • pointer type *image/jpeg.eofReader implements Reader
  • pointer type *image/png.decoder implements Reader
  • pointer type *internal/poll.FD implements Reader
  • pointer type *internal/trace.Writer implements Reader
  • pointer type *internal/x/crypto/hkdf.hkdf implements Reader
  • pointer type *internal/x/text/transform.Reader implements Reader
  • pointer type *internal/x/text/unicode/norm.normReader implements Reader
  • pointer type *LimitedReader implements Reader
  • pointer type *PipeReader implements Reader
  • pointer type *SectionReader implements Reader
  • pointer type *multiReader implements Reader
  • pointer type *pipe implements Reader
  • pointer type *teeReader implements Reader
  • pointer type *io_test.Buffer implements Reader
  • pointer type *io_test.dataAndErrorBuffer implements Reader
  • pointer type *io_test.writeToChecker implements Reader
  • pointer type *math/rand.Rand implements Reader
  • pointer type *mime/multipart.Part implements Reader
  • pointer type *mime/multipart.failOnReadAfterErrorReader implements Reader
  • pointer type *mime/multipart.maliciousReader implements Reader
  • pointer type *mime/multipart.sentinelReader implements Reader
  • pointer type *mime/multipart.slowReader implements Reader
  • pointer type *mime/multipart.stickyErrorReader implements Reader
  • pointer type *mime/quotedprintable.Reader implements Reader
  • pointer type *net.Buffers implements Reader
  • pointer type *net.IPConn implements Reader
  • pointer type *net.TCPConn implements Reader
  • pointer type *net.UDPConn implements Reader
  • pointer type *net.UnixConn implements Reader
  • pointer type *net.conn implements Reader
  • pointer type *net.fakeDNSConn implements Reader
  • pointer type *net.fakeDNSPacketConn implements Reader
  • pointer type *net.netFD implements Reader
  • pointer type *net.pipe implements Reader
  • pointer type *net/http.body implements Reader
  • pointer type *net/http.bodyEOFSignal implements Reader
  • pointer type *net/http.byteReader implements Reader
  • pointer type *net/http.cancelTimerBody implements Reader
  • pointer type *net/http.connReader implements Reader
  • pointer type *net/http.delegateReader implements Reader
  • pointer type *net/http.expectContinueReader implements Reader
  • pointer type *net/http.gzipReader implements Reader
  • pointer type *net/http.http2dataBuffer implements Reader
  • pointer type *net/http.http2gzipReader implements Reader
  • pointer type *net/http.http2pipe implements Reader
  • pointer type *net/http.http2requestBody implements Reader
  • pointer type *net/http.loggingConn implements Reader
  • pointer type *net/http.maxBytesReader implements Reader
  • pointer type *net/http.persistConn implements Reader
  • pointer type *net/http.readWriteCloserBody implements Reader
  • pointer type *net/http/fcgi.writeOnlyConn implements Reader
  • pointer type *net/http/httputil.checkCloser implements Reader
  • pointer type *net/http/httputil.delegateReader implements Reader
  • pointer type *net/http/internal.chunkedReader implements Reader
  • pointer type *net/http_test.infiniteReader implements Reader
  • pointer type *net/http_test.lockedBytesBuffer implements Reader
  • pointer type *net/http_test.logWritesConn implements Reader
  • pointer type *net/http_test.repeatReader implements Reader
  • pointer type *net/http_test.slowTestConn implements Reader
  • pointer type *net/http_test.testConn implements Reader
  • pointer type *net/rpc.writeCrasher implements Reader
  • pointer type *net/textproto.dotReader implements Reader
  • pointer type *os.File implements Reader
  • pointer type *strings.Reader implements Reader
  • pointer type *testing/iotest.dataErrReader implements Reader
  • pointer type *testing/iotest.halfReader implements Reader
  • pointer type *testing/iotest.oneByteReader implements Reader
  • pointer type *testing/iotest.readLogger implements Reader
  • pointer type *testing/iotest.timeoutReader implements Reader
  • pointer type *text/scanner.StringReader implements Reader
  • pointer type *text/scanner.countReader implements Reader
  • struct type archive/tar.readBadSeeker implements Reader
  • struct type archive/tar.readSeeker implements Reader
  • struct type archive/tar.reader implements Reader
  • struct type archive/tar.testNonEmptyReader implements Reader
  • struct type archive/tar.zeroReader implements Reader
  • struct type archive/zip.zeros implements Reader
  • struct type bufio.ReadWriter implements Reader
  • struct type bufio_test.alwaysError implements Reader
  • basic type bufio_test.dataAndEOFReader implements Reader
  • struct type bufio_test.endlessZeros implements Reader
  • struct type bufio_test.errorReaderFromTest implements Reader
  • struct type bufio_test.errorWriterToTest implements Reader
  • struct type bufio_test.onlyReader implements Reader
  • struct type bufio_test.zeroReader implements Reader
  • struct type bytes_test.justReader implements Reader
  • struct type bytes_test.panicReader implements Reader
  • struct type cmd/compile/internal/gc.intReader implements Reader
  • struct type cmd/go/internal/lockedfile.File implements Reader
  • struct type cmd/go/internal/lockedfile.osFile implements Reader
  • struct type cmd/go/internal/modfetch/codehost.deleteCloser implements Reader
  • struct type cmd/internal/bio.Reader implements Reader
  • struct type cmd/link/internal/loadpe.peBiobuf implements Reader
  • struct type cmd/link/internal/loadxcoff.xcoffBiobuf implements Reader
  • basic type cmd/vendor/golang.org/x/crypto/ssh/terminal.passwordReader implements Reader
  • struct type compress/lzw.devZero implements Reader
  • struct type crypto/cipher.StreamReader implements Reader
  • struct type crypto/rand.hideAgainReader implements Reader
  • struct type crypto/tls.brokenConn implements Reader
  • struct type crypto/tls.changeImplConn implements Reader
  • struct type crypto/tls.hairpinConn implements Reader
  • chan type crypto/tls.opensslInput implements Reader
  • struct type crypto/tls.slowConn implements Reader
  • struct type crypto/tls.writeCountingConn implements Reader
  • struct type crypto/tls.zeroSource implements Reader
  • struct type crypto/tls_test.zeroSource implements Reader
  • struct type debug/elf.errorReader implements Reader
  • struct type go/build.readNopCloser implements Reader
  • struct type go/internal/gcimporter.intReader implements Reader
  • struct type eofReader implements Reader
  • struct type io/ioutil.nopCloser implements Reader
  • basic type io_test.byteAndEOFReader implements Reader
  • signature type io_test.readerFunc implements Reader
  • struct type io_test.wantedAndErrReader implements Reader
  • struct type io_test.zeroErrReader implements Reader
  • struct type math/big.byteReader implements Reader
  • struct type mime/multipart.partReader implements Reader
  • struct type mime/multipart.sectionReadCloser implements Reader
  • struct type net.dialResult implements Reader
  • basic type net.neverEnding implements Reader
  • struct type net/http.bodyLocked implements Reader
  • struct type net/http.closeChecker implements Reader
  • struct type net/http.connCloseListener implements Reader
  • struct type net/http.dumpConn implements Reader
  • struct type net/http.errorReader implements Reader
  • struct type net/http.finishAsyncByteRead implements Reader
  • struct type net/http.http2errorReader implements Reader
  • struct type net/http.http2transportResponseBody implements Reader
  • struct type net/http.noBody implements Reader
  • struct type net/http.readerAndCloser implements Reader
  • struct type net/http.socksConn implements Reader
  • struct type net/http.transferBodyReader implements Reader
  • basic type net/http/cgi.neverEnding implements Reader
  • struct type net/http/fcgi.nilCloser implements Reader
  • struct type net/http/fcgi.nopWriteCloser implements Reader
  • struct type net/http/fcgi.rwNopCloser implements Reader
  • struct type net/http/httputil.dumpConn implements Reader
  • struct type net/http/httputil.failureToReadBody implements Reader
  • basic type net/http/httputil.neverEnding implements Reader
  • struct type net/http_test.blockingRemoteAddrConn implements Reader
  • chan type net/http_test.byteFromChanReader implements Reader
  • struct type net/http_test.closeWriteTestConn implements Reader
  • struct type net/http_test.countCloseReader implements Reader
  • struct type net/http_test.countReader implements Reader
  • struct type net/http_test.delayedEOFReader implements Reader
  • struct type net/http_test.dotFileHidingFile implements Reader
  • signature type net/http_test.eofReaderFunc implements Reader
  • struct type net/http_test.errorReader implements Reader
  • struct type net/http_test.fakeFile implements Reader
  • struct type net/http_test.funcConn implements Reader
  • struct type net/http_test.issue12991File implements Reader
  • struct type net/http_test.issue18239Body implements Reader
  • basic type net/http_test.neverEnding implements Reader
  • struct type net/http_test.noteCloseConn implements Reader
  • struct type net/http_test.panicOnSeek implements Reader
  • struct type net/http_test.rwTestConn implements Reader
  • struct type net/http_test.slurpResult implements Reader
  • struct type net/http_test.testCloseConn implements Reader
  • struct type net/http_test.testErrorReader implements Reader
  • struct type net/http_test.writeCountingConn implements Reader
  • struct type net/http_test.writerFuncConn implements Reader
  • struct type net/rpc/jsonrpc.pipe implements Reader
  • struct type net/smtp.faker implements Reader
  • struct type os/exec.closeOnce implements Reader
  • struct type os/exec_test.delayedInfiniteReader implements Reader
  • basic type os_test.neverEnding implements Reader
  • struct type text/scanner.errReader implements Reader
  • interface type archive/tar.fileReader implements Reader
  • interface type cmd/pack.FileLike implements Reader
  • interface type compress/flate.Reader implements Reader
  • interface type fmt.ScanState implements Reader
  • interface type image.reader implements Reader
  • interface type image/gif.reader implements Reader
  • interface type image/jpeg.Reader implements Reader
  • interface type ReadCloser implements Reader
  • interface type ReadSeeker implements Reader
  • interface type ReadWriteCloser implements Reader
  • interface type ReadWriteSeeker implements Reader
  • interface type ReadWriter implements Reader
  • interface type mime/multipart.File implements Reader
  • interface type net.Conn implements Reader
  • interface type net/http.File implements Reader
  • interface type net/http.http2pipeBuffer implements Reader

Writerのimplements一覧

WriterはReaderに比べると少ないです。

  • pointer type *archive/tar.Writer implements Writer
  • pointer type *archive/tar.failOnceWriter implements Writer
  • pointer type *archive/tar.regFileWriter implements Writer
  • pointer type *archive/tar.sparseFileWriter implements Writer
  • pointer type *archive/tar.testFile implements Writer
  • pointer type *archive/zip.countWriter implements Writer
  • pointer type *archive/zip.fileWriter implements Writer
  • pointer type *archive/zip.pooledFlateWriter implements Writer
  • pointer type *archive/zip.rleBuffer implements Writer
  • pointer type *archive/zip.suffixSaver implements Writer
  • pointer type *bufio.Writer implements Writer
  • pointer type *bufio_test.writeCountingDiscard implements Writer
  • pointer type *bytes.Buffer implements Writer
  • pointer type *cmd/compile/internal/gc.dumper implements Writer
  • pointer type *cmd/compile/internal/gc.intWriter implements Writer
  • pointer type *cmd/compile/internal/syntax.dumper implements Writer
  • pointer type *cmd/go/internal/cache.Hash implements Writer
  • pointer type *cmd/go/internal/help.commentWriter implements Writer
  • pointer type *cmd/go/internal/help.errWriter implements Writer
  • pointer type *cmd/go/internal/list.TrackingWriter implements Writer
  • pointer type *cmd/go/internal/modfile.printer implements Writer
  • pointer type *cmd/internal/test2json.converter implements Writer
  • pointer type *cmd/link/internal/ld.OutBuf implements Writer
  • pointer type *cmd/test2json.countWriter implements Writer
  • pointer type *cmd/trace.countingWriter implements Writer
  • pointer type *cmd/vendor/golang.org/x/crypto/ssh/terminal.MockTerminal implements Writer
  • pointer type *cmd/vendor/golang.org/x/crypto/ssh/terminal.Terminal implements Writer
  • pointer type *compress/flate.Writer implements Writer
  • pointer type *compress/flate.dictWriter implements Writer
  • pointer type *compress/flate.errorWriter implements Writer
  • pointer type *compress/flate.failWriter implements Writer
  • pointer type *compress/flate.syncBuffer implements Writer
  • pointer type *compress/gzip.Writer implements Writer
  • pointer type *compress/gzip.limitedWriter implements Writer
  • pointer type *compress/lzw.encoder implements Writer
  • pointer type *compress/lzw.errWriteCloser implements Writer
  • pointer type *compress/zlib.Writer implements Writer
  • pointer type *crypto/hmac.hmac implements Writer
  • pointer type *crypto/md5.digest implements Writer
  • pointer type *crypto/sha1.digest implements Writer
  • pointer type *crypto/sha256.digest implements Writer
  • pointer type *crypto/sha512.digest implements Writer
  • pointer type *crypto/tls.Conn implements Writer
  • pointer type *crypto/tls.brokenConn implements Writer
  • pointer type *crypto/tls.changeImplConn implements Writer
  • pointer type *crypto/tls.cthWrapper implements Writer
  • pointer type *crypto/tls.finishedHash implements Writer
  • pointer type *crypto/tls.opensslOutputSink implements Writer
  • pointer type *crypto/tls.recordingConn implements Writer
  • pointer type *crypto/tls.slowConn implements Writer
  • pointer type *crypto/tls.writeCountingConn implements Writer
  • pointer type *encoding/ascii85.encoder implements Writer
  • pointer type *encoding/base32.encoder implements Writer
  • pointer type *encoding/base64.encoder implements Writer
  • pointer type *encoding/gob.encBuffer implements Writer
  • pointer type *encoding/hex.dumper implements Writer
  • pointer type *encoding/hex.encoder implements Writer
  • pointer type *encoding/json.encodeState implements Writer
  • pointer type *encoding/pem.lineBreaker implements Writer
  • pointer type *encoding/xml.limitedBytesWriter implements Writer
  • pointer type *fmt.pp implements Writer
  • pointer type *go/ast.printer implements Writer
  • pointer type *go/printer.limitWriter implements Writer
  • pointer type *go/printer.trimmer implements Writer
  • pointer type *go/types_test.gen implements Writer
  • pointer type *hash/adler32.digest implements Writer
  • pointer type *hash/crc32.digest implements Writer
  • pointer type *hash/crc64.digest implements Writer
  • pointer type *hash/fnv.sum128 implements Writer
  • pointer type *hash/fnv.sum128a implements Writer
  • pointer type *hash/fnv.sum32 implements Writer
  • pointer type *hash/fnv.sum32a implements Writer
  • pointer type *hash/fnv.sum64 implements Writer
  • pointer type *hash/fnv.sum64a implements Writer
  • pointer type *image/png.encoder implements Writer
  • pointer type *internal/poll.FD implements Writer
  • pointer type *internal/trace.Writer implements Writer
  • pointer type *internal/x/net/http2/hpack.Decoder implements Writer
  • pointer type *internal/x/text/transform.Writer implements Writer
  • pointer type *internal/x/text/unicode/norm.normWriter implements Writer
  • pointer type *PipeWriter implements Writer
  • pointer type *multiWriter implements Writer
  • pointer type *pipe implements Writer
  • pointer type *io_test.Buffer implements Writer
  • pointer type *io_test.dataAndErrorBuffer implements Writer
  • pointer type *io_test.noReadFrom implements Writer
  • pointer type *io_test.writeStringChecker implements Writer
  • pointer type *io_test.writeToChecker implements Writer
  • pointer type *log/syslog.Writer implements Writer
  • pointer type *mime/multipart.part implements Writer
  • pointer type *mime/quotedprintable.Writer implements Writer
  • pointer type *net.IPConn implements Writer
  • pointer type *net.TCPConn implements Writer
  • pointer type *net.UDPConn implements Writer
  • pointer type *net.UnixConn implements Writer
  • pointer type *net.conn implements Writer
  • pointer type *net.fakeDNSConn implements Writer
  • pointer type *net.fakeDNSPacketConn implements Writer
  • pointer type *net.netFD implements Writer
  • pointer type *net.pipe implements Writer
  • pointer type *net/http.chunkWriter implements Writer
  • pointer type *net/http.countingWriter implements Writer
  • pointer type *net/http.http2bufferedWriter implements Writer
  • pointer type *net/http.http2dataBuffer implements Writer
  • pointer type *net/http.http2pipe implements Writer
  • pointer type *net/http.http2responseWriter implements Writer
  • pointer type *net/http.loggingConn implements Writer
  • pointer type *net/http.populateResponse implements Writer
  • pointer type *net/http.response implements Writer
  • pointer type *net/http.timeoutWriter implements Writer
  • pointer type *net/http/cgi.customWriterRecorder implements Writer
  • pointer type *net/http/cgi.limitWriter implements Writer
  • pointer type *net/http/cgi.response implements Writer
  • pointer type *net/http/fcgi.response implements Writer
  • pointer type *net/http/fcgi.streamWriter implements Writer
  • pointer type *net/http/fcgi.writeOnlyConn implements Writer
  • pointer type *net/http/httptest.ResponseRecorder implements Writer
  • pointer type *net/http/httputil.maxLatencyWriter implements Writer
  • pointer type *net/http/internal.chunkedWriter implements Writer
  • pointer type *net/http_test.lockedBytesBuffer implements Writer
  • pointer type *net/http_test.logWritesConn implements Writer
  • pointer type *net/http_test.slowTestConn implements Writer
  • pointer type *net/http_test.testConn implements Writer
  • pointer type *net/http_test.writeCountingConn implements Writer
  • pointer type *net/textproto.dotWriter implements Writer
  • pointer type *os.File implements Writer
  • pointer type *os/exec.prefixSuffixSaver implements Writer
  • pointer type *os/exec_test.badWriter implements Writer
  • pointer type *strings.Builder implements Writer
  • pointer type *strings.appendSliceWriter implements Writer
  • pointer type *testing/iotest.truncateWriter implements Writer
  • pointer type *testing/iotest.writeLogger implements Writer
  • pointer type *text/tabwriter.Writer implements Writer
  • pointer type *text/tabwriter_test.buffer implements Writer
  • struct type archive/tar.testNonEmptyWriter implements Writer
  • struct type archive/tar.zeroWriter implements Writer
  • struct type archive/zip.dirWriter implements Writer
  • struct type archive/zip.fakeHash32 implements Writer
  • struct type archive/zip.nopCloser implements Writer
  • struct type bufio.ReadWriter implements Writer
  • struct type bufio_test.errorReaderFromTest implements Writer
  • struct type bufio_test.errorWriterTest implements Writer
  • struct type bufio_test.errorWriterToTest implements Writer
  • struct type bufio_test.onlyWriter implements Writer
  • struct type bytes_test.justWriter implements Writer
  • struct type cmd/go/internal/lockedfile.File implements Writer
  • struct type cmd/go/internal/lockedfile.osFile implements Writer
  • struct type cmd/go/internal/modfetch/codehost.deleteCloser implements Writer
  • struct type cmd/go/internal/test.lockedStdout implements Writer
  • struct type cmd/internal/bio.Writer implements Writer
  • struct type cmd/internal/bio.mustWriter implements Writer
  • struct type cmd/vendor/github.com/google/pprof/internal/graph.builder implements Writer
  • struct type crypto/cipher.StreamWriter implements Writer
  • struct type crypto/tls.hairpinConn implements Writer
  • struct type encoding/csv.errorWriter implements Writer
  • struct type encoding/xml.errWriter implements Writer
  • struct type encoding/xml.printer implements Writer
  • struct type image/gif.blockWriter implements Writer
  • basic type io/ioutil.devNull implements Writer
  • struct type io_test.errWriter implements Writer
  • struct type io_test.simpleWriter implements Writer
  • signature type io_test.writerFunc implements Writer
  • struct type net.dialResult implements Writer
  • struct type net.writerOnly implements Writer
  • struct type net/http.bufioFlushWriter implements Writer
  • struct type net/http.checkConnErrorWriter implements Writer
  • struct type net/http.connCloseListener implements Writer
  • struct type net/http.dumpConn implements Writer
  • map type net/http.headerOnlyResponseWriter implements Writer
  • struct type net/http.http2chunkWriter implements Writer
  • struct type net/http.http2stickyErrWriter implements Writer
  • struct type net/http.persistConnWriter implements Writer
  • struct type net/http.readWriteCloserBody implements Writer
  • struct type net/http.socksConn implements Writer
  • signature type net/http.writerFunc implements Writer
  • struct type net/http.writerOnly implements Writer
  • struct type net/http/fcgi.bufWriter implements Writer
  • struct type net/http/fcgi.nilCloser implements Writer
  • struct type net/http/fcgi.nopWriteCloser implements Writer
  • struct type net/http/fcgi.rwNopCloser implements Writer
  • struct type net/http/httputil.dumpConn implements Writer
  • struct type net/http/internal.FlushAfterChunkWriter implements Writer
  • struct type net/http_test.blockingRemoteAddrConn implements Writer
  • chan type net/http_test.chanWriter implements Writer
  • struct type net/http_test.closeWriteTestConn implements Writer
  • struct type net/http_test.funcConn implements Writer
  • signature type net/http_test.funcWriter implements Writer
  • struct type net/http_test.http09Writer implements Writer
  • struct type net/http_test.logWrites implements Writer
  • struct type net/http_test.noteCloseConn implements Writer
  • struct type net/http_test.responseWriterJustWriter implements Writer
  • struct type net/http_test.rwTestConn implements Writer
  • struct type net/http_test.terrorWriter implements Writer
  • struct type net/http_test.testCloseConn implements Writer
  • struct type net/http_test.writerFuncConn implements Writer
  • struct type net/rpc.writeCrasher implements Writer
  • struct type net/rpc/jsonrpc.pipe implements Writer
  • struct type net/smtp.dataCloser implements Writer
  • struct type net/smtp.faker implements Writer
  • struct type os/exec.closeOnce implements Writer
  • struct type strings_test.errWriter implements Writer
  • struct type testing.discard implements Writer
  • signature type testing.funcWriter implements Writer
  • struct type testing.indenter implements Writer
  • struct type text/tabwriter_test.panicWriter implements Writer
  • basic type text/template.ErrorWriter implements Writer
  • interface type archive/tar.fileWriter implements Writer
  • interface type cmd/compile/internal/ssa.writeSyncer implements Writer
  • interface type cmd/link/internal/wasm.nameWriter implements Writer
  • interface type crypto/tls.constantTimeHash implements Writer
  • interface type fmt.State implements Writer
  • interface type hash.Hash implements Writer
  • interface type hash.Hash32 implements Writer
  • interface type hash.Hash64 implements Writer
  • interface type image/gif.writer implements Writer
  • interface type image/jpeg.writer implements Writer
  • interface type ReadWriteCloser implements Writer
  • interface type ReadWriteSeeker implements Writer
  • interface type ReadWriter implements Writer
  • interface type WriteCloser implements Writer
  • interface type WriteSeeker implements Writer
  • interface type net.Conn implements Writer
  • interface type net/http.ResponseWriter implements Writer
  • interface type net/http.http2pipeBuffer implements Writer
  • interface type net/http/httputil.writeFlusher implements Writer

ioパッケージのパブリックメソッドを叩いてみる

ioパッケージにはインターフェース以外に実装されている関数があるので叩いてみました。

WriteString

writerに文字列を書き込む

func WriteString() string {
    file, _ := os.Create("testdata/src.txt")
    _, _ = io.WriteString(file, "0123456789")
    b, _ := ioutil.ReadFile("testdata/src.txt")
    return string(b)
}
WriteStringのテスト
func TestWriteString(t *testing.T) {
    str := WriteString()
    if str != "0123456789" {
        t.Errorf("WriteString Error %s", str)
    }
}

ReadAtLeast

指定した値は最低限読み込みバッファー分すべて読み込む(パッケージ内部では使われていますが結構ニッチな機能)

func ReadAtLeast(b int, min int) (string, error) {
    file, _ := os.Open("testdata/src.txt")
    buf := make([]byte, b)
    n, err := io.ReadAtLeast(file, buf, min)
    return string(buf[:n]), err
}
ReadAtLeastのテスト
func TestReadAtLeast(t *testing.T) {
    str, _ := ReadAtLeast(5, 2)
    if str != "01234" {
        t.Errorf("TestReadAtLeast Error %s", str)
    }
}

確保するバッファーよりも最低限読み込むバッファーの方が大きい場合はshort bufferエラーとなる

func TestReadAtLeast_shortBuffer(t *testing.T) {
    _, err := ReadAtLeast(1, 2)
    if err.Error() != "short buffer" {
        t.Errorf("TestReadAtLeast Error %s", err.Error())
    }
}

読み込む対象のバッファーより最低限読み込むバッファーの方が大きい場合はunexpected EOFエラーがとなる

func TestReadAtLeast_unexpectedEOF(t *testing.T) {
    _, err := ReadAtLeast(20, 15)
    if err.Error() != "unexpected EOF" {
        t.Errorf("TestReadAtLeast Error %s", err.Error())
    }
}

ReadFull

指定したバッファー分すべて読み込む

func ReadFull() string {
    file, _ := os.Open("testdata/src.txt")
    buf := make([]byte, 5)
    n, _ := io.ReadFull(file, buf)
    return string(buf[:n])
}
ReadFullのテスト
func TestReadFull(t *testing.T) {
    str := ReadFull()
    if str != "01234" {
        t.Errorf("TestReadFull Error %s", str)
    }
}

CopyN

writerへ指定した値をコピーする

func CopyN() string {
    srcFile, _ := os.Open("testdata/src.txt")
    dstFile, _ := os.Create("testdata/dst.txt")
    written, _ := io.CopyN(dstFile, srcFile, 5)
    file, _ := os.Open("testdata/dst.txt")
    buf := make([]byte, written)
    n, _ := file.Read(buf)
    return string(buf[:n])
}
CopyNのテスト
func TestCopyN(t *testing.T) {
    str := CopyN()
    if str != "01234" {
        t.Errorf("TestCopyN Error %s", str)
    }
}

Copy

readerをwriterにコピーする

func Copy() string {
    srcFile, _ := os.Open("testdata/src.txt")
    dstFile, _ := os.Create("testdata/dst.txt")
    written, _ := io.Copy(dstFile, srcFile)
    buf := make([]byte, written)
    file, _ := os.Open("testdata/dst.txt")
    n, _ := file.Read(buf)
    return string(buf[:n])
}
Copyのテスト
func TestCopy(t *testing.T) {
    str := Copy()
    if str != "0123456789" {
        t.Errorf("TestCopy Error %s", str)
    }
}

CopyBuffer

readerをwriterに指定したバッファー分コピーする

func CopyBuffer() string {
    srcFile, _ := os.Open("testdata/src.txt")
    dstFile, _ := os.Create("testdata/dst.txt")
    written, _ := io.CopyBuffer(dstFile, srcFile, make([]byte, 5))
    buf := make([]byte, written)
    file, _ := os.Open("testdata/dst.txt")
    n, _ := file.Read(buf)
    return string(buf[:n])
}
CopyBufferのテスト
func TestCopyBuffer(t *testing.T) {
    str := CopyBuffer()
    if str != "0123456789" {
        t.Errorf("TestCopyBuffer Error %s", str)
    }
}

LimitReaderRead

値を制限したreaderを取得する

func LimitReaderRead() string {
    srcFile, _ := os.Open("testdata/src.txt")
    limitedReader := io.LimitedReader{srcFile, 5}
    buf := make([]byte, 3)
    n, _ := limitedReader.Read(buf)
    return string(buf[:n])
}

値を制限したreaderを取得し読み込む

func LimitReaderRead2() string {
    srcFile, _ := os.Open("testdata/src.txt")
    limitedReader := io.LimitedReader{srcFile, 5}
    buf := make([]byte, 8)
    n, _ := limitedReader.Read(buf)
    return string(buf[:n])
}
LimitReaderReadのテスト
func TestLimitReaderRead(t *testing.T) {
    str := LimitReaderRead()
    if str != "012" {
        t.Errorf("TestLimitReaderRead Error %s" , str)
    }
}
func TestLimitReaderRead2(t *testing.T) {
    str := LimitReaderRead2()
    if str != "01234" {
        t.Errorf("TestLimitReaderRead Error %s" , str)
    }
}

SectionReaderRead

指定した値でセクション分けしたreaderを取得し読み込む

func SectionReaderRead() string {
    file, _ := os.Open("testdata/src.txt")
    sectionReader := io.NewSectionReader(file, 3, 5)
    buf := make([]byte, 3)
    n, _ := sectionReader.Read(buf)
    return string(buf[:n])
}
SectionReaderReadのテスト
func TestSectionReaderRead(t *testing.T) {
    str := SectionReaderRead()
    if str != "345" {
        t.Errorf("TestSectionReaderRead Error %s", str)
    }
}

SectionReaderSeek

セクションリーダーの読み込み位置を変更する

func SectionReaderSeek() string {
    file, _ := os.Open("testdata/src.txt")
    sectionReader := io.NewSectionReader(file, 5, 10)
    buf := make([]byte, 3)
    n, _ := sectionReader.Read(buf)
    _, _ = sectionReader.Seek(0, 0)
    return string(buf[:n])
}
SectionReaderSeekのテスト
func TestSectionReaderSeek(t *testing.T) {
    str := SectionReaderSeek()
    if str != "567" {
        t.Errorf("TestSectionReaderSeek Error %s", str)
    }
}

SectionReaderReadAt

セクションリーダーを指定した位置から読み込む

func SectionReaderReadAt() string {
    file, _ := os.Open("testdata/src.txt")
    sectionReader := io.NewSectionReader(file, 5, 10)
    buf := make([]byte, 3)
    n, _ := sectionReader.ReadAt(buf, 2)
    return string(buf[:n])
}

SectionReaderReadAtのテスト

func TestSectionReaderReadAt(t *testing.T) {
    str := SectionReaderReadAt()
    if str != "789" {
        t.Errorf("TestSectionReaderReadAt Error %s", str)
    }
}

SectionReaderSize

セクションリーダーのサイズを取得する

func SectionReaderSize() int64 {
    file, _ := os.Open("testdata/src.txt")
    sectionReader := io.NewSectionReader(file, 5, 9)
    return sectionReader.Size()
}
SectionReaderSizeのテスト
func TestSectionReaderSize(t *testing.T) {
    size := SectionReaderSize()
    if size != 9 {
        t.Errorf("TestSectionReaderSize Error %d", size)
    }
}

TeeReaderRead

readerとwriterを渡しておいて読み込んだら同時にwriterにも書き込む

func TeeReaderRead() string {
    srcFile, _ := os.Open("testdata/src.txt")
    dstFile, _ := os.Create("testdata/dst.txt")
    teeReader := io.TeeReader(srcFile, dstFile)
    buf := make([]byte, 3)
    _, _ = teeReader.Read(buf)
    dstFile2, _ := os.Open("testdata/dst.txt")
    n, _ := dstFile2.Read(buf)
    return string(buf[:n])
}
TeeReaderReadのテスト
func TestTeeReaderRead(t *testing.T) {
    str := TeeReaderRead()
    if str != "012" {
        t.Errorf("TestTeeReaderRead Error %s", str)
    }
}

※ 他にもMultiWriterPipeもありますが省略してます

ソースコード

本記事のソースコード

taisa831/sandbox-go

You can’t perform that action at this time. You signed in with another tab or window. You signed out in another tab or window. Reload to refresh your session. Reload to refresh your session.

コピペ実行用ソース

以下をファイルにコピペすればすぐ実行できます

実装

package io
import (
    "io"
    "io/ioutil"
    "os"
)
// writerに文字列を書き込む
func WriteString() string {
    file, _ := os.Create("testdata/src.txt")
    _, _ = io.WriteString(file, "0123456789")
    b, _ := ioutil.ReadFile("testdata/src.txt")
    return string(b)
}
// 指定した値は最低限読み込みバッファー分すべて読み込む
func ReadAtLeast(b int, min int) (string, error) {
    file, _ := os.Open("testdata/src.txt")
    buf := make([]byte, b)
    n, err := io.ReadAtLeast(file, buf, min)
    return string(buf[:n]), err
}
// 指定したバッファー分すべて読み込む
func ReadFull() string {
    file, _ := os.Open("testdata/src.txt")
    buf := make([]byte, 5)
    n, _ := io.ReadFull(file, buf)
    return string(buf[:n])
}
// writerへ指定した値をコピーする
func CopyN() string {
    srcFile, _ := os.Open("testdata/src.txt")
    dstFile, _ := os.Create("testdata/dst.txt")
    written, _ := io.CopyN(dstFile, srcFile, 5)
    file, _ := os.Open("testdata/dst.txt")
    buf := make([]byte, written)
    n, _ := file.Read(buf)
    return string(buf[:n])
}
// readerをwriterにコピーする
func Copy() string {
    srcFile, _ := os.Open("testdata/src.txt")
    dstFile, _ := os.Create("testdata/dst.txt")
    written, _ := io.Copy(dstFile, srcFile)
    buf := make([]byte, written)
    file, _ := os.Open("testdata/dst.txt")
    n, _ := file.Read(buf)
    return string(buf[:n])
}
// readerをwriterに指定したバッファー分コピーする
func CopyBuffer() string {
    srcFile, _ := os.Open("testdata/src.txt")
    dstFile, _ := os.Create("testdata/dst.txt")
    written, _ := io.CopyBuffer(dstFile, srcFile, make([]byte, 5))
    buf := make([]byte, written)
    file, _ := os.Open("testdata/dst.txt")
    n, _ := file.Read(buf)
    return string(buf[:n])
}
// 値を制限したreaderを取得する
func LimitReaderRead() string {
    srcFile, _ := os.Open("testdata/src.txt")
    limitedReader := io.LimitedReader{srcFile, 5}
    buf := make([]byte, 3)
    n, _ := limitedReader.Read(buf)
    return string(buf[:n])
}
// 値を制限したreaderを取得し読み込む
func LimitReaderRead2() string {
    srcFile, _ := os.Open("testdata/src.txt")
    limitedReader := io.LimitedReader{srcFile, 5}
    buf := make([]byte, 8)
    n, _ := limitedReader.Read(buf)
    return string(buf[:n])
}
// 指定した値でセクション分けしたreaderを取得し読み込む
func SectionReaderRead() string {
    file, _ := os.Open("testdata/src.txt")
    sectionReader := io.NewSectionReader(file, 3, 5)
    buf := make([]byte, 3)
    n, _ := sectionReader.Read(buf)
    return string(buf[:n])
}
// セクションリーダーの読み込み位置を変更する
func SectionReaderSeek() string {
    file, _ := os.Open("testdata/src.txt")
    sectionReader := io.NewSectionReader(file, 5, 10)
    buf := make([]byte, 3)
    n, _ := sectionReader.Read(buf)
    _, _ = sectionReader.Seek(0, 0)
    return string(buf[:n])
}
// セクションリーダーを指定した位置から読み込む
func SectionReaderReadAt() string {
    file, _ := os.Open("testdata/src.txt")
    sectionReader := io.NewSectionReader(file, 5, 10)
    buf := make([]byte, 3)
    n, _ := sectionReader.ReadAt(buf, 2)
    return string(buf[:n])
}
// セクションリーダーのサイズを取得する
func SectionReaderSize() int64 {
    file, _ := os.Open("testdata/src.txt")
    sectionReader := io.NewSectionReader(file, 5, 9)
    return sectionReader.Size()
}
// readerとwriterを渡しておいて読み込んだら同時にwriterにも書き込む
func TeeReaderRead() string {
    srcFile, _ := os.Open("testdata/src.txt")
    dstFile, _ := os.Create("testdata/dst.txt")
    teeReader := io.TeeReader(srcFile, dstFile)
    buf := make([]byte, 3)
    _, _ = teeReader.Read(buf)
    dstFile2, _ := os.Open("testdata/dst.txt")
    n, _ := dstFile2.Read(buf)
    return string(buf[:n])
}

テスト

package io
import (
    "testing"
)
func TestWriteString(t *testing.T) {
    str := WriteString()
    if str != "0123456789" {
        t.Errorf("WriteString Error %s", str)
    }
}
func TestReadAtLeast(t *testing.T) {
    str, _ := ReadAtLeast(5, 2)
    if str != "01234" {
        t.Errorf("TestReadAtLeast Error %s", str)
    }
}
func TestReadAtLeast_shortBuffer(t *testing.T) {
    _, err := ReadAtLeast(1, 2)
    if err.Error() != "short buffer" {
        t.Errorf("TestReadAtLeast Error %s", err.Error())
    }
}
func TestReadAtLeast_unexpectedEOF(t *testing.T) {
    _, err := ReadAtLeast(20, 15)
    if err.Error() != "unexpected EOF" {
        t.Errorf("TestReadAtLeast Error %s", err.Error())
    }
}
func TestReadFull(t *testing.T) {
    str := ReadFull()
    if str != "01234" {
        t.Errorf("TestReadFull Error %s", str)
    }
}
func TestCopyN(t *testing.T) {
    str := CopyN()
    if str != "01234" {
        t.Errorf("TestCopyN Error %s", str)
    }
}
func TestCopy(t *testing.T) {
    str := Copy()
    if str != "0123456789" {
        t.Errorf("TestCopy Error %s", str)
    }
}
func TestCopyBuffer(t *testing.T) {
    str := CopyBuffer()
    if str != "0123456789" {
        t.Errorf("TestCopyBuffer Error %s", str)
    }
}
func TestLimitReaderRead(t *testing.T) {
    str := LimitReaderRead()
    if str != "012" {
        t.Errorf("TestLimitReaderRead Error %s" , str)
    }
}
func TestLimitReaderRead2(t *testing.T) {
    str := LimitReaderRead2()
    if str != "01234" {
        t.Errorf("TestLimitReaderRead Error %s" , str)
    }
}
func TestSectionReaderRead(t *testing.T) {
    str := SectionReaderRead()
    if str != "345" {
        t.Errorf("TestSectionReaderRead Error %s", str)
    }
}
func TestSectionReaderSeek(t *testing.T) {
    str := SectionReaderSeek()
    if str != "567" {
        t.Errorf("TestSectionReaderSeek Error %s", str)
    }
}
func TestSectionReaderReadAt(t *testing.T) {
    str := SectionReaderReadAt()
    if str != "789" {
        t.Errorf("TestSectionReaderReadAt Error %s", str)
    }
}
func TestSectionReaderSize(t *testing.T) {
    size := SectionReaderSize()
    if size != 9 {
        t.Errorf("TestSectionReaderSize Error %d", size)
    }
}
func TestTeeReaderRead(t *testing.T) {
    str := TeeReaderRead()
    if str != "012" {
        t.Errorf("TestTeeReaderRead Error %s", str)
    }
}
Posted on

[Golang]Goのio/ioutilパッケージは分かりやすくて使いやすい

Goのioパッケージは主にインターフェースになっていて他のパッケージで多く実装されています。またioパッケージにもパブリックな関数がありファイルの入出力はできますが少し細かい処理になります。io/ioutilパッケージを使うとファイルの入出力処理が簡単にできます。以下にio/ioutilパッケージを使った処理とそれに対するテストコードを記載します。

io/ioutil/ioutil.go

ReadAll()

func ReadAll() string {
    file, _ := os.Open("testdata/src.txt")
    b, _ := ioutil.ReadAll(file)
    return string(b)
}

ReadAllテスト

func TestReadAll(t *testing.T) {
    str := ReadAll()
    if str != "0123456789" {
        t.Errorf("TestReadAll Error. %s", str)
    }
}

ReadFile()

func ReadFile() string {
    b, _ := ioutil.ReadFile("testdata/src.txt")
    return string(b)
}

ReadFileテスト

func TestReadFile(t *testing.T) {
    str := ReadFile()
    if str != "0123456789" {
        t.Errorf("TestReadAll Error. %s", str)
    }
}

WriteFile()

func WriteFile() string {
    b := []byte("0123456789")
    _ = ioutil.WriteFile("testdata/dst.txt", b, os.ModePerm)
    b, _ = ioutil.ReadFile("testdata/dst.txt")
    return string(b)
}

WriteFileテスト

func TestWriteFile(t *testing.T) {
    str := WriteFile()
    if str != "0123456789" {
        t.Errorf("TestReadAll Error. %s", str)
    }
}

ReadDir()

func ReadDir() []os.FileInfo {
    fileInfo, _ := ioutil.ReadDir("testdata")
    return fileInfo
}

ReadDirテスト

func TestReadDir(t *testing.T) {
    fileInfoList := ReadDir()
    for _, fileInfo := range fileInfoList {
        if !(fileInfo.Name() == "src.txt" || fileInfo.Name() == "dst.txt") {
            t.Errorf("TestReadDir Error. %s", fileInfo.Name())
        }
    }
}

NopCloser Close()

NopCloserreaderインターフェースしかなく、closerを持っていない場合にダミーでcloserインターフェースを追加することができます。NopCloserClose()は何もしません。

func NopCloserClose() error {
    reader := bytes.NewBufferString("test")
    readCloser := ioutil.NopCloser(reader)
    return readCloser.Close()
}

NopCloser Closeテスト

func TestNopCloserClose(t *testing.T) {
    if NopCloserClose() != nil {
        t.Errorf("TestNopCloserClose Error. %v", NopCloserClose())
    }
}

DevNull Write()

DevNullを使うと吐き捨て用のWriterを利用することができます。Writeの他にもWriteStringReadFrom関数が用意されているのですが、今の所Writeしか呼び出すことができません(叩き方がわかりません)。これについては気になったのでWriteStringReadFromも叩けるようにGo本体にコードを変更してコントリビュートしてみたのですが、あえなくNGをくらいました。有り難いことに金曜日夕方にリクエストして夜には返信が来たのでレスポンスは早かったです。

Sorry, but we don’t want new public API here.
The type doesn’t need to be exported in order to implement WriteString, etc.
https://go-review.googlesource.com/c/go/+/203377
func DevNullWrite() int {
    writer := ioutil.Discard
    n, _ := writer.Write(make([]byte, 1024))
    return n
}

DevNull Writeテスト

func TestDevNullWrite(t *testing.T) {
    if DevNullWrite() != 1024 {
        t.Errorf("TestDevNullWrite Error. %v", DevNullWrite())
    }
}

Go本体へコントリビュートするにはGerritを使う必要があるのですが、本家のドキュメントがしっかりしているのと、日本語でも「Go にコントリビュートするまでの手順」という記事を公開してくれているので比較的すんなり行うことができました。

Contribution Guide

The Go project welcomes all contributors. This document is a guide to help you through the process of contributing to the Go project, which is a little different from that used by other open source projects. We assume you have a basic understanding of Git and Go.

Go にコントリビュートするまでの手順 – blog.syfm

最近、ついに Go ( golang/go) にコントリビュートしました! 正確にはレビュー中で、レビューが通ってもマージされるのは Go 1.10 なので、だいぶ先は長いですが取り敢えず自分のできることは一通り終わりました。 Go ではレビューシステムに GitHub は使っておらず、代わりに Google が開発した Gerrit を使っています。 普段の GitHub を利用したフローと結構異なっていたので備忘録がてらブログに書いておこうと思います。

io/ioutil/tempfile.go

TempFile()

func TempFile() *os.File {
    f , _ := ioutil.TempFile("testdata/tempfile", "test")
    return f
}

TempFileテスト

func TestTempFile(t *testing.T) {
    f := TempFile()
    if f.Name() == "" {
        t.Errorf("TestTempFile error %s", f.Name())
    }
    defer os.RemoveAll("testdata/tempfile")
}

TempDir()

func TempDir() string {
    name, _ := ioutil.TempDir("testdata", "test")
    return name
}

TempDirテスト

func TestTempDir(t *testing.T) {
    name := TempDir()
    if name == "" {
        t.Errorf("TestTempDir error %s", name)
    }
    fileInfoList, _ := ioutil.ReadDir("testdata")
    for _, fileInfo := range fileInfoList {
        if fileInfo.IsDir() {
            os.RemoveAll("testdata/" + fileInfo.Name())
        }
    }
}

ソースコード

taisa831/sandbox-go

You can’t perform that action at this time. You signed in with another tab or window. You signed out in another tab or window. Reload to refresh your session. Reload to refresh your session.

参考書籍

本書はGoio.Writerio.Readerからはじまりシステムの深いところまで丁寧に説明されているのでとてもおすすめです。Webで無料で見れますし書籍版、PDF版で購入も可能です。

Web版

Goならわかるシステムプログラミング

書籍版

Posted on

Nuxt.js v2.9のリリース情報とDiff

2019年8月20日にNuxt.jsのバージョンが2.8.1から2.9へがリリースされました。2019年9月19日時点ではバージョン2.9.2まであがっています。本投稿ではリリース内容と変更点が見れるように2.8.1から2.9.1のソースのDiffと変更されたファイル一覧を載せておきます。変更ファイル数は、examplestestを除くと約142ファイルでした。またバージョンが上がっただけ、関数にスペースが追加されただけのファイルも多く、それを除くともっと少なくなります。
本リリースでの大きな変更はImportant Notesの中でもTypescript関連の変更が個人的には大きいと思います。Nuxt Typescriptの導入やMigrationはリリースノートにあるページの SetupMigration from Nuxt 2.8 を見れば簡単に行えます。(本記事の一番下でも試しにやってみました。)

リリースノート

nuxtjs.org

Due to a known problem (webpack/webpack#8656, #4869, #4839) users of npm should either remove package-lock.json and reinstall before upgrade or use npm update acorn –depth 20 && npm dedupe after upgrading to 2.4.0. users should have no problems but removing yarn.lock still recommended before the upgrade process.

Diff

v2.8.1からv2.9.2のDiffをつくってみました。

v2.9.2 diff by taisa831 · Pull Request #1 · taisa831/nuxt.js

Types of changes Bug fix (a non-breaking change which fixes an issue) New feature (a non-breaking change which adds functionality) Breaking change (fix or feature that would cause existing functionality to change) Description Checklist: My change requires a change to the documentation. I have updated the documentation accordingly.

変更ファイル

nuxt.js v2.8.1 -> v2.9.2

examples examples,examples/async-component-injection/pages/_slug.vue examples,examples/async-data/pages/posts/_id.vue examples,examples/async-data/pages/posts/index.vue …

examplestestを除いた変更ファイル一覧

  • .circleci/config.yml
  • .eslintignore
  • .eslintrc.js
  • README.md
  • babel.config.js
  • distributions/nuxt-start/package.js
  • distributions/nuxt-start/package.json
  • distributions/nuxt-ts/README.md
  • distributions/nuxt-ts/bin/nuxt-ts.js
  • distributions/nuxt-ts/package.js
  • distributions/nuxt-ts/package.json
  • distributions/nuxt/README.md
  • distributions/nuxt/package.js
  • distributions/nuxt/package.json
  • jest.config.js
  • lerna.json
  • package.json
  • packages/babel-preset-app/README.md
  • packages/babel-preset-app/package.json
  • packages/babel-preset-app/src/index.js
  • packages/babel-preset-app/src/polyfills-plugin.js
  • packages/builder/package.json
  • packages/builder/src/builder.js
  • packages/builder/src/context/build.js
  • packages/builder/src/context/template.js
  • packages/builder/src/ignore.js
  • packages/cli/package.json
  • packages/cli/src/command.js
  • packages/cli/src/commands/build.js
  • packages/cli/src/commands/dev.js
  • packages/cli/src/commands/generate.js
  • packages/cli/src/commands/help.js
  • packages/cli/src/commands/index.js
  • packages/cli/src/commands/start.js
  • packages/cli/src/imports.js
  • packages/cli/src/index.js
  • packages/cli/src/list.js
  • packages/cli/src/options/common.js
  • packages/cli/src/options/index.js
  • packages/cli/src/options/server.js
  • packages/cli/src/run.js
  • packages/cli/src/setup.js
  • packages/cli/src/utils/banner.js
  • packages/cli/src/utils/config.js
  • packages/cli/src/utils/formatting.js
  • packages/cli/src/utils/index.js
  • packages/cli/src/utils/memory.js
  • packages/cli/src/utils/typescript.js
  • packages/config/index.d.ts
  • packages/config/package.json
  • packages/config/src/config/_app.js
  • packages/config/src/config/_common.js
  • packages/config/src/config/build.js
  • packages/config/src/config/index.js
  • packages/config/src/options.js
  • packages/config/types/build.d.ts
  • packages/config/types/env.d.ts
  • packages/config/types/fetch.d.ts
  • packages/config/types/generate.d.ts
  • packages/config/types/globals.d.ts
  • packages/config/types/head.d.ts
  • packages/config/types/hooks.d.ts
  • packages/config/types/index.d.ts
  • packages/config/types/loading.d.ts
  • packages/config/types/module.d.ts
  • packages/config/types/plugin.d.ts
  • packages/config/types/render.d.ts
  • packages/config/types/router.d.ts
  • packages/config/types/server-middleware.d.ts
  • packages/config/types/server.d.ts
  • packages/config/types/vue-configuration.d.ts
  • packages/config/types/watchers.d.ts
  • packages/core/package.json
  • packages/core/src/hookable.js
  • packages/core/src/module.js
  • packages/core/src/nuxt.js
  • packages/core/src/resolver.js
  • packages/generator/package.json
  • packages/generator/src/generator.js
  • packages/server/package.json
  • packages/server/src/context.js
  • packages/server/src/jsdom.js
  • packages/server/src/listener.js
  • packages/server/src/middleware/error.js
  • packages/server/src/middleware/nuxt.js
  • packages/server/src/middleware/timing.js
  • packages/server/src/server.js
  • packages/typescript/package.js
  • packages/typescript/package.json
  • packages/typescript/src/index.js
  • packages/utils/package.json
  • packages/utils/src/cjs.js
  • packages/utils/src/context.js
  • packages/utils/src/lang.js
  • packages/utils/src/locking.js
  • packages/utils/src/modern.js
  • packages/utils/src/resolve.js
  • packages/utils/src/route.js
  • packages/utils/src/serialize.js
  • packages/utils/src/task.js
  • packages/utils/src/timer.js
  • packages/vue-app/index.d.ts
  • packages/vue-app/package.json
  • packages/vue-app/src/index.js
  • packages/vue-app/template/App.js
  • packages/vue-app/template/client.js
  • packages/vue-app/template/components/no-ssr.js
  • packages/vue-app/template/components/nuxt-build-indicator.vue
  • packages/vue-app/template/components/nuxt-child.js
  • packages/vue-app/template/index.js
  • packages/vue-app/template/router.js
  • packages/vue-app/template/router.scrollBehavior.js
  • packages/vue-app/template/utils.js
  • packages/vue-app/types/index.d.ts
  • packages/vue-app/types/process.d.ts
  • packages/vue-app/types/vue.d.ts
  • packages/server/src/middleware/nuxt.js
  • packages/server/src/middleware/timing.js
  • packages/server/src/server.js
  • packages/typescript/package.js
  • packages/typescript/package.json
  • packages/typescript/src/index.js
  • packages/utils/package.json
  • packages/utils/src/cjs.js
  • packages/utils/src/context.js
  • packages/utils/src/lang.js
  • packages/utils/src/locking.js
  • packages/utils/src/modern.js
  • packages/utils/src/resolve.js
  • packages/utils/src/route.js
  • packages/utils/src/serialize.js
  • packages/utils/src/task.js
  • packages/utils/src/timer.js
  • packages/vue-app/index.d.ts
  • packages/vue-app/package.json
  • packages/vue-app/src/index.js
  • packages/vue-app/template/App.js
  • packages/vue-app/template/client.js
  • packages/vue-app/template/components/no-ssr.js
  • packages/vue-app/template/components/nuxt-build-indicator.vue
  • packages/vue-app/template/components/nuxt-child.js
  • packages/vue-app/template/index.js
  • packages/vue-app/template/router.js
  • packages/vue-app/template/router.scrollBehavior.js
  • packages/vue-app/template/utils.js
  • packages/vue-app/types/index.d.ts
  • packages/vue-app/types/process.d.ts
  • packages/vue-app/types/vue.d.ts
  • packages/vue-renderer/package.json
  • packages/vue-renderer/src/renderer.js
  • packages/vue-renderer/src/renderers/base.js
  • packages/vue-renderer/src/renderers/modern.js
  • packages/vue-renderer/src/renderers/spa.js
  • packages/vue-renderer/src/renderers/ssr.js
  • packages/webpack/package.json
  • packages/webpack/src/builder.js
  • packages/webpack/src/config/base.js
  • packages/webpack/src/config/client.js
  • packages/webpack/src/config/modern.js
  • packages/webpack/src/config/server.js
  • packages/webpack/src/plugins/vue/client.js
  • packages/webpack/src/plugins/vue/cors.js
  • packages/webpack/src/plugins/vue/modern.js
  • packages/webpack/src/plugins/vue/server.js
  • packages/webpack/src/plugins/warning-ignore.js
  • packages/webpack/src/utils/perf-loader.js
  • packages/webpack/src/utils/postcss.js
  • packages/webpack/src/utils/style-loader.js
  • renovate.json
  • scripts/changelog.js
  • scripts/dev.js
  • scripts/link.js
  • scripts/package.js
  • scripts/rollup.config.js

Nuxt Typescriptプロジェクトを新規作成する

npx create-nuxt-appNuxtプロジェクトを新規作成する
※ この時点ではTypescriptのソースがある場合は起動できませんがなにもしなければ起動します。

npx create-nuxt-app 
cd 

分離されて新しくなったnuxt/typescript-buildをインストールする

yarn add --dev @nuxt/typescript-build
# OR
npm install --save-dev @nuxt/typescript-build

nuxt.config.jsbuildModules@nuxt/typescript-buildを追記する

// nuxt.config.js
buildModules: ['@nuxt/typescript-build']

tsconfig.jsonを新規作成する

// tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "lib": [
      "esnext",
      "esnext.asynciterable",
      "dom"
    ],
    "esModuleInterop": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "baseUrl": ".",
    "paths": {
      "~/*": [
        "./*"
      ],
      "@/*": [
        "./*"
      ]
    },
    "types": [
      "@types/node",
      "@nuxt/types"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}

このまま起動すると以下のように怒られる場合があるのでtsconfig.jsoncompilerOptions"experimentalDecorators"を追記する

ERROR in /Users/tt-dev/src/taisa831/nuxt-typescript/components/TodoList.vue(47,22):                                                                                                                                                           nuxt:typescript 11:06:14
47:22 Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.
// tsconfig.json
  "compilerOptions": {
    "experimentalDecorators": true
  }

起動するとTypescriptのコードが入っていても問題なく表示されます🎉

npm run dev

※ 既存プロジェクトからのMigration方法は以下です。
https://typescript.nuxtjs.org/migration.html

参考

https://typescript.nuxtjs.org/guide/setup.html#configuration

Posted on

Golang 1.13 released! The difference from 1.12 to 1.13

Golang 1.13 was released in 3 September 2019. This post has difference from 1.12 to 1.13. I check the changes at the release notes and difference of src.

Release Notes

Go 1.13 Release Notes

The latest Go release, version 1.13, arrives six months after Go 1.12. Most of its changes are in the implementation of the toolchain, runtime, and libraries. As always, the release maintains the Go 1 promise of compatibility. We expect almost all Go programs to continue to compile and run as before.

Diff on GitHub

Release branch.go1.13 by taisa831 · Pull Request #1 · taisa831/go

This PR will be imported into Gerrit with the title and first comment (this text) used to generate the subject and body of the Gerrit change. Please ensure you adhere to every item in this list.

Changed Files

golang 1.12 > 1.13

all AUTHORS CONTRIBUTORS SECURITY.md VERSION api/except.txt api/go1.13.txt doc/articles/race_detector.html doc/asm.html doc/code.html doc/contrib.html doc/contribute.html doc/debugging_with_gdb.html doc/devel/release.html doc/effective_go.html doc/go1.13.html doc…

Posted on

Golangを使ってJWTを15分で理解する

JWTとは

JWT(ジョットと言うらしい)はJSON Web Tokenの略で、JSONをベースとしたアクセストークンのためのオープン標準 (RFC 7519) です。色々記事を見ましたが、最終的にWikipediaが分かりやすく一番参考にしました。
https://ja.wikipedia.org/wiki/JSON_Web_Token

JWTの構造

JWTは以下の3つの要素をピリオドで区切った文字列で構成されます。

ヘッダー

署名生成に使用するアルゴリズムを格納します。下記のHS256は、このトークンがHMAC-SHA256で署名されていることを示しています。署名アルゴリズムとしては、SHA-256を使用したHMAC (HS256) や、SHA-256を使用したRSA署名 (RS256) がよく用いられます。

{
 "alg" : "HS256",
 "typ" : "JWT"
}

ペイロード

認証情報などのクレームを格納します。クレームとはペイロードに含める以下のような標準フィールド(クレーム)を指します。JWTの仕様では、トークンに一般的に含まれる7つの標準フィールドが定義されています。また用途に応じた独自のカスタムフィールドを含むこともできます。下記の例では、トークン発行日時を示す標準のクレーム (iat) と、カスタムクレーム (loggedInAs) を格納しています。

{
 "loggedInAs" : "admin",
 "iat" : 1422779638
}
7つのペイロードの標準クレーム

署名

トークン検証用の署名です。署名はヘッダーとペイロードをBase64urlエンコーディングしてピリオドで結合したものから生成します。署名はヘッダーで指定された暗号化アルゴリズムにより生成されます。下記はHMAC-SHA256形式でのコード例です。

HMAC-SHA256(base64urlEncoding(header) + '.' + base64urlEncoding(payload), 'secret key')

JWTを使用するにあたって

JWTはトークンが返され、それをローカルに保存して利用します(主にlocal storageやsession storageが用いられますが、セッションIDのようにCookieを用いる場合もあります。) 認証時にはAuthorizationヘッダーでBearerスキーマを利用します。またサーバー上に認証状態を保持しないステートレスな認証方式です。その為JWT単体ではトークンを無効にすることが出来ません。サーバーに状態を保持すれば可能ですが、その場合ステートレスの利点は失われることになります。
さて、ここまではほぼ Wikipedia に書いてある内容そのままです。ここから実際にGo/GinJWT Middlewareを使って実際の動作を確認してみます。

Go/GinJWT Middlewareを使った動作確認

利用するJWT Middlewareについて

ここでは、「https://github.com/gin-gonic/gin」 を使う前提で、次のMiddlewareを利用します。「https://github.com/appleboy/gin-jwt」。このMiddlewareは、auth_jwt.goの1ファイルでで構成されていて、「https://github.com/dgrijalva/jwt-go」Gin用に薄くラップしたものです。jwt-goはトークンを作成したりパースしたり様々な機能が用意されています。

サンプルソース

サンプルソースは、https://github.com/appleboy/gin-jwt/blob/master/README.md に載っているのでこれを元に確認します。処理は大きく「ログイン時にToken発行する」と「トークン認証&処理実行する」の2種類あります。

ログイン時にToken発行する

ログイン時にTokenを発行する処理は、LoginHandlerです。Routerでは次のように定義しています。LoginHandlerではAuthenticatorPayloadFuncが呼ばれる為、Middlewareにてこれらを実装する必要があります。

r.POST("/login", authMiddleware.LoginHandler)

Authenticatorはログイン認証の為の関数です。例では固定値が設定されていますが、実際は主にDBから値を取得することになると思います。PayloadFuncはペイロードに含めるクレームを設定します。ペイロードには任意のクレームを追加可能なので、ログインIDとなるuserIDをセットしています。

// ログインに基づいたユーザの認証振る舞いをするコールバック
Authenticator: func(c *gin.Context) (interface{}, error) {
  var loginVals login
  if err := c.ShouldBind(&loginVals); err != nil {
    return "", jwt.ErrMissingLoginValues
  }
  userID := loginVals.Username
  password := loginVals.Password
  // ユーザIDとパスワード認証(実際は主にDBから値を取得する)
  if (userID == "admin" && password == "admin") || (userID == "test" && password == "test") {
    return &User{
      UserName:  userID,
    }, nil
  }
  return nil, jwt.ErrFailedAuthentication
},
// ペイロードのクレーム設定
PayloadFunc: func(data interface{}) jwt.MapClaims {
  if v, ok := data.(*User); ok {
    return jwt.MapClaims{
      IdentityKey: v.UserName,
    }
  }
  return jwt.MapClaims{}
}

ログイン時にアクセストークンを取得するにはサーバ起動後に以下のコマンドを実行します。

% http -v --json POST localhost:8000/login username=admin password=admin

実行すると次のような結果が得られます。

POST login HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 42
Content-Type: application/json
Host: localhost:8000
User-Agent: HTTPie/1.0.2
{
    "password": "admin",
    "username": "admin"
}
HTTP/1.1 200 OK
Content-Length: 213
Content-Type: application/json; charset=utf-8
Date: Tue, 27 Aug 2019 03:26:18 GMT
{
    "code": 200,
    "expire": "2019-08-27T13:26:18+09:00",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjY4Nzk5NzgsImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTU2Njg3NjM3OH0.D0z_y8vLEeQW_mtgOCw6gfrmz6eSGfW6uOG7KoEaMAo"
}

LoginHandlerの処理を見てみる

ここであらためてauth_jwt.goLoginHandlerの処理を見てみると内部で何をやっているかが具体的に分かります。エラーチェックなどを省いて主要なところだけ確認してみます。
上記middlewareで実装しているAuthenticatorを呼んでログイン認証します。

data, err := mw.Authenticator(c)

ログイン認証が通ったらクレームを取得します。

// Create the token
token := jwt.New(jwt.GetSigningMethod(mw.SigningAlgorithm))
claims := token.Claims.(jwt.MapClaims)

上記で実装しているPayloadFuncを呼んで独自クレームを設定します。

if mw.PayloadFunc != nil {
  for key, value := range mw.PayloadFunc(data) {
    claims[key] = value
  }
}

更にペイロードに必要なクレーム情報を設定します。expはトークン切れタイムで、orig_iatはトークン生成タイムです。

expire := mw.TimeFunc().Add(mw.Timeout)
claims["exp"] = expire.Unix()
claims["orig_iat"] = mw.TimeFunc().Unix()

最後に署名付きトークンを生成してレスポンスとして返します。

tokenString, err := mw.signedString(token)
mw.LoginResponse(c, http.StatusOK, tokenString, expire)

これらを組み合わせたLoginHandlerの実際の処理です。

func (mw *GinJWTMiddleware) LoginHandler(c *gin.Context) {
    if mw.Authenticator == nil {
        mw.unauthorized(c, http.StatusInternalServerError, mw.HTTPStatusMessageFunc(ErrMissingAuthenticatorFunc, c))
        return
    }
    data, err := mw.Authenticator(c)
    if err != nil {
        mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(err, c))
        return
    }
    // Create the token
    token := jwt.New(jwt.GetSigningMethod(mw.SigningAlgorithm))
    claims := token.Claims.(jwt.MapClaims)
    if mw.PayloadFunc != nil {
        for key, value := range mw.PayloadFunc(data) {
            claims[key] = value
        }
    }
    expire := mw.TimeFunc().Add(mw.Timeout)
    claims["exp"] = expire.Unix()
    claims["orig_iat"] = mw.TimeFunc().Unix()
    tokenString, err := mw.signedString(token)
    if err != nil {
        mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(ErrFailedTokenCreation, c))
        return
    }
    // set cookie
    if mw.SendCookie {
        maxage := int(expire.Unix() - time.Now().Unix())
        c.SetCookie(
            mw.CookieName,
            tokenString,
            maxage,
            "/",
            mw.CookieDomain,
            mw.SecureCookie,
            mw.CookieHTTPOnly,
        )
    }
    mw.LoginResponse(c, http.StatusOK, tokenString, expire)
}
func (mw *GinJWTMiddleware) signedString(token *jwt.Token) (string, error) {
    var tokenString string
    var err error
    if mw.usingPublicKeyAlgo() {
        tokenString, err = token.SignedString(mw.privKey)
    } else {
        tokenString, err = token.SignedString(mw.Key)
    }
    return tokenString, err
}

払い出されたTokenを検証してみる

払い出されたTokenを検証してみます。トークンはピリオドで分割し3つに分けることができます。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjY4Nzk5NzgsImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTU2Njg3NjM3OH0.D0z_y8vLEeQW_mtgOCw6gfrmz6eSGfW6uOG7KoEaMAo
  • ヘッダー:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
  • ペイロード:eyJleHAiOjE1NjY4Nzk5NzgsImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTU2Njg3NjM3OH0
  • 署名:D0z_y8vLEeQW_mtgOCw6gfrmz6eSGfW6uOG7KoEaMAo

ヘッダーとペイロードはbase64でエンコードされているだけなので以下のコマンドで確認することができます。
・ヘッダー

% echo eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 | base64 -D
{"alg":"HS256","typ":"JWT"}%

・ペイロード

% echo eyJleHAiOjE1NjY4Nzk5NzgsImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTU2Njg3NjM3OH0 | base64 -D
{"exp":1566879978,"id":"admin","orig_iat":156687637%

・署名は暗号化されているので当然復元できません。

% echo D0z_y8vLEeQW_mtgOCw6gfrmz6eSGfW6uOG7KoEaMAo | base64 -D
L������k`8,:���ϧ���*�%

またトークンを確認するのに便利なサイトがあります。このサイトを利用するとJWT認証の確認が簡単にできます。認証ができていないと、Invalid Signatureとなりますが、シークレットキーに今回使った文字列secret keyと入力し今回のトークンを貼り付けるとSignature Verifiedと認証成功が確認できます。
https://jwt.io/
・認証NG

・認証OK

トークン認証&処理実行する

次にトークン認証と認証後の処理の実行をみてみます。auth_jwt.goではMiddlewareFuncを呼び出します。RouterにはauthMiddleware.MiddlewareFunc()をかまし、その中に実行したいRouteを設定します。

auth := r.Group("/auth")
auth.Use(authMiddleware.MiddlewareFunc())
{
    auth.GET("/hello", helloHandler)
}
func helloHandler(c *gin.Context) {
    claims := jwt.ExtractClaims(c)
    user, _ := c.Get(middleware.IdentityKey)
    c.JSON(200, gin.H{
        "userID":   claims["id"],
        "userName": user.(*middleware.User).UserName,
        "text":     "Hello World.",
    })
}

middleware側では、IdentityHandlerAuthorizatorをあらかじめ実装しておく必要があります。

// クレームからログインIDを取得する
IdentityHandler: func(c *gin.Context) interface{} {
  claims := jwt.ExtractClaims(c)
  return &User{
    UserName: claims["id"].(string),
  }
},
// トークンのユーザ情報からの認証
Authorizator: func(data interface{}, c *gin.Context) bool {
  // UserNameは主にDBから取得
  if v, ok := data.(*User); ok && v.UserName == "admin" {
    return true
  }
  return false
},

トークンを使ってリクエストを投げるとリクエストが実行されます。

http -f GET localhost:8000/auth/hello "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjY4Nzk5NzgsImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTU2Njg3NjM3OH0.D0z_y8vLEeQW_mtgOCw6gfrmz6eSGfW6uOG7KoEaMAo"  "Content-Type: application/json"

正常にレスポンスが取得できました。

http -f GET localhost:8000/auth/hello "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjY4ODQ5ODQsImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTU2Njg4MTM4NH0.b22SkPtS5q5-YRHf9GCsUstvcKsNh2ds1fRdZN-Yxac"  "Content-Type: application/json"
HTTP/1.1 200 OK
Content-Length: 60
Content-Type: application/json; charset=utf-8
Date: Tue, 27 Aug 2019 04:50:04 GMT
{
    "text": "Hello World.",
    "userID": "admin",
    "userName": "admin"
}

試しに署名の最後の4文字をaaaaに変更してみると署名の認証エラーであるsignature is invalidが返ってきます。

http -f GET localhost:8000/auth/hello "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjY4ODQ5ODQsImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTU2Njg4MTM4NH0.b22SkPtS5q5-YRHf9GCsUstvcKsNh2ds1fRdZN-aaaa"  "Content-Type: application/json"
HTTP/1.1 401 Unauthorized
Content-Length: 46
Content-Type: application/json; charset=utf-8
Date: Tue, 27 Aug 2019 04:50:59 GMT
Www-Authenticate: JWT realm=test zone
{
    "code": 401,
    "message": "signature is invalid"
}

MiddlewareFuncの処理を見てみる

では実際中ではどのような処理が行われているかauth_jwt.goMiddlewareFuncの主要処理をみてみます。
トークンからクレームを取得します。

claims, err := mw.GetClaimsFromJWT(c)

クレーム内のexpをチェックしトークンが有効かをチェックします。

if claims["exp"] == nil {
  mw.unauthorized(c, http.StatusBadRequest, mw.HTTPStatusMessageFunc(ErrMissingExpField, c))
  return
}
if _, ok := claims["exp"].(float64); !ok {
  mw.unauthorized(c, http.StatusBadRequest, mw.HTTPStatusMessageFunc(ErrWrongFormatOfExp, c))
  return
}
if int64(claims["exp"].(float64)) < mw.TimeFunc().Unix() {
  mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(ErrExpiredToken, c))
  return
}

ミドルウェアで実装しているIdentityHandlerを呼び出し、クレームからユーザ情報を取得します。

IdentityHandler: func(c *gin.Context) interface{} {
  claims := jwt.ExtractClaims(c)
  return &User{
    UserName: claims["id"].(string),
  }
},

ミドルウェアで実装しているAuthorizatorを呼び出しIDが一致していたら認証OKとなります。

// トークンのユーザ情報からの認証
Authorizator: func(data interface{}, c *gin.Context) bool {
  // UserNameは主にDBから取得
  if v, ok := data.(*User); ok && v.UserName == "admin" {
    return true
  }
  return false
},

MiddlewareFuncの全処理です。

func (mw *GinJWTMiddleware) MiddlewareFunc() gin.HandlerFunc {
    return func(c *gin.Context) {
        mw.middlewareImpl(c)
    }
}
func (mw *GinJWTMiddleware) middlewareImpl(c *gin.Context) {
    claims, err := mw.GetClaimsFromJWT(c)
    if err != nil {
        mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(err, c))
        return
    }
    if claims["exp"] == nil {
        mw.unauthorized(c, http.StatusBadRequest, mw.HTTPStatusMessageFunc(ErrMissingExpField, c))
        return
    }
    if _, ok := claims["exp"].(float64); !ok {
        mw.unauthorized(c, http.StatusBadRequest, mw.HTTPStatusMessageFunc(ErrWrongFormatOfExp, c))
        return
    }
    if int64(claims["exp"].(float64)) < mw.TimeFunc().Unix() {
        mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(ErrExpiredToken, c))
        return
    }
    c.Set("JWT_PAYLOAD", claims)
    identity := mw.IdentityHandler(c)
    if identity != nil {
        c.Set(mw.IdentityKey, identity)
    }
    if !mw.Authorizator(identity, c) {
        mw.unauthorized(c, http.StatusForbidden, mw.HTTPStatusMessageFunc(ErrForbidden, c))
        return
    }
    c.Next()
}

これで一通りの動作確認ができました。

まとめ

はじめ知らないワードが出てきて理解しきれなかったのですが、実際に触ってみることでJWTについてある程度理解できました。触ってみてステートレスであり、ステートレスであるがゆえにJWT単体ではトークンを無効にすることが出来ないという理由も実感できました。

その他の参考記事

https://qiita.com/k_k_hogetaro/items/0c97f42ecb8207767db2

Posted on

コーチング入門 オススメコーチング書籍3冊!!

これまで特別意識してこなかったコーチングについて意識する機会があったのでオススメしてもらった3冊を読みました。学びがあったこととその感覚の定着の為にメモしておきます。



背景

大学を卒業してソフトウェアエンジニアになり、いちエンジニアからチームリーダー、テックリード、マネージャー、スタートアップのCTOを経験してきました。その中でチームマネージメントや1on1などもやってきました。ただこれまでやってきたことは自分の経験であったり周囲との関わりで得たものをベースに自分なりに考えてやっていました。
そして今、新しい環境(スタートアップ)にジョインしたところ、今の環境には1on1(コーチング)を積極的に進めているエンジニアがいました。そしてそれはこれまで半ばやらされている感じでやっていた1on1とは印象が違いました。そこでもれなく自分もコーチングをしてもらうようになったわけですが、そのコミュニケーションの中でこれまでの自分の活動を振り返ってみると自分が今「コーチング」に興味があることが分かりました(年齢的なこともあると思います)。「マネージメント」と聞くとちょっと抵抗感があるけど「コーチング」と聞くと抵抗感がないという不思議さもあります。そんなこんなでオススメしてもらった書籍を読み、体系的に学びを得た上で、今後の活動に活かそうと思います。

1冊目 コーチング・マネジメント―人と組織のハイパフォーマンスをつくる

書評

2002年に出版された本なので今から約20年程前の本ですが、読んでいても時間の経過による内容のずれなどは全く気になりません。前半から後半にかけては、コーチングの基本・詳細と進んでいき最後の方にはコーチングを導入するにあたってのチェックリストがあるといった構成になっています。中でも一番の要点は、「コーチング・フロー」と「いかに聞くのか?」ということだと思います。「コーチング・フロー」は次のとおりです。

  1. 現状の明確化
  2. 望ましい状態の明確化
  3. ギャップを引き起こしている理由と背景の発見
  4. 行動計画を立てる
  5. フォロー

これだけ見ると当たり前のようですが、これを実際に実践する・させる為に「いかに聞くのか?」というスキルが求められます。本の中ではそのコミュニケーションへのアプローチの方法やコミュニケーションが如何に大事かが書かれています。やり方や捉え方によっては詰めているようにも感じられそうな程の質問攻めですがそこのバランスが重要なポイントになるかと思います。

2冊目 この1冊ですべてわかる 新版 コーチングの基本

書評

2009年に出版された書籍の新版(2019年)です。この本は「コーチング・マネジメント」より詳細に具体的にコーチングについて書かれています。後半では実例による説明もあります。目次がパッと見分かりやすかったので記載しておきます。

  • 1章 コーチングとは何か
  • 2章 コーチのもつべき視点
  • 3章 コーチングの3原則
  • 4章 コーチング・プロセス
  • 5章 コーチングのスキルと実践例
  • 6章 組織へのコーチング

本書でも4章で「コーチング・プロセス」とし「コーチング・マネジメント」の「コーチング・フロー」が書かれています。本書で特筆すべきは「コーチが持つべき3つの視点」として以下の3つ(PBPの視点)について言及されていることです。これらは三角形となりそれぞれ相互に作用しているようです。

  • Possesion(身につけるもの)
  • Behavior(行動)
  • Presence(考え方、信念)

全体的に図が多く具体的に書かれているので「コーチング・マネジメント」と合わせて読むとより理解が深まると思います。

3冊目 0秒リーダーシップ:「これからの世界」で圧倒的な成果を上げる仕事術

書評

2016年に出版された本で、上記2冊と違い著者が外国人です。著者は日本に長年いながらも、グーグルやモルガン・スタンレーで人材開発を務めていたとあって外国から見た日本という視点がおもしろい点です。上記2冊と違いコーチングではなく、リーダーシップはこうあるべきということが書かれています。グーグルの話や、マインドフルネス、禅などの話も出てきます。コーチングという文脈ではないので上記2冊+αな気持ちで読むとよいかもしれません。
本書で気になったワード

僕はよく英語で、「Leadership is mobilzing people to tackle tough problems.(リーダーシップとは、難問に取り組むために人々を動かしていくこと)」という定義を用います。
Learn, Relearn, Unlearn
学ぶことは大事だが、ただ知識を増やす(Learn)だけではなく、学び直す(relearn)の必要があります。完全に時代遅れになった考え方、価値観や信念は手放す(unlearn)べきです。
Posted on

Vue.js+TypeScriptで外部APIを使ったTODOリストを作ってみた

Vue.jsで外部APIを使ったTODOリストを作ってみた に続き、それのTypeScript版を作ってみました。TODOリスト用のAPIは以前書いたこちらのAPI「Go言語 GORM+GinでTODOリストのAPIを作ってみた」を利用します。CORSを全て許可しているのでどこからでも叩けるようになっています。TypeScriptを書くのは今回が初めてなので、誤っている箇所やもっとよい書き方などがあれば指摘して頂ければと思います。

できたもの

できたものはこちらです。
http://vuejs-ts.taisablog.com/todo

APIのエンドポイント

APIのエンドポイントは以下としました。

URL    http://gin.taisablog.com/api/v1/
GET    /todo       // 一覧表示
POST   /todo       // 新規作成
GET    /todo/:id   // 編集画面表示
PUT    /todo/:id   // 編集(今回未使用)
DELETE /todo/:id   // 削除

プロジェクト作成

vue-cliを使ってプロジェクト作成をしました。プロジェクト作成のコマンドを打つと、色々と聞かれますが、TypeScriptを利用する為にManually select featuresを選択し、TypeScriptをONにします。ここではRouterもONにしました。

% npm install -g @vue/cli
% vue create my-project
  default (babel, eslint)
❯ Manually select features

vue-cliでできたプロジェクトのsrc配下の構成は以下となっています。今回はそこにTodo.vueTodoList.vueを追加して実装しました。views配下で実装するだけでも大丈夫ですが、今回はあえてviews/Todo.vueからTodoList.vueコンポーネントを呼び出す形としました。

.
├── App.vue
├── assets
│   └── logo.png
├── components
│   ├── HelloWorld.vue
│   └── TodoList.vue ← 新規追加
├── main.ts
├── router.ts
├── shims-tsx.d.ts
├── shims-vue.d.ts
└── views
    ├── About.vue
    ├── Home.vue
    └── Todo.vue ← 新規追加

views/Todo.vue

TodoList.vueコンポーネントを呼び出します。


  
Vue logo
import { Component, Vue } from 'vue-property-decorator' import TodoList from '@/components/TodoList.vue' @Component({ components: { TodoList, }, }) export default class Todo extends Vue {}

components/TodoList : importとクラス定義

axiosを利用するのでインストールします。

% npm i axios
import { Component, Vue } from 'vue-property-decorator'
import axios from 'axios'
const NOT_STARTED = 1
const FINISHED = 3
@Component
export default class TodoList extends Vue {
   ここに実装
}

インスタンス変数定義

private todoList: string[] = []
private inputField: string = ''
private baseUrl: string = 'http://gin.taisablog.com/api/v1/'

createdフック

createdフックでロード時に一覧を取得します。

public created() {
  this.getTodo()
}

一覧を取得する

public async getTodo() {
  try {
    const response = await axios.get(this.baseUrl + 'todo')
    this.todoList = response.data
    return this.todoList
  } catch (e) {
    return e
  }
}

タスクを追加する

public async addTodo() {
  if (!this.inputField) {
    return
  }
  try {
    const params = {
      text: this.inputField,
      status: 1,
    }
    await axios.post(this.baseUrl + 'todo', JSON.stringify(params))
    this.getTodo()
    this.inputField = ''
  } catch (e) {
    return e
  }
}

タスクを削除する

public async deleteTodo(todo: any) {
  try {
    await axios.delete(this.baseUrl + 'todo/' + todo.ID)
    this.getTodo()
  } catch (e) {
    return e
  }
}

タスクを完了にする

public async toggle(todo: any) {
  try {
    let status = 0
    if (todo.Status === NOT_STARTED) {
      status = FINISHED
    } else {
      status = NOT_STARTED
    }
    const params = {
      '{status}': status,
    }
    await axios.put(this.baseUrl + 'todo/' + todo.ID, JSON.stringify(params))
    todo.Status = status
  } catch (e) {
    return e
  }
}

HTML

HTMLとCSSはもう少し書き直したいです。


    

Vue.js TODO List

  • {{ todo.Text }}
    {{ todo.Text }}
    X

TodoList.vue全部


    

Vue.js TODO List

  • {{ todo.Text }}
    {{ todo.Text }}
    X
import { Component, Vue } from 'vue-property-decorator' import axios from 'axios' const NOT_STARTED = 1 const FINISHED = 3 @Component export default class TodoList extends Vue { private todoList: string[] = [] private inputField: string = '' private baseUrl: string = 'http://gin.taisablog.com/api/v1/' public created() { this.getTodo() } public async getTodo() { try { const response = await axios.get(this.baseUrl + 'todo') this.todoList = response.data return this.todoList } catch (e) { return e } } public async addTodo() { if (!this.inputField) { return } try { const params = { text: this.inputField, status: 1, } await axios.post(this.baseUrl + 'todo', JSON.stringify(params)) this.getTodo() this.inputField = '' } catch (e) { return e } } public async deleteTodo(todo: any) { try { await axios.delete(this.baseUrl + 'todo/' + todo.ID) this.getTodo() } catch (e) { return e } } public async toggle(todo: any) { try { let status = 0 if (todo.Status === NOT_STARTED) { status = FINISHED } else { status = NOT_STARTED } const params = { '{status}': status, } await axios.put(this.baseUrl + 'todo/' + todo.ID, JSON.stringify(params)) todo.Status = status } catch (e) { return e } } } .todoList { width: 100%; } .clearfix::after { content: ''; display: block; clear: both; } .inputWrapper { position: relative; width: 380px; margin: auto; display: block; } .inputWrapper input[type='text'] { font: 15px/24px sans-serif; box-sizing: border-box; width: 100%; padding: 0.3em; transition: 0.3s; letter-spacing: 1px; border: 1px solid #1b2538; border-radius: 4px; } .ef input[type='text']:focus { border: 1px solid #da3c41; outline: none; box-shadow: 0 0 5px 1px rgba(218, 60, 65, .5); } .txtBoxWrapper { float: left; width: 270px; } .addBtnWrapper { float: right; } .addBtn { position: relative; display: block; text-decoration: none; color: #FFF; background: #007bff; border: solid 1px #007bff; border-radius: 4px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2); text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2); width: 100px; height: 35px; font-size: 16px; } ul { list-style: none; } li { border: 1px solid #dee2e6; border-top-left-radius: .25rem; border-top-right-radius: .25rem; margin: 10px auto; width: 50%; height: 80px; } .todo { display: flex; justify-content: space-between; align-items: center; width: 100%; height: 100%; } .chkboxLabel { width: 20px; display: inline-block; text-align: left; margin-right: 10px; } .chkbox { transform: scale(1.3); margin-left: 10px; } .todoTxt { font-size: 20px; width: 100%; text-align: center; vertical-align: middle; display: inline-block; } .todoTxt.NotStarted { text-decoration: none; } .todoTxt.Finished { text-decoration: line-through; } .deleteBtn { color: pink; text-align: right; margin-right: 20px; margin-left: 10px; width: 20px; display: block; font-size: 20px; cursor: pointer; } @media screen and (max-width: 520px) { ul { list-style: none; padding: 0; margin: 0; } li { border: 1px solid #dee2e6; border-top-left-radius: .25rem; border-top-right-radius: .25rem; margin: 10px 0; width: 100%; height: 80px; padding: 0; } .inputWrapper { margin: 0px auto } }

ソース

https://github.com/taisa831/sandbox-vuejs-ts

まとめ

TypeScriptを書くのが初めてだったので(書籍自体あまりなかったですが)事前に2冊購入して読みました。学習コストが大変かと思っていましたが、評判通りJavaやC#のようなサーバサイドのように書けるので書きやすくてよいです。


Posted on

Vue.jsで外部APIを使ったTODOリストを作ってみた

APIを使ったTODOリストをVue.jsで作ってみました。TODOリスト用のAPIは以前書いたこちらのAPI「Go言語 GORM+GinでTODOリストのAPIを作ってみた」を利用します。CORSを全て許可しているのでどこからでも叩けるようになっています。

できたもの

できたものはこちらです。http://vuejs.taisablog.com/todo

APIのエンドポイント

APIのエンドポイントは以下としました。

URL    http://gin.taisablog.com/api/v1/
GET    /todo       // 一覧表示
POST   /todo       // 新規作成
GET    /todo/:id   // 編集画面表示
PUT    /todo/:id   // 編集(今回未使用)
DELETE /todo/:id   // 削除

TODOリストの処理

プロジェクトはvue-cliで作成し、APIはaxiosを利用しました。componentsTodo.vueファイルを作成しそこにすべての処理を書いています。

インポート

import axios from 'axios'
const NOT_STARTED = 1 // 未対応
const STARTED = 2 // 対応中
const FINISHED = 3 // 完了

data function

name: "Todo",
data() {
  return {
    todoList: [],
    inputField: '',
    isActive: false,
    baseUrl: 'http://gin.taisablog.com/api/v1/'
  }
},

created function

created() {
  this.getTodo()
},

methods : 一覧を取得する

async getTodo() {
  try {
    let response = await axios.get(this.baseUrl + 'todo')
    this.todoList = response.data
  } catch (e) {
    console.log(e)
  }
},

methods : タスクを追加する

async addTodo() {
  // inputFieldの空チェック
  if (!this.inputField) {
    return
  }
  try {
    let params = {
      'text': this.inputField,
      'status': 1
    }
    await axios.post(this.baseUrl + 'todo', JSON.stringify(params))
    // 一覧取得
    this.getTodo()
    // inputFieldを空にする
    this.inputField = ''
  } catch (error) {
    console.log(error)
  }
},

methods : タスクを削除する

async deleteTodo(todo) {
  try {
    await axios.delete(this.baseUrl + 'todo/' + todo.ID)
    // 一覧を取得する
    this.getTodo()
  } catch (e) {
    console.log(e)
  }
},

methods : タスクを完了にする

async toggle(todo) {
  try {
    let status = 0
    if (todo.Status === NOT_STARTED) {
      status = FINISHED
    } else {
      status = NOT_STARTED
    }
    let params = {
      'status': status
    }
    await axios.put(this.baseUrl + 'todo/' + todo.ID, JSON.stringify(params))
    todo.Status = status
  } catch (e) {
    console.log(e)
  }
}

HTML


    

Vue.js TODO List

  • {{ todo.Text }}
    {{ todo.Text }}
    X

Todo.vue全部

HTMLとスタイルはあえてフレームワークを使わず自力で作成したのでボロボロです。


    

Vue.js TODO List

  • {{ todo.Text }}
    {{ todo.Text }}
    X
import axios from 'axios' const NOT_STARTED = 1 // 未対応 const STARTED = 2 // 対応中 const FINISHED = 3 // 完了 export default { name: "Todo", data() { return { todoList: [], inputField: '', isActive: false, baseUrl: 'http://gin.taisablog.com/api/v1/' } }, created() { this.getTodo() }, methods: { async getTodo() { try { let response = await axios.get(this.baseUrl + 'todo') this.todoList = response.data } catch (e) { console.log(e) } }, async addTodo() { if (!this.inputField) { return } try { let params = { 'text': this.inputField, 'status': 1 } await axios.post(this.baseUrl + 'todo', JSON.stringify(params)) this.getTodo() this.inputField = '' } catch (error) { console.log(error) } }, async deleteTodo(todo) { try { await axios.delete(this.baseUrl + 'todo/' + todo.ID) this.getTodo() } catch (e) { console.log(e) } }, async toggle(todo) { try { let status = 0 if (todo.Status === NOT_STARTED) { status = FINISHED } else { status = NOT_STARTED } let params = { 'status': status } await axios.put(this.baseUrl + 'todo/' + todo.ID, JSON.stringify(params)) todo.Status = status } catch (e) { console.log(e) } } }, } .todoList { width: 100%; } .clearfix::after { content: ''; display: block; clear: both; } .inputWrapper { position: relative; width: 380px; margin: auto; display: block; } .inputWrapper input[type='text'] { font: 15px/24px sans-serif; box-sizing: border-box; width: 100%; padding: 0.3em; transition: 0.3s; letter-spacing: 1px; border: 1px solid #1b2538; border-radius: 4px; } .ef input[type='text']:focus { border: 1px solid #da3c41; outline: none; box-shadow: 0 0 5px 1px rgba(218, 60, 65, .5); } .txtBoxWrapper { float: left; width: 270px; } .addBtnWrapper { float: right; } .addBtn { position: relative; display: block; text-decoration: none; color: #FFF; background: #007bff; border: solid 1px #007bff; border-radius: 4px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2); text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2); width: 100px; height: 35px; font-size: 16px; } ul { list-style: none; } li { border: 1px solid #dee2e6; border-top-left-radius: .25rem; border-top-right-radius: .25rem; margin: 10px auto; width: 50%; height: 80px; } .todo { display: flex; justify-content: space-between; align-items: center; width: 100%; height: 100%; } .chkboxLabel { width: 20px; display: inline-block; text-align: left; margin-right: 10px; } .chkbox { transform: scale(1.3); margin-left: 10px; } .todoTxt { font-size: 20px; width: 100%; text-align: center; vertical-align: middle; display: inline-block; } .todoTxt.NotStarted { text-decoration: none; } .todoTxt.Finished { text-decoration: line-through; } .deleteBtn { color: pink; text-align: right; margin-right: 20px; margin-left: 10px; width: 20px; display: block; font-size: 20px; cursor: pointer; } @media screen and (max-width: 520px) { ul { list-style: none; padding: 0; margin: 0; } li { border: 1px solid #dee2e6; border-top-left-radius: .25rem; border-top-right-radius: .25rem; margin: 10px 0; width: 100%; height: 80px; padding: 0; } .inputWrapper { margin: 0px auto } }

ソース

ソースはこちらにあがっています。
https://github.com/taisa831/sandbox-vuejs

まとめ

もっとスマートな書き方があるかしれませんが、これで一通り動作するようになりました。次はこれをTypeScriptに書き換えたものを作成する予定です。HTMLとCSSに一番時間がかかったのでもっとスマートにさらっと書けるようになりたいです。

Posted on

2019年前半の振り返り

2019年が半年を過ぎたので振り返ります。

2019年の抱負

本業頑張るのはもとより、2019年の抱負がある程度固まってきたので書いておきます。 これまでAWSを自分で多く触るケースはあまりありませんでしたが、GCPを使うケースが増えてきたので今年からはAWSではなくGCPをたくさん触っていこうと思います。 …

Google Cloud Platformを使う

Firebaseを触る機会があって、FirestoreCloud Functionsを扱いました。あとGCEに migro instance を使って sandbox ページ( http://sandbox.taisablog.com/)を立ててみたりしました。 FirebaseGCEもかなりいい感じなので今後は個人でちょくちょく使っていくと思います。

数学をやる

間に合わせ程度に以下の投稿にある内容をやりました。次はまたしばらく必要になりそうなタイミングになったら進めてみようと思います。

宅建をとる

いろいろと調べたり少しやってみたりしたところ、以下をやればなんとか合格できる気がしましたが状況が変わってきたので受験するのはやめました。

  • 宅建みやざき塾でテキストを購入(テキストだけの購入は不可)
  • 何回か講義を受ける
  • 3月頃にはじめて10月に向けて通勤時間などを活用して約300時間くらい費やす
  • 直前に対策講義を受ける

体力をつける

相変わらず走れていませんが軽い筋トレをしつつ体重は4キロ程減らせました。あと3キロ減が目標です。

ブログ記事

1月から6月末までで11記事でした。年間月2記事で24記事を目安にしているので1記事程足りないペースでした。

7月の記事

目標の見直し

2019年後半は以下をがっちり身に着けることを目標にします。目標の詳細については違うタイミングで少しブレイクダウンする予定です。

技術系

  • JavaScript(Vue.js)
  • Go
  • CSS / Stylus
  • AWS

その他

  • 健康体を維持する / 体力をつける