あいつの日誌β

働きながら旅しています。

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

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 だと思います。

おしまい

完全栄養食パスタ BASE PASTA を調理してみた

2017-03-04 追記あり

懸念点として茹でた栄養素について記述したところコメントを頂いたので追記しました。

f:id:okamuuu:20170301154328j:plain

あらすじ

実は以前から完全栄養食に関心があったので早速 Amazon.jp で注文してみましたので食感をレポートしました。

BASE PASTAパスタとは

厚生労働省が定めた「日本人の食事摂取基準(2015年版)」に基いて、1食に必要な栄養素をすべて賄えるように作られたパスタです。

完全栄養食とは

人間が生きていくために必要な栄養素がすべて入った食品のことです。ソイレントと呼ばれるミルクのような流動食などがそうであると言われています。私は完全栄養食の概念は Geek House のブログで知りました。完全栄養食を自作するなんてとてもギークですね。

それはさておき、栄養素は一要素でも不足すると不健康になりやすいという「生命の鎖」理論というものがあるので以前から食生活に取り入れたいと思っていました。

生命の鎖理論とは

世界的に有名な栄養学者であるロジャー・ウィリアムス博士によると、生命を維持するには必須となる栄養素が存在し、それらのうち1つが水準以下となれば不健康や病気となる、という 46種類だと言われています。

内海 聡氏の facebook から引用:

ロジャー・ウィリアム博士によると、生命の鎖を構成する栄養素は九種類の必須アミノ酸、一六種類のミネラル、二〇種類のビタミンなどだが、これらのどれにも絶対必要最低水準というものがあり、どの一つがその水準以下になっても不健康となったり、病気となると提唱する。

簡単にいうとまんべんなく栄養とりましょう。という理論です。

完全栄養食を食べたら後は何も要らないのか?

そもそも人間が生きていくために必要な栄養素が入っていてもそれを100%体内で吸収できるかどうかは別問題だと思います。 なのですが、生命の鎖理論のように、各栄養素は単体では働かない、ということを鑑みると、栄養のとりこぼしを防ぐために完全栄養食を選ぶという選択肢が増えたことはとても良いことなんじゃないかと思います。

調理してみた

お皿に取り出したところです。生麺です。美味しそうですね。実際に麺は割と美味しかったです。

f:id:okamuuu:20170301154902j:plain

パスタソースはこちらを使いました。生麺とは相性よくないのかな?まあインスタントですからねえ。

f:id:okamuuu:20170301154514j:plain

はい、できました。

f:id:okamuuu:20170301155243j:plain

懸念点

表記されている栄養価は茹でる前であって茹でた後はどうなるのかわからない。

f:id:okamuuu:20170301154242j:plain

追記: 2017-03-04

茹でた際の栄養素に関しては以下のコメントを頂きました。ありがとうございます。

まとめ

割と美味しいですし、栄養価が高いので悪い印象はなかったです。 茹で汁に栄養素が流れていると思うのでなにか有効活用できればいい感じになるかも。できるのかな?

日頃から常用するとなるとちょっとお値段が高いのかもしれません。マルチビタミンマルチミネラルサプリメントとるほうがコスパはいいと思います。

逆に言い換えるとサプリメントで栄養をとるより食事でとりたい人にとってはとてもいいものになるんじゃないかなあと思います。おしまい。

Amazon アソシエイツ

参考URL

ES2015 でとまどいがちな記法 Object initializer と Spread Operator

あらすじ

ES6 の記法を全て覚えてから ES6 を書くよりも、書きながら勉強したほうが効率が良かったりします。 なんですが写経している時に分からない記法が出てきた場合検索しづらくて困ると思います。

というわけで私がどうやって Google に質問すればいいのか分からなかった記法を2つ紹介します。

Object initializer: オブジェクト初期化子

以下のような ES5 の記法が

const a = 1
const b = 2 
const c = 3
const object = {a: a, b: b, c: c}

ES6 では以下のように省略できます。

const a = 1
const b = 2 
const c = 3
const object = {a, b, c}

また、以下のような ES5 の記法が

function x(obj) {
  const a = obj.a
  const b = obj.b
  console.log(a, b)
}

const a = 1 
const b = 2 
x({a: a, b: b})

ES6 では以下のように省略できます。

function x({a, b}) {
  console.log(a, b)
}

const a = 1 
const b = 2 
x({a, b}) 

この書き方は最初慣れないと気持ち悪いですが使っているうちにだんだん気持ちよくなります。

Spread Operator: スプレッド演算子

...array とか ...object を初めて見た人は多分意味が分からなくて困ると思います。

array を spread する

ES5 では以下のように記述していたものが

function f(array) {
  console.log([array[0], array[1], array[2], 4, 5])
}

f([1, 2, 3])

ES6 では以下のように省略できます。

function f(array) {
  console.log([...array, 4, 5])
}

f([1, 2, 3])

object を spread する

ES5 では以下のように記述していたものが

function f(obj) {
  const a = obj.a
  const b = obj.b
  const c = obj.c
  console.log({a: a, b: b, c: c, d: 4, e: 5}) 
}

const a = 1 
const b = 2 
const c = 3 
f({a: a, b: b, c: c}) 

ES6 では以下のように省略できます。

function f(obj) {
  console.log({...obj, d: 4, e: 5}) 
}

const a = 1 
const b = 2 
const c = 3 
f({a, b, c})

npm publish を念頭において React Components 用のプロジェクトを作成する

あらすじ

先日 react-paginators という React components な module を publish しました。

github.com

その時に色々調べた事があるので備忘録として残します。

作成手順

create-react-appstorybook を使ってプロジェクトの雛形を作成します。create-react-app を使うと webpack の事を考えなくて良いのでとても楽でよいです。

create-react-app react-xxxxx && cd $_
getstorybook

必要なディレクトリとファイルを作成します

mkdir test
touch .envrc .babelrc .npmignore .travis.yml CHANGELOG.md test/xxxxx.test.js 
npm install --saave react react-dom
npm install --save-dev babel babel-cli babel-preset-es2015 babel-preset-react babel-preset-stage-0
npm install --save-dev assert eslint eslint-plugin-react rimraf
npm install --save-dev in-publish safe-publish-latest

create CHANGELOG.md

# Change Log

## v0.1.0
 - Initial commit

そういえば $_ って何ですか?と聞かれる事があったのですがこれは前回使用したコマンドの最後の引数が格納される変数です。 上記のコマンドだと create-react-app react-xxxxxreact-xxxxx となります。

npm run test の環境を整える

雛形ができたら npm test の環境を整えます

create test/xxxxx.test.js

import assert from 'assert'

describe('XXX', function() {
  it('should ...', () => {
    assert.ok(true)
  })  
})

動作確認します。雛形として生成された src/App.test.js と test/xxxxx.test.js が実行されるのを確認します。 ちなみに環境変数 CI=true を忘れると launch mode で起動します

CI=true npm test

> react-xxxxx@0.1.0 test /Users/okamuuu/react-xxxxx
> react-scripts test --env=jsdom

 PASS  src/App.test.js
 PASS  test/xxxxx.test.js

Test Suites: 2 passed, 2 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.167s
Ran all test suites.

src/App.test.js は不要となった時点で適宜 remove してください

npm run version の環境を整える

check-changelog と check-only-changelog-changed

airbnb が公開している react-dates の真似をします。

https://github.com/airbnb/react-dates

github.com

彼らは version コマンドを実行する時は CHANGELOG.md 以外の差分が無い状態でしか実行できないようにしています。 npm-scripts に check-changelogcheck-only-changelog-changed を追加します。

edit package.json:

--- a/package.json
+++ b/package.json
     "start": "react-scripts start",
     "build": "react-scripts build",
     "test": "react-scripts test --env=jsdom",
-    "eject": "react-scripts eject"
+    "eject": "react-scripts eject",
+    "check-changelog": "expr $(git status --porcelain 2>/dev/null| grep \"^\\s*M.*CHANGELOG.md\" | wc -l) >/dev/null || (echo 'Please edit CHANGELOG.md' && exit 1)",
+    "check-only-changelog-changed": "(expr $(git status --porcelain 2>/dev/null| grep -v \"CHANGELOG.md\" | wc -l) >/dev/null && echo 'Only CHANGELOG.md may have uncommitted changes' && exit 1) || exit 0"
   }
 }

以下のような使い方を想定しいています。

CI=true npm run test && npm run check-changelog && npm run check-only-changelog-changed

上記のコマンドは以下の状態でないとエラーが発生するので version up の際に一貫性が生まれます。

git status
  modified:   CHANGELOG.md

postversion, version, preversion を準備

npm version をそのまま使うと Git working directory not clean. と怒られてしまうので --no-git-tag-version オプションをつけて実行します。 package.jsonnpm run version:patch, npm run version:minor, npm run version:major を用意します。

preversion に先ほど準備した test の実行と CHNAGELOG.md のチェックを行います。 postversion で package.json(verson が変更されている) と CHANGELOG.md をcommit します。コミットも自動的に行います。git tag でタグを切り、その tag も push します。

edit package.json

--- a/package.json
+++ b/package.json
     "test": "react-scripts test --env=jsdom",
     "eject": "react-scripts eject",
     "check-changelog": "expr $(git status --porcelain 2>/dev/null| grep \"^\\s*M.*CHANGELOG.md\" | wc -l) >/dev/null || (echo 'Please edit CHANGELOG.md' && exit 1)",
-    "check-only-changelog-changed": "(expr $(git status --porcelain 2>/dev/null| grep -v \"CHANGELOG.md\" | wc -l) >/dev/null && echo 'Only CHANGELOG.md may have uncommitted changes' && exit 1) || exit 0"
+    "check-only-changelog-changed": "(expr $(git status --porcelain 2>/dev/null| grep -v \"CHANGELOG.md\" | wc -l) >/dev/null && echo 'Only CHANGELOG.md may have uncommitted changes' && exit 1) || exit 0",
+    "tag": "git tag v$npm_package_version",
+    "version:patch": "npm --no-git-tag-version version patch",
+    "version:minor": "npm --no-git-tag-version version minor",
+    "version:major": "npm --no-git-tag-version version major",
+    "preversion": "CI=true npm run test && npm run check-changelog && npm run check-only-changelog-changed",
+    "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag && git push && git push --tags"
   }
 }

後ほど postversion の最後に npm publish を追加しますす

gh-pages へのデプロイを準備する

react の component を公開するので実際に見た目がどのように表示されるのかを皆さんに伝える必要があります。 gh-pages に static page をデプロイする方法は色々ありますが、@kadira/storybook-deployer を install するだけで実現できます。

npm install --save-dev @kadira/storybook-deployer

edit package.json

     "version:major": "npm --no-git-tag-version version major",
     "preversion": "CI=true npm run test && npm run check-changelog && npm run check-only-changelog-changed",
-     "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag"
+    "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag",
+    "deploy-storybook": "storybook-to-ghpages"
  }
}

npm run deploy-storybook を実行して gh-pages を確認します。

npm run build

src/ 以下に ES6 で書かれた Javascript を lib/ 以下に ES5 に変換したものを配置します。 npm publish しないのであれば不要な作業ですが、現時点では node_modules/ 配下に install するモジュールは es5 が前提だと思いますのでその習慣に従います。

babel コマンドを実行します。もしかしたら react-scripts が bundle しない 6to5 なコマンドを用意しているのかもしれないのですがよくわからないので自前で babelrc を用意します。

create .babelrc

{
  "presets": ["react", "es2015", "stage-0"]
}

以下を実行し、lib 以下に es5 のコードが生成されることを確認します。

$(npm bin)/babel -d lib/ src/

lib 配下は git で管理する必要が無いので .gitignore に追加します

echo "/lib/" >> .gitignore

また lib 配下は npm publish で使用されるので build 直前で clearn にするようにしておきます。 そして main, files も同様に npm publish される前提の修正を行います。

edit package.json

--- a/package.json
+++ b/package.json
-  "private": true,
+  "license": "MIT",
+  "main": "lib/index.js",
+  "files": "lib",
+  "author": "okamuuu<okamuuu@gmail.com>",
   "devDependencies": {
     "@kadira/storybook": "^2.21.0",
     "@kadira/storybook-deployer": "^1.2.0",
   },
   ...
   "scripts": {
     "start": "react-scripts start",
-    "build": "react-scripts build",
+    "build": "rimraf lib && babel -d lib/ src/",
     "test": "react-scripts test --env=jsdom",
     "eject": "react-scripts eject",
     "check-changelog": "expr $(git status --porcelain 2>/dev/null| grep \"^\\s*M.*CHANGELOG.md\" | wc -l) >/dev/null || (echo 'Please edit CHANGELOG.md' && exit 1)",

npm run publish

最後に publish の設定を行います。

postversion のタイミングで npm publish が実行されるようにします。 npm prepublish は、npm install 時に実行してしまいます。npm run buildnpm install の時に実行しないようにします。 それから npm publish が成功した場合は gh-pages の更新を行うようにします

-    "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag",
+    "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag && git push && git push --tags && npm publish --registry=https://registry.npmjs.org/",
...
+    "build-storybook": "build-storybook -s public",
+    "prepublish": "in-publish && safe-publish-latest && npm run build || not-in-publish",
+    "postpublish": "npm run deploy-storybook"
   }

package されるファイルを確認する

npm publish --dryrun のようなコマンドを実行したいのですが現時点では存在しないようです:(

代わりに npm pack を利用します。 http://qiita.com/inuscript/items/5b3c1466a6ddb9ba6231

実際に package されるファイルを確認するコマンドは下記のとおりです。 npm pack -s はこの記事の通りに設定していると react-xxxxx-0.1.X.tgz という標準出力を一行だけ吐き出します。 npm buildprebuild, postbuild の設定にもよると思うのでもし同じ状況にならなかったら各自修正をお願いします。

tar -tf $(npm pack -s) && $(npm bin)/rimraf $_

一連の作業をコマンド化します。npm run build で lib 配下を最新版にし、$(npm pack -s) で packing とファイル名を取得し、それに含まれるファイル一覧を取得します。ついでに不要となった tgz ファイルも削除しましょう。 再び $_ が登場しますが、これは最後に実行したコマンドが tar -tf ARG なので tgz ファイルを指しています。

+    "build:test": "npm run build >/dev/null && tar -tf $(npm pack -s) && rimraf $_",

このコマンドで視認しながら .npmignore に不要なファイルを追加していきます。

.babelrc
.envrc
.storybook
.travis.yml
stories
src
test
public

期待しているファイルが publish されることを確認します。

% npm run build:test

> react-xxxxx@0.1.3 build:test /Users/okamuuu/react-xxxxx
> tar -tf $(npm pack -s) && rimraf $_

package/package.json
package/.npmignore
package/README.md
package/lib/App.js
package/lib/App.test.js
package/lib/index.js
package/CHANGELOG.md

.travis.yml

手元の環境では動くのだけどもデプロイすると動かなかったりしますので CI 環境を用意します。 ここでは travis.yml を使っていますが CircleCI でも Wercker でも OK だと思います。

注意点として create-react-app で作成したプロジェクトの test は環境変数に CI=true をセットしていない場合は launch します。

create .travis.yml

language: node_js
node_js:
  - "6"
  - "4"
env:
  CI: true
sudo: false

npm publish

最後に npm publish を実行します。無事成功したら最後に package.json を以下の通りに修正します。

- "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag && gitpush && git push --tags",
+ "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag && gitpush && git push --tags && npm publish",

おしまい

react-paginators という npm module を publish しました

github.com

どんなの?

こんなの

f:id:okamuuu:20170302173403g:plain

作った経緯

最初は react-paginate を使う予定でした

https://github.com/AdeleD/react-paginate

なんですが最初のページを指定する機能がどうやら動作しない模様

https://github.com/AdeleD/react-paginate/issues/124

initialPage, forcePage ともにうまく動かなかったのとソースコードを読んだ感じだとロジックと View が混在していて直しづらそうだったので自前で実装しようと思いました。

なんとなく車輪の再発明をしているような気もしなくもないですが良かったら試して見てください。 https://okamuuu.github.io/react-paginators/

ところで

4月1日から働きたいのですがサーバーサイドもしくはフロントエンドなフリーランスのエンジニアを雇ってくれる会社をご存知の方教えて下さい。

getstorybook で Unsupported Project type. (code: UNDETECTED)

以下のコマンドを実行すると

mkdir example-project && cd $_
npm init -y
getstorybook

以下のようなエラーが表示される

 getstorybook - the simplest way to add a storybook to your project. 

 • Detecting project type. ✓
    Unsupported Project type. (code: UNDETECTED)
    Visit http://getstorybook.io for more information.

原因

getstorybook コマンドは package.json の中身を見て判断しています。

https://github.com/storybooks/getstorybook/blob/master/lib/detect.js

どうやら最低限 react.js が install されていれば良さそうです ということで先に react を install します

npm install --save react react-dom

この状態で retry します。

getstorybook

npm run storybook を実行できるようになります。おしまい

React Storybook で Material-UI を試す

create-react-app material-ui-storybook && cd $_
getstorybook
npm install --save react-tap-event-plugin material-ui
npm install --save-dev  storybook-addon-material-ui

edit `src/stories/index.js

 import React from 'react';
-import { storiesOf, action, linkTo } from '@kadira/storybook';
+import { storiesOf, action, linkTo, addDecorator } from '@kadira/storybook';
 import Button from './Button';
 import Welcome from './Welcome';
+import {muiTheme} from 'storybook-addon-material-ui';
+import RaisedButton from 'material-ui/RaisedButton';
 
 storiesOf('Welcome', module)
   .add('to Storybook', () => (
@@ -15,3 +17,9 @@ storiesOf('Button', module)
   .add('with some emoji', () => (
     <Button onClick={action('clicked')}>😀 😎 👍 💯</Button>
   ));
+
+storiesOf('Material-UI', module)
+  .addDecorator(muiTheme())
+  .add('RaiseButton', () => (
+    <RaisedButton label="Default" />
+  ));

open http://localhost:9009