Yuri’s Tech Note

技術系アウトプット

Gophercises vol.2 url redirect (YAML / middleware / http-headers)

つくったもの

Webリクエストのパスを見て、URL短縮名のようにユーザーを新しいページにリダイレクトするかどうかを判断するhttp.Handler

github.com

使用したパッケージ

"gopkg.in/yaml.v2"
yaml.v2 - gopkg.in/yaml.v2

YAML: 構造化されたデータを表現するのに便利な、ファイルの書き方ルールのひとつ

学び

net/http
  • Server: HTTPをどのネットワークソースで提供するかを定義
  • Handler: HTTPリクエストを実際にどう処理するかを抽象化したinterfaceで、ServeHTTP(w http.ResponseWriter req *http.Request)を提供し、HTTP リクエストを受けてレスポンスを返すことを責務とする。
ServeMux

TTP要求マルチプレクサ(複数の信号を受け取り、1つの信号にして返却する)
各受信要求のURLと登録されたパターンのリストを照合し、URLに最も近いパターンのハンドラーを呼び出す。

middleware

qiita.com


本来は便利なパッケージ使うのかもしれないがgoはデータの整形するだけでも一苦労...

React Native for Web / dom 関連の記事多読まとめ

f:id:yuri_iOS:20180717205642p:plain
記事一覧

  • React NativeをWebに持ってくることの意味
  • react-native-web と react-native-dom
  • GitHub - vincentriemer/react-native-dom
  • GitHub - necolas/react-native-web
  • react-native-dom の何がすごいのか
  • Web最新技術がてんこ盛りのreact-native-domから目が離せない



- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


React Native for Web 概念関連記事

✒︎React NativeをWebに持ってくることの意味

要点

・ブラウザはそもそもドキュメントビューアであり、アプリのランタイムではない。
・React Nativeはプラットフォームを限定しない抽象化を行なっているため、
 react-native-windowsやreact-native-macosなど
 様々なプラットフォームの抽象化を実現化できている。
 まさに "learn once write anywhere"
・React Native for Webは、ネイティブアプリをブラウザ上で再現しようとしたものではなく、
 React Nativeが提唱したGUIの抽象化を、Webアプリケーションの世界でも使えるようにした、
 ブラウザ向けのReactコンポーネント&便利モジュール集ライブラリである。
・事例としてはTwitter Liteが挙げられる。
・React Nativeによって抽象化された「モバイルアプリ開発の当たり前」をブラウザの世界に持ち込んでくれる存在。

まとめ

もともとReact Natibeは "Webの技術であったReactをモバイルで使えるようにしたにもの" という発祥であったがゆえに "Webに回帰するなんておかしいのではないか" という意見もあった。
しかし、そもそもReact Nativeは様々なプラットフォームの抽象化を実現化したものであるのでWebでReact Nativeを動かそうという発想自体が理にかなったものであるということがわかり、技術を採用するにあたり不安を払拭することができた。

react-native-dom 概念関連記事

✒︎react-native-web と react-native-dom

要点

・react-native-domは既存のreact-nativeの仕組みを使ってさらに新しいプラットフォームをターゲットとする。
・react-native-webはreact-nativeという本来アプリを作るための基盤・コンポーネントを、Webでも動かせるようにしたもの。

..........理解できなかったので一旦本家リポジトリで各自の説明を読み比べてみる。



✒︎GitHub - react-native-dom
マルチスレッド
モバイルのReact Nativeと全く同じ仕組みでコンポーネントやロジックはweb workerによって実行される。
(web worker: JavaScriptにスレッド機能を提供することでWeb コンテンツがスクリプトをバックグラウンドのスレッドで実行可能にする手段)

モバイルのReact Nativeと同じレイアウトの挙動
yogaへのカスタムバインディングを使い、webアセンブリコンパイルすることでネイティブでもwebでも同じレイアウトを実現させる。
(yoga: クロスプラットフォームを意識して作られてたレイアウトエンジン)

既存のReact Nativeと同じバンドラで構築されている
React Nativeで使用されているJavaScriptのbundlerであるMetroを使用しており、ネィティブとJSが別スレッドでJS関連(ホットローディングなど)のビルドは早く、開発者にとっても良い体験を提供する。
(bundler: gem同士の互換性を保ちながらパッケージの種類やバージョンを管理してくれる仕組みのこと)

DOMの循環
同じネイティブモジュールブリッジを使用することで... 既存のReact Nativeのモジュールを使うことができるよね。というようなことを言っているとおもうのだが曖昧。

react-native-domのinitial commitは2017年の6月。
発表されたのは2018年5月のReactEurope2018なので今の所大きな変更が起こる可能性は多いにある。



✒︎[GitHub - react-native-web
高品質のインターフェース
JavaScriptで高速かつ柔軟なWeb UIを作ることができる。
ネイティブ品質のインタラクション、複数の入力モード(タッチ、マウス、キーボード)のサポート、最適化されたベンダープレフィックススタイル、RTLレイアウトのビルトインサポート、組み込みのアクセシビリティ、React Dev Toolsとの統合を提供する。

Write once, render anywhere
既存のReact DOMコンポーネントと相互運用し、React Native APIの大半と互換性があるため、既存のコードを書き直すことなく、ネイティブおよびWeb用の新しいコンポーネントを開発できる。

導入事例は既にあり、TwitterをはじめUberメジャーリーグサッカーやプレステなどが挙げられる。
ブラウザはChromeFirefox、Edge、Safari 7以上、IE 10以上に対応。



✒︎react-native-dom の何がすごいのか

要点

・react-native-domのすごいことはブリッジの仕組みにのっとって構築されているということ

ブリッジ

ブリッジとはJSとネィティブの処理を成立させるための仕組みであるが、実現する上でで課題となることが2つある。

ブリッジを実現する際の課題

1: リソースの競合
2: JSとネィティブ間のやりとりのすべての往復に伴う一定のオーバーヘッドがある
f:id:yuri_iOS:20180804140228p:plain

ブリッジを実現するためのポイント

これらを解消するために意識しないといけないポイントが2つ
1: JSとネィティブ間のやりとりを非同期で行うこと
2: JSとネィティブ間のやりとりによるスレッド間通信オーバーヘッドを最小限に抑えること

ブリッジの仕組み

・ブリッジはReact Nativeがもつ3つのスレッド(下記参照)の実行と、JSからネイティブ機能を呼び出す際、キューへのタスク追加、またネイティブからJS機能を呼び出す際にキューへのタスク追加を行う

スレッド1: シャドウキュー(コンポーネント再配置時に使用)
スレッド2: メインスレッド(UIKitが使用)
スレッド3: JSスレッド(開発者が書いたJSコードが動く。主にレンダリングを行う)

JavaScript スレッドとその他スレッドはそれぞれ機能呼び出しを媒介しているキューが存在し、キューに積まれたタスクを各スレッドが消化していって JS 層とネイティブ層の連携がなされている


・このブリッジ仕組みをreact-native-domではWeb Workerをスレッドとして使用することで実現
・Web Workerをスレッドとして使用することで実現
レンダリングスレッドを Yoga の DOM 実装ベースで作っている

まとめ

私にとって魅力的にうつったのはコンポーネントベース開発の加速
atomic design でいうところの atoms や molecules はプラットフォーム共通のコンポーネントを使い、OrganismsやTemplateのみ各プラットフォームにあわせて作成する。
これを実現するためにフォルダ構成を粒度単位でわけることを進めていきたいと考えた。

また、既存のReact Nativeプラットフォームに使用されているのと同じバンドラで構築されているためにwebpackなども必要なく、本当にReact Nativeと同じ感覚での開発をすることができそうである。




✒︎Web最新技術がてんこ盛りのreact-native-domから目が離せない
・React Native向けのUIライブラリや各種モジュールが、ブラウザ上でも動く
・React Nativeは UIスレッドと別にJavaScriptを実行するためのバックグラウンドスレッドを持っていて、react-natieve-domではWeb Workerの中でReactアプリケーションを動かす ことで、ブラウザ上であるにも関わらずReact Nativeの2スレッド制を完全再現している
・レイアウト計算をするYogaをWebで実現
Yoga: AndroidiOSといったプラットフォーム上でFlexboxによるレイアウト計算する

まとめ

噛み砕けていないが、webのviewを完璧にネィティブのViewだとして扱わせることに成功しているということだと思う。
故にReact Nativeで使っていたモジュールが使えて、しかもWeb workerを使ってブラウザ上でマルチスレッドを実現しているので処理速度的にも問題ないものが出来上がっており、その内情はメンテ性などを顧みないかなりゴリ押しな面があるが大いに期待できるものである。

Gophercises vol.1 console quiz (csv / flag / Scanf)

つくったもの

CSVファイルから読み取ったクイズの問題データを整形して出力し、
コマンド上からユーザーの入力を受け取り正解数をカウントする。
github.com

学び

Sprintfの中で%sなどを使用するが最初これは任意の文字列で良いのかと思っていた。
%sや%dは指定子と呼ばれ出力・受取する際に型を指定するために使われる。
ちゃんと文字に意味があり、指定子は共通なので覚えてしまった方が良い。

フォーマット指定子

DOSプロンプト

変換指定子

scanf関数

A Tour of Go vol.8 (Goroutines)

Goroutines

・軽量なスレッド
・goキーワードに続く関数を新しいスレッド(=goroutine)で実行する
・goroutineが実行されていてもmain()が終了するとプロセスが終了する点は注意
・Message-passing communication(各プロセスはメッセージを送り合い、内容は書き変わらない)

Channels

・goroutine 間でのメッセージパッシングをするためのもの
・メッセージの型を指定できる
・bufferで1度に扱えるメッセージの量を指定できる
・送信用チャネルはクローズする必要がある
・後述のselectで複数のチャネルから同時にメッセージを受信できる

// チャネルの生成
make(chan 要素数,キャパシティ)
make(chan 要素型)

// チャネルへの値の送受信
チャネル <- 送信する値
<- チャネル

// バッファの指定
ch := make(chan int, 100)
// 第二引数で送信先であるチャネル(変数:c)を受け取る
func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    // send sum to c
    c <- sum
}

func main() {
    s := []int{7, 2, 8, -9, 4, 0}

    // int型のチャネルを生成
    c := make(chan int)

    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)

    // receive from c
    x, y := <-c, <-c

    fmt.Println(x, y, x+y)
}

Select

・複数ある case のいずれかが準備できるようになるまでブロックし、準備ができた case を実行
・複数の case の準備ができている場合、 case はランダムに選択される

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
            // 変数xの値をチャネルcを通して送信
            case c <- x:
                x, y = y, x+y
            // チャネルquitが準備できたら"quit"を出力
            case <-quit:
                fmt.Println("quit")
                return
            }
    }
}

func main() {
    // 1:チャネル作成
    c := make(chan int)
    quit := make(chan int)
    go func() {
        // 2:0~9まで10回反復処理
        for i := 0; i < 10; i++ {
            // 受信したメッセージを表示
            fmt.Println(<-c)
        }
        // 送信用チャネルquitにメッセージ0を送信
        quit <- 0
    }()
    // フィボナッチ関数にチャネルを引数として渡して実行
    fibonacci(c, quit)
}

A Tour of Go vol.7(Interfaces/Type assertions)

Interfaces

・メソッドの型だけを定義した型
・オブジェクトの振る舞いを定義する

下記の例では Abserをインターフェースとする変数aに対し、Abserで定義しているメソッドAbs()を実装していない型の変数を代入しようとしてエラーになる。

// インターフェースAbserはメソッドAbsを振る舞いとして定義
type Abser interface {
    Abs() float64
}

func main() {
    var a Abser
    f := MyFloat(-math.Sqrt2)
    v := Vertex{3, 4}

    a = f  // a MyFloat implements Abser -> 1
    a = &v // a *Vertex implements Abser -> 2

    // In the following line, v is a Vertex (not *Vertex)
    // and does NOT implement Abser.
    a = v

    fmt.Println(a.Abs())
}

type MyFloat float64

// 1: MyFloatはメソッドabs()を持つ
func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

type Vertex struct {
    X, Y float64
}

// 2: *Vertex(ポインタ型)はメソッドabs()を持つ
func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

インターフェースを実装する際はimplementsの表記は必要ない。

インターフェースのわかりやすい実装例
dev.classmethod.jp

nil

一旦離脱するがGoではnilは型を持つ

func main() {
    var i32 *int32
    fmt.Println(i32 == nil)  // true

    var arr []int
    fmt.Println(arr == nil)  // true

    fmt.Println(compare(i32, arr))  // false
}

int32型のnilと配列型のnilは頭皮比較false

基本的にnilは "==nil" がtrueになるが、インターフェースの時だけ型もnilでないと==nilはfalseとなる。

func main() {
    var arr []int
    fmt.Println(arr == nil)  // true
    fmt.Println(reflect.ValueOf(arr).IsNil())  // true
    fmt.Println(reflect.TypeOf(arr))  // []int

    var arr2 interface{} = arr
    fmt.Println(arr2 == nil)  // false
    fmt.Println(reflect.ValueOf(arr2).IsNil())  // true
    fmt.Println(reflect.TypeOf(arr2))  // []int

    var arr3 interface{}
    fmt.Println(arr3 == nil)  // true
    fmt.Println(reflect.TypeOf(arr3))  // <nil>
}

arr2はinterfaceとして扱っているため、型がnilでないので== nilはfalseとなる

isNilはあくまでBalueOfの下から値がnilだということを言いたいのか。
== nilは値に限らずnilをチェックしているのだろうが深堀はしない。

Type assertions

インターフェースはどんな型でも受け入れるので引数の型をインターフェース型にすればどんな型の値も受け取る関数をつくることができる。

しかし、この方法で受け渡された値は元の型がなにだったのかということを知ることができない。

この時に型アサーションを使えば動的に元の型がなんであるのかをチェックすることができる。

value, ok := <変数>.(<型>)

valueには型アサーション成功時に実際の値が格納され、2番目の変数okにはアサーションの成功否が格納される。

func main() {
    printIf(12)
    printIf("hello")
    printIf([]string{"cat", "dog"})
    printIf([2]string{"hello", "world"})
}

func printIf(src interface{}) {
    if value, ok := src.(int); ok {
        fmt.Printf("parameter is integer. [value: %d]\n", value)
        return
    }

    if value, ok := src.(string); ok {
        value = strings.ToUpper(value) // 対象がstring型なのでstringを引数に取る関数が実行できる
        fmt.Printf("parameter is string. [value: %s]\n", value)
        return
    }

    if value, ok := src.([]string); ok {
        value = append(value, "unknown") // 対象がsliceなのでAppendができる
        fmt.Printf("parameter is slice string. [value: %s]\n", value)
        return
    }

    fmt.Printf("parameter is unknown type. [valueType: %T]\n", src)
}

Type switches

アサーションと分岐を組み合わせた処理を手軽に記述することができる。

func printIf(src interface{}) {
    switch value := src.(type) {
    case int:
        fmt.Printf("parameter is integer. [value: %d]\n", value)
    case string:
        value = strings.ToUpper(value) // 対象がstring型なのでstringを引数に取る関数が実行できる
        fmt.Printf("parameter is string. [value: %s]\n", value)
    case []string:
        value = append(value, "<不明>") // 対象がsliceなのでAppendができる
        fmt.Printf("parameter is slice string. [value: %s]\n", value)
    default:
        fmt.Printf("parameter is unknown type. [valueType: %T]\n", src)
    }
}

A Tour of Go vol.6 (Methods/Receiver)

Methods

Goはクラスの仕組みを持たないが型にメソッドを持つことができる。
メソッドは引数を

type Vertex struct {
   X, Y float64
}

func (v Vertex) Abs() float64 {
   return math.Sqrt(v.X+v.X + v.Y*v.Y)
}

func main() {
   v := Vertex{3, 4}
   fmt.Println(v.Abs())
}

Vertex型はレシーバ引数によってメソッドAbs()を持つ。

レシーバー: メソッドを呼び出される対象

クラスでメソッドを実装してきた身としてはメソッドありきでどの型に属すかを引数で決めるという順番はすごく慣れない。

レシーバーは値型もポインタ型も取ることができる。

ポインタ型

type Vertex struct {
   X, Y float64
}

func (v Vertex) Abs() float64 {
   fmt.Println(v) //{30 40}
   return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (v *Vertex) Scale(f float64) {
   v.X = v.X * f //3*10=30
   v.Y = v.Y * f //4*10=40
}

func main() {
   v := Vertex{3, 4}
   v.Scale(10)
   fmt.Println(v.Abs())
}

ポインタ型ではmain関数で宣言したVertext変数を操作する

値型

type Vertex struct {
   X, Y float64
}

func (v Vertex) Abs() float64 {
   fmt.Println(v) //{3 4}
   return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (v Vertex) Scale(f float64) {
   v.X = v.X * f
   v.Y = v.Y * f
}

func main() {
   v := Vertex{3, 4}
   v.Scale(10)
   fmt.Println(v.Abs())
}

値型ではVertext変数のコピーを操作する

type Vertex struct {
   X, Y float64
}

// メソッドがポインタレシーバ
func (v *Vertex) Scale(f float64) {
   fmt.Println(v)
   v.X = v.X * f 
   v.Y = v.Y * f
   fmt.Println(v)
}

func ScaleFunc(v *Vertex, f float64) {
   v.X = v.X * f
   v.Y = v.Y * f
}

func main() {
   v := Vertex{3, 4}
   // vは値型
   v.Scale(2)
   // ScaleFuncの第一引数はポインタ型なので&v
   ScaleFunc(&v, 10)

   p := &Vertex{4, 3}
   // pはポインタ型
   p.Scale(3)
   // ScaleFuncの第一引数はポインタ型でpはもともとポインタ型
   ScaleFunc(p, 8)

   fmt.Println(v, p)
}
type Vertex struct {
	X, Y float64
}

// 値型のレシーバー
func (v Vertex) Abs() float64 {
fmt.Println(v)
   return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func AbsFunc(v Vertex) float64 {
   return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
   v := Vertex{3, 4}
   fmt.Println(v.Abs())
   fmt.Println(AbsFunc(v))

   // pはポインタ型
   p := &Vertex{4, 3}
   // Absは値型のレシーバだけどポインタ型も受け取る
   fmt.Println(p.Abs())
   fmt.Println(AbsFunc(*p))
}

主なポインタレシーバを使う2つの理由

  • メソッドでレシーバが指す先の変数を変更させたい
  • メソッドの呼び出し毎に変数のコピーを避けたい

レシーバーを値型/ポインタ型使い分ける考え
qiita.com

POLARの運動データを取得してかっこいいグラフにしたい

開発動機

私が通っている暗闇ボクシングではPOLARという心拍計測器を使うことができ、心拍数や運動時間から下記の項目を算出してくれます。

計測項目

  • 運動時間
  • 最高心拍数
  • 平均心拍数
  • 心拍数推移
  • 消費カロリー

f:id:yuri_iOS:20180708171633p:plain

2日に1回以上のペースで通って2ヶ月以上経ちましたが、全然動けない時、高い心拍数を叩き出せたときなど差があり、その差をいまいち把握できていません。
実際自分のデータから何か傾向を掴みたいと思い、"マイページに蓄積されているPOLARデータから時間別・パフォーマー別など自分でカスタマイズしたグラフを出す"というものをつくろうと思いました。

使用する技術

言語

ライブラリ・フレームワーク

プロダクト概要

f:id:yuri_iOS:20180708170943p:plain

作業をおおまかに分割します。

  1. POLARサイトにログインし、スクレイピングでデータを取得する
  2. 取得したデータを整形してDBに保存する
  3. DBを見てグラフを描画する
  4. ログイン画面を用意

いきごみ

本業はアプリ開発でWebに関して知識が乏しいため、できるだけシンプルなプロダクトにしました。
つまずきながらもなんとか自分で進められるようレベル設定したつもりです。

これから分割した作業を少しずつ順番にこなして、目標は2ヶ月以内に完成させたいと思っています。
そして、現在の運動のペースを保ち続けること。
ゆくゆくは朝食食べてから行った時とか前日の睡眠時間とかも記録していきたいです。