よく使うコードをgithubからダウンロードするコマンドラインツール

# 自己紹介

じゅんじゅんと言うニックネームで、関西を拠点に活動しているフロントエンドエンジニアです。

HAL大阪3回生です。（2017/05/17現在）

イベントや、勉強会に参加してるので是非お会いした際はお声掛けください！

# よく使うコード、毎回書くのめんどくさい...

よく使うコードとは、例えばReactを開発する際の`webpack.config.js`とか`gulpfile.js`とか、環境系のコードが多いかもしれません。

このファイルは、だいたい雛形が決まっているし毎回0から書くのもアホらしいし、いちいち書き方覚えていないので、毎回自分の過去のコードをみてコピペして、今のプロジェクトのパスなどを最適化したりしていました。

しかし、なんせブラウザ立ち上げて、自分のgithub開いて。。。。など手順がめんどくさくなってきました。

コマンドラインで完結してほしい...

ということで、作りました。まだv1.0.0で機能がとても少ないですが今後機能の追加をしていこうと思います。

# code get（cget）

wgetというコマンドありますよね。似たような感じで使いたかったのでcode get（cget）コマンドとしてつくりました。いい名前あったらコメントで教えてください（もしくは、githubにissueください）

今回のフォルダ構成を作っていきます

```
- main.go
- menu.json
- README.md
- Manager/
   |- menuManager.go
- Utils/
   |- exportUtils.go
   |- fetchUtils.go
   |- menuUtils.go
```

こんな感じになります。

では、順番に作っていきます。

まずやらないとイケないものとしては

- githubからコードを取得する
- コマンドラインのオプションを元に分岐するマネージャー
- ファイルに書き出す

ぐらいだと、思います。

流れとしては、

`$ cget list` で、取得可能なファイルを一覧する

`$ cget fetch <file name>` で、カレントディレクトリにそのファイルを保存する

では、まず**コマンドラインのオプションを元に分岐するマネージャー**を作っていきます

## マネージャー作り

`Manager/menuManager.go`を作っていきます。

```go:menuManager.go
package Managet

import(
  "flag"
  "fmt"
  "github.com/konojunya/cget/Utils"
)

func Init(){

  flag.Parse()
  items := flag.Args()

  switch items[0] {
  case "list":
    showAllFile()
  case "fetch":
    getCodeUrl(items[1)
  case "help":
    help()
  default:
    notfound()
  }
}

func showAllFile(){
  for _, key := range Utils.GetMenuKeys(){
    fmt.Printf("%s\n",key)
  }
}

func getCodeUrl(key string){
  url := Utils.GetCodeUrl(key)
  code := Utils.FetchCode(url)

  fmt.Printf("Downloading %s from %s...\n",key,url)

  Utils.Export(key,code)
}

func help(){
  fmt.Println("command list.")
  fmt.Println("$ cget list\t\tYou can get code list.")
  fmt.Println("$ cget fetch [key]\tYou can save file from key.")
}

func notfound(){
  fmt.Println("this args is not found.")

  fmt.Println("$ cget list\t\tYou can get code list.")
  fmt.Println("$ cget fetch [key]\tYou can save file from key.")
  fmt.Println("$ cget help\t\thelp.")
}
```

まず、`Init()`を実行します。コマンドライン引数を取得して`list`か`fetch`か`help`かを見ています。

`list`の場合は、取得可能なコードのファイル名を一覧します。

`fetch`は、そのファイル名を元にコードをダウンロードします。

次に、githubからコードを取得するコードをかいていきたいと思います。

## githubから取得するぞ

```go:fetchUtils.go
package Utils

import (
  "io/ioutil"
  "log"
  "net/http"
)

func FetchCode(url string) []byte {
  res, err := http.Get(url)
  if err != nil { log.Fatal(err) }

  code, err := ioutil.ReadAll(res.Body)
  defer res.Body.Close()

  if err != nil { log.Fatal(err) }

  return code
}
```

`FetchCode`メソッドを実装します。

引数にURLをとって、`net/http`モジュールをつかって取得してきます。

ここで注意するべきは、`res.Body.Close()`を`defer`を使って`Close`が呼ばれるまで待つ処理を書いておくことです。

これで、`code`を`byte`配列で取得できたので、それをファイルへ書き出したいと思います。

## exportするぞい

```go:exportUtils.go
package Utils

import (
  "log"
  "os"
)

func Export(filename string, code []byte){
  crr, _ := os.Getwd()
  file, err := os.Create(crr + "/" + filename)
  if err != nil { log.Fatal(err) }
  defer file.Close()

  file.Write(code)
}
```

`os.Getwd`で、カレントディレクトリの絶対パスを取得します。

`os.Create`でファイル名を入れて、ファイルをつくって、`file.Write`で書き込みをします。

## 取得可能なファイルをjsonで管理するヨ

`Utils/menuUtils.go`を編集していきます。

```go:menuUtils.go
package Utils

import (
  "encoding/json"
  "log"
  "net/http"
  "io/ioutil"
)

type Menu struct {
  Key string
  Url string
}

func initMenu() []Menu {

  res, err := http.Get("https://raw.githubusercontent.com/konojunya/cget/master/menu.json")
  if err != nil { log.Fatal(err) }

  bytes, err := ioutil.ReadAll(res.Body)
  defer res.Body.Close()
  if err != nil { log.Fatal(err) }

  var menus []Menu
  if err := json.Unmarshal(bytes,&menus); err != nil {
    log.Fatal(err)  
  }

  return menus

}

func GetMenuKeys() []string {
  menus := initMenu()

  var menuKeys []string
  
  for _, item := range menus {
    menuKeys = append(menuKeys,item.Key)
  }

  return menuKeys
}

func GetCodeUrl(key string) string {
  menus := initMenu()

  var url string
  for _, item := range menus {
    if item.Key == key {
      url = item.Url
    }
  }

  return url
}
```

まずjsonの形式の通りに構造体を作ります。

構造体の`Menu`は、`Key`と`Url`を持つものと定義しておきます。

プライベートメソッドの`initMenu`は、`cget`のリポジトリにあるjsonファイルを確認して、メニューを構築するものです。

取得したあと、`json.Unmarshal()`をつかって、`Menu`にポインター経由で値を渡していきます。

パブリックメソッドの`GetMenuKeys`は、取得可能なファイルのファイル名の配列を返して、`GetCodeUrl`は、選択されたkeyを元にjsonから検索をして返します。

*このQiitaを書いている時に、もし`cget fetch`で全然jsonにないファイル名を書かれた時にエラーがでることに気づきました*

## 完成

これで全ての実装を終えたので、`main.go`を編集していきます。

```go:main.go
package main

import "github.com/konojunya/cget/Manager"

func main(){
  Manager.Init()
}
```

これで、完成です。

#あとがき

このコマンドは、Goの勉強をするのに丁度いいと思いつくってみました。

今後、ファイル名を`cget fetch <filename> <filename>`としたときにgoroutineをつかってみるとか、アップデートをかけていきたいと思います。

Goを書き始めて1ヶ月で、Goらしくないコードがあったり、下手くそな実装があると思うので、ぜひプルリク投げてください。。

[cget/GitHub](https://github.com/konojunya/cget)

なにかありましたら、Twitterへお願いします

[@konojunya/Twitter](https://twitter.com/konojunya)