天童木工のスポークチェアを買った

仕事がフルリモートになり、7月に少し広めのお家に引っ越しまして。 だだっ広い部屋にモノが少ないと、なんとも侘しいので何か置きたいなと考えてました。

半床生活なので、低めの椅子を置くと良さそうだと雑誌の名作椅子特集を眺めていると、そこに彼はいました。

天童木工のスポークチェア。 一目惚れです。

とはいえ暫しの葛藤の末、また会社の人に背中を押してもらい、購入して待つこと1ヶ月。 先日ついに届いたのでした。

f:id:uu_ka:20211011005756j:plain:w360
愛らしいフォルム
f:id:uu_ka:20211011005800j:plain:w360
成形合板のところ

毎日座っていますが、良いです。 気分の上がる家具って素晴らしいですね。

大切に使いたいと思います。

余談ですが、配達時に開梱設置してくれると勝手に思い込んでいたら違いました。 あまりの箱の大きさに、配達のお兄さんも私も困惑しつつ、無理やり搬入してもらいました。 箱越しにお互いに笑顔でよかったぁと言葉を交わしたのは良い思い出ですw

f:id:uu_ka:20211011010807j:plain:w180
ギリギリ

情緒的な

バックエンドのエンジニアをやっていると、あれこれ運用、調査に追われることが多い。
これらの毎日の対応や必要な開発業務をやっていると、気づけば目の前のことだけで精一杯になっている。
こういう時って、あまり物事を深く考えなくなる傾向がある。

コロナウイルスの影響もあって、久しぶりのミートアップをオンラインで行った。(と言っても二人だけど)
このミートアップでは、物事に対して深い質問を何度も投げかけられる。
一つ一つのことをなんでだっけ?って考えることの大切さを思い出す。

今関わっているサービスについて、ふと考えてみた。
例えばお米。
お米は買ってもすぐには無くならなくて、割と長期間同じお米を食べる。
米びつからお米を取り出す時に、作り手のことを一瞬思い出す。

これは土屋さんのお米。

これって凄いことじゃないだろうか。
自分はオンラインでもコミュ障なので、特段深いやり取りがあるわけではない。
ありがとうございます、美味しくいただきます。と伝える程度。

作っている本人とのほんの小さな関係性が、いろいろな想像を掻き立てるのかもしれない。
そんなとき、このツイートを発見。

関係性に情緒は生まれます。

うんうん、これこれ。
情緒あふれるサービスっていいよね。(少しの合理性もほしいけど)

先日のミートアップで、合理性と情緒性の話をしてもらった。
タイミングって重なるもんだなぁ。

poke-m.com

近況

年が明けて2020年になりました。明けましておめでとうございます。 昨年に続き、年初に近況を綴ろうと思います。

昨年はとても苦しい1年でした。 仕事では多方面に力不足を痛感し、どうしたら良いのかわからない状態になってしまう事を多々経験しました。 組織としても、プロダクトとしても問題があり過ぎて、このままだとヤバいなと思いつつも目の前の事に忙殺される日々でした。 「このまま続けるの厳しくないですか?」と、大黒柱の先輩エンジニアと話していた時、

「実は僕、辞めるんだよね」

と言われて、色々な意味で終わったなと、空笑いしながら思ったのを鮮明に覚えています。 紆余曲折ありエンジニアの人数も増え今に至るのですが、大黒柱不在になってしまったことにより、大事なことを2つ学ぶ機会を得ることができました。

一つは問題に対して、自分の分野を超えて、メンバーとコミュニケーションを取り、ポジティブな心持ちで改善のための行動を取ると事態は動くという事です。 これをリーダーシップというのでしょうか。 自分に足りない、大事な大事な事を学んだ気がします。 全然出来てはいないのですが、日々湧き出る不平不満と戦いつつ、ポジティブに動き続けられるか、チャレンジは続きます。

もう一つは、最も大きな問題⋯、みんなバラバラでチーム感がまるで無い、に対する、こういうことかなぁという考えを持てたことです。 チームとして戦うためには、共通の目標を持つべきで、いわゆるミッション・ビジョンがいかに大切か。 またマネジメント層は、チームの情熱ややる気を引き出して、成長を促し、自走させることがいかに大切か。 そのためには、チームを知ること。メンバーそれぞれの使命感などを理解することがやっぱり大事。 いわゆるマネジメントだと思うのですが、人間を知る必要があるので、難しくも興味深い分野だということに気づきました。

この時期、心に刺さる記事や、本、漫画を読む機会がありました。

konifar-zatsu.hatenadiary.jp

funfunhouse.hatenadiary.jp

repl.info

blog.livedoor.jp

さて、2020年は、チームになれるかで今後の運命が大きく変わるような気がします。 なれるかな? きっと大人になる必要がある1年なんでしょう。楽しみです。

今年も、一歩ずつ進んでいきます。

f:id:uu_ka:20200104043448j:plain

SPAでない普通のMPA Railsアプリにwebpacker + vueを導入した時の設計メモ

CoffeeScript + jQueryで作られている普通のMPA railsアプリに既存の実装はそのまま、リニューアルする画面からwebpacker + vueに移行したのでその時のメモです。 細かい設定についてはたくさん記事が出ているので、今回はこんな設計方針にしたよというところにフォーカスしてます。

はじめに

webpackについては基本的な設定などは理解してるものの、フロント専任の人がいないのでwebpackerに乗っかることにしました。

無理なく、できるだけシンプルで簡単にやろうという方針です。 フロント専任の人が現れたら、より良くしてもらおうと考えています。

下記を前提としてます。

  • webpackerは、当時最新の3.x
  • SPAではなく、ページごとに画面遷移を伴う一般的なRailsアプリがベース
  • 単一ファイルコンポーネントは利用せず、Railsのviewにvueを組み込む方式

全体構成

root
├── app/frontend
│   ├── images          # 画像
│   ├── javascripts     # js実装はココ
│   ├── packs           # webpackのentryファイル(ページ毎に1entry想定)
│   └── stylesheets     # css
│
├── config
│   ├── webpack  # webpack.configはココ
│   │   ├── loaders  # 追加したloader設定
│   │   │   └── **    
│   │   ├── configuration.js    # 設定ファイル
│   │   ├── custom.js           # 追加config
│   │   ├── development.js      # dev環境向けconfig
│   │   ├── staging.js          # stg環境向けconfig
│   │   ├── production.js       # production環境向けconfig
│   │   └── test.js             # test環境向けconfig
│   │
│   └── webpacker.yml  # webpackerの設定
│
├── node_modules  # yarn installした依存ライブラリはここに出力される
│   └── ...
│
├── public
│   └── assets/packs  # コンパイルしたbundleファイルが出力される
│       └── ...
│
├── .babelrc
├── .eslintrc.js
├── .postcss.config.js
├── package.json
└── yarn.lock

※ webpackerにより追加されたもののみ記載

webpack管理下のファイルは、app/frontendに配置します。

  • entryファイルについて
    • 必要なjs/cssをimportするだけのファイル
    • ページごとにentry fileを作成する(各ページに必要なjs/cssだけを読み込むようにする)
    • app/frontend/packs以下にcontroller同様のパッケージ構成で配置する
  • jsについて
    • app/frontend/javascripts以下にcontroller同様のパッケージ構成で配置する
  • cssについて
    • app/frontend/stylesheets以下にcontroller同様のパッケージ構成で配置する
  • imageについて
    • app/frontend/images以下に配置する

具体例

下記のような画面を持つRailsアプリをもとに具体的に見ていきます。

  • ホーム画面を持つ
  • ユーザー画面の詳細、編集画面を持つ
  • PCとモバイルでレイアウトは別々で、variantでviewを切り分けている
app/controllers
├── users_controller.rb  # show, editアクション
└── home_controller.rb   # indexアクション

app/views
├── users
│   ├── edit.html.slim
│   ├── edit.html+mobile.slim
│   ├── show.html.erb
│   └── show.html+mobile.slim
├── home
│   ├── index.html.slim
│   └── index.html+mobile.slim
└── layouts
    ├── application.html.slim
    └── application.html+mobile.slim

application全体のファイルを準備する

  • app/frontend/packs直下にapplication.jsを作成する
  • app/frontend/stylesheets直下にapplication.scssを作成する

1画面ごとにvueのインスタンスを1つ生成するので、レイアウト全体のスタイルと画像を出力するためのファイルになります。

app/frontend/packs/application.js

/**
 * Entry file for application.
 */
import '@css/application.scss';

// 必要画像を読み込む
function importAll(context) {
  context.keys().forEach(context);
}
importAll(require.context('@img/, true, /\.svg$/));

app/frontend/stylesheets/application.scss

// 全体のcssは必要なものをimportするだけ
// イメージはこのような感じ
@charset "utf-8";

// globals(変数やらmixnやら)
@import "globals/variables";

// third party
@import '~@fortawesome/fontawesome-free/scss/fontawesome';
@import '~@fortawesome/fontawesome-free/scss/solid';

// base (リセットcssなど)
@import "reset.css";

// structure(構造系のcss)
@import "header";

// modules
@import "modules/btn-hoge";
@import "modules/btn-foo";

出力したcssをviewで読み込みます。

app/views/layouts/application.html.slim

doctype html
  html lang='ja'
    head
      / stylesheets
      = stylesheet_pack_tag 'application', media: 'all'

      / ページ固有のcssなどをロード
      - if content_for?(:head_tags)
        == yield(:head_tags)
    body
     .main
        / 各画面のコンテンツ
        == yield

     / 各画面のjsをロード
     - if content_for?(:js)
      == yield(:js)

各画面のファイルを準備する

例えば、ユーザー編集画面(editアクション)のファイルを準備します。

  • app/frontend/packs/users/直下にedit.jsを作成する
  • app/frontend/stylesheets/users直下にedit.scssを作成する

各画面のpacksファイルでは、必要なjsとcssを読み込みます。 これにより、jsとcssがpublic以下に出力されます。(出力先は設定ファイルで指定)

app/frontend/packs/users/edit.js

/**
 * Entry file for users/edit
 */
import '@css/users/edit.scss';
import '@js/users/edit';

app/frontend/javascripts/users/edit.js

/**
 * users/edit
 */
export default new Vue({
  el: '.main',
  data:{
  },
  created() {
  },
  methods: {
  },
  computed: {
  }
});

app/frontend/stylesheets/users/edit.scss

@import "globals/variables";

.users-edit {
  ...
}

出力したjs/cssをviewで読み込みます。

app/views/layouts/users/edit.html.slim

/ application.htmlで差し込めるようにしておく

/ head
- content_for :head_tags do
  = stylesheet_pack_tag 'users/edit'

/ js
- content_for :js do
  = javascript_pack_tag 'users/edit'

このような形で、すべての画面分のファイルを準備します。

モバイル用の各画面のファイルを準備する

今回はレスポンシブデザインではなく、PC/モバイルをvariantによって切り分けている前提なので、モバイル用のファイルも準備します。

frontend側は、各パッケージ以下にpacks/mobileのようにmobileパッケージを切って同じようにファイルを用意し出力したjs/cssを読み込みます。

完成形

↓↓↓のような形になります。

app/frontend
├── javascripts
│   └── users
│       ├── edit.js
│       └── show.js
├── packs
│   ├── mobile
│   │   ├── users
│   │   │   ├── edit.js
│   │   │   └── show.js
│   │   └── application.js
│   ├── users
│   │   ├── edit.js
│   │   └── show.js
│   └── application.js
└── stylesheets
    ├── mobile
    │   ├── users
    │   │   ├── edit.scss
    │   │   └── show.scss
    │   └── application.scss
    ├── users
    │   ├── edit.scss
    │   └── show.scss
    └── application.scss

よく見るとjsの実装ファイルは、PC/モバイルと別れていません。画面の作りは違うけど機能は同じ前提で作りました。 万が一異なる場合は、実装ファイルできょうつの処理は親クラスを用意して別の処理は同様にmobileパッケージを切ってそれぞれに実装して、pakcs以下のEntryファイル内で読み込むjsを切り替えるなど一工夫必要になってしまいます。

おわりに

設計メモのため、webpacker周辺知識をあるていど持っていることが前提となってしまいました。(しかも力尽きて全体的に雑に...) 冒頭にも書きましたが、そのあたりはたくさんの方が記事を書いてくれているので調べてみると良いと思います。

具体的にどういう構成で作ったら良いかちょっと悩んでしまって、調べても割にSPA系の記事が多かったような気がするので一つの例として参考になったら嬉しいです。

私はバックエンドのエンジニアなので、css周りでも結構悩んで調べていろいろと発見がありました。 書くのが大変なので、良かったリンクを参考に貼り付けておきます。

qiita.com

気づけばwebpacker4.xがリリースされていたのでアップデートしないと!

近況

11月からポケマルでご一緒させて頂いてます。 開発は思ったよりも大変で、中々寝れない自分が毎日15分もあれば寝れてしまうくらいです。 ポケマルは生きていく上でとても大切な考えに向き合っているように思います。 なので、今までの経験を上手く活かして貢献できると嬉しいなと考えています。

実は、前月の1ヶ月間は人生初の無職を経験していました。 今思えばそんなこともあったなぁと、何事もそんなものなのかもしれません。 案ずるより産むが易しと何時ぞや、祖母に言われたことを思い出しました。

転職活動も並行して行っていたので(働く気力はあった)、気持ち忙しない感じもありましたがあっという間に終了。 前職の方々には会社を紹介して頂いたり、頻繁にメッセなどで話しかけて何時も通りに接してもらったりと、たくさんお世話になりました。感謝!

そして、前職の会社はM&Aにより大手企業の子会社となりました。 結婚生活とは難しいものなのだなとひしひしと感じました。 また、大きく飛躍するためにはメンバー同士の信頼関係と協力が必要なことを学びました。

結局残る人、次の場を探す人、それぞれの思う道に別れていきました。 その後の話を聞くと、これが人生なんだと感じます。

人生思うがまま。

書こうと思っていたら、2019年になっていました。 もうすぐ母の命日です。でも、それすら過去のことになっている気がします。 と言ったら、怒られちゃうでしょうか。 行き着くところは同じなので、前に進むことが大事です。

2019年も?一歩ずつ、自分のペースで進んでいきます。

f:id:uu_ka:20181029161604j:plain

vue init webpackで作成したプロジェクトをwebpack4に移行する

今更ながらvue init webpackで作成したプロジェクトをwebpack4系に移行したのでメモ。

Vue CLI3系を利用すればwebpack4系のプロジェクトが生成されるけど、webpack.config自体は@vue/cli-serviceに内包されているため、全体像が掴みづらいので勉強の意味も込めて現状のものをアップデートした。
vue cli-serviceのコードを見ると、webpack-chainなどを利用して設定が生成され各種コマンドを実行している様子が伺える。

プロジェクト構成は大雑把に↓な感じ。(特に生成時から変えていない)

app
├── build
│   ├── utils.js
│   ├── webpack.base.conf.js
│   ├── webpack.dev.conf.js
│   ├── webpack.prod.conf.js
│   └── ...
├── config
│   ├── dev.env.js
│   ├── index.js
│   └── prod.env.js
├── dist
├── node_modules
├── src
├── static
├── .eslintrc.js
├── package.json
├── postcss.config.js
└── ...

依存関係アップデート

webpack-cliが必要になったのでこれも追加

// webpack関連
npm isntall --save-dev webpack@latest
npm isntall --save-dev webpack-bundle-analyzer@latest
npm isntall --save-dev webpack-cli@latest
npm isntall --save-dev webpack-merge@latest
npm isntall --save-dev html-webpack-plugin@latest
npm isntall --save-dev mini-css-extract-plugin@latest
npm uninstall extract-text-webpack-plugin

// vue関連
npm isntall vue@latest
npm isntall --save-dev vue-loader@latest
npm isntall --save-dev vue-style-loader@latest
npm isntall --save-dev vue-template-compiler@latest
   "dependencies": {
-    "vue": "^2.5.2"
+    "vue": "^2.5.17"
   },
"devDependencies": {
...
-    "extract-text-webpack-plugin": "^3.0.0",
     "file-loader": "^1.1.4",
     "friendly-errors-webpack-plugin": "^1.7.0",
-    "html-webpack-plugin": "^2.30.1",
+    "html-webpack-plugin": "^3.2.0",
+    "mini-css-extract-plugin": "^0.4.3",
-    "vue-loader": "^13.7.2",
-    "vue-style-loader": "^3.0.1",
-    "vue-template-compiler": "^2.5.2",
+    "vue-loader": "^15.4.2",
+    "vue-style-loader": "^4.1.2",
+    "vue-template-compiler": "^2.5.17",
-    "webpack": "^3.12.0",
-    "webpack-bundle-analyzer": "^2.13.1",
-    "webpack-dev-server": "^2.9.1",
-    "webpack-merge": "^4.1.3"
+    "webpack": "^4.20.2",
+    "webpack-bundle-analyzer": "^3.0.2",
+    "webpack-cli": "^3.1.1",
+    "webpack-dev-server": "^3.1.9",
+    "webpack-merge": "^4.1.4"

modeオプション追加

dev, prodのwebpack.confにmodeを追加し、mode設定により自動的にenableになるPluginを削除する。

  • build/webpack.dev.conf.js
    • mode: 'development' を追加
    • webpack.NamedModulesPluginを削除
    • webpack.NoEmitOnErrorsPluginを削除
  • build/webpack.prod.conf.js
    • mode: 'production' を追加
    • webpack.optimize.ModuleConcatenationPluginを削除
  • NODE_ENVの設定削除(modeで自動設定される)
    • config/dev.env.js
    • config/prod.env.js

https://webpack.js.org/concepts/mode/

mini-css-extract-plugin追加

build時にスタイルを.cssファイルに抽出するのにextract-text-webpack-pluginを利用しているが、

⚠️ Since webpack v4 the extract-text-webpack-plugin should not be used for css. Use mini-css-extract-plugin instead.

なので、mini-css-extract-pluginを代わりに使用する。

  • mini-css-extract-pluginを追加
npm isntall --save-dev mini-css-extract-plugin@latest
  • build/webpack.prod.conf.js
    • ExtractTextPluginをMiniCssExtractPluginに変更
-    new ExtractTextPlugin({
+    new MiniCssExtractPlugin({
       filename: utils.assetsPath('css/[name].[contenthash].css'),
-      // Setting the following option to `false` will not extract CSS from codesplit chunks.
-      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
-      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 
-      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
-      allChunks: true,
-    }),
  • build/utils.js
    • loader生成している、generateLoaders関数内のExtractTextPluginをMiniCssExtractPluginに変更(vue-style-loaderを最初にもってくること)
     // Extract CSS when that option is specified
     // (which is the case during production build)
     if (options.extract) {
-      return ExtractTextPlugin.extract({
-        use: loaders,
-        fallback: 'vue-style-loader'
-      })
+      return ['vue-style-loader', MiniCssExtractPlugin.loader].concat(loaders)
     } else {
       return ['vue-style-loader'].concat(loaders)
     }

github.com

optimization.minimizerの設定

production modeの場合はデフォルトでminifyされるが、UglifyJsPluginのオプションを利用していしてるためoptimization.minimizerに移す。同時にOptimizeCSSPluginも。

  • build/webpack.prod.conf.js
+  optimization: {
+    minimizer: [
+      new UglifyJsPlugin({
+        uglifyOptions: {
+          compress: {
+            warnings: false
+          }
+        },
+        sourceMap: config.build.productionSourceMap,
+        parallel: true
+      }),
+      new OptimizeCSSPlugin({
+        cssProcessorOptions: config.build.productionSourceMap
+          ? { safe: true, map: { inline: false } }
+          : { safe: true }
+      })
+    ],
....
   plugins: [
-    new UglifyJsPlugin({
-      uglifyOptions: {
-        compress: {
-          warnings: false
-        }
-      },
-      sourceMap: config.build.productionSourceMap,
-      parallel: true
-    }),
-    // Compress extracted CSS. We are using this plugin so that possible
-    // duplicated CSS from different components can be deduped.
-    new OptimizeCSSPlugin({
-      cssProcessorOptions: config.build.productionSourceMap
-        ? { safe: true, map: { inline: false } }
-        : { safe: true }
     }),

https://webpack.js.org/configuration/optimization/#optimization-minimizer

optimization. splitChunksの設定

v4からCommonsChunkPluginは削除され、SplitChunksPluginになったので移行する。 どのように分割するかはプロジェクトによって異なるので、各自で設定する。

  • build/webpack.prod.conf.js
+  optimization: {
...
+    splitChunks: {
+      cacheGroups: {
+        vendors: {
+          test: /[\\/]node_modules[\\/]/,
+          name: 'vendors',
+          chunks: 'all'
+        }
+      }
+    }
+  },

...
   plugins: [
-    // split vendor js into its own file
-    new webpack.optimize.CommonsChunkPlugin({
-      name: 'vendor',
-      minChunks (module) {
-        // any required modules inside node_modules are extracted to vendor
-        return (
-          module.resource &&
-          /\.js$/.test(module.resource) &&
-          module.resource.indexOf(
-            path.join(__dirname, '../node_modules')
-          ) === 0
-        )
-      }
-    }),
-    // This instance extracts shared chunks from code splitted chunks and bundles them
-    // in a separate chunk, similar to the vendor chunk
-    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
-    new webpack.optimize.CommonsChunkPlugin({
-      name: 'app',
-      async: 'vendor-async',
-      children: true,
-      minChunks: 3
-    }),

意図通りに分割できているかは、webpack-bundle-analyzerで確認する。

npm run build --report

https://webpack.js.org/plugins/split-chunks-plugin/ medium.com

optimization. runtimeChunkの設定

webpackは、モジュール管理のためruntimeとmanifestのコードを生成するのでそれらをmanifest.jsとして出力させる。

  • build/webpack.prod.conf.js
+  optimization: {
...
+    runtimeChunk: {
+      name: 'manifest'
+    },
...
   plugins: [
-    // extract webpack runtime and module manifest to its own file in order to
-    // prevent vendor hash from being updated whenever app bundle is updated
-    new webpack.optimize.CommonsChunkPlugin({
-      name: 'manifest',
-      minChunks: Infinity
-    }),

こちらも、意図通りに分割できているかは、webpack-bundle-analyzerで確認する。

npm run build --report

https://webpack.js.org/concepts/manifest/ survivejs.com

VueLoaderPlugin追加

  • build/webpack.base.conf.js
 const config = require('../config')
+const VueLoaderPlugin = require('vue-loader/lib/plugin')
 const vueLoaderConfig = require('./vue-loader.conf')
...
+  plugins: [
+    new VueLoaderPlugin()
+  ],
   module: {

postcss.config.js追加

対象ブラウザの設定は各自行う。

  • postcss.config.js
+module.exports = {
+  plugins: {
+    autoprefixer: {},
+  },
+};

雑ですがこんなイメージ。 あ、babelも上げないと。

生ウニ丼を食べる

ウニが大好きなワタクシはたまに贅沢をします。
それがこちら...

f:id:uu_ka:20180830205017j:plain

ジャーン!
岩手県産生ウニ 牛乳瓶入り!!
4年ぶり2回目の出場。いや、登場。

今回もヤマコー片桐鮮魚店さんで購入しました。
(しかもサービスで割引きやワカメまでつけてくれました。感謝。)

気合が入ってしまい、本わさびを用意しすりおろし、手抜き味噌汁まで準備し、しれっと一緒に注文してしまったいくらと併せて丼ぶりに盛り付けました。

f:id:uu_ka:20180901160357j:plain

美味しい....!!
でも食べ過ぎは要注意、気持ち悪くなりますからね。

偶然にも前日もウニをいただきました。
こちらも美味しかったですが、瓶入りウニのほうが別格。

f:id:uu_ka:20180831202907j:plain

こちらは....
9年前にも似たような行動をとっていたようで、初めて握ったまぐろのお寿司🍣
見た目が大事だということが、とてもよく分かる例ですね。

f:id:uu_ka:20090129184422j:plain

みなさんもお試しあれ。

www.yamakou-katagiri.jp