elmで作った静的サイトをgithub pagesで公開してみた
最近ブログ書くのをサボっていたのでなにか書こうと思い、そうだ最近触っているElmについて書こう!と思い書きました。時間がかなり遡るのですが年末年始は実家に帰省していて、その際にElmを試しに触っていて簡単なサイトを作ってGithub Pagesで公開したので、その手順を書き残しておきます。
Elmとは
ちょっと触っただけなので詳しくは説明できないのですが公式から抜粋すると以下の特徴があります。
- 実行時の例外が一切起きない
- virtual DOMを使用するがすべての値はイミュータブルなのでベンチマークがすごくいい
- セマンティックバージョンを強制できる
などの特徴があります。
完成品
Elmで書いて動いたものは下のサイトになります
https://jon20.github.io/mypage/
Githubは下になります
最終的にディレクトリは以下になりました。
. ├── README.md ├── elm-stuff ├── elm.json ├── package.json ├── src │ ├── Main.elm │ ├── assets │ │ └── flog.jpg │ ├── index.html │ ├── index.js │ └── main.scss ├── webpack.config.js └── yarn.lock
Elmの準備
Elmのインストールに関しては割愛させていただきます。
Elmのインストールができたら対象のディレクトリでelm init
を行ってください。
src配下にElmのファイルを書いていく形になります。
特にElmで書いたものが手元になければElm-lang.orgのQuick Sampleにあるカウンターアプリがあります。コードは下のIntroductionにかかれています。
Htmlファイル
Javascript Interopに書かれているようなやり方になります。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="icon" href="./assets/flog.jpg"> <title>jon</title> </head> <body> <div id="elm"></div> </body> </html>
Javascriptファイル
汚いですが、idがelmの要素にelmを紐付ける形になります。
'use strict'; require('./main.scss'); import Image from './assets/flog.jpg' const { Elm } = require("./Main.elm") const app = Elm.Main.init({ node: document.getElementById('elm') });
Webpackの使用
あんまりwebpack詳しくないのですが、色々参考にしながら作成しました。 必須になるプラグインは
html関連
- Elm関連
- CSS関連
- style-loader
- css-loader
- sass-loader ※Sass使う場合
だと思います...
画像とか扱う場合はfile-loaderが必要になると思います。
必要ない場合はfile-loaderらへんはコメントアウトしてください
const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: './src/index.js', output: { path: __dirname + '/dist', filename: 'index.js' }, resolve: { extensions: ['.js', '.elm', '.scss'] }, module: { rules: [{ test: /\.html$/, exclude: /node_modules/, use: [{ loader: 'html-loader', }, ] }, { test: /\.(jpg|png)$/, use: { loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'assets/', } } }, { test: /\.elm$/, exclude: [/elm-stuff/, /node_modules/], use: { loader: 'elm-webpack-loader', options: { verbose: true, } } }, { test: /\.scss$/, use: [{ loader: 'style-loader', }, { loader: 'css-loader', options: { url: true } }, { loader: 'sass-loader', } ], } ], noParse: /\.elm$/ }, plugins: [ new HtmlWebpackPlugin({ template: "./src/index.html" }) ], devServer: { inline: true, stats: 'errors-only' } };
CIでビルド・公開周りを自動化
今回はTravis-CIを使いました。
masterにpushされたらyarn install
走らせて、gh-pages branchにpushする形となっています。
yarn buildはpackage.jsonで定義されたスクリプトが走っています。
language: node_js elm: - elm0.19.0 node_js: - "10" cache: yarn: true directories: - "node_modules" branches: only: - master install: - yarn install - yarn build script: - echo "Skipping tests" deploy: provider: pages skip-cleanup: true github-token: $GITHUB_ACCESS_TOKEN target-branch: gh-pages local-dir: dist on: branch: master
まとめ
超ざっくりですがElmをgithub pagesで公開する手順を書きました。簡単にElmで作ったプロダクトがノーコストで公開できるので便利ですね。
WebpackらへんやCIらへんも一緒に試せるのでぜひ挑戦してみてください。
Quick Sampleをgithub pagesで公開するだけなら余分な所が多いので余力があったらサンプルを追加します...
あと、今回作ったものを読み込むときに一瞬ちらつくのですが読み込み順の問題なのかな...
gRPCで動画ファイルの送受信を行ってみる
Go4 Advent Calendar 2018 11日目です。
今回はgRPCを使ってmp4で作成した動画ファイルをstreamを使って送受信してみようと思います。
コードがあまりきれいでないかもしれませんがよろしくお願いします。
はじめに
今回作成したサンプルコードはgithubに置いておきます。
gRPCとは
Googleが開発しているOpen SourceのRPC frameworkになり、Protocol Buffersを使ってサービス間のインターフェースをprotoファイルに定義します。また、試してませんが他言語間の通信も可能みたいです。
Protocol Buffersでどう定義するのかは下記のドキュメントに詳しく書かれています。
protoファイルの定義
syntax = "proto3"; package upload; service UploadHandler { rpc Upload(stream UploadRequest) returns (UploadReply) {}; } message UploadRequest { bytes VideoData = 1; } message UploadReply { string UploadStatus = 1; }
syntaxはproto3を利用しています。
serviceブロックの中に利用するメソッドを定義します。今回はmp4ファイルを受け取り、成功したらメッセージを返すようなメソッドを定義しています。
ここでUploadRequestの前にstreamをつけているのですが、これを記述することで一つのリクエストを複数のリクエストに分割することができます。今回はClient Streaming RPC方式になると思います。
また、このprotoファイルで生成したgoのファイルはclientとserverの両方必要になります。
Serverの作成
func NewUploadServer(gserver *grpc.Server) { uploadserver := &server{} upload.RegisterUploadHandlerServer(gserver, uploadserver) reflection.Register(gserver) } type server struct{} func (s *server) Upload(stream upload.UploadHandler_UploadServer) error { err := os.MkdirAll("Sample", 0777) if err != nil { return err } file, err := os.Create(filepath.Join("Sample", "tmp.mp4")) defer file.Close() if err != nil { return err } for { resp, err := stream.Recv() if err == io.EOF { break } if err != nil { return err } file.Write(resp.VideoData) } err = stream.SendAndClose(&upload.UploadReply{UploadStatus: "OK"}) if err != nil { return err } return nil }
handler.goではclientから受け取った動画データをSampleディレクトリにtmp.mp4として保存するような処理を行っています。成功したらOKの文字をclientに送信します。
main.go
func main() { lis, err := net.Listen("tcp", "localhost:8080") if err != nil { panic(err) } server := grpc.NewServer() handler.NewUploadServer(server) if err := server.Serve(lis); err != nil { panic(err) } }
main.goではNewServerで空のgrpcサーバーの作成を行い、NewUploadServerで作成したserviceを登録してlocalhost:8080でサーバーを立ち上げます。NewServerは色々オプションを渡せるみたいです。
Clientの作成
func main() { connect, _ := grpc.Dial("localhost:8080", grpc.WithInsecure()) defer connect.Close() uploadhalder := upload.NewUploadHandlerClient(connect) stream, err := uploadhalder.Upload(context.Background()) err = Upload(stream) if err != nil { fmt.Println(err) } } func Upload(stream upload.UploadHandler_UploadClient) error { file, _ := os.Open("./sample.mp4") defer file.Close() buf := make([]byte, 1024) for { _, err := file.Read(buf) if err == io.EOF { break } if err != nil { return err } stream.Send(&upload.UploadRequest{VideoData: buf}) } resp, err := stream.CloseAndRecv() if err != nil { return err } fmt.Println(resp.UploadStatus) return nil }
予めsample.mp4ファイルを対象のディレクトリに用意してmakeでどれくらい送るか定義しています。そしてfor内でひたすらserverにリクエストを投げまくってます。 送り終わったらCloseAndRecvでstreamをcloseしてサーバーからのメッセージを待ちます。うまく行けばOKと返ってきます。
完成!
実際に試してみるとclientから送られてきたSample.mp4ファイルがserverのSampleディレクトリ内にtmp.mp4として保存されたことが確認できました。
まとめ
- streamを扱うことで動画データを分割して送ることができた。特に大容量の動画ファイルを送信したときに少しずつデータを送ることができるのでネットワークの負荷が減りそう
- ライブストリーミングの変換処理とかstreamで実装すると良さそうな気がする
Go言語でgraphqlを触ってみた
ゆるっとアドベントカレンダー Advent Calendar 2018 2日目です。
最近ハッカソンでGraphqlを使って開発を行っているので、今回はGraphqlの復習で簡単なGraphqlサーバをGo言語を使って実装してみます。
Graphqlとは?
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data
GraphqlはAPIへ問い合わせるためのクエリ言語になります。一つのエンドポイントを用意して、予め定義しておいたGraphqlのクエリを投げることで必要な情報を持ってくることができます。
今回使う外部ライブラリ
- labstack/echo
- graphql-go/graphql
- graphql-go/handler
今回実装したもの
localhost:8080/graphqlにブラウザでアクセスするとGraphqlのIDEが起動して、Graphqlのqueryを投げると下の写真の右のようにdataが返ってくるようなものになります。
今回実装したものは↓のGithubに置いておきます。
解説
基本的にgraphql-go/graphqlのexampleを参考に実装しています。
まずREST APIのように何か投げたら何か返ってくるものをGraphqlで実装したいためSchemaを定義します。ここで気をつけることは投げられるqueryにはQueryとMutationがあります。SchemaConfigを見ているとSubscriptionがあるのですが調べたかんじだとリアルタイムのデータを取得するときに使うらしいです。まだ使ったことはないので今回は触れません。
QueryとMutationなのですが、QueryはCRUDでいうとCの部分で、単にデータを取得したいときに使います。MutationはR、U、Dの部分で、データに変更を加えたい際に使います。
今回はSampleとしてQueryとMutationを使ってschemaを定義します。
仕様は以下のとおりです。
- Query
- IDとPasswordを渡したらMessageが返ってくる
- Mutation
- UsernameとAgeを渡したらMessageが返ってくる(Mutationなので何かしらの更新処理を入れる必要があるが今回は省略します)
QueryのSchemaの実装
graphql-sample/query.go at master · jon20/graphql-sample · GitHub
func SetQuery() graphql.Fields { query := graphql.Fields{ "SampleQuery": &graphql.Field{ Type: SampleQueryType(), Args: SampleQueryArgs(), Resolve: SampleQueryResolve, }, } return query } func SampleQueryType() *graphql.Object { sampleType := graphql.NewObject(graphql.ObjectConfig{ Name: "SampleQuery", Fields: graphql.Fields{ "Message": &graphql.Field{ Type: graphql.String, }, }, }) return sampleType } func SampleQueryArgs() map[string]*graphql.ArgumentConfig { sampleArgs := graphql.FieldConfigArgument{ "ID": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.String), }, "Password": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.String), }, } return sampleArgs } func SampleQueryResolve(params graphql.ResolveParams) (interface{}, error) { resp := &models.SampleMutateResp{ Message: "Hello, This is SampleQuery", } return resp, nil }
汚い実装ですが、肝となるのはSetQuery()です。Fieldsに定義したいFieldを定義します。FieldはArgsがqueryを使うときに渡す値の定義、Resolveにはqueryを叩いたときの処理を渡してあげます。
MutationのSchemaの実装
graphql-sample/mutation.go at master · jon20/graphql-sample · GitHub
func SetMutation() graphql.Fields { mutateQuery := graphql.Fields{ "SampleMutate": &graphql.Field{ Type: SampleMutateType(), Args: SampleMutateArgs(), Resolve: SampleMutateResolve, }, } return mutateQuery } func SampleMutateType() *graphql.Object { sampleType := graphql.NewObject(graphql.ObjectConfig{ Name: "SampleMutate", Fields: graphql.Fields{ "Message": &graphql.Field{ Type: graphql.String, }, }, }) return sampleType } func SampleMutateArgs() map[string]*graphql.ArgumentConfig { sampleArgs := graphql.FieldConfigArgument{ "Username": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.String), }, "Age": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.Int), }, } return sampleArgs } func SampleMutateResolve(params graphql.ResolveParams) (interface{}, error) { resp := &models.SampleMutateResp{ Message: "Hello, This is SampleMutate", } return resp, nil }
実装手順としてはqueryと同じです。どちらも共通して気をつけるポイントとしては指定できる型です。Scalar型があるのですが公式のDocumentを見たかんじだとID、String、Int、Float、Booleanだけになります。もしそれ以外の型を使いたい場合は自分で定義しなければなりません。
SchemaのSetupとIDEの用意
graphql-sample/graphql.go at master · jon20/graphql-sample · GitHub
func GraphqlSetting() http.Handler { query := fields.SetQuery() mutateQuery := fields.SetMutation() rootMutation := graphql.NewObject(graphql.ObjectConfig{Name: "RootMutation", Fields: mutateQuery}) rootQuery := graphql.NewObject(graphql.ObjectConfig{Name: "RootQuery", Fields: query}) schema, err := graphql.NewSchema(graphql.SchemaConfig{ Query: rootQuery, Mutation: rootMutation, }) if err != nil { panic(err) } h := handler.New(&handler.Config{ Schema: &schema, Pretty: true, GraphiQL: false, Playground: true, }) return h }
GraphqlのIDEはhandler.Config{}のところで設定できます。指定できるIDEの種類としてはgraphiqlとgraphql-playgroundがあります。今回はgraphql-playgroundを使います。
Graphqlサーバの立ち上げ
graphql-sample/main.go at master · jon20/graphql-sample · GitHub
func main() { route := echo.New() route.POST("/graphql", echo.WrapHandler(graphql.GraphqlSetting())) route.GET("/graphql", echo.WrapHandler(graphql.GraphqlSetting())) route.Logger.Fatal(route.Start("localhost:8080")) }
あとは localhost:8080/graphqlのroutingを追加するためechoを使います。
完成!!
定義したSchemaは↓のように見れます。
Queryに定義したqueryの実行結果 Mutationに定義したqueryの実行結果
まとめ
- 単一エンドポイントにまとめられるのでREST APIで実装する場合と比べてエンドポイントの管理が楽になりそう
- Documentを自動で生成してくれるのでSwaggerなどを用いてDocumentを実装する手間が省ける。
- ファイルアップロードとかを実装する場合はどうじっそうすればいいのだろうか?
今日作る料理をサジェストしてくれるlinebotを作ったよ!
少し前からLINE community labというワーキンググループに友達と参加し面白いLine Botが作れたので紹介しようと思います
作成したもの
機能としては例として「なんか、豚肉とか使ったものない?」とメッセージを送信してますが、言葉の中にある材料が含まれる料理を提案してくれます。 またレシピ確認ボタンで詳細レシピURLの表示、材料確認ボタンで材料の表示をしてくれます。 これで冷蔵庫に残った余り物で何を作ろうか考えなくて済みますね!
構成
私は今回バックエンドの方を主にやっていたのでlineのapiはよくわかってません(友達がやってくれました)ので、バックエンドを中心に説明します。
使用言語
今回はRubyを使いました。 web frameworkは軽量のsinatraを使用
実装
まず、レシピのデータについてですが、RakutenレシピがAPIを公開していたので負荷をかけないように注意しながらレシピ情報をいただきました(利用規約に引っかかってそう...)。
材料名のところに全角スペースや★がついててそこら辺整形するのが面倒くさかったです。
次に柔軟に文章に対応したいため送られてきた文章に対して形態素解析を行い材料と思われる単語を抜き出します。
そしてElasticsearchで全文検索を行いレシピをピックアップしています。
最後に開発にあたってはコンテナ環境を使いたかったのでDockerを使用してます。で、docker-composeを使って複数のコンテナを管理してます。
本番サーバはGCPを借りて動かしました。
まとめ
小規模ですが初めてチーム開発をしました。
gitやrubyをもっと学ばないといけないなと思いました。
途中までテストを書いていたのですが、途中でめんどくさくなってやめてしまったのでちゃんと書かないとな...
今回は公にサービスを公開する気はないのですが、いざサービスを公開するときに外部のサービスを利用している場合、利用規約ちゃんと見ないといけないな。
Amazon Echoを使って自宅の照明を操作してみた
目的
最近家のリモコンがよくなくなるのでAmazon Echoで操作できたら最強なのでは?と思い作成に至りました。
完成品
作成手順(雑にしか書きません)
- 赤外線LED,赤外線センサー、Raspberry piをなんとかして手に入れる。
- lircを使い赤外線を記憶させて送るところまでを行うのですが、以下の記事が参考になりました。(ありがたや〜)
- コマンドで照明がON, OFFできたらいよいよAmazon echoと連携させます
- raspberry piでsmart home(正確にはBelkin WeMoデバイス)をエミュレートしてくれるものを見つけたのでそれを使います
- 使い方としてはREADMEに書いているとおりにセットアップすれば問題なく動かせました。(Python3.6以上を要求されることに注意)
- セットアップが終わったらfauxmo/config-sample.jsonをconfig.jsonにリネームしてPLUGINSのとこを書き換えます。
私はRaspberry pi上にFlaskでサーバーを立てて、httpリクエストを受け取ったらON, OFFのコマンドが走るようにしました。
まとめ
この内容はPycon Kyushuの懇親会のLTで発表した内容となっています。
発表したスライドも一応載せておきます
AnsibleでArchLinuxの自動インストール
AnsibleでArchLinuxの自動インストールをやってみた
今回、AnsibleでArchlinuxの自動インストールに挑戦しました。
結果として成功したのですが、tasksが結構汚いのでまだまだ改善の余地はあります…
インストールの説明はGithubに載せているのでそっち見たほうがいいです。
並列処理と平行処理について
ふと並列処理と平行処理の違いは?と考えると毎回う〜んとなるので一番しっくりくる考え方を書き残しておこうと思います。
定義
システムが複数の動作(処理の流れ)を同時に実行状態(in progress)に保てる機能を備えている場合を 並行(concurrent)と言い、複数の動作を同時に実行できる場合を並列(parallel)と言います。 重要な概念、違いは「実行状態」という点です。
平行処理
平行処理はシングルコアプロセッサで考えると理解しやすいです。シングルコアなので基本は1つの仕事しかできません。なので1つの仕事にだけ集中していると他の仕事はこなせません。なので複数の仕事を分割して処理すれば多くの仕事をこなすことができます。これが平行処理の考えです。
平列処理
平列処理はマルチコアプロセッサで考えると理解しやすいです。マルチプロセスで動く仕事をそれぞれのコアで分担して、さらに同時に処理していきます。これが平行処理です。
まとめ
並列処理の中に平行処理の考え方があるみたいな感じかな?