code climateのsmellチェックが厳しすぎる件

モチベーション

GithubのREADMEにバッジがあるだけでそれっぽく見える

code climateはコードの不吉な臭い(code smell)とかtest coverageを管理してくれるツール

codeclimate.com

今やってるbotリポジトリと連携させてるけどめちゃくちゃチェックが厳しい

Issue

code climateでissueが3個発見された

issueってのはお前のコードくせえわってやつ

発見されたのは3つだけども、大別すると2つだった

1 functionあたりの行数多すぎて臭い

NewLineClient has 31 lines of code (exceeds 25 allowed). Consider refactoring. こんな表示がされる。

1 functionあたりのコード行は25行以内が推奨されているらしい。これを意地でも守ろうとすると結構辛くないか? 25行以内であれば何個functionがあってもいいよって考えなんだろうか? 特にポリシーをみたわけじゃないから邪推しかできない。

func NewLineClient() LineClient {
    values := url.Values{}
    values.Set("grant_type", "client_credentials")
    values.Set("client_id", os.Getenv("CHANNEL_ID"))
    values.Set("client_secret", os.Getenv("CHANNEL_SECRET"))
 
    url := "https://api.line.me/v2/oauth/accessToken"
 
    req, err := http.NewRequest(
        "POST",
        url,
        strings.NewReader(values.Encode()),
    )
    if err != nil {
        log.Fatal(err)
    }
 
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 
    client := &http.Client{}
    res, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer res.Body.Close()
 
    authConfig := new(lineClient)
 
    if err := json.NewDecoder(res.Body).Decode(authConfig); err != nil {
        log.Fatal(err)
    }
 
    client := &lineClient{
        AccessToken:   authConfig.AccessToken,
        ExpiresIn:     authConfig.ExpiresIn,
        TokenType:     authConfig.TokenType,
        ChannelSecret: os.Getenv("CHANNEL_SECRET"),
    }
 
    return client
}

引っかかったのはこのfunction。空行は含めずに31行あるみたいだ。

あと6行削るとなるとどこかを別のfuncに切り出すか、lineClientのフィールドをAuthConfigChannelSecretの2つにしてちょいとスタイリッシュにするかが考えられると後者だとぎりぎり足りないな。

ただ、前者にしても問題は発生する。 それが次の警告だ

1functionで参照しすぎて臭い

Method lineReminder.lineReminder.lineReminder.GetWebHook has a Cognitive Complexity of 9 (exceeds 5 allowed).

1functionあたり、参照する回数は5回以下が望ましいらしい。

func (l *lineReminder) GetWebHook(req *http.Request) (string, error) {
    received, err := l.client.ReceiveEvent(req)
    if err != nil {
        return "", err
    }

    var status = "false"

    for _, event := range received {
        //log.Println("groupId: " + event.Source.GroupID)
        //log.Println("userId: " + event.Source.UserID)
        textMsg := new(linebot.TextMessage)
        byteMsg, _ := event.Message.MarshalJSON()
        if err := json.Unmarshal(byteMsg, textMsg); err != nil {
            //画像メッセージの場合もあるからただエラーを出力するだけにする
            log.Println(err.Error())
        }

        if textMsg.Text == os.Getenv("REPORT_MESSAGE") {
            status = SetStatus(event.Source.UserID, "true")
            err := l.client.ReplyMessage(event.ReplyToken, os.Getenv("REPLY_SUCCESS"))
            if err != nil {
                return "", err
            }
        }
    }

    return status, nil
}

うん。確かにこのfunctionは臭いな。 lineに投稿があった際に飛ぶwebhookを受け取ってeventを取得するfuntionなんだけども、messageを抽出する部分が臭いを発している気がする。

変更してみた

func (l *lineReminder) GetWebHook(req *http.Request) (string, error) {
    received, err := l.client.ReceiveEvent(req)
    if err != nil {
        return "", err
    }

    var status = "false"

    for _, event := range received {
        //log.Println("groupId: " + event.Source.GroupID)
        //log.Println("userId: " + event.Source.UserID)
        msg, _ := ExtractMessage(event)
        if msg == os.Getenv("REPORT_MESSAGE") {
            status = SetStatus(event.Source.UserID, "true")
            err := l.client.ReplyMessage(event.ReplyToken, os.Getenv("REPLY_SUCCESS"))
            if err != nil {
                return "", err
            }
        }
    }

    return status, nil
}

func ExtractMessage(event linebot.Event) (string, error) {
    textMsg := new(linebot.TextMessage)
    byteMsg, _ := event.Message.MarshalJSON()
    if err := json.Unmarshal(byteMsg, textMsg); err != nil {
        return "", err
    }
    return textMsg.Text, nil
}

Method lineReminder.lineReminder.lineReminder.GetWebHook has a Cognitive Complexity of 7 (exceeds 5 allowed).

デスヨネ。でもこれ以上function切ると可読性が著しく落ちる気がする。 眠いからもうやめた

おわりに

一応MaintainabilityはAだよ

https://api.codeclimate.com/v1/badges/d294b82b1934b6bec7fb/maintainability

今更だけどgitコマンドにaliasを貼った話

モチベーション

PRを細い粒度でだそうとする(結果としてそうならない時は多々ある)と、ローカルリポジトリに使い終わったブランチが溜まってしまうことがよくある。

こうなってしまうと補完する際に目的のブランチに辿り着くのが遅くなる。 feature/loginfeature/logoutの2つのブランチがあった場合、

$ git push origin fea <Tabおす>
feature/login feature/logout

と出てしまって気持ちよくない。 かといってブランチを消すときに、都度都度 git branch -D feature/loginと打たなきゃいけないかと思うと結構億劫

というわけで今更だけどgitコマンドにaliasを貼って行こうと思う

Reference

qiita.com git aliasで一発目にヒットしたやつ

設定

自分だけに反映させる場合は ~/.gitconfigに書いていけばいいそうだな

user.name、user.email設定時の時と同様 git config --glabalでも設定できるそう。

とりあえずブランチの削除のaliasだけやってみたいと思う。

$ git config --global alias.d branch -D

こうしたら~/.gitconfigに反映されているはず。

$ less ~/.gitconfig
..
[alias]
        d = branch

引数として"branch"しか渡されてなかったんだなきっと

$ git config --global --unset alias.d
$ git config --global alias.d "branch -D"
$ less ~/.gitconfig    
..
[alias]
        d = branch -D

見かけ上はいけるはず。

$ git branch         
  feature/test-status
* master

feature/test-status ブランチを削除してみる

$ git d feature/test-status 
Deleted branch feature/test-status (was fefc87d).
$ git branch
* master

できた。

おわりに

簡単に設定できるからこれからどんどん追加して行こうと思う。

IFTTT + Line Bot + Herokuでリマインダーを半自動化した話 ~環境編~

モチベーション

新年早々母親が体調を崩してしばらく家をあけることになってしまい、父親が毎日飲む薬を飲み忘れるという事態が発生しまくりました。

かといってこちらが毎日決まった時間に"薬飲めよ〜〜"っていうのはさすがにきついですよね。 決まった時間に何かするのは人間のやる仕事じゃない。

まぁ決まった時間に一方的にリマインドを送るのは簡単なんですが、きちんと薬を飲んだかはわからないわけです。 Lineで"飲んだ"といえば、きちんと飲んだものだとしても、決まった時間から一定時間が過ぎたのにも関わらずアクションがない場合は人間が"薬飲めよ〜〜"って言う必要がでてきます。

これもある程度ルールがあるわけだから人間より機械の方が得意そうです。

ってなわけでIFTTT + Line Bot + Herokuでリマインダーを半自動化したよという話をします。 今回は環境編で、なんでそのツールを選んだのだとか、どんな言語で開発したのだとかを書いていきます。

ちなみに"半"自動化のアナログの部分は父親のapple watchでボタンを押す動作だけですw

現時点でのプロトタイプ

f:id:cappyzawa:20180126024955p:plain こんな感じでbotだけがしゃべりまくります

ちなみに人間の手で飲んだと報告すると f:id:cappyzawa:20180126025202p:plain

こんな感じでbotが褒めてくれるようにしました

本当はbotでのメッセージを読み取りたかったのですが、botの投稿にはwebhookが飛ばない様になっていてapp側でメッセージが受信できませんでした 残念 簡単に自動応答できる仕組み(とりあえず投稿があったら決まったメッセージを投稿するというやつ)があるからbotの投稿も検知してしまうと無限にメッセージを連鎖させてしまうことを避けるための措置なのかな だったらそれを無効にするようなオプションつけてほしいなぁ

しゃべりすぎました

環境

IFTTT

IFTTT

IFTTT

  • IFTTT
  • 仕事効率化
  • 無料
これです。

これを使う前まではmythingsでアプリだけで半自動化していました

myThings

myThings

  • Yahoo Japan Corp.
  • ユーティリティ
  • 無料

f:id:cappyzawa:20180126030258p:plain

mythingsは簡単にトリガーとアクションを紐づけられて、非エンジニアにも優しいアプリなのですが、それゆえにちょっと難しいことをやるのはできなそうでした。 あとトリガーとして時刻起動がないのはなんでだよって感じw googleカレンダーと連携して無理やり時刻起動みたいなことをしていました。。。

でもそれ以外は使いやすいですよ

さて、IFTTTに戻ります。 IFTTTはトリガーの種類、アクションの種類共にめちゃくちゃ豊富ですね。全部英語ですが。 トリガーとして、時刻での起動があり、アクションとしてLineの投稿もあるので、リマインダーをIFTTT、報告をmythingsにして運用してみました。

f:id:cappyzawa:20180126030459p:plain

報告もIFTTTでいいんじゃって思うかもしれませんが、IFTTTには共有の機能がない(mythingsはある)のでちょっと登録してもらうのが面倒だったのでmythingsを使っていました。

これでいいじゃんと思うかもしれませんが、

まぁ決まった時間に一方的にリマインドを送るのは簡単なんですが、きちんと薬を飲んだかはわからないわけです。 Lineで"飲んだ"といえば、きちんと飲んだものだとしても、決まった時間から一定時間が過ぎたのにも関わらずアクションがない場合は人間が"薬飲めよ〜〜"って言う必要がでてきます。

この問題が解決されません。

これを解決するためには、やはりこちら側でbotを制御する必要がでてくるわけです。

LineBot

リマインダーの投稿先としてLineを選んだのは自明ですが、一番メッセージアプリの中で(報告者側が)開く頻度が多いからです。

今回はgoでAppを開発してるんですが、以下のSDKを利用させてもらいました。

github.com

goは最近触り始めたんですが、書いててなんか楽しいですね ググラビリティが低いのがなんともいえないですが、GoDocがしっかりしてるのでリファレンス読めば大抵解決します。

Heroku

www.heroku.com PaaSです。 業務ではcloudfoundryを使用していますが、今回はherokuを使いました。 利用可能になるまでが早いですし、1Appなら何しても無料(たぶん)なので良いです。

PaaSを使うことによって、こちらはAppの開発をしてればいいだけなのでめっちゃ楽でした。 herokuの環境設定は秒で終わったので何したかも覚えてないです。

PaaSを使うにあたり、困ったことが一点ありまして、

  • Appに状態を持たせないことが望ましい

という点です。PaaSのapp内でファイルをもったりするのはよろしくないということです。

ただ、じゃあDBと接続するかといわれてもそこまでしたくないしどのDB使えばええんやということで今回がっつりルールを無視して環境変数に状態を持たせました。 業務でPaaSの推進をしているわけですが、アンチパターンとしていいのかなと思ってますw

App

  • 言語: go
  • WebFramework: Gin
  • CI: travis
  • CD: Heroku

ざっくりこんな環境ですかね。 goだとWebFrameworkは別に使用しなくてもさらりとかけるのかもしれませんが、なんだかんだWebFramework使った方が簡単にかけますよねw いろいろWebFrameworkはありますが、Ginが一番POSTのBodyを受け取り易そうだと感じたのでGinにしました。

github.com

CIはtravisです。ただ、現時点でテストをかけていないのでCIと呼べるかは怪しいですw 今はdummyのテストが置いてあり一応PRやmaster push時にtravisでテストが走る様になっています。 ただ動作するコードは猿でもかけると思ってるので、テストとかインタフェースとかもっと理解したいなあと思ってます

Travis CI - Test and Deploy Your Code with Confidence

CDはHeroku使ってます。 使ってるというかherokuが勝手にmasterにmergeされたらdeployしてくれます。 本来ならmasterにdeployされたらtravisでテストが走り、成功したらheroku deployが正解だと思うのであとでここは直す必要がありそうです。

おわりに

だらだら書きましたが環境は以上です

次は実装編を書くかも

実装といってもただ動くだけの猿コードですが。。。

レガシーコード改善ガイドを買った

レガシーコード改善ガイド

Amazon CAPTCHA

モチベーション

どの言語でもロジックは(ぐぐれば)書けるけどテストを書くのが難しいなあと思って

ライブラリを開発している場合、変更に問題がないと確かめるためにそのライブラリを使う別のプロダクトを動かす必要があったりして面倒だなと思っていた

テストがあれば些細な変更がもっと簡単になるのはわかるんだけど、実際mockとかどうやって作ればいいんだよっていう感じだった

リクエスト、DBなどmock化すべきものがたくさんあるし、単に動くだけのロジックを書いている場合testableじゃないことがほとんど。また、インタフェースなどがなく、mockableでないことも多い。

testable, mockableなコードを書き、テストコードを書いて効率的な開発がしたい。

ちょっとだけ読んで見た

テストにかける情熱(?)みたいなものがすごい まだはじめにしか読んでないけど

記事のデザインをカスタマイズする(見出し編)

モチベーション

見出しレベルを変えてもあまり違いがわからないからもっとわかりやすくしたい。

saruwakakun.com

これを丸ぱくりするぜ

修正していくぞ

プロパティ

.entry-content h2.entry-content h3... って感じ

修正

修正前

@import "/css/theme/bordeaux/bordeaux.css";

.entry blockquote {
    padding: 0 1em;
    color: #6a737d;
    border-left: 0.25em solid #dfe2e5;
    border-top: none;
    border-right: none;
    border-bottom: none;
}

このように見出しのプロパティは設定されていない。

修正後

@import "/css/theme/bordeaux/bordeaux.css";

.entry blockquote {
    padding: 0 1em;
    color: #6a737d;
    border-left: 0.25em solid #dfe2e5;
    border-top: none;
    border-right: none;
    border-bottom: none;
}
    
.entry-content h2 {
   padding: 0 1em 0;/*文字周りの余白*/
   color: #494949;/*文字色*/
   border-left: solid 5px #dc143c;/*左線(実線 太さ 色)*/
   font-size: 130%;
   border-bottom: none;
}

.entry-content h3 {
   position: relative;
   border-bottom: none;
}

.entry-content h3:after {
   content: "";
   position: absolute;
   left: 0;
   bottom: 0;
   width: 100%;
   height: 7px;
   background: -webkit-repeating-linear-gradient(-45deg, #dc143c, #dc143c 2px, #fff 2px, #fff 4px);
   background: repeating-linear-gradient(-45deg, #dc143c, #dc143c 2px, #fff 2px, #fff 4px);
}

.entry-content h4 {
   border-bottom: dashed 2px #dc143c;
   /*線の種類(点線)2px 線色*/
   font-size: 100%;
}

上記Referenceの数値とか色のコードだけ適宜変更しました。(font-sizeも若干)

ネクス

なんか手作り感が前面にでてきて嫌だなぁと思うけどこれ以上やっても仕方ないし、知識もなくできないので時間があるときちょいちょい修正してくことにする。
今更なんだけどスマホ版にはこういうのは反映されないのかな(?)
f:id:cappyzawa:20171220015708p:plain
変更前のは撮り忘れたけど、だいたいこんな感じになりました。

次はmacにbosh-liteの環境を作ったことを書くつもり。

記事のデザインをカスタマイズする(引用編)

直したい箇所

引用のデザイン

理由

希望としては、GitHubのような引用の見え方にしたい
理想: f:id:cappyzawa:20171219222445p:plain
現状: f:id:cappyzawa:20171219222532p:plain
エンジニアの人ならわかってくれると思うが圧倒的これじゃない感がある。

じゃあ違うテンプレ使えよって思うが、そこまでCSSを触りたいわけじゃない。 (このテンプレが一番スニペットが見やすかったんや。。)

修正していく

プロパティ

現状確認

まずは採用テンプレにおいて、引用にどのプロパティが使われているのか知る必要がある。
Chromeで検証したぞ。

blockquote {
    display: block;
    -webkit-margin-before: 1em;
    -webkit-margin-after: 1em;
    -webkit-margin-start: 40px;
    -webkit-margin-end: 40px;
}

これな気がする。
CSSから逃げてきた人生だったからいまいち確証は持てない。
やっぱり違かった。 これはデフォルトのCSS(user agent stylesheet)でみるべき対象じゃなかった。

.entry blockquote {
    padding: 15px;
    margin: 1em 0;
    border: 1px solid #ddd;
}

こっちだった。

理想確認

blockquote {
    padding: 0 1em;
    color: #6a737d;
    border-left: 0.25em solid #dfe2e5;
}

考えてもわからないのでとりあえず実際に適用してみる。
このテンプレはこのテーマのcssをimportしてて上書きできないかも。。。と懸念を抱きながらやってみる。
importの優先度は最低だとのこと。(参照: 段階化の順序|前提知識|CSS2.1)

修正

cssは以下のようになった。

@import "/css/theme/bordeaux/bordeaux.css";

.entry blockquote {
    padding: 0 1em;
    color: #6a737d;
    border-left: 0.25em solid #dfe2e5;
    border-top: none;
    border-right: none;
    border-bottom: none;
}

変更が反映され、ちょっとだけGitHubらしくなった。

修正後

user agent styleseetって何なの。公式から引っ張ろうと思ったけど見つけれなかったので、以下から引用 www.webernote.net

これはブラウザごとに定義されたデフォルトのCSS設定

ネクス

現状見出しレベルを変えてもあまり違いがわからないから、h2, h3, h4... のプロパティの修正を行う。

はてな開設

初投稿

エンジニアとして人生で1回はブログを開設してみたくなるときがくるのかもしれない。
Qiitaにポエムかいたら怒られちゃうから
最近こういう区切りがはっきりしてるレイアウト流行ってるのかな
テーマは選んだけどこれが一番コードスニペットが見やすかった。
どうも引用のところが気にくわない。。。 それに見出しレベルも効いてないな。。。

テスト

プレビューがpcとスマホの2通りあるのいいね

文字

太字: 太字
斜字: 斜字 太斜字: 太斜字
打ち消し: 打ち消し

コード関係

インライン: inline
スニペット:

groups:
- name: overview
  jobs:
  - check-pr
  - check-master
  - dark-launch
- name: develop
  jobs:
  - check-pr
- name: deploy(dark-launch)
  jobs:
  - check-master
  - dark-launch

いまいちシンタックスハイライトが効いてるかわからん
引用:

引用

箇条書き

  • 箇条書き

    • 箇条書き
  • ナンバリング1

  • ナンバリング2
  • ナンバリング3

なんでここらへん表示おかしいんだろ