Edited at

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

More than 1 year has passed since last update.


自己紹介

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

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を作っていきます。


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()を実行します。コマンドライン引数を取得してlistfetchhelpかを見ています。

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

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

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


githubから取得するぞ


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が呼ばれるまで待つ処理を書いておくことです。

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


exportするぞい


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を編集していきます。


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は、KeyUrlを持つものと定義しておきます。

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

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

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

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


完成

これで全ての実装を終えたので、main.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

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

@konojunya/Twitter