フルリモートワークを続ける上で業務以外で実践していること

COVID-19の影響で現職も全社員が原則自宅でのリモートワークを推奨されており、緊急事態宣言の発令1週間前から連続して3ヶ月間、在宅で毎日仕事をしている。その中で、個人的に心身を整えるために実践している取り組みについていくつか紹介します。

決まった時間に寝て、出社していた頃と同じ時間に起きる

だいたい就寝が0時半〜1時半の間で、起きるのは8時〜8時半の間と決めている(平日のみ。週末や休日は遅寝遅起きになることも)。よくフルフレックスやリモート勤務になると始業ギリギリまで寝ている、という人の話を聞くが、不自由なく段階的に出社できるようになった時に体内時計のリズムが崩れると困るのでとりあえず以前と同じ睡眠のタイミングを保ち、最低でも7時間は眠るようにしている。これだけで体調を崩す要因をだいぶ減らすことができている気がする。

朝は起きたらすぐに散歩に出かける

ちょうどゴミ出しのタイミングで部屋の外に出るので、そのついでに20〜30分かけて近所の人通りの少ない道や川沿いをウォーキングしている。土砂降りでもない限り、ちょっと暑い日でも保冷剤を首に巻いて、人があまりいないタイミングではマスクを少し外して熱中症にならない程度に体を動かす。 運動不足を解消したいという動機もあるけれど、日光に浴びることによってビタミンDが生成されるので、それが気分の不調や免疫力を保つのに役立ってくれると聞いて実行している。あとは夏本番に向けての熱中症対策として暑熱順化の一環。

熱中症対策 「暑熱順化」で暑さに負けない体づくり - ウェザーニュース

それにしても、緊急事態宣言発令中は川沿いをノーマスクでジョギングする人が多くてヒヤヒヤさせられた。最近はめっきり減ったけど。

健康ステッパーで30分くらい有酸素運動をする

これも運動不足解消の一環。ステッパーは全社的にリモートワークに移行したタイミングでネットで購入した。こういうのって自分の親世代くらいの人が部屋で使うものだと思い込んでいたけど、いざ軽いジョギングくらいの強度で20分ほどやってみるとダラダラ汗が出てくる。基本的に毎日、業務が終わって退勤の打刻を押した後にやっている。あまり遅い時間にやるとギシギシと音が響いて下の階に迷惑がかかりそうなので、20時までにはこの運動を終わらせている。 ステッパーを踏んでいる間は本を読もうが動画を見ようが自由なので、時間を効果的に使いながら体を動かせて結構お得感がある。

ぬるめのお湯に20分ほど浸かる

ほぼ毎日湯船にお湯を貼ってリラックスタイムに充てている。入浴も暑熱順化と安眠の効果があるので、家にユニットバスしかない人にも週2 - 3回は実践するのをおすすめしたい。シャワーで済ますよりも血行もよくなる。

きちんと感染対策をすればそろそろ銭湯やサウナにも行ってよさそうだけど、まだあまり踏ん切りがつかない。

もずくやめかぶなどの海藻類、青魚を食べる

海藻類には感染症の重症化であるサイトカインストームを抑える働きがあるらしい。本当かどうかは詳しくはわからない。 ただ、スーパーでも小分けにしてパックで売られていてそのまま食べられるし、これから夏に向けて酢の物を摂取して夏バテに強い体を作りたいと思って食べている。 青魚はいわしやサバなど、缶詰やレトルトで簡単に調理して食べることができ、欠乏しがちなビタミンDを多く含むので自炊がめんどくさい僕のような人間にとっては貴重なタンパク源だ。ただ毎日食べ続けているとアレルギーを発症する恐れもあるので、週に何回まで、と上限を決めた方がよさそう。

React の軽量なルーティングライブラリ「Navi」を試してみる

We Are JavaScripters!【執筆初心者歓迎】 Advent Calendar 2018 の12日目の記事です。大遅刻してしまいました。 WeJSは聞く側で参加したことはあるのですが登壇したことはまだ無いので来年はLT枠で出場したいです。

Navi について

github.com

Navi は React で使える軽量なルーターで、SEOが必要な大規模な静的サイトの構築に適したライブラリとして開発されています。作者は東京在住の James K. Nelson さん。先日の東京Node学園祭でご本人の登壇を聞いて存在を知りました。React のルーターは React-Router が有名ですが、使っていてちょっとつらみがあったので他の手段があるならぜひ試してみたいな、と思い GitHub の Readme を読みながら試してみました。

Getting Started

Create React App を使ってプロジェクトの雛形を作成してからNaviを導入してみます。Jamesさん本人も Create React App との組み合わせが強力、と押しているので早速それにならってチュートリアルを読みながらデモを動かしてみましょう。

インストール

create-react-app navi-demo cd navi-demo

Navi のパッケージは npm に登録された react-navi navi の二つからなるので、両方インストールします。

npm i --save react-navi navi

インストールが完了したらsrc直下にpageというディレクトリを作成します。

cd src && mkdir page

ルーティングの作成

次に、page/index.js を作成します。ここにサイトマップとそのルーティングを以下のように書きます。 ルーティングにはcreateSwitchを、ページコンテンツの生成にはcreatePageというAPIがそれぞれ用いられます。 createSwitchの中に paths という名前のオブジェクトを生成します。paths オブジェクトはネストしており、もう一段階下にサイトのURLを相対パスをキーとして登録し、中身にcreatePageというコンポーネントを登録します。

// pages/index.js
import { createPage, createSwitch } from 'navi'
import * as React from 'react'
import { NavLink } from 'react-navi'

export default createSwitch({
  paths: {
    '/': createPage({
      title: "Navi",
      content:
        <div>
          <h2>Navi</h2>
          <p>A router/loader for React</p>
          <nav><NavLink href='/hello'>Hello</NavLink></nav>
        </div>
    }),
    '/hello': createPage({
      title: "Hello",
      getContent: () => import('./hello.js')
    }),
  }
})

createPageの中身はtitle (ブラウザに表示するページタイトル)とcontent に分かれます。contentについてはJSXをベタ書きしたり、getContent関数で外部ファイルを読み込んだり、JSONを記述することもできます。上記の例ではサイトのindex('/')のcontentはJSXがベタ書きされていて、helloというページへのルーティングの中は外部ファイルをimportしていますね。 それでは同階層にある hello.js の中身をみてみましょう。

// pages/hello.js
import * as React from 'react'
import { NavLink } from 'react-navi'

export default function Hello() {
  return(
    <div>
      <h2>Hello</h2>
      <p>This is demo.</p>
    </div>
  )
}

一見するとごく普通の React の Functional Component が読み込まれているように見えますがここに Navi のミソがあります。 pages/index.jsでは<NavLink>というオブジェクトを介して/referenceへのリンクが貼られています。

Naviでは、<NavLink>へ登録されたリンクへジャンプすると一度中身が空のページをレンダリングし、そこへPromiseとして定義されたオブジェクトを非同期的に読み込んでルーティングを実現する仕組みになっています。

ルーティングの流れ

  1. <NavLink>オブジェクトに登録されたリンクをクリックする
  2. リンクに遷移すると同時に空白のページをレンダリング
  3. pages/index.jsで定義されたルーティングと対応するPromiseコンポーネントを読み込み、ローディングが完了するまで待機。
  4. Promiseが解決(ローディングが完了)されたらコンテンツとしてレンダリング

Navigationオブジェクトの作成

Navi による強力なルーティングの仕組みを利用するためには、src直下のindex.jsNavigationという Navi の根幹をなすオブジェクトを作成する必要があります。NavigationオブジェクトはcreateBrowserNavigation()という API を用いて以下のように作成します。

// index.js
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { createBrowserNavigation } from 'navi'
import pages from './pages'
import App from './App'

async function main() {
  let navigation = createBrowserNavigation({ pages })

  // Wait until async content is ready, or has failed.
  await navigation.steady()

  ReactDOM.render(
    <App navigation={navigation} />,
    document.getElementById('root')
  );
}

// Start the app
main()

main()関数をasyncで定義し、その中でcreateBrowserNavigation()./pages直下のコンポーネントとをまるごとラップする形で呼び出します。navigationに内包されたPromiseオブジェクトが解決されるとsteady()が実行されて非同期的に各ルーティングのコンテンツをレンダリングする仕組み、という解釈でいいのかな。

Appを定義する

これまでに定義した各種コンポーネント<App>内に仕込めば静的サイトの構築がいよいよ完了です。ここで新たに、<NavProvider><NavRoute>というコンポーネントが登場します。これらの仕組みは React の Context APIに近いっぽい? <NavProvider>が Context API の Provider で、<NavRoute>が Consumer という風に理解しています。

// App.js
import * as React from 'react'
import { NavLink, NavProvider, NavRoute } from 'react-navi'
import './App.css'

class App extends React.Component {
  render() {
    return (
      <NavProvider navigation={this.props.navigation}>
        <div className="App">
          <header className="App-header">
            <h1 className="App-title">
              <NavLink href='/'>Navi</NavLink>
            </h1>
          </header>
          <NavRoute />
        </div>
      </NavProvider>
    );
  }
}

src/index.js<App>Navigationオブジェクトを props として渡していますね。Consumer にあたる<NavRoute />を記述することでルーティングに紐づいた各コンポーネントを非同期的にレンダリングしてくれます。

routing
Navi

その他

その他、ローディングインジケータを表示したり存在しないページへのアクセスのハンドリングといった機能もありますが今回の記事ではその説明は省きます。

自分なりに英語のドキュメントを解釈しながらここまで説明してきましたが、より正確に理解するには公式のドキュメントを読みながら各自で動かしてみてください。 また公式チュートリアル動画が作られたようなので、より深くNaviを知りたい方はこちらを試聴してみてください。

www.youtube.com

【Laravel】Eloquent ORM で $model->save(); がうまくいかなかったときの解決法

Qiita Laravel#2 Advent Calender 21日目の記事です。 furuichi と申します。Laravel 歴は大体4ヶ月です。

こういうことをやりたいとき

Eloquent の Model を使ってDBのレコードを更新( $model->save(); )したい。Model と更新用のコードは以下のような感じ。

Model.php

class Something extends Model
{
    protected $table = 'something';

    public $timestamps = false;
}

ExampleController.php

public function store(Request $request) //post
{
        $something = new Something();
        $something->data = $request->data;
        $something->save();
}

チュートリアルによく似た方法で行なっているはずなのに、レコードが更新されなかった。save()の戻り値もTRUEだった。 「なぜ?」と思って公式のチュートリアルを読んでみたら以下のような一文が。

解決の手がかり

https://readouble.com/laravel/5.5/ja/eloquent.html

主キー

Eloquentは更にテーブルの主キーがidというカラム名であると想定しています。この規約をオーバーライドする場合は、protectedのprimaryKeyプロパティを定義してください。

Somethingと紐づいているテーブルの主キーを確認してみた。大文字で ID と設定されていた。どうやら小文字のidでないと思った通りに振舞ってくれないらしい。ということで Model のコードに主キーを以下のように追記した。

Model.php

class Something extends Model
{
    protected $table = 'something';
    // Eloquentの主キーは'id'というカラムを想定しているため、テーブル側で異なる場合は
    // このように指定しないといけない。
    protected $primaryKey = 'ID';

    public $timestamps = false;
}

これで、save()メソッドがうまく動いてくれてDBのレコードに更新をかけられるようになった。DBの主キーのカラム名には要注意です。

Laravel Eloquent ORM のモデルによく使うスコープをメソッドで登録する

Qiita Laravel Advent Calendar 2018 の18日目の記事となります。furuichiです。

クエリスコープ

Laravel の Eloquent ORM では、モデルに紐づけたメソッドを使うとDBからのデータの取得がらくらくできる。

モデル名::find(主キー)

$customer = Customer::find($customer_ID)

上のコードでは、$customer_IDを主キーとして持つレコードを Customer テーブルから取得できる。 これがクエリスコープという仕組みで、デフォルトで使えるメソッドとしてはfindが有名。そのほかにオリジナルでよく使うクエリをモデルに紐づけて登録することができる。

よく使うwhere句をモデルに紐づける場合

Customer->where('address' '=' $address)->get() のようなクエリを多用する時、いちいち同じコードを書いていると冗長になるので、クエリスコープとして登録すると短く書ける。

クエリスコープの作り方・使い方

作り方

モデル名.php ファイルにメソッドを追加するだけ。上の Customer モデルを例にとって作成して見ましょう。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Customer extends Model
{
    protected $table = 'customer'; //参照するテーブル名

    public function scopeAddress($query, $address) //スコープのメソッド。
    {
        return $query->where('address', '=', $address);

    }
}

クエリスコープのメソッドは scopeXxx のようにscopeから始まるキャメルケースで命名します。第1引数に $query を、第2以降の引数に変数を指定します。

使い方

Controller など、任意の場所で以下のように使えます。

$address = '北海道';

$customer = Customer::address($address)->get();

scopeHoge という名前のメソッドの Hoge の部分を小文字から始める形で使います。 上のコードは、Customer->where('address' '=' '北海道')->get() というクエリを書いた時と同じ結果を取得できます。

というのを社内で共有したのですが、社内からこんあツッコミがありました。

例えば住所であれば、英語で "customer living in Hokkaido"

と表現することができますよね。その考えを反映させて、 スコープ名を $customer = Customer::livingIn('北海道'); にするとかどうでしょうか? あくまで参考です。

このツッコミで、メソッドの命名を工夫すれば自然言語を読む感覚でクエリスコープを使えそうだな、と思いました。 上記の命名方法でクエリスコープを書き直すとこうなりますね。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Customer extends Model
{
    protected $table = 'customer'; //参照するテーブル名

    public function scopeLivingIn($query, $address) //スコープのメソッド。
    {
        return $query->where('address', '=', $address);
    }
}

クエリスコープはより複雑な条件もメソッドとして登録できるので、頻繁に使うクエリはどんどんモデル配下のメソッドとして書いておくと全体のコードがスリムになるかもしれません。

何かに貢献したいエンジニアはGASを使うと手軽に実現できるかもしれない、という話

Google Apps Script Advent Calendar 2018 - Qiita の3日目を担当させていただきます。furuichiと申します。

作ったもの

JSおじさん寺子屋」という Slack のチャンネルがあります。JSおじさんこと中西さんが1日に1つ出題する JavaScript のお題を参加者が codesandbox.io 上で解いていき、技術力の向上を目指していくオンライン道場です。 お題は Google Docsスプレッドシート上で共有され、出題者は回答のURLをそれぞれ自分の欄に貼り付けていきます。 スプレッドシートに回答が貼り付けられた時、それがトリガーとなって回答者名、URL、何日目のお題に対する回答という内容が Slack のチャンネルに自動投稿される Bot を GAS で作成しました。コードについては今回は省略いたします。 f:id:takflife:20181129003806p:plain 寺子屋なのでBot名は「teraco」です。スプレッドシートと Slack の連携は GAS で作られるツールとしてはオーソドックスな内容ですが、チャンネル参加者の方には喜んでもらえたので作ってみてよかったなと思いました。

作成した経緯

まず、「JSおじさん寺子屋」の母体である「もくもく寺」について軽くご説明をしたいと思います。

もくもく寺

frontend-temple.connpass.com

フロントエンド・Railsなどの領域をテーマに、各自もくもく作業をする会です。connpass にコミュニティがあり、過去にはReact、Nuxt.js、Dockerなどのハンズオンも開催されました。先日の Docker ハンズオンでは私もスタッフとしてお手伝いさせていただきました。

オンライン上では Slack にワークスペースがあり、ほぼ毎日何かしらの話題が共有され意見交換が活発に行われています。JS, Rails 含め経験豊かな人たちが集まっていて眺めているだけでも勉強になるし、都内勤務ではない私も輪の中に入れてもらって色々とお世話になっています。もともと、Webエンジニアに転職する前からこのコミュニティに参加しており、ワナビー状態の私にも様々なお役立ち情報や技術のTipsなどを参加者の方々から教えていただけて転職の夢を叶えることができました。

JSおじさん寺子屋について

寺子屋の発足はもくもく寺運営者のむろさんのこのツイートがきっかけでした。

寺子屋の詳細についてはむろさんのこちらの記事をご覧ください。

murokaco.hatenablog.com

私が作ろうと思った理由

チャンネル内でスプレッドシートが運用されることとなり、その流れで Slack に回答が自動連携される仕組みが欲しいな、という声が上がったので「GASなら多分実現できますよ」と自分からツール作成に名乗り出ました。最初は手探りでしたが、JSベースで書けることからネット上の知見を色々参考にすることで1週間で完成させることができました。

今年の春くらいからGASで何かを作ろう、と思ってはいたのですが何も思い浮かばず手をつけていない状態でした。また、お世話になっているコミュニティに対し、技術的な成果物で恩返しがしたいと思っていたので、ちょうどよいタイミングで自分の2つの目標を達成することができました。(余談ですが、このアドベントカレンダーに登録したタイミングでは何をやるかまだ決めていなかったので、ツールを作ることでここに書くネタを生み出すこともできて一石三鳥といった感じです)

GASでコミュニティに貢献するメリット

GASでのツール開発のメリットは、大掛かりな環境構築が必要ない点にあります。ブラウザ上のエディタからプロジェクトを開始することができ、運用フェーズに入っても Logger API を使うことで手軽に運用状況の監視が可能です。

tonari-it.com

唯一の難点としては ES2015 記法に対応していないことですが、旧来のJS記法に耐性や経験がある人にとっては些細な問題でしょうか。また、clasp などを使えばローカル環境で開発・テストも行えます。

qiita.com

また、自動投稿という形でスプレッドシートや Slack など業務ツール上で大きな力を発揮してくれるので、Webアプリに必要とされるようなUI設計に頭を悩ませずとも手軽に有用なものが作れます。エンジニアとして技術を使って人に価値を提供する上で、社内や所属コミュニティといった同業者同士の集まりの中でもそれを達成することは非常に意味があると考えています。GAS はミニマルな形でそういった価値創造が実現可能です。目に見える形で素早く何かを作って他者に貢献したい、という願望を持っている人にとってはうってつけのツールなので、ぜひ活用してみてください。

転職して1年が経った

10月中旬で、Web系のエンジニアとして転職してから1年が経った。仕事とは辛いことに耐え忍んでお金をもらうこと、という刷り込みから逃れられた体験はとても価値が大きかった。自分を自分で育てていくこと、得た知的資産を周囲に共有することで還元でき、さらに自分の価値が高められるのでいい業界(会社)に転職できたと思っている。 仕事内容や社風など色々と体験してみて嬉しいことがやはり多かった。1番嬉しいのは勉強したいという欲求に会社がサポートしてくれること。書籍購入代(紙媒体に限るけど)・社外勉強会への参加費用は全額負担してくれるのでたびたび利用させてもらっている。業務経験としてはほぼゼロだった自分を採用してくれたのは「機会を与えれば勝手に勉強して伸びていくだろう」という前のめりな姿勢を評価してのことじゃないか、と思っているのでこれからも勝手にどんどん伸びていきたい。

入社してからはしばらくはReact + Node.js + Firebase でサーバーレスSPAをフルスクラッチで作っていて、当初自主的に勉強していた Rails とはスキルセットがかなり違ったので色々と壁にぶつかったけど、お客さんと直で製品について話し合ったり上司から助け舟をもらって何とか壁を乗り越えることができて大変だったけど楽しかった。最近は JS から少し離れ PHP の各種 MVC フレームワークに触っていてまたスキルセットが大きく変わったのだけど、最初の案件(上記)の技術構成がなかなかトリッキーだったので下のレイヤ(RDBなど)に再入門している感じで刺激的ではある。わからないことが増え、それがまた少しずつわかっていくという面白さはこれからも続くだろうし、もっと色々なことをわかっていきたい。

Laradock と闘った話

概要

Laravel + Docker の勉強の一環として、Laradock で環境構築しようとしたらハマりポイントがめちゃくちゃあったので奮闘の記録を書きます。環境とバージョンにかなり左右されるので、同じようにやってもうまくいく保証は無いですが一応自分の備忘録といて残します。

試した環境について

Laradock を使う前に、YYPHP のハンズオンで Docker から Laravel を動かす方法について動画を見ながら勉強した。こちらはうまくいった。

www.youtube.com

やりたかったこと

下記の記事のように小さなアプリを作りたいと思って環境構築をしていたのだけど、どうしてもうまく Vagrant 上にマウントすることができなかったので Laradock なら必要な実行環境がすべて用意できるらしいと聞き、環境構築を始めた。

Laravel + Vue.js で出席管理Webアプリを作成する - Part.1 | LuftGarden

Laradock の公式はこっち。

Laradock

つまずきポイント

まずは公式ドキュメントにそって進める

コンテナ上に Laravel プロジェクトを作成する

Qiita や stackoverflow などの記事をdigっていたら同じポイントでつまずいていた人の記事があったので参考に。

DockerでLaravelの開発環境を構築する(更新:2017/07/03)

$ docker exec -it --user=laradock laradock_workspace_1 /bin/bash で workspace コンテナにログインし、コンテナの中で composer からプロジェクトを作成。 laravel/laravel 以降のプロジェクト名は任意で決めてよい。 ただし、root ユーザーで composer を実行しようとすると警告が出るので、上記のように必ず--userオプションでユーザー名を指定する必要がある。

下記のコマンドで laravel_docker_test という名前のプロジェクトを作成する。

laradock@<container-id>:/var/www$ composer create-project laravel/laravel laravel_docker_test

YAMLファイルの設定方法がわからない

プロジェクトの作成が終わったらdocker-compose stopで各コンテナを一旦停止。上の記事では docker-compose.yml のapplicationsを変更する手順で進めているが、自分が手元でgit cloneした Laradock の YAMLファイルには項目が存在しない・・・。 どうやら、docker-compose のバージョンが「3」に上がってから項目が削除されたみたい。 github.com

YAMLファイルではなく.env ファイルを変更する

現行の Laradock ではローカルとコンテナ上のディレクトリの紐付けはYAMLファイルではなく、/laravel/.envで管理されているのでそこに該当する変数をいじれば良いということがわかった。

Laradock + nginx + MySQL + phpMyAdmin => macOS

というわけで、.envファイルを以下のように変更

APP_CODE_PATH_HOST=../laravel_docker_test

ついでに、MySQL と NGINX の HOST の設定も上記の記事の通りに変えておく。 .envファイルを変更した場合、コンテナに反映させるためには必ずdocker-compose up -d-dオプションを付けて再実行しなければならない。 ここで再度 localhost に接続すれば・・・

f:id:takflife:20181013170210p:plain

はい。 本番はここから。

MySQLマイグレーションがしたい

先ほどの記事にある、[laravelプロジェクト]/app/Providers/AppServiceProvider.phpの設定変更までやったら MySQL 上にテーブルを作成する。 でも、現段階だとマイグレーションファイルを作ってphp artisan migrateを実行してもエラーになる。

SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client
PDO::__construct(): The server requested authentication method unknown to the client [caching_sha2_password] 

うう。。。ヤックシェービング。。。

ここが最後の難関で、なんとかこの記事の通り認証方式を変更することで解決できた。

Laradock+MySQL マイグレーションするまで

docker-compose exec mysql mysql -uroot -prootMySQLにログイン。

mysql> ALTER USER 'default'@'%' IDENTIFIED WITH mysql_native_password BY 'secret';`
mysql> ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'secret';

先にマイグレーションファイルを作る前に、このコマンドで認証方式を変更しておいたほうがよさそう。 これで認証方式が mysql_native_passwordに変わったのでphp artisan migrateできるようになった。 ただ、MySQLにログインする際 -prootCLIでオプションを付けているのでログイン後に安全では無いよ、と怒られる。この辺のことも解決しておきたいけど。今回は疲れたのでとりあえずここまで。