読者です 読者をやめる 読者になる 読者になる

あいつの日誌β

あいつの日誌です。

hachioji.pm #65 に行ってきた

66回目のはちぴーに行ってきました。

atnd.org

開催された場所: や台ずし 八王子 横山町

f:id:okamuuu:20170325190021j:plain

割と安くて美味しいいいお店でした。開店~19時迄ドリンク半額なのでちょっとお得。

nintendo switch

f:id:okamuuu:20170325180959j:plain

みんなでガンマンになったり真剣白刃どりしたり。この会の参加者は新しいモノが好きな人が多いですね。

LT

hkoba: mosh

途切れがちなネットワークで効果があったそうです。

macopy: kuiperbelt と入院

Commiter が特定の人になってきているそうです。 あと鎖骨のお話

myfinder: GVFS

ほかにも MS でのエンジニアの取組みについてなど。

私: フロントエンドのはじめかた

一人でやらずにみんなでやりましょう。https://okamuuu.github.io/talks2

papix: IR kit

エアコンを付けたり消したり

neko_gata_s(shinpei): NekogataDrumSequencer

MVVMの文脈から見る、ViewModelとModelの関係は執筆中 https://shinpeim.github.io/NekogataDrumSequencer/build/

uzulla: PHP7.1 ベンチマーク

wrk doesn’t work は英語圏の人にうけたそうです

makamaka_at_donzoko: 同人誌

電子化発表!

フロントエンドのはじめかた

一人でやると色々大変なのでチームメイトと分業して作業しましょうという話をしました。React.js の場合はコーダーさんに components を作ってもらって storybook への commit をお願いするところまでレクチャーするとかなり捗ると思います。

あとそういうハンズオンをやりたい気分です。次のステップに進みたがっているHTMLコーダーさんが集まる勉強会どこかにないかな?

Google の QUIC プロトコルは勉強したほうがいいですか?

参加する直前に知り合いのネットワークエンジニアがインフラを QUIC に対応させる云々言っていたのでそれは私(サーバーサイドエンジニア)も勉強する必要あるのか聞いてみました。

Nginx の config いじるとかそういうレイヤーは関係ないそうです。職務的に関係するとしたら HTTP2 のほうではないでしょうか。との意見を頂きました。

ガンマン

こちらからは以上です。

React.js の tutorial (2017 spring)を書きました

React.js

github.com

React.js で SPA しようとすると redux が登場するので学習コストが割と高くなると思ったのでいっその事 redux 使わなければいいんじゃないかと思いました。README.md に手順を書いてあるのでお試しください。

一応日本語で書いたものをなんとなく Qiita に書きました。 http://qiita.com/okamuuu/items/1b94ab69d1bc3fb82d27

react-router@v4 を調査した

React.js

あらすじ

react-router@v4 にしたら色々な変更が入っていて全く動かなくなったので react-router の機能を確認しました。

追記: 2017-03-23 16:30

withRouter について追記しました。

言及すること

  • <Route> の挙動が v3 と異なる
  • <Switch><Route exact ...> の挙動を理解して対処しましょう
  • browserHistory.push から `this.props.history.push へ変わった
  • <NavLink> が追加されました
  • Nested Routes が廃止された?
  • withRouter

準備

create-react-app practice-react-router4 && cd $_
npm install --save react-router-dom

<Route>

最初に src/App.js を以下のように修正します。

import React from 'react'
import {
  BrowserRouter as Router, Switch, Route, Redirect,  Link, NavLink, withRouter
} from 'react-router-dom'

const Home = () => (<h2>Home</h2>)
const About = () => (<h2>About</h2>)
const User = () => (<h2>User</h2>)

const App = () => (
  <Router>
    <div style={{padding: "60px"}}>
      <Route path="/" component={Home} />
      <Route path="/about" component={About}/>
      <Route path="/:user" component={User}/>
      <Route component={NoMatch}/>
    </div>
  </Router>
)

export default App 

この状態で http://localhost:3000/, http://localhost:3000/okamuuu, `http://localhost:3000/about にアクセスするとそれぞれ以下の画面が表示されます。

open http://localhost:3000/

<div data-reactroot="" style="padding: 60px;">
  <h2>Home</h2>
  <h2>NoMatch</h2>
</div>

open http://localhost:3000/okamuuu

<div data-reactroot="" style="padding: 60px;">
  <h2>Home</h2>
  <h2>User: okamuuu</h2>
  <h2>NoMatch</h2>
</div>

open http://localhost:3000/about

<div data-reactroot="" style="padding: 60px;">
  <h2>Home</h2>
  <h2>About</h2>
  <h2>User: okamuuu</h2>
  <h2>NoMatch</h2>
</div>

このような <Route> の使い方をすると、version4 では URL にマッチする component を全て含んでしまいます。

<Swtich>

先ほどの <Route>s の直上に <Switch> を加えます。

 const App = () => (
   <Router>
     <div style={{padding: "60px"}}>
+      <Switch>
         <Route path="/" component={Home} />
         <Route path="/about" component={About}/>
         <Route path="/:user" component={User}/>
         <Route component={NoMatch}/>
+      </Switch>
     </div>
   </Router>
 )

この状態で http://localhost:3000/, http://localhost:3000/okamuuu, `http://localhost:3000/about にアクセスすると全て以下の画面が表示されます。

<div data-reactroot="" style="padding: 60px;">
  <h2>Home</h2>
</div>

これは最初に Match する要素が発見され次第、その要素だけを返すからです。いまのままでは全ての location に対して <Home> を返してしまうので以下のように正確に Match した場合だけ判定するように <Route>exact=true を追加します。

open http://localhost:3000/

<div data-reactroot="" style="padding: 60px;">
  <h2>Home</h2>
</div>

open http://localhost:3000/okamuuu

<div data-reactroot="" style="padding: 60px;">
  <h2>User: okamuuu</h2>
</div>

open http://localhost:3000/about

<div data-reactroot="" style="padding: 60px;">
  <h2>About</h2>
</div>

browserHistory.push

以下のように props で受け取る形式に変わっています。

-const User = (props) => (<h2>User: {props.match.params.user}</h2>)
+const User = (props) => (
+  <div>
+    <h2>User: {props.match.params.user}</h2>
+    <button onClick={() => props.history.push("/")}>Back to Home</button>
+  </div>
+)

<NavLink>

src/App.js を以下のように修正します。

 const App = () => (
   <Router>
     <div style={{padding: "60px"}}>
+      <ul>
+        <li><NavLink to="/">Home</NavLink></li>
+        <li><NavLink to="/about">About</NavLink></li>
+        <li><NavLink to="/okamuuu">User</NavLink></li>
+      </ul>
       <Switch>
         <Route exact path="/" component={Home} />

Match する場合は className に active が追加されます。

open http://localhost:3000/

<ul>
    <li><a class="active " href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a href="/okamuuu">User</a></li>
</ul>

open http://localhost:3000/okamuuu

<ul>
    <li><a class="active " href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a class="active " href="/okamuuu">User</a></li>
</ul>

open http://localhost:3000/about

<ul>
    <li><a class="active " href="/">Home</a></li>
    <li><a class="active  href="/about">About</a></li>
    <li><a href="/okamuuu">User</a></li>
</ul>

便利な機能ですがこのままだと / も Active になってしまいます。<Route> の時のように extract を指定します。

-        <li><NavLink to="/">Home</NavLink></li>
+        <li><NavLink exact to="/">Home</NavLink></li>

また CSS in JS したい場合は className を各自で好きなように指定したい場合は以下のようにします。

CSS in JS したい

<NavLink
  to="/about"
  activeStyle={{
    fontWeight: 'bold',
    color: 'red'
   }}
>About</NavLink>

className を好きにしたい

<NavLink
  to="/about"
  activeClassName="selected"
>About</NavLink>

個人的な意見ですが NavLink を使うような components はロジックと分離したいのであまり使わないかもしれないのですが一応使い方は覚えておいたほうがいいと思います。

ロジックの分離: About がクリックされたら親コンポーネントに「今 About が押されたよ。あとよろ」と実装して web components として再利用しやすい状態にしたい。

Nested Routes 廃止?

v3 では共通の components を使用したい場合は {this.props.children} と書いて Nested Routes していたのですが、v4 からはそうではなくなっているようです。

withRouter

Home へ戻るボタンを追加したいのですが、ボタン自体に hisotry オブジェクトを渡さない場合、呼び出し元で hisotry.push 関数を実行したいのですが react-router@v4 から browserHistory が import できなくなっています。

代わりに withRouter を使います。

<button onClick={() => props.history.push("/")}>push Home</button>

これを下記のように書きたい場合は withRouter をつかうと良いと思います。

<button onClick={() => props.history.push("/")}>push Home</button>

edit src/App.js

const HomeButton = ({onClick}) => (
  <button onClick={onClick}>push Home</button>
)

const Routes = withRouter((props) => {
  const {push} = props.history

  return (
    <div style={{padding: "60px"}}>
      <ul>
        <li><NavLink exact to="/">Home</NavLink></li>
        <li><NavLink to="/about">About</NavLink></li>
        <li><NavLink to="/okamuuu">User</NavLink></li>
      </ul>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/about" component={About}/>
        <Route path="/:user" component={User}/>
        <Route component={NoMatch}/>
      </Switch>
      <HomeButton onClick={() => push("/")} />
    </div>
  )
})

const App = () => (
  <Router>
    <Routes />
  </Router>
)

まとめ

というわけで v3 から v4 に移行する場合、小さなアプリはいいと思いますがそうでない場合はちょっと面倒くさいかもしれません。こちらからは以上です。

クーリエ・ジャポン・ハッカソンに行ってきた

Hackathon

f:id:okamuuu:20170319155613j:plain

あらすじ

3/18(土)と3/19(日)に人生初のハッカソンに行ってきました。

courrierjapon024.peatix.com

初日

初日は以下のスケジュールで進行しました。

初日午前: アイデアソン

f:id:okamuuu:20170319151303j:plain

最初に仮チームへの振り分けがされます。進行にしたがってチーム内で簡単なゲームをこなして行きます。ゲームが進むうちにだんだんとクーリエの課題をどうにかしようというブレーンストーミングになってきて、いつのまにやらアイデアソンが開始されていました。

最終的にはクーリエジャポンが抱えている問題点を参加者全員が共有して、各自で気になる問題点に対して改善案を考えてA4用紙に書き出して行きます。

参加者全員が個人としてプレゼンを行います。

初日午前: 個人のプレゼンタイム

先ほど考えたA4の企画書を全員で見回って良いと思った改善案に対して☆印をつけて行きます。☆が多いほど優先的にプレゼンを行う権利を得られます。このプレゼンをする側はこの時にチームに欲しい人物像(エンジニアだったら企画が得意な人、デザイナーだったらエンジニア募集など)と一緒に発表するので、プレゼンを聞いている側は自分にマッチするチームに各自で加わっていきます。

私は改善案として3つほど書き出したのですが「あとで読む機能を追加する」という改善案に星が5個ついただけだったのプレゼンせず。他にも類似した改善案でインターフェースを工夫した案があったのでそのチームに参加しました。

ここからハッカソンが開始されます。

初日午後: ハッカソン

軽く打ち合わせして早速手を動かすことにしました。どういうわけかこのチーム4人中4人サーバーサイドエンジニアが集まりました。私はとにかく手を動かしたかったので実装を担当させてもらいました。率先する人から役割が決まります。

今回は実装メインだったので次回は違う役割でチームに貢献したいと思います。

中間発表ができる段階まで画面をつくります。あとは画面をインタラクティブにするなりデザインを考える作業を翌日に行う予定でこの日は実装終了

初日午後: 中間発表

用事があったので私は早退しました。経過報告については翌日他のメンバーに聞く事にし会場を後に。

2日目

2日目は以下のスケジュールで進行しました。

私は前日の中間発表を聞いていなかったのでチームメイトからヒアリング ほとんどのチームが実装ができてないという報告を受けたのですが、1チームだけTinder のようにクーリエの写真を見て見たい記事を選別するといったアプリを作ったチームがいたそうで、かなりキャッチーなものだったそうです。

中間発表に参加したメンバーから現時点の実装ではプレゼン力が弱いという事で画面の動線を追加してほしいという依頼があったので、この日はその作業を起こってから画面をかっこよくする作業をする事にしました。

2日目午後

午前中で画面を追加する作業が完了したのであとはデザインを何とかしたいです。なんですがこのチームは全員サーバーサイドエンジニア。デザインについて詳しいメンバーが一人もいません。そのことを運営側に相談し、クーリエジャポンのデザイナーさんに直接画面をみてもらって意見をもらいました。このフィードバックを反映し、これで実装は完了です。結構時間が足りないなあと思いました。実際支給されたお弁当をみんな食べる時間がなかったようです。

f:id:okamuuu:20170319151035j:plain

プレゼン

各チーム5分間のプレゼンを行った後、審査員からの質疑応答に5分間の時間が割り当てられます。以下私が記憶している限りのメモですので間違っている箇所があると思いますがご容赦ください。

チームH: 夢のイベント実現 カラダで選ぶNEWS

発表者は普段から Kinect を使っているらしく、デバイスに近づくとスクリーンに文字が投影され、最後にジャンプするとクーリエのHPへ画面が切り替わるデモンストレーションが行われました。プレゼン終了後は質疑応答、審査員からは「普通のブラウジングではなく kinect を使う優位性は?」という質問に対してはクーリエジャポンの会員は新しいモノに敏感な層であるので kinect を使う事自体でキャッチーになるのではないか。という事をおっしゃってました。 分程度で滞在2,3時間だと思います。

チームA: 月間編集者Ranking機能

雑誌ではなく WEB ならではの機能として人気のある編集者を投票する機能を提案。Like であったり Follow であったり。

質疑応答ではランキングの定義について議論がされていました。ランキングという名目にすると何を持って優劣なのか、という話になるのでまあ難しい議題ではあるのでなかなか全員を満足する解が見つけづらいという話ではあります。

それから実はクーリエジャポンは編集者が8名との事です。

チームB: クーリエジャポンが持っていた紙の読書体験を再現する

チーム内に雑誌時代のクーリエジャポン愛読者がいたそうで、その方の意見を取り入れてスマホ用のビューアーを作成していました。

質疑応答で、紙の良さを再現するのは良いアイデアだと思いますが、扉以外はどのようにすると良いかアイデアはありますか?という具体的な質問がありました。紙から WEB へ移行したクーリエにとっては命題ともなっているテーマだったので質疑応答が活発に行われました。

クーリエジャポンとしては雑誌は定期購買、 WEB はいつでも更新可能。紙とWEB の良さをまとめるアイデアが欲しいんだろうなあと思いました。

チームC: from 国での配信最適化

ユーザーが興味を持つ記事を検索しやすくする、という課題に対して、興味がある国を選択する機能を提案していました。

質疑応答では審査員からトランプ氏がアフリカに、国内の美術館にオランダのといった場合はどの国にで起きた出来事になるのか?という質問がありました。

これは読者次第によるので結構判断難しいとは思いますが、編集者側から「我々で定義する事は可能」との事でした。公開されている記事を全て読む必要はないのだけれど関心がある国の記事は全て読みたいという読者もいると思うので良いアイデアだと思いました。

チームD: カード型レイアウト

WEB らしいレイアウトを提案。読みたい記事をフィルタリング、並び替えを行えるようにしていました。個人的にはよくできたデザインだったのでそのまま差し替えてもいいような気がしましたが審査員側からは色々な機能があり、なんの問題を解決したかったのかが伝わっていないようでした。

チームE: 有料記事の一定期間シェア機能

Facebook などで有料記事を行って期間シェアする機能を提案。審査員のうちの一人がずいぶんと Facebook における承認欲求のメカニズムに関心があったようでかなり好感触のプレゼンでした

チームF: 一度読んだ記事を見つけやすくする仕組み

我々は問題点として使いづらいから使いやすくしましょうというごく真っ当な改善案を提出しました。まあ割とありきたりの機能なのでだれもが予想のつく機能でした。残念ながら審査員の反応が芳しくありませんでした。

チームH: Tinder のように写真を選ぶサービス

クーリエジャポンは写真がきれいです。その写真だけで興味があるかどうかを選んで取捨選択する仕組みを提案。WEB ならではのサービスということでとても好評のサービスでした。

結果発表

編集長より結果発表。審査の基準は色々あり、審査員側でも紙媒体の延長で行くのかWEBならではで行くの議論が別れたそうです。

というわけで結果は以下の通り

懇親会

参加せず

Hackathon で役に立つ技術

役に立った知識と、役に立つであろう知識を紹介します。

json-server

Single Page Application と json-server は Hackathon では強力な武器になると思いました。反面 Node.js の使い手が少ない為他のメンバーの負担が増えてしまい申し訳なかったです。

絵心

初日の午前に参加者全員がA4でのプレゼンを行ったのですが、やはりデザイナーさんとそうでない人ととでは見栄えが全く違いました。やっぱ絵心大事すねえ。

Line API

今回参加しなかったのですがオススメの記事の要約を Line Bot が教えてくれるという企画があり、すごく興味がありました。なんとなくですが Hackathon では頻出アイデアな気がするので実装経験ある人は戦力になるかも。

BitBucket

ソースコードを共有する方法として無料で team と private な repository を作れるのでとても便利です。

感想

所属したチームがなぜか全員サーバーサイドエンジニアになってしまい、フロントエンドができるのが私だけだったので私は実装を担当しました。そんなわけで React.js で SPA する事にしました。

とはいえ機能を実装する事はできましたが情報を伝えたり楽しませたりする能力が足りないなと感じました。今後はフロントエンド側もやる事が増えるのでまだまだ修行が山積みだなと思いました。

あとは他人のアイデアに触れてきた事が良い経験でした。うまく言えないのですが発想力を分けてもらえた気がします。たぶんこの感覚は参加してみないとわからないかも。

それからプレゼンを見て思ったのは実装が足りなくても伝える事はできるなと感じました。もし営業担当から「この機能がないと売りづらい」と言われて実装してあげるのも優しさだと思いますが、「いや、あなただったら上手く説明できる。他に大事な実装があるから私はそっちに注力します」とつっぱねるのもやっぱり大事なんだなと思いました。タイムイスマネー。

色々と自分自身に足りないものが見つかって補完がされた2日間でした。なのでハッカソンに出場した事ない人は参加すると良いとおもいます。

というわけで今回フロントエンドとして参加したわけですがデザインだったりインタラクティブなところだったりとかちゃんと勉強しないとだめだなあと思いました。もうしばらく勉強の日々が続きそうです。

ソースコード

今回使用したデモアプリはこちらにおいてあります。

github.com

Ansible での provisioning を継続的にインテグレーションしたいので circleci で Docker を使う

Ansible CircleCI Docker

あらすじ

Provisioning を継続インテグレーションしたくなったのですが、そんな時は CircleCI で Docker を使えばいいんじゃないかと思いました。

コンテナ内で ServerSpec を実行する方法などがあると思いますが、ここではコンテナ内に SSHD を使って外部からプロビジョンします。

テストに関しては Serverspec などがあると思いますが、ここではシンプルに 外部から SSH でコマンドを実行して結果の確認をするだけにします。

ソースコード

ここで紹介しているソースコードは下記の置いてあります。

github.com

準備

mkdir circleci-docker-provisioning && cd $_
mkdir -p keys provisioning/inventory provisioning/roles/nodejs/tasks
touch .gitignore Makefile Dockerfile circle.yml 
touch provisioning/playbook.yml provisioning/inventory/docker provisioning/roles/nodejs/tasks/main.yml keys/.gitkeep

keys/* に公開鍵と秘密鍵を保存します。あくまで CI 用の鍵なので git で管理しても問題無いのですが、万が一これを見て秘密鍵github.com で公開している事を心配する人がいるかもしれないので一応このようにします。

create .gitignore

keys/*
!.gitkeep

とりあえず commit します。

git init && git add . && git commit -m "initial commit"

秘密鍵と公開鍵を作成

パスワード無しで作成します。keys/docker_id_rsakeys/docker_id_rsa.pub が生成されます。

ssh-keygen -P "" -f keys/docker_id_rsa

Dockerfile

通常は Docker コンテナーSSHD を入れないのですがここでは SSHD でのプロビジョニングを行いたいのでこのようにしています。

Docker に ansible をマウントして Ansible を local connection してテストする方法もありますが今回はこのようにしています。

create Dockerfile

FROM ubuntu:16.04

RUN apt-get update && apt-get install -y sudo openssh-server python
RUN mkdir -p /var/run/sshd

RUN useradd -m -d /home/docker -s /bin/bash docker
RUN echo "docker:docker" | chpasswd
RUN mkdir /home/docker/.ssh
RUN chmod 700 /home/docker/.ssh
COPY ./keys/docker_id_rsa.pub /home/docker/.ssh/authorized_keys
RUN chmod 600 /home/docker/.ssh/authorized_keys
RUN chown -R docker:docker /home/docker/.ssh

RUN echo "docker ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]

Build

docker build -t provisioning_sshd .

Run

docker run -d -p 40122:22 --name test_sshd provisioning_sshd

ssh で接続確認をします。

ssh docker@localhost -p 40122 -i keys/docker_id_rsa 

Ansible

とりあえず Node.js をインストールしてみます。

create provisioning/inventory/docker

docker ansible_user=docker ansible_port=40122 ansible_host=localhost ansible_ssh_private_key_file=./keys/docker_id_rsa

create provisioning/roles/nodejs/tasks/main.yml

---
- name: install Node.js
  become: yes 
  apt: name={{ item }} state=installed
  with_items:
    - nodejs
    - npm

- name: nmp cache clean
  command: npm cache clean

- name: install n command
  become: yes 
  command: npm install n -g

- name: install node v6.10.0
  become: yes 
  command: n 6.10.0

create provisioning/playbook.yml

---
- hosts: docker
  become: true
  roles:
    - nodejs

provision 開始

ansible-playbook ./provisioning/playbook.yml -i ./provisioning/inventory/docker

動作確認

ssh docker@localhost -p 40122 -i keys/docker_id_rsa 'node -v'
v6.10.0

これを以下のコマンドでテストします。status code を echo $? で確認します。 0であればOKです。

test `ssh docker@localhost -p 40122 -i keys/docker_id_rsa 'node -v'` = "v6.10.0"
echo $? 

Makefile

以下のような Makefile を作っておきます。

docker.keygen:
    ssh-keygen -P "" -f keys/docker_id_rsa

docker.build:
    docker build -t provisioning_sshd .

docker.start:
    docker run -d -p 40122:22 --name test_sshd provisioning_sshd

docker.stop:
    ssh-keygen -R "[localhost]:40122"
    docker rm -f `docker ps -aq`

docker.provision:
    ansible-playbook ./provisioning/playbook.yml -i ./provisioning/inventory/docker

docker.provision.ci:
    ansible-playbook ./provisioning/playbook.yml -i ./provisioning/inventory/docker --private-key=./keys/docker_id_rsa

docker.test:
    test `ssh docker@localhost -p 40122 -i keys/docker_id_rsa 'node -v'` = "v6.10.0"
    @echo "It looks like optimal"

CircleCI

CircleCI 上ではホストOS上で Docker を使います。Circle CI のホストOS上で Python と Ansible を install して、コンテナに対して SSH 接続を行います。

create circle.yml

machine:
  python:
    version: 2.7.10
  services:
    - docker

dependencies:
  pre:
    - pip install ansible
  override:
    - docker info
    - make docker.keygen
    - docker build -t provisioning_sshd .

test:
  override:
    - docker run -d -p 40122:22 provisioning_sshd
    - make docker.provision.ci
    - make docker.test

最終チェック

これらをコミットして実際に Circle Ci が動作する事を確認します。

まとめ

というわけで無事 CircleCI 上で Docker のコンテナに対して provisioning を行う事ができました。

サーバーサイドエンジニアがインフラエンジニアに代わってどれだけ作業すべきなのかは良く分からないのですが、インフラエンジニアにソースコード渡す場合はきちんと動作チェックをするのが礼儀だと思うのでこういうやり方があるという事は覚えておいたほうがいいなあと思いました。おしまい。

PORT 22 以外を使用していて WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! と怒られた時に .ssh/known_hosts のホスト情報を消したい場合

Memo

例えば port を 40122 にしていた場合はこんな感じ

ssh-keygen -R [localhost]:40122

zsh だとこんな感じ

ssh-keygen -R "[localhost]:40122"

okamuuu.com を Gatsby と Netlify に移行しました

Javascript

f:id:okamuuu:20170306115548p:plain

Gatsby x Netlify

netlify.com github.com

あらすじ

先日まで okamuuu.com を Google App Engine 上で Go と React.js で運用していましたが github.com の gh-pages に移動しました。Google App Engine を使う案件にアサインされていたので練習のために運用しようと思ったのですが案件が終了したのでとりあえずお役目御免にしました。

あと、諸事情で okamuuu.com から www.okamuuu.com へ変えました。

Gatsby

日本人男性だと Gatsby は整髪料のアレを連想すると思いますが Static Generator です。markdown でブログを書くアレだと言えば話は早いのですが、類似のツールと違う点として Template Engine が React であるということです。

xxxx.md といった makrdown あるいは xxxx.js といった React Components をそのまま記事にする事ができるので、割と自由にカスタマイズをする事ができます。最近 React にはまっていて、オレオレ React Components を作りたい今日この頃の私にとっては涎が出るような Static Generator です

Just Do it

gatsby のインストールとプロジェクトの作成。プロジェクトの作成にやや時間がかかります。

npm install -g gatsby
gatsby new okamuuu.com2 https://github.com/gatsbyjs/gatsby-starter-blog

開発サーバーを起動

cd okamuuu.com2
gatsby develop

open "http://localhost:8000" で画面を確認します。

記事一覧表示を日付の新しい順にする

pages/index.js を以下のように修正します。

 import React from 'react'
 import { Link } from 'react-router'
 import sortBy from 'lodash/sortBy'
+import reverse from 'lodash/reverse'
 import get from 'lodash/get'
 import { prefixLink } from 'gatsby-helpers'
 import { rhythm } from 'utils/typography'
@@ -12,7 +13,7 @@ import Bio from 'components/Bio'
 class BlogIndex extends React.Component {
   render () {
     // Sort pages.
-    const sortedPages = sortBy(this.props.route.pages, 'data.date')
+    const sortedPages = reverse(sortBy(this.props.route.pages, 'data.date'))
     // Posts are those with md extension that are not 404 pages OR have a date (meaning they're a react component post).
     const isPublished = page => (get(page, "data.isPublished"))
     const visiblePages = sortedPages.filter(isPublished).filter(page => (

記事に下書き機能を追加する

pages/index.js を以下のように修正します。

@@ -14,7 +14,8 @@ class BlogIndex extends React.Component {
     // Sort pages.
     const sortedPages = sortBy(this.props.route.pages, 'data.date')
     // Posts are those with md extension that are not 404 pages OR have a date (meaning they're a react component post).
-    const visiblePages = sortedPages.filter(page => (
+    const isPublished = page => (get(page, "data.isPublished"))
+    const visiblePages = sortedPages.filter(isPublished).filter(page => (
       get(page, 'file.ext') === 'md' && !include(page.path, '/404') || get(page, 'data.date')
     ))

記事を追加

すでに作成されている記事をコピーします。

mkdir pages/articles
cp pages/2015-05-01-hello-world/index.md pages/articles/index.md

記事の冒頭を以下のように修正します。本文は適宜変更してください

 ---
-title: Hello World
-date: "2015-05-01T22:12:03.284Z"
-readNext: "/my-second-post/"
-path: "/hello-world/"
+title: "Using Bootstrap4 with React"
+date: "2016-12-25T00:00:00.284Z"
+isPublished: true
+path: "/articles/using-bootstrap4-with-react/"

config を修正

-blogTitle = "My Awesome Blog"
-authorName = "Kyle Mathews"
-linkPrefix = "/gatsby-starter-blog"
+blogTitle = "okamuuu.com"
+authorName = "okamuuu"
+linkPrefix = ""

Bio component を修正

Profile 画像を修正します。ここでは Gravatar を使います。

npm install --save

Netlify へアップロード

もしくは CLI を使ってデプロイも可能です。その場合は gatsby build であらかじめ静的ファイルを作成しておく必要があります。

npm install netlify-cli -g
gatsby build
netlify deploy
? No site id specified, create a new site Yes
? Path to deploy? (current dir) public
Deploying folder: public

注意点としては netlify deploy で対話モードが始まるのでデプロイするディレクトリを public に指定します。 このコマンドが実行されると設定ファイル .netlify が生成されて、以後 netlify update を実行するだけで build したファイルが upload されます。

もしくは Github と repository を Linking する方法もあります。 master branch への upload がトリガーになって Netlify 上で gatsby build が実行されます。

f:id:okamuuu:20170306115659p:plain

まとめ

静的ファイルの配信であれば Github page や S3 といった選択肢ががあるとおもいますが Netlify は無料のSSL/HTTPS が提供されているところが今の所、頭ひとつ出ている感がします。

Gastby はテキスト主体の記事は markdown で記述して、動的な処理を埋め込みたい時に Javascript(React Component) で記事を記述する事ができるので React.js を使っているエンジニアにとってはとても面白い CMS だと思います。

おしまい