PhpStormと僕

日々周りを巻き込むことをモットーに。気まぐれでJetBrains製のIDEネタとか書いてます。

グルーディア 燻製器を買ったので長谷園のいぶし銀と比較した

2年ほど前から燻製にハマっていて、長谷園のいぶし銀っていう燻製用の土鍋を愛用して使っている。

そこそこお値段がするだけあって結構本格的なものまで作れて、これで作る燻製たまごやベーコンはマジで美味い。

けど、いぶ し銀にも欠点はいくつかあって

  • 調理時間に最低25分〜掛かる
  • 食材の下ごしらえとかも必要
  • 煙に関してはほぼ無煙だが、そこそこ燻製臭は部屋に残る

ということもあり、土日とかに「やるぞ!」と思って気合を入れた日じゃないと作りづらい。


そんなわけもあり最近は家で作る頻度が落ちていたが、 グルーディア 燻製器 というものを知ったので買ってみた。

f:id:rinrin900:20180324130942p:plain

こんなチャッカマンの進化版みたいなものでどれくらい燻香が付けられるのか半信半疑ではあるものの、色々作って比較してみた。

ちなみにはスモークチップはヒッコリーを使用した。

いぶし銀

f:id:rinrin900:20180324131152p:plain おなじみいぶし銀。まだ火をつける前だけどこの時点で既に美味そう。

グルーディア 燻製器

f:id:rinrin900:20180324131216p:plain いぶし銀の網がボールにちょうどサイズがあったので、二重底にして食材を並べる。

f:id:rinrin900:20180324131237p:plain ボールにラップを念入りに被せて、付属のチューブを差し込んで煙を入れる。

完成

f:id:rinrin900:20180324131828p:plain 左がいぶし銀(燻製時間20分)、右がグルーディア燻製器(燻製時間10分)

できあがりはこんな感じ(鳥のササミとししゃもは別途焼いた状態)。

グルーディア燻製器で作ったほうは「若干色味が付いたかな?」程度。でも、香りはある程度しっかり付いている模様。

実食

いぶし銀は熱燻、グルーディア燻製器は冷燻なので、たこやサーモンなど食感が変わっているものもあるが、個人評価ではこんな感じ。

食材 いぶし銀 グルーディア 燻製器 一言コメント
たこ グルーディアは刺し身の状態で燻製されている
食感でとても新鮮
ベビーチーズ
ししゃも ほとんど燻香が付かなかった
ササミ ほとんど燻香が付かなかった
燻製たまご ほとんど燻香が付かなかった。
固茹でにして半分切ってからやるといいかも。
サーモン 酸味が出てしまった。
ポップコーン 新しい感覚。
ポップコーンはマッシュルーム型より
チューリップ型が味が染みそう

全体的にはいぶし銀に劣るが、熱を加えずに燻製ができる(手軽に冷燻ができる)という点は評価できそう。

熱燻の燻製器と比較するというよりは、食材や用途が棲み分けできるので共存できそうな印象を受けた。 たこやポップコーンしかり、冷燻で映える食材を開拓していくのはとてもおもしろそう。

個人的な課題としては、煙が結構漏れてしまうので蓋付きの専用の容器が欲しいなぁと。

なにかいい食材見つけたら追記します。

最近の自分の働き方と思ったこと

家でリモートで働いていると移動時間とかも気にせず延々とコードを書き続けられるわけで、とはいえ仕事のコードばっかり書くわけにもいかないから趣味のコードも書く。

ただ仕事の進捗を遅延させるわけにはいかないので、必然的に仕事を終わらせてから1日の中の余った時間で趣味の方を書くことになる。

この感覚がなにかに似てるなあと思ったんだけど、昔ネトゲをやってた頃に似ているのかなあと。

10代の頃は毎日12-16時間くらいネトゲ(MMO)をやってたけど、そのゲームでは効率が良いと1時間80-100万経験値くらい稼げるので「毎日1,000万/経験値を稼ぐ」みたいなノルマを自分に課していた。 で、ノルマを達成したらレアアイテム狙いの狩場に移動してもいいしPvPやっても装備強化してもいい、みたいな。

特にオチもないんですが、「わかるわー」って人と語り合いたい気持ち。

Cloud Functionsトリガー実行時にFirestoreに認証情報を渡す

firestore.ruleで request.auth を使って「レコードは自身の保持しているデータしか操作できない」ような制約を掛けるケースはよくあると思います。

match /profiles/{uid} {
  allow read, create, update if resource.data.userUid == request.auth.uid;
}

普通に操作する分には問題ないんですが、例えばCloud Functionsの認証トリガーを利用して「ユーザが更新されたら、ユーザに紐づくプロフィール情報(Document)を更新したい」ようなケース。

exports.updateExternalProfile = functions.auth.user().onUpdate(event => {
    const user = event.data;
    const email = user.email; 
    const displayName = user.displayName; 

    db.collection('profiles').doc(user.email).update({ displayName })
});

そのまま更新すれば良いように思えますが、Cloud Functions内でのこの外部トリガーからの実行処理中はユーザログイン情報がないため、つまり request.auth がないので更新しようとすると権限エラーになります。 Cloud Functions + Firestoreを使っていると苦しむこの問題。どうするか。

github.com

Issuesも上がっていますが、2018年03月時点ではまだfirebaseAdminでは解決していない模様。ちなみにRealtime Databaseは同様のことを満たすための databaseAuthVariableOverride っていうオプションがあるみたいですね。


ではどうすれば良いか。 認証情報を冗長に持つ必要があるものの、firebase-admin-node に加えて firebase-js-sdk を使って、signInWithCustomTokenメソッドを使ってログイン状態にさせると一応動くようになります。

const admin = require('firebase-admin');
const serviceAccount = require('/path/to/serviceAccount.json');
admin.initializeApp({
  credential : admin.credential.cert(serviceAccount),
});

// 追加処理
const firebase = require('firebase');
require('firebase/auth');
require('firebase/firestore');
firebase.initializeApp({
  // webAppの設定
});

const signIn = async (userUid) => {
  const customToken = await admin.auth().createCustomToken(userUid)
  return firebase.auth().signInWithCustomToken(customToken)
}

exports.updateExternalProfile = functions.auth.user().onUpdate(async event => {
  const user = event.data;
  const email = user.email;
  const displayName = user.displayName;

  // 追加処理
  await signIn(email)

  db.collection('profiles').doc(user.email).update({ displayName })
});

こんな感じ。 認証周りの冗長感が否めないので、早くfirebaseAdmin側で対応してくれないかなー。

FirestoreのSubCollectionに対してQueryが使えない問題にどう立ち向かうか

Firestore、便利ですよねぇ。 ただ、2018/03執筆時点ではまだβなので荒削りだったり要件満たしにくい部分でつらいなぁっていう部分はいくつかあります。

その1つが表題の件の「SubCollectionにQueryが使えない」問題。 どういうことかというと、例えば「CDごとに曲名とその曲番号(連番)を保持する」構造を例に考えてみます。

- albums [collection]
    - title
    - musics [collection]
        - title
        - songNumber

Firestoreでどう表現するかというと、disksっていうCollectionを作って、その中にSubCollectionとしてmusicsを持つ形が一番シンプルに見えます。

f:id:rinrin900:20180309180400p:plain

こんなイメージ。ただ、この構造の場合は表題の制限があり、musics内のデータを直に参照したいケース、 具体的には「ユーザ属性は曲のタイトルを持っているが、その曲がどのアルバムに属しているか分からない(紐付けることができない)」時に、sounds内のデータを特定することができません。 この件についてStackOverFlowGoogleエンジニアが「Collection group queryという機能で提供する予定だが、提供はすぐではない」という回答をしています。

ObjectやArrayとして下位データを持つ方法もなくはないですが、そもそものSubCollectionのメリットを活かせないのではここでは考慮から外しつつ、 Collectionであることメリットを活かし現時点で解決できるパターンをいくつか見ていきます。

親CollectionにSubCollectionのキー一覧を持つ方法

f:id:rinrin900:20180309181719p:plain

親のCollection側に、soundsのキー一覧を持つのが一案としてあります。

soundKeys: [
    "1. 主よ,人の望みの喜びよ(In 4 Mix)"
    "2. 眠れる森~Sleeping Forest",
    "3. Mouring The Passing Time"
]

キー一覧を持つ際、上記のようにFirestore内のデータとして配列で持ちたくなります。 が、Queryでwhere in句のように配列を対象に検索することができないので、以下のようなObject形式で持つ必要があります。

soundKeys: {
    "1. 主よ,人の望みの喜びよ(In 4 Mix)": true,
    "2. 眠れる森~Sleeping Forest": true,
    "3. Mouring The Passing Time": true
}

sound側のkeyを持っている状態からこのalbums -> soundsのdocを取得する場合には

const userPlayingTitle = '2. 眠れる森~Sleeping Forest'
const querySnapshot = await db.collection('albums')
    .where(`albums.${userPlayingTitle}`, '==', true).get()
...

というような形でquerySnapshotを持ってくることができるようになりました。

Puppeteerで page.$(selector) で絞り込んだ要素から更に子要素指定を行う

最近がっつりとPuppeteerを触っている。 Puppeteer(書きづらい)とは、Headless ChromeをNode.jsから扱うためのライブラリ。

開発が速いのでググって出てくる情報は陳腐化していることがままあるので基本的には公式ドキュメントを読んでもらうのが大前提として、v1.1.1を触っている時点で得られた知見や小ネタちょこちょこと共有したい。

page.$(selector) で絞り込んだ要素から更にselector指定を行う

<div class="row">
  <div class="col">
    <div class="itemDetail">りんご</div>
  </div>
  <p class="price">120円</p>
</div>
<div class="row">
  <div class="col">
    <div class="itemDetail">みかん/袋</div>
  </div>
  <p class="price">520円</p>
</div>

もともと page.$ page.$$ で絞り込んだ子要素 ElementHandle は .$ .$$ メソッドを持っていなかったので、page.$$evalで指定してループ内で取り出す・・・みたいな必要があったが、 v0.13.0 からは以下のように、取得したSelectorのオブジェクト(ElementHandle)から更に絞り込むような書き方ができるので前よりだいぶ直感的に書けるようになった。

const rows = await page.$$('.row')
for (const row of rows) {
  const itemDiv = await row.$('div.col > div.itemDetail')
  console.log(await (await itemDiv.getProperty('textContent')).jsonValue())
  const priceDiv = await row.$('p.price')
  console.log(await (await priceDiv.getProperty('textContent')).jsonValue())
}

IntelliJ IDEAのSplash Screenを集めてみた

IntelliJ IDEAの15 EAP使い始めてスプラッシュスクリーンかっこいいなーと思って、 ふと思いついたので過去のバージョンのも集めてみた。

下に行くほど新しいバージョンのやつ。バージョン9以降はUltimate EditionとCommunity Editionあるけど、とりあえずごちゃまぜで。

色々探しまわったけどバージョン4以前のはどうしても見当たらず・・・。

バージョン12辺りから急に洗練され始めた感。 最近のバージョンのスプラッシュスクリーンはEAPと製品版でも違ったりと凝ってるのでおもしろい。

f:id:rinrin900:20051119033542j:plain f:id:rinrin900:20150814121631g:plain f:id:rinrin900:20150814121632p:plain f:id:rinrin900:20150814121635p:plain f:id:rinrin900:20150814121637j:plain f:id:rinrin900:20150814121638j:plain f:id:rinrin900:20150814121639j:plain f:id:rinrin900:20150814121640j:plain f:id:rinrin900:20150814121641p:plain f:id:rinrin900:20150814121642p:plain f:id:rinrin900:20150814121644p:plain f:id:rinrin900:20150814121645p:plain

Scalaコップ本メモ

そんなわけで最近Scalaを書き始めているわけですが、1ヶ月ほど前に今更ながらScalaスケーラブルプログラミング(通称コップ本)読んだので、その過程のメモ書きをまとめてみます。

ちなみに全部じゃなくて17章まで読みました。

実際書き始めてみると全然理解しきれてないところも多いのでもう一周読みなおそう・・・

Scalaスケーラブルプログラミング第2版

Scalaスケーラブルプログラミング第2版

コップ本メモ

  • Scalaは、配列から式に至るまで、あらゆるものがメソッドを持つオブジェクトだとすることによって、概念を単純化している(P62)

  • メソッドは副作用を持っていてはならない」というのは、関数型プログラミングの考え方の大きな特徴である。メソッドの作用は、計算して値を返すことだけでなければならない。このアプローチをとると、メソッドが入り組んだものにならないため、信頼性が上がり、再利用しやすくなる。(P63)

  • (関数型スタイルに慣れる)第一歩は、コードに現れる2つのスタイルの違いを理解することである。簡単な見分け方が1つある。コードにvarが含まれていたら、それはおそらく命令形のスタイルで書かれている。コードにvarが含まれていなければ、つまりvalだけが使われていれば、それはたぶん関数型のスタイルで書かれている。だから、関数型のスタイルに近づくための1角方法は、varを使わずにプログラムを書く努力をすることだ。(P72)

  • 関数が副作用を持つかどうかを簡単に見分けるには、結果がたがUnitかどうかを見ればよい。Unitとなっていれば副作用がある。Unitという結果がたは関数が意味のある値を返さないことを意味する。(P73)

  • Scalaは、toStringやHashSetといったキャメルケースの識別子を使うというJavaの習慣に従っている。アンダースコアは識別子の中で使ってよいことになっているが、Javaとの一貫性を保つため、またScalaコードではアンダースコアが識別子以外の目的でよく使われているため、Scalaプログラムの識別子にはあまりアンダースコアを使わない。(P121)

  • Scalaでは定数という単語はvalを意味するわけではない。valは初期化された後は一定だが、それでも変数と呼ばれる。たとえば、メソッドのパラメーターはvalだが、メソッドが呼び出されるたびに、これらのvalは異なる値を保持して良い。(P121)

  • valを使うチャンスを探そう。valはコードを読みやすく、リファクタリングしやすいものにしてくれる。(P129)

  • 一般に、varを避けるのと同じように、whileループも避けることをお勧めする。実際、whileループとvarはセットになっていることが多い。コードの中のwhileループには、疑いの目を向けてみるべきだ。whileやdo-whileを使う正当な理由が説明できないときは、これらを使わない方法を探してみよう (P131)

  • files <- filesHere というような構文はジェネレーター(generator)と呼ばれ、filesHereからの要素に対する反復処理に使う (P132)

    • はじめて呼び方出てきた
  • breakやcontinueを使わずにプログラムを書く方法はいくらもある。そして、関数リテラルを利用すれば、元のコードよりも短いコードが書けることが多い。もっとも簡単な解決方法は、すべてのcontinueをifに、すべてのbreakをBoolean変数に置き換えることだ。 (P141)

  • Scalaのスコープ規則はJavaのものとほとんど同じだということが分かるだろう。JavaScalaの違いは、Scalaでは入れ子になったスコープで同じ名前の変数を定義できることである。 (P143)

  • ScalaJavaの違いとして注意すべきなのは、Javaでは、外側のスコープ変数と同じ名前を持つ変数を内側のスコープでは作れないことである。Scalaプログラムでは、外側の変数は内側のスコープでは見えなくなるので、内側の変数は同じ名前の外側の変数をシャドウイング(shadow)するということができる。(中略)通常は、外側の変数をシャドウイングするよりも、新しい意味のある変数名を選んだほうがよい (P145)

  • 部分適用された関数とは、関数が必要とするすべての引数を渡していない関数呼び出し式である。必要な引数を一部または全く渡していないのである。部分適用関数式を作るにはメソッド名のうしろにアンダースコア書く。 (P157)

  • 最後の処理として自分を呼び出す再帰関数を末尾再帰(tail recursion)と呼ぶ。Scalaコンパイラーは、末尾再帰を検知したら、パラメーターを新しい値に更新した後、再帰呼び出しを関数の冒頭にジャンプするコードに書き換える。解が末尾再帰になっていれば、実行時に余分なオーバーヘッドがかかったりはしない (P167)

  • パラメーターなしメソッドは、Scalaでは頻繁に見られるものだ。それとは対照的に、 def height(): Int のように、空括弧付きで定義されているメソッドは、空括弧メソッドと呼ぶ。パラメーターがなく、ミュータブルな状態へのアクセスがオブジェクトのフィールドの読み出しだけなら、パラメーターなしメソッドを使うべきだとされている(特にミュータブルな状態を書き換えないことが大切)。この方法は、「属性をフィールドとメソッドのどちらで実装するかによってクライアントコードが影響を受けてはならない」という統一形式アクセスの原則に従っている。 (P184)

  • 原則として、Scalaの関数呼び出しでは、全ての空括弧を省略できる。しかし、呼び出されるメソッドがレシーバーオブジェクトのプロパティ以上のものを表す場合は、空括弧を書くほうが良いとされている。たとえば、メソッドがI/Oを実行したり、再代入できる変数(var)に書き込みを行ったり、ミュータブルなオブジェクトを直接または間接的に使って、レシーバーのフィールド以外のvarを読み出したりする場合には、空括弧を付けるようにすべきである。 (P185)

  • 合成と継承は、既存の他クラスから新しいクラスを定義するための2種類の方法である。主としてコードの再利用を追求する場合には、一般に継承よりも合成を選んだほうがよい。継承は脆弱な基底クラス問題を抱えており、スーパークラスを書き換えると、サブクラスが利用できなくなることがある。 (P196)

  • 配列の ++ 操作は、2つの配列を連結する(P197)

  • case修飾子が付けられたクラスをケースクラスと呼ぶ。この修飾子を使うと、Scalaコンパイラーはこのクラスの構文に適切な変更を加えるようになる。第1にコンパイラーはクラスと同じ名前のファクトリーメソッドを追加する。そのため、Varオブジェクトを作るのに、 new Var("x") と書く代わりに、少し簡潔に Var("x") と書くこともできる。第2にコンパイラーは、ケースクラスのパラメータリスト内の全てのパラメーターに暗黙のうちにvalプレフィックスをつける。そのため、パラメーターはフィールドとして管理される。第3に、コンパイラーがクラスにtoString, hashCode, equalsメソッドの「自然な」実装を追加してくれる。最後に、コンパイラーは変更を加えたcopyを作成するために、クラスにcopyメソッドを追加する。このメソッドは、1つか2つの属性が異なるだけでほぼ同じクラスのインスタンスを新たに作成する場合に便利だ。

  • headやtailはともに一定の時間で実行されるが、initとlastはリスト全体をたどらないと結果値を計算できないので、リストの長さに比例する計算時間を要する。データ構造を設計するときには、リストの末尾ではなく、先頭にアクセスして操作できるようにするとよい。(P297)

写経しながらメモってたやつ

  • Scala Worksheetが便利。IntelliJ IDEAから立ち上げられる。RubyのPry的な感じでガンガン使ってる。

  • 再帰構造の基本形

def approximage(guess: Double): Double =
  if (isGoodEnough(guess)) guess
  else approximage(improve(guess))
  • カリー化を使うことによってクライアントプログラマーが中括弧の間に関数リテラルを書き込めるようにできる
withPrintWriter(
  new File("data.txt"),
  writer => writer.println(new java.util.Date)
)

val file = new File("date.txt")
withPrintWriter(file) { writer => writer.println(new java.util.Date)