emahiro/b.log

Drastically Repeat Yourself !!!!

GoのtimeのFormat表記方法でハマったこと

GolangUnixタイムをISO8601形式に変換したいということがあって、実際に返還する際にFormatの指定の仕方でかなりハマりました。

こういった方の記事を参考にしたらすぐわかったのですが、

GO言語での日付処理

そもそもGoで時刻表記をするときにちょっとクセがあるのでメモとしてまとめてます。

GAE上では必ずUTC

最初にローカルで time を使ったときはJSTだったのですが、GAE上で動かしたときは自動的にUTCになってしまうので、JSTに変換しなければならない、というところでハマりました。
TimeZoneの話ではないんですね。

なので日本時間に合わせます。

import time

func main () {

    var (
        location := time.FixedZone("Asia/Tokyo", 9*60*60)
        now := func() time.Time { return time.Now().In(location) }
    )

    fmt.Printf("%v", now.Format(time.RFC3339))

}

独特なフォーマットの表記方法

日本時間への変更でも記述してありますが、goで指定した形式への変換は time.Format を使います。
ここで、RFC3339 ではなく、明示的にISO8601形式に変えます。

通常の言語であれば、年をY、月をMみたいに形式が決まっています。 なので、goでも同じことしようとしたらgoでは明示的に 2000-01-01T01:00:00+09:00 みたいに書式の結果で形式を指定します。 そこで試しに、表示させたい形式を適当に作ってみたところ。

now := time.Now()
fmt.Printf("%s", now.Format("2000-01-01T01:00:00+09:00"))

// 7074-09-78T77:79:14+07:00 ????

なんかよくわからない形式になってて、変換が失敗している...

ここでどうして変換したい書式を指定してもうまくいかないのか結構ハマりました。

結論としては goでのtimeのformat変換では使える文字が決まっている っていうことになります。

どういうことかというとgoのTimeパッケージのドキュメントを見るとかいてありました。
time - The Go Programming Language

変換したいフォーマットで使えるのは年なら Y ではなく 2006 みたいな感じです。

now := time.Now()
fmt.Printf("%s", now.Format("2006-01-02T15:04:05+09:00"))
// 2017-7-12T22:24:43+09:00

ちゃんと変換できました。

※ ISO8601形式で +09:00 と表記する形式は標準パッケージには用意されてなかったので、自前で書いてます。

このフォーマットの一覧はtimeパッケージの format.goの中に書いてます。

const (
        ANSIC       = "Mon Jan _2 15:04:05 2006"
        UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
        RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
        RFC822      = "02 Jan 06 15:04 MST"
        RFC822Z     = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
        RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
        RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
        RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
        RFC3339     = "2006-01-02T15:04:05Z07:00"
        RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
        Kitchen     = "3:04PM"
        // Handy time stamps.
        Stamp      = "Jan _2 15:04:05"
        StampMilli = "Jan _2 15:04:05.000"
        StampMicro = "Jan _2 15:04:05.000000"
        StampNano  = "Jan _2 15:04:05.000000000"
)

Goのsliceで重複を削除する

重複削除処理を実装する

ruby でいうところの uniq メソッドみたいなものが golang の slice にもないのかと思って調べてみたけどないらしいので、重複のある slice に対して独自に処理を実装しなければ行けない。

go は非常にシンプルでLL言語をずっと書いていたエンジニアとしては覚えることが比較的少なく、静的型付けされていて安全で、高速という点で有用なツールだと思うけど、シンプルすぎる反面、LL言語ならデフォルトのAPIやメソッドとして提供されていそうな処理が実装されてなかったりすることが多い。。。

LL言語の処理をブラックボックス化してしまうことに比べれば、いいことのように感じる一方で、これくらい用意しておいてよ~的な感想もしばしば(笑)

というわけで go における slice の重複削除の実装方法を調べてみた。

方法

  1. mapを使う
  2. mapと空のstructを使う

map を使う方法

make(map[string]int) or make(map[string]bool) のようにkeyに配列の要素をおいて値を適当にintやboolをぶち込みます。

arr := [string]{"a", "b", "c", "a"}
m := make(map[string]bool)
uniq := [] string{}

for _, ele := range arr {
    if !m[ele] {
        m[ele] = true
        uniq = append(uniq, ele)
    }
}

fmt.Printf("%v", uniq) // ["a", "b", "c"]

どういうことかというと、重複キーがあるので、同様のキーを持つmapの場合は新しく値を上書きしない。
ここの処理で言うところの m["a"] = true は一度目はこれが呼ばれるけど、二度目はすでに true なので if句の中に入ってず、resultに a が二度入ることがない。
こんな感じでmapにboolをいれることで重複を削除する方法はgoでは一般的です。

空のstructとの合わせ技

だいたいのユースケースにおいてintかboolをぶち込む方法で事足りる前提で付け足しですが、空のstructを使っても同じことができます。
実際に出会う機会があるかはわかりませんが、メモリ的にきついめの状況があるかもしれず、intやboolでいちいちメモリ空間とってられん、的な状況があるかもしれません。(ないかもしれません)

arr := [string]{"a", "b", "c", "a"}
m := make(map[string]struct{}) // 空のstructを使う

for _, ele := range arr {
    m[ele] = struct{}{} // m["a"] = struct{}{} が二度目は同じものとみなされて重複が消える。
}

uniq := [] string{}
for i := range m {
    uniq := append(uniq, i)
}

fmt.Printf("%v", uniq) // ["a", "b", "c"]

loopを二度使わなければなりませんが、intやboolようにメモリを確保しなくていいので省メモリで実装できます。 struct{}{} はgoで出て来るお作法みないなもので、valueに意味はないよっていうこと表します。

Goでシェルを実行するCLIToolを書く

goで外部コマンド(デフォルトのコマンドやshellスクリプト)を実行するCLIツールを作ったのでその触りをまとめます。
内容は、goでCLIツール書く時の実装方法について。

やったこと

  1. shellスクリプトを外部コマンドとして叩く
  2. goの os/exec パッケージを利用してコマンドをひたすら記述していく

参考 Golangで外部コマンドを実行する方法まとめ

ディレクトリ構成

-- 
 |- app.go
 |- test.sh

shellスクリプトを外部コマンドとして叩く

使ったシェルスクリプトはとりあえず Hello を表示するだけ

#!bin/shell
echo "Hello"

goのスクリプトこち

package main

import (
    "fmt"
    "os"
    "os/exec"
)

func main() {
    // カレントディレクトリを移動
    os.Chdir("./cmds")
    // コマンドを実行 .(ピリオド)は使えずにちゃんと sh コマンドを使う
    out, err := exec.Command("sh", "./test.sh").Output()
    if err != nil {
        fmt.Println(err.Error())
        os.Exit(1)
    }
    fmt.Println(string(out))
}

実行結果

$ go run app.go
Hello

できた。

パッケージを利用してコマンドをひたすら記述していく

使用パッケージ: os/exec

外部コマンド実行に際してとく使うインターフェイス

  1. exec.Command(cmd, args).Run() 結果を出力せずに実行。戻り値はerror
  2. exec.Command(cmd, args).Output() 実行結果で何かしら出力される場合はこちら。戻り値はoutput結果([]byte型)とerror
package main

import (
  "fmt"
  "os.exec"
)

func main(){
out, err := exec.Command("ls", "-la").Output()
  if err != nil {
    fmt.Println(err.Error())
    os.Exit(1)
  }
}

引数が複数ある時

git add XXXX 等など…

この場合、exec.Command(cmd, args) のargs に git 以下を入れるんですが、argsはslice型で取らないければなりません。 そこで git を例に取るならば add XXXX がargsの中身なり、これはargsは配列でする必要がある。

import (
  // 追加
  "strings"
)

func main (){
  // スペースごとに区切って配列化
  args := Strings.Filelds("add XXXX") // => ["add", "XXXX"]
  out, err := exec.Command("git", args...).Output() 
  if err != nil {
    fmt.Println(err.Error())
    os.Exit(1)
  }
  fmt.Pringln(string(out))

}

exec.Command は第一引数にコマンドを取り、それ以外は第2引数に入れなければならない。

goで実際にコマンドをつらつら書きながら進める場合、上記実装のようにひたすらコマンドの実行と成功の可否(outputとエラー内容)を愚直に確認しながら書いていきます。
もし処理が止まったら必ずexitしましょう。

goからshを呼ぶのはただたんにshellを書いているだけ。むしろそちらよりもgoにコマンドを実行してもらう方法の方がgoでCLIツールを書くには向いているような気がする。

【fish】CLI上で利用する変数を保持する

fishでのお話。

ターミナル上で特定の文字列を変数にセットして使う方法

$ set x (ls) | echo $x
# 当該ディレクトリ上で ls した内容を出力する

set x (何か出力をともなうコマンド) とすると () 内で実行されたコマンドの出力結果を x に格納する。

セットした変数をコマンドとして実行する

sample:

$ set x "pwd"; and echo $x
pwd # 文字列をそのまま出力するだけでは文字列化された `pwd` コマンドは実行されない
$ set x "pwd"; and eval $x
~/PROJECT_ROOT # 出力された

eval コマンドを実行すると文字列化されたコマンドを入れた変数をコマンドとして実行する。

fishでのevalの設定方法

fish を使っていて、 eval の設定方法がわからなかったので調べました。

参考
eval command in config.fish

# evalの設定方法
# bashrc での rbenv の設定
# eval "$(rbenv init -)" と同様のことを書きたい時
eval (rbenv init - | source)

# その他の書き方
# eval () ← zshやbashで言うところの $ を付けない
# source () 直に指定

などなど

rubyでオブジェクトの持つメソッドを探索する

ユースケース

オブジェクトの中に意図するプロパティを取り出せるのかを確かめる方法に、オブジェクトが持つメソッドを調査するという方法があります。
rubyではjsonオブジェクトのプロパティもメソッドとして取り出せるので、プロパティ not found エラーが発生してプログラムの処理が中断してしまわないか予め調べておくことができたら便利です。

Obj.methods.grep(regexp) を使用する。

例えばある特定のAPIを叩いて返ってきたレスポンスオブジェウトのプロパティを調べるという動作を想定します。

response.methods.grep(/任意のメソッド名 or プロパティ名/)

grepメソッドを使って正規表現で調べればいとするメソッドを調査できます。
methods で全てのメソッドを取り出さずとも、特定のメソッド(プロパティ)を取り出すことが可能。

※ 上記サンプルでは前方後方一致を使っています。

これは rails console でも使えるので、api叩いて、ちゃんと想定するレスポンスの型になっているかを事前に調査できるので便利だと思う。

githubのユーザーネームを変更した話

ユーザーネームを変更しようと思った背景

もともとのgithubのユーザーネームは ememhr っていうのを使っていたのですが、これ、前職で会社用のgithub.comを作るために、もともと作っていた個人用のgithubとは別にとったもので、インターネット周りでは全てのユーザーネームを emahiro で統一しているので、どうしても座りが悪いと感じていました。

もともと ememhr で作成したアカウントはそこまで運用していくつもりはなかったのですが、気づくと会社用で作ったアカウントがメインのアカウントとして稼働してしまっていて、もともと個人でもっていたアカウントは使わなくなってしまってました。
githubのアカウントが2つあるのも変な話なので、古いアカウントを削除して、空いたユーザーネームに変更しました。

変更に際して考えられた懸念点

  1. 今まで ememhr で作っていたリポジトリへのアクセス
  2. ローカル環境での古いユーザー名で作ったリポジトリ
  3. Organizationのアカウントのアクセスについて
  4. githubユーザーのリンクを表示しているサイトのリンクの変更

の4つが懸念点として考えられました。

参考

Changing your GitHub username
What happens when I change my username?

githubの公式のヘルプページを参照

変更方法

  1. Settingへ移動
    f:id:ema_hiro:20170518013042p:plain
  2. Account ▷ Change username を選択
    f:id:ema_hiro:20170518013100p:plain
  3. 警告を食らう
    f:id:ema_hiro:20170518013134p:plain

  4. 古いプロフィールページにはリダイレクトしないよ

  5. 古いユーザー名で作成したPage(github page)にはリダイレクトしないよ
  6. リポジトリは新しいユーザー名でリダイレクトするよ ← すご!!
  7. ちょっと時間かかるよ

ってことが書いてある。

なので問題なくユーザー名を変更します。

この時点で懸念点だった1はクリア。
変更後もOrganizationのアカウントには問題なくアクセスできました。 また、旧ユーザー名でリンクを貼っていたサイトもwantedlyとlinkedinとqiitaくらいだったので、この時点ではそこまで問題にならず。

んで最後のローカルのリポジトリについてですが、個人的に開発したものについてはほとんど更新する必要もなかったので、ほっといても良かったのですが、とりあえず地道に1つずつ新しいユーザー名でのURLに変更

# まず globalのユーザーネームを変更する
$ git config --global user.name 'emahiro'

# 変更したいリポジトリで
$ git remote -v  
origin https://github.com/ememehr/(repository_name).git (fetch)
origin https://github.com/ememehr/(repository_name).git (push)

# 新しいユーザーネームの形に変更
$ git remote set-url origin git@github.com:emahiro/(repository_name).git 

これでOK。

※1 もともとhttpで設定していたのに、ssh経由に変えた理由は ※2 リポジトリを一つ一つremote先を変更するのはめんどくさいですが、このためにリポジトリを断捨離しておいて良かったです。たまに、githubにあげておいてもしょうがないようなリポジトリは掃除しておくのもいいかもしれません。

  1. sshの方が容量が大きくてもpushできる
  2. httpでアクセスした時にusernameとpasswordを聞かれたが新しいのに変更してもうまく行かなかったので、ssh形式に変更することで対応。

これでかねてより取りたかったusernameでgithub生活を送れるようになりました。

【go】構造体にメソッドを追加する

goはオブジェクト指向言語とは違って、書いていてC言語を書いている印象に近く、OOPで言うところのクラスが構造体で、クラスにメソッドを定義することは、構造体にメソッドを定義することになる。

しかし、クラスでカプセル化したりするわけではないので、構造体にメソッドを定義する、その定義のしかたがちょっと理解しづらかったのでそのメモ。

理解しづらかったポイント

goを使って簡易的なHTTPサーバーを立てたときに、http.Handle にServeHTTPのメソッドを持つ構造体を作るって箇所で詰まって色々調べた備忘録。

package main

import (
    "fmt"
    "net/http"
)

// AppHandler アプリ
type AppHandler struct {
    appName string
}

func (h *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Println(h.appName)
}

func main() {
    http.Handle("/", &AppHandler{appName: "myApp"})
    if err := http.ListenAndServe(":8080", nil); err != nil {
        fmt.Println("エラー発生")
    }

    fmt.Println("サーバースタート")
}

こういうコードを想定。特に最初???だったのはここ

func (h *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Println(h.appName)
}

これは

type AppHandler struct {
  appName string
}

の構造体に ServeHTTP メソッドを定義している。

https://golang.org/pkg/net/http/#Handler を確認すると、

type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
}

Handlerはinterface型なので、定義先はどんな型でもいいのだけれど、ServeHTTPというメソッドを持っていなくてはならない。
つまりAppHandlerの構造体であってもServeHTTPを定義することができる

func (構造体) (登録するfunc) {}

という形で定義が可能。

これでAppHandlerの構造体はServeHTTPというメソッドを持つことができ、http.Handleに登録して、指定したPortでHTTPリクエストを受け付けることができるようになる。

構造体以外にメソッドが定義できる。

HTTPサーバーを立てる際に以下のような定義の仕方もある。

func main() {
  http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        res, err := w.Write([]byte(`Test`))
        if err != nil {
            log.Println("Error")
        }
        log.Printf("%d", res)
  }))
}

HTTPサーバーを立てる上で行っている動作は直観的でわかりやすいのであるが、HanderFuncメソッドは一体どこからきていた何なのかがわからず。これもドキュメントを確認すると下記のようになっている。

type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request)

HandlerFuncfunc(ResponseWriter, *Request) の関数型として定義されていて、HanderFunc 型に ServeHTTP が定義されている。これは構造体に ServeHTTP を定義していたのと似ていると思う。特定のエイリアスを関数型として定義して、その関数型に関数を持たせるという個人的には摩訶不思議な言語仕様のなせる技なんだなーと。

goは言語仕様がシンプルで柔軟というはこういうところから来ているような気がしました。

fishに乗り換えた話

zshの管理がめんどくさくなってきたのでもう少しイケてるshell環境をつくれないものかと調べてたら、fish なるものがあるらしく、使い勝手がよさげなので、期間限定で乗り換えてたときの備忘録

パッケージ管理

  1. oh-my-fish
  2. fisherman

1と2もどちらもパッケージ管理ツールに変わりはないので、どちらでも良さげ。
軽く調べたらfishermanの方がイケてるらしい。
最初に1を入れてその後2を入れたものの、 agnoster というテーマを使いたかったのに fisherman では表示がおかしかったので、oh-my-fish を使用。

shellの設定

fishでの標準的なシェルの設定は、 ~/.config/fish/config.fish を使用する。
しかし、oh_my_fish を使うと ~/.config/fish/conf.d/omf.fish になる。
ただし、ここに書きすぎると後々やっぱり fisherman 使いたいなーとか思った時に足かせになるので、自前で ~/.config/fish/config.fish を作って、そちらにちまちま設定を書いていった方が無難(のような気がする)

簡単に設定

pecoとghqを入れておく。

$ brew install peco
$ brew insatll ghq

冗長にbrewで入れようと思ったけど、oh-my-fishを入れると自動的にpecoもghqも入るっぽい。

というわけでoh-my-fishを入れる。

$ curl -L https://get.oh-my.fish | fish

これで omf コマンドが使えるようになる。
fishではoh-my-fishにしろ、fishermanにしろ、パッケージ管理ツールを使ってテーマ等々shellのカスタマイズを行うツールをインストールする。

例えば、agnoster というテーマを入れようと思った場合、

$ omf install agnoster

と入力すると、テーマがインストールされて自動的に反映される。インストールしているテーマは ~/.config/omf/theme のファイルに記録されている。 設定ファイル(config.fish)上で set theme (theme名) と書いておくことで使いたいテーマを決める。※ 1つしか入っていないと自動的に決まるみたい。

※ 注 : agnosterというテーマはPowerlineというフォントを指定しないと正確にテーマが反映されないので注意

とりあえず今回はここまで。
fishはパッケージ管理の容量で簡単にshellがカスタマイズできるので使い勝手が良さげ。専用のコマンドがあるのもありがたい。
設定ファイルはもう少し使い込んで、もろもろ設定してからまたエントリーにする予定。

go で簡易HTTPサーバー立てる

http.HandlerFuncを使う場合

package main

import (
  "fmt"
  "net/http"
)

func main () {
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
    w.Writer([]bytes(`表示内容`))
  }
}

if err := http.ListenAndServe(":8080", nil); err ! = nil{
  fmt.Printf("エラー表示")
}

http.Handleを使う

package main

import (
  "fmt"
  "net/http"
)

type AppHandler struct{
  appName string
}

func (h *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  fmt.Printf(h.appName)
}

func main () {
  http.Handler("/", &AppHandler{appName: "myApp"})
  if err := http.ListenAndSerce(":8080", nil); err ! = nil {
    fmt.Printf("エラー発生")
  }
}

Linterのエラーが発生する

exported type AppHandler should have comment or be unexported (golint)  というエラーが発生。これはキャメルケースの構造体(外部からアクセス可能)なのにコメントが無いよっていうエラー

// AppHandler
type AppHandler struct{
  appName string
}

とコメントをつける

comment on exported type AppHandler should be of the form "AppHandler ..." (with optional leading article) (golint) というLinterのエラーがまだ発生。これはコメントの形式の警告。 構造体名 (半角スペース) 説明 みたいな書き方が必要になる。

// AppHandler アプリケーションに関わる〜
type AppHandler struct{
  appName string
}

というコメントに説明箇所を半角スペースで追加する。

go言語を学び始めてみた

go言語を学び始めました。
最近専らクライアントサイドの開発がメインだったので、サーバーサイドやりたくなったので、徐々に浸透してきたgoについてキャッチアップはじめました。

環境構築

環境

エディター

goのインストール

とりあえず自前のPCで動かす環境を簡単に構築します。

$ brew install go
$ go version
go version go1.8.1 darwin/amd64

atomの設定

goを書くにあたってエディターもろもろ調べましたが、どうやらatomでのgoの開発環境構築がイケている感じを受けたので、atomでgoの開発環境を構築します。

Package

go-plusをどうさせるためにgoに依存パッケージをインストールする必要があります。
※ go-plusのドキュメントに記載してあります。

goではrubyのgemのように簡単にgoのパッケージを管理するインターフェースが用意されています。

$ go get ~

コマンドでgoのパッケージをローカルの環境にインストールできます。
普段は、ドキュメントに書いている順序でコマンド打ち込んでいくのですが、めんどくさいのでshellスクリプトを書いて一括でインストールさせます。

#!/bin/sh

#go-delveをbrewで入れる
$ brew install go-delve/delve/delve

$ go get -u golang.org/x/tools/cmd/goimports
$ go get -u golang.org/x/tools/cmd/gorename
$ go get -u github.com/golang/lint/golint
$ go get -u github.com/sqs/goreturns
$ go get -u github.com/nsf/gocode
$ go get -u github.com/alecthomas/gometalinter
$ go get -u github.com/zmb3/gogetdoc
$ go get -u github.com/rogpeppe/godef
$ go get -v github.com/golang/lint/golint

完了したら、atomで上記go-defgo-plus の2つのパーケージを入れます。

GOPATHを設定する

packageをインストール後、atomを再起動すると、 liter-golint のパッケージのところでエラーが発生します。
これはGOPATHを設定していなかったことが問題で、GOPATHを設定し、packageの設定画面のGOPATHに設定したGOPATHのパスを入力します。 GOPATHを設定しないと、goでインストールしたライブラリが正常に動作しません。

GOPATHは適当に決めていいらしいので、今回は ~/.go にGOPATHを設定しました。
設定方法はPATHを通すだけなので、bashrc or zshrc にGOPATHのパスを追加します。

$ emacs .zshrc

# GOPATH を追加
export GOPATH=$HOME/.go

# シェルを再起動
$ source .zshrc

atomの設定画面から「パッケージ」を選択し、go-plusの設定画面のGOPATH項目、 ~/.go を入力します。 これでlinter-golintのエラーは解消され、atomでgoを書く上で最低限の設定は完了しました。

GOPATH設定のところで少しハマりましがが、Goの言語仕様を説明した入門本もさくっと読んだので、コツコツ開発始めていこうと思います。

読書 Note -『なぜ、あなたの仕事は終わらないのか スピードが最強の武器である』

『なぜ、あなたの仕事は終わらないのか スピードが最強の武器である』を読んでみての感想。
新卒時代に、当時の上司から口酸っぱく言われていたことが全て書いてあり、当時の上司はこの著者の言っていることを指して指導してしくれていたのだとここに来て気づきました。

書籍そのものは、厚みの割に文字が大きくさっと読み流せるボリューム感です。
著者が伝えたいことは一冊を通して同じで「スピードが最強の武器である」ってことだけです。
それを色んなエピソードや仕事の仕方を交えて解説しています。

多分読む人によっては拍子抜けするかもしれません。
とは言え、同じエンジニアとして、仕事をする上で改めて大事だと思うことも書いてあったのでまとめてみました。

書かれていた内容

  1. 納期を守ること
  2. 時間を守ること

の2つです。これは著者も色んな言葉で書いていましたが、僕が感じたのは、一冊を通して、ほぼこの内容しか言っていないということです。
多分この2つさえ、ちゃんとできるようになれば、意味不明な飲み会のルールなんか覚えなくてもいいと思います。

この本に書いてあるのは、大事なことは上の2つで、それを常に実践するためにはどうするかのスタンスが書かれているのみです。
中でもしきりに書かれているのは、 仕事全体を2:8に分け、最初の2割の日程で仕事全体の8割を終わらせる ってこと。

このセンテンスは頻出で、それだけ大事なことで、著者である中島さんのキャリアは全て上記のスタンスに集約されているのでしょう。

もちろん、今の立場にあるからこそできる内容(ex 昼寝の仕方 etc...)もあって全てが全て実施できるわけではありませんが、もし、仕事の仕方として自分で納期を切れる、もしくは納期を明確に伝えられている場合は、参考になる内容がかなりあると思います。

とりあえずやってみて、肌感覚掴んでから一旦報告して、納期までに終わりそうか確認する。
とはいえ、これって案外難しくて、やはり任された仕事である以上、1人でやり遂げたいし、周りに相談したり、ましては納期を変更するなんて、自分の無能をさらけ出しているみたいでかっこ悪くてしづらいと思います。
自身にも似たような経験がありますし、それで迷惑かけたこともあります。
でも、結局仕事は自分1人でしているわけではないので、そこはちゃんと伝えるべきなんですよね...(自戒も込めて)

報告が遅くて困ることはありますが、報告が速くて困ることはまずありません。

書籍の内容は平易で、エピソードもなかなかわかりづらいところもありますが、これは社会人歴浅い自分みたいな年代の人にはぜひ知ってほしい内容です。
仕事の仕方は組織や社会のルールに染まる前の方が矯正し易いです。

個人的に印象に残ったこと

約束の納期を常に守り続けていれば、自分より高い能力の人間よりも上に行ける。 と触れられている内容の箇所です。
約束を守ること = 信頼 を勝ち取ることであって、スキル上優秀だから信頼できるわけでなく、セルフマネジメントまでできて、常に予定通りに成果を出してくる、または早いタイミングで予定の変更を依頼してくる方の方が、信頼感がある、なんて当たり前のことのようですが、それが実は仕事の全てではないかと。
仕事は結局、人と人の関わりの中で行われることである以上、スキルセットは優秀でも、人として信頼できるかどうかが最終的な意思決定にもっとも効いてきます。
そして、信頼されて任された仕事 = キャリアなので、信頼される人の方がキャリアアップにつながっていく。

もちろん、有無を言わせない天才的なスキルセットを持っている人間はいるので、そういう人間には勝負しても勝てないかもしれませんが、とは言え、そういう一握りの人間と勝負するよりも、凡人同士の中で勝負する中で、いかに相手より信頼されるか、どう差別化して、少しでも上に行くのか、ってことの方が自分にとっては身近な内容で、再現性が高いです。

どう活かしていくか

とは言え、最初の2割の日程の中で普段の10~20倍の生産性で業務にとりかかるなんて凡人には無理ですよね。
著者の中島さんも凡人みたいな事欠かれますが、僕から見たら十分天才の部類です。

なので、実践で活かすとするならば、プロトタイプを作るってところから始めようかなと。
そして、日程の算出の仕方とスケジュールの組み方。
現在、あまり納期が明示されることはありませんが、その場合、仮納期を設定して、どれくらい終わるのかってところ、仮納期を設定して一気に作業することで、自分が躓いているポイント、時間がかかっているポイントを洗い出して、予め潰す、もしくは、部分的に変わってもらう、アドバイスをもらいやすくするなど、色々方法は考えられると思います。

あくまでゴールは時間、納期を守ることで、少しずつ回していくのはその訓練にすぎません。少しずつやっていけばそのうち慣れて行くと思います。

まとめ

書籍はHowToを謳っていますが、書いてあるのは、即実践で使える具体的なHowToではなく、どちらかというと、スタンスや意識、考え方の側面が強いです。

納期と時間は守るもの、そういう風に最初に決めてしまえばいいのだと思います。
そうすれば、「どうすれば納期を守れるのか▷とりあえずやってみて報告しよう」といった具合に仕事のやり方に行き着きます。

こんな時期なので、フレッシャーズの皆さんには一読してほしい本かと思います。

MacOSをアップデートしたらbrew でインストールしたツールが`command not found`になったときの対処

トラブル概要

MacOSX Sierraを2017/04/06時点の最新版にアップデートしたら

$ mysql
mysql not found

になってしまった。

対応した手順

brew list でインストールされているか確認

$ brew list | grep mysql
mysql56
mysql@5.6

入っている…
mysqlの最新版は5.7ですが、現状の業務では5.6を使用していたので、5.6を動作させなければなりません。

※ ちなみにbrew でインストールできるソフトウェアのバージョンは以下で調査できる

$ brew info mysql # インストールできる最新版を表示してくれる。
mysql: stable 5.7.17 (bottled)
Open source relational database management system
https://dev.mysql.com/doc/refman/5.7/en/
Conflicts with: mariadb, mariadb-connector-c, mysql-cluster, mysql-connector-c, percona-server
Not installed

brew doctor で Homebrew の状態を確認

$ brew doctor
Your System is ready to brew

問題ない…

brew uninstall ▷ install で入れ直そう

$ brew uninstall mysql56
Uninstalling /usr/local/Cellar/mysql@5.6/5.6.35... (345 files, 154.7MB)
mysql@5.6 5.6.32, 5.6.34 are still installed.
Remove all versions with `brew uninstall --force mysql@5.6`.
$ brew install mysql56
brew install mysql56
Updating Homebrew...

#中略

Warning: The post-install step did not complete successfully
You can try again using `brew postinstall mysql@5.6`
==> Caveats
A "/etc/my.cnf" from another install may interfere with a Homebrew-built
server starting up correctly.

To connect:
    mysql -uroot

This formula is keg-only, which means it was not symlinked into /usr/local.

This is an alternate version of another formula.

If you need to have this software first in your PATH run:
  echo 'export PATH="/usr/local/opt/mysql@5.6/bin:$PATH"' >> ~/.zshrc

For compilers to find this software you may need to set:
    LDFLAGS:  -L/usr/local/opt/mysql@5.6/lib
    CPPFLAGS: -I/usr/local/opt/mysql@5.6/include


To have launchd start mysql@5.6 now and restart at login:
  brew services start mysql@5.6
Or, if you don't want/need a background service you can just run:
  /usr/local/opt/mysql@5.6/bin/mysql.server start
==> Summary
🍺  /usr/local/Cellar/mysql@5.6/5.6.35: 345 files, 154.7MB
➜  ~ which mysql
mysql not found

ここで、

To have launchd start mysql@5.6 now and restart at login:
  brew services start mysql@5.6
Or, if you don't want/need a background service you can just run:
  /usr/local/opt/mysql@5.6/bin/mysql.server start

パスが /usr/local/opt/mysql@5.6/bin/ 以下であることに気づく。 Homebrewで入れた場合は自動的に /usr/local/bin 配下に設置されるものだと理解していたけど、ちょっと違う???

とりあえずパスの在り処がわかったので /usr/local/bin 配下にコマンドをコピーする

$ sudo cp /usr/local/opt/mysql@5.6/bin/mysql /usr/local/bin
$ which mysql
/usr/local/bin/mysql

と、ここまで来てコマンドの場所がわかったのなら、PATH通せば終わりじゃね?ってことに気づき、 .zshrcexport PATH=/usr/local/opt/mysql@5.6/bin:$PATH を通し

$ which mysql
/usr/local/opt/mysql@5.6/bin/mysql

と表示されたので完了。

WebViewを使ったHTMLの描画

アプリのTableViewから詳細画面に遷移するインターフェースを考えます。
詳細画面に遷移する時に、HTMLをViewに描画するとき、

  1. URLをLoadして直にwebViewを読み込む
  2. HTMLの文字列をHTMLの段組みに合わせてWebViewで表示する。

参考 How to load a HTML string into a WKWebView or UIWebView: loadHTMLString()

URLをLoadして直にwebViewを読み込む

let webView = UIWebView()
let urlRequest: URLRequest = URLRequest(URL: URL(string: "https://sample.com/XXX")!) // 表示したいページのURL(URLRequest型)
webView.loadRequest(urlRequest)

URLを指定して、UIWebViewで表示させるだけなので、簡単ではあるものの、ページの描画に時間がかかってしまう。 そこで、APIのレスポンスでHTMLの文字列が返ってきたような場合、レスポンスで返ってきたHTMLの文字列をHTMLの段組みでWebViewに表示してみます。

HTMLの文字列をHTMLの段組みに合わせてWebViewで表示する。

QiitaのAPIを例にとって試してみます。
qiitaのAPIのレスポンスは

$ curl https://qiita.com/api/v2/items/97819a1910859a6f2ef9 | jq
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 14941    0 14941    0     0  29195      0 --:--:-- --:--:-- --:--:-- 29181
{
  "rendered_body": "HTMLの文字列",
  "coediting": false
  # 略
  
}

という形で rendered_body のプロパティにHTMLの文字列が返ってきます。
この rendered_body プロパティのHTMLの文字列を取り出してUIWebViewの loadHTMLString メソッドに渡します。

var webView: UIWebView!
let html: String = "rendered_bodyの中身"
webView.loadHTMLString(html, baseURL: nil)

これでHTMLの文字列をWebViewに描画することができます。
以下のような感じになります。

f:id:ema_hiro:20170403161835g:plain

【2017年度版】今年度はどんなこと学んでいこうかなってこと

今日から新年度ですね。
渋谷ではおそらく新卒入社と呼ばれるみなさんが飲み会をしてました。
賑やかでこの時期ならではの華やかな空気感が色んなところに感じられるようになりました。

自身も新卒で社会に出てから4年目になってしまい、世間的ににはまだまだ若造の部類ですが、業界的にはもうおっさんの域に差し掛かってきたので、目下焦りしかありません。
こんな時期なので今年度はどういったことに意識して技術を学習していこうかなとということを備忘録として記録しておきます。

目次

  1. 現状の自分のスキルセットと傾向
  2. 今年度学ぼうと思っていること

現状の自分のスキルセットと傾向

Web系の言語を中心に1からサービスを立ち上げた経験はあり。
サービスをグロースさせた経験 はなし

新卒で入社した会社でかなり幅広く経験させてもらいましたが、一つの言語に特化して深く理解したことはなし。
言語仕様を完璧に理解する前に、サービスを作りながら徐々に覚えていくタイプ。

今年度学ぼうと思っていること

年明けからiOSの開発を続けているので、今年はクライアントサイドの開発の知見を深めようと今のところは考えています。

クライアントサイドは、端末の規格が変わったり、プラットフォームの影響をもろに受けるので、実は避けていた分野でした。
ただ、年明けからクライアントサイドの開発に携わる中で、ユーザーに直接触れる領域の良し悪しって露骨にサービスの成長に直結しているなという当たり前のことに気づくようになってきました。
世の中に浸透しているサービスには何か使っている心地よさや、使わないと行けない、使うことが当たり前だよね!といえる感覚があり、また、そういったサービスはアプリだけで完結せず、アプリを中心としたコミュニティというか世界観まで作り上げているものが多く、実装に囚われない、 ソフトウェアだけに収まらないものづくり をしているなという感覚があります。

ユーザーに価値を届けるってソフトウェアだけに留まらない体験を届けることなのではないかと漠然と考えたいことが、ストンと腹落ちしました。

少し話はそれますが、個人的に考えていたことが2つあります。

  1. バズっているIoTや人工知能といった分野も結局それらは考え方やツールでしかなく、ユーザーにはアプリというインターフェースを経ないと価値を届けられないこと。
  2. サーバーサイドは技術の選択肢は増加してくる一方、インフラ周りの仕組みは自動化されたり、ブラックボックス化されていて、知見を持っていることの技術的な優位性は徐々に薄れてきているようにも感じること。

この2点から、流行りに乗っかったり、今のスキルセットのままいくには漠然とリスクを感じていました。

もともとあるスキルに加えて、ユーザー体験を考えることは、ユーザーの理解、技術の理解、領域の理解、そしてその先にある世界観の理解まで含めて作っていく必要があると思います。

おそらくエンジニアという職業において今後数十年のうちに実際にプログラムを書くことは、人間がやる範囲のことではなくなっていくと思います。
実際、Googleの作っている人口知能はプログラムを書けるようになっているらしいですし、実際にコードを書くということはもはや人間がやらなくてもいいことになりつつあるのかなと。(コード書くのは楽しいですが。)

そんなときに、エンジニアとして何ができるのか、機械に代替できないことはなんなのか、ってことを考えた時、それは体験を作り出し、提供することではないかと一つの仮説を持ちました。
UXを提供する領域、ユーザーに直に触れる領域の知見は今だからこそ身につけるに最適なタイミングなのではないかと。
自分のスキルセットと照らし合わせても、キャリアの幅を増やせるいい機会になると思っています。

今年こそ、何か作りきった!と言えるものを残せるように。