無銘雑記

これ書けばいいの?

Ergodox EZ キーボードのカスタム

ちょっとエルゴノミクスキーボード(キーボード分割したような)を作ってみたいという気持ちがありつつ、 行ってみたいキーボード屋さんにも行けない状況なので、 既に出来上がっているキーボードで先に慣らしておこうという気分で購入しました。

f:id:igara1119:20200509213205j:plain

ergodox-ez.com

注文画面でいろいろ、光らせるようにするかとかリストレストも付属するとか選択肢があり、注文を終えると3週間ぐらいでお家に届きました。 (時期が時期だったので注文からの発注大変そうと思いました。)

注文内容としては以下のもので

f:id:igara1119:20200509211850p:plain

  • ErgoDox EZ Glow Standalone × 1
    • Glowというのが光るやつ ただし親指の箇所など端の箇所は光らない
    • Backlit / Black / Cherry MX Brown (RGB)
      • BlackのほかWhiteもある
      • 茶軸以外も選択可能
  • The Wing: Custom ErgoDox EZ Wrist Rest × 1
    • Black
      • 黒しかない
  • Tilt/Tent Kit × 1
    • Black
      • これも黒しかない

全部込みで注文しました。

注文したもの以外でキーキャップを外す道具もおまけでついてきたのですがこれ結構使いやすかったです。

f:id:igara1119:20200509223008j:plain

あと、キーのイメージ変えたいのと自作でキーキャップ 欲しかったので↓のを注文したのですが今回あまり使いませんでした。 一部数字など使用しています。

f:id:igara1119:20200509213536j:plain

www2.razer.com

さすがゲーミングの製品を扱っているだけあって光るキーボード用のキーキャップも販売していました。

shop.tvc.jp

shop.tvc.jp

こちらのキーキャップ はPOSレジ用のですが万が一、やっぱりちゃんと文字が印刷されたキーキャップ欲しいとなったとき、自由に紙に印刷したものを使用できるような保険のものです。

今回はわりと不自由しなかったので使いませんでした。

で、あとはキーの配置も変えたりしました。

画像にするとこんな感じです。

f:id:igara1119:20200509235036j:plain

もしこのキーマップを使用したい場合は下記を使っていただければと思います。

github.com

configure.ergodox-ez.com

このキーボードを使用してまだ1週間かかってないぐらいですが使ってみての感想として

格子配列っていうんですかね? 今まで使用していたキーボードと比べてキーの位置が水平垂直にあるのでちょっとまだ慣れてないところがあってまだ満足に使用できていないなぁという感じです。 よく使うキーボードってQWERTY配列というらしいのですが初めてキーボードの歴史を調べるきっかけを作ってくれた気がします。

あとは親指で選択するキーが多いのでどれを設置しようかという迷いがありました。 よく使いそうなのを親指にやろうとしても今まで使っていた箇所にやたら指を動かしてしまうみたいなのが癖で多かったりするのでしばらくは慣れるように頑張るしかないなぁって思います。 特にEnterとDelete

マウス操作もできるようにしましたが今まで使用していたトラックパッドの方が操作しやすいです。 ただキーボードから手を離さないでマウスとしての操作ができるので細かい操作を要求されなければキーボードからの操作でもいいかなと思いました。

しばらくこのキーボードを使い倒して安心して外出できるようになったらキーボード屋に行きたいと思うのでキーボード作りたい人いたらDMください。

yushakobo.jp

2020/2時点で最近やっていること思うことをいろいろ書いてみる

1つの人生観としてみていただければと思います
最近会わなくなった人にも対して生存確認報告的なものとして記載します

最近やっていること

  • お仕事関連

    • 本業
      • 作業
        • マスターデータ作成
          • サイトの内容を構成するYAMLとDBのテーブルを構成するCSVとか編集してページ作成する作業
        • マスターデータ反映するためのWeb画面の雛形の改修
          • もともと作成済だったものの未対応だったものやバグの修正とか
        • 効率化
          • マスターデータ反映など自社の開発側でやっていたが外部の会社に依頼して大量のページを複製できるようにする対応してる
          • Google Spreadsheetで全て管理するようなものにしてたりとか
        • 3月末に退職予定なので引き継ぎとか
          • 秘伝のタレのようなLP作成のナレッジとかしてる
      • 雇用
        • よくフリーランスと思われるが自社と直契約の契約社員
          • 正社員とほぼ変わらないが権限周りなどの違いがある
          • ボーナスないが月給高いので正直今までの会社よりも年収はもらっている
        • もともと正社員雇用目指していたがあまり正社員になろうという気が起きなくなった
          • CTOとの面接があったりと正社員になるための段階が嫌だった
            • どっかの退職エントリの「正社員にしてくださってありがとうございます」みたいなの好まない
          • 目標管理が嫌
            • 最近、社会主義に傾倒しているとこがあって割りに合わないと感じているから
              • (脱線)理想の主義ではあるが結果的に資本主義な国の存在知ってる
            • 他人を結構気にするところがあるので
          • 社会不適合者なとこあるので
            • コミュ障だと思っている
          • サービス自体にもあまり興味を持てなくなった
            • いろいろ企画しても強いユーザ・弱いユーザというのが出てきてみていられなくなってきた
            • だんだんソシャゲの運用になってきて何かあったときの補填対応とか嫌になってきた
    • 副業しなくなりました
    • 転職活動しなきゃなと思いながらあまりしていない
      • 職業訓練校行ってみようかと思ってみたり
        • 3Dの加工とかちょっとやってみたいと思ったりする
        • 職業訓練校のレベル感があまりわかっていない
      • 結局自分は何をしたいのか明確ではない
        • ものつくりは何かと掛け合わせで生まれるものだと思っているのでその時の気分によるものが大きいと思う
      • 自分が文句を垂れない環境というものを探し出せていない
        • どういった環境が自分に合っているのかも理解できていない
  • プライベート

    • 動画サイトで見ているもの
      • ゲーム実況者 幕末志士
        • 西郷さんの体調が心配
        • 動画で出てくる自作ゲーム開発者のリスペクトがある
          • 西郷ゲームとかTeam Mの作品とか
      • 政治系の解説動画
        • 右左の主張の違いあるなと思っていろいろ見ている
        • どの情報が正しいのかよくわからないからさらに別の解説も見てみるのループに入る
    • 動画作成の授業受けてる
      • 開発系の仕事をするのをやめようかと思い始めた
        • 地上最強のものつくり屋になりたいというのがあるので
          • 個人的にハマってる漫画の刃牙で心境を例えると
            • カンフーで強い烈海王がなぜか空手やボクシングをやり始めるのような
            • もともと1つの技術でやっていこうと思っていないので本部以蔵みたいな人物に近いのかもしれない
      • 地元に帰ろうかというのも思っているが開発系の仕事が少ないので
        • (補足)地元に帰ろうかと思うことについて
          • 地元でも開発している人を拝見したので関わってみたいというのがある
          • なんだかんだで東京は仕事があるので生存できることに飽きがあった
            • 都内じゃない環境に対応したい
              • 住めば都みたいなものがあるのでどうにかなることではあるが
              • 東京オリンピック前に東京でたい
                • 福島も野球の開催地ではあるので例外ではないが
                • オリンピック開催前後に何か東京に起きると予知してる
        • (補足)地元に帰った時にお話しにいった企業があって開発の仕事は0ではない
          • ラビックス rabbix さん
            • 大学のシステムをRailsで作っている
            • 福島大学の学生さんとかと勉強会開いている
              • connpassでみたことある
          • オリエンタルエージェンシー さん
            • 福島駅前の中合にある大型ビジョンの広告代理店
            • WordPressのHP作成もやってる
            • 市町村のページの多言語化とか大変みたいな話を聞いた
              • 大河ドラマゆかりの観光地とかに海外の人来てたりするので
      • Adobe製品いじってた
        • Premiere Pro
          • レーザーツールによるカット編集
          • リンク解除(動画と音声の切り離し)して2カメ撮影動画の編集
          • エフェクト・トランジションとか
          • Lumetriスコープ(色・明るさ調整)とか
        • After Effects
          • キーフレームアニメーション作成とか
        • Photoshop
          • ビットマップ画像な画像編集
            • 透過とか
        • Illustrator
    • UnityとVRMを独学
      • 最近はVRoid Studioで簡単にバ美肉できてしまうことがわかった
      • 定期的にやってる開発合宿で見てもらうために何かを作成中
        • ご期待ください

サークルのHPリニューアルしました

f:id:igara1119:20190303145348p:plain
平成の終わりが近いですね

先にサークル現状の報告

この場を借りて先に今のサークル(shin・DO・meeeee)がどうしているのかという報告をさせていただきます。

結論言ってしまうと 先月の2月5日に 技術書典6 のサークル当落通知というのがあって落選してしまったので技術書典6には参加しません。

技術書典5以降の技術書典6に向けたサークル内部の動きとして

  • GitLabのサブプロジェクト機能・issues board機能使っていい感じに細かいタスクの可視化 f:id:igara1119:20190303145554p:plain
  • Hangoutで打ち合わせするようになった

とかあって前回とは違った問題解決できてきたなぁと思ったんですけどね。

HPリニューアルしました

こちらになります。

shindomeeee.github.io

リニューアルをおこなった理由としては先ほどのissues boardの画像で目移りしたかもしれませんが

ホームページメンテしんどい問題

があって告知用とかマルチに使おうとして結果的に放置されてしまったというのがありました。 HPの作成段階では実績もなく なんとなくなデザイン作成 とかの負債があったなぁと感じてあのHPの立ち位置を考え直し、

  • みんなブログなら記載する
  • 実績のみ載せるLP的なページの認識であった方がライトで良さそう

というのに気づいて思い切って今までのホームページを捨てることにしました。

HPにあるリンクとかもGoogleのスプレットシートで管理するようにしたのでよりサークル内部の情報をまとめやすくなったんじゃないかなと思っています。 f:id:igara1119:20190303152113p:plain

あとはイラストなど素材も充実してきたのでHPにも導入するようになったのも大きな変更です。 (あのイラストは汎用性高いので気に入ってます。

ここから玄人バイニン向け

あのHPを僕1人でメンテするのもあれで、やったこと結構エグいのが多かったのでナレッジ残す意味あいで色々記載します。 ※記載している内容はほとんど僕の趣味によるものが強いです。

Blogs一覧取得API作成

  • API・DBはFirebaseのCloud Firestore(最近GAになったらしいですね
  • マスタ管理としてSpreadSheet
  • SpreadSheet -> Cloud Firestoreにデータ反映する仕組みとしてGoogle Apps Scriptを使用しています。

図にするとこんな感じです

f:id:igara1119:20190303162325p:plain

なぜの構成にしたかというとサークルメンバーのGoogleアカウントわかっていたのでIAM管理もGoogleにさせてしまった方が楽だったからです。

Cloud Firestoreの設定

ルール

SpreadSheetに入力できてFirebaseのロールを持っている人への書込み権限 一覧のデータを取得するための読み込み権限 を下記のようなので設定

service cloud.firestore {
  match /databases/{database}/documents {
    match /blogs/{document=**} {
      allow read;
      allow write: if request.auth;
      allow delete: if request.auth;
    }
    match /events/{document=**} {
      allow read;
      allow write: if request.auth;
      allow delete: if request.auth;
    }
  }
}

SpreadSheetの設定

f:id:igara1119:20190303170609p:plain

CSVにすると

id,title,url,tags,created_at,document_id
1,技術書典5当選しました!!!,https://ultrabirdtech.hatenablog.com/entry/2018/08/02/065033,["技術書典", "技術書典5"],2018-08-02,hogehoge

な構成にし、列の説明として

  • id -> 順番
  • title -> ブログタイトル
  • tags -> 現在使用してないけど絞り込み検索とかで使用する想定
  • created_at -> ブログの公開日
  • document_id -> Firestoreのdocument_id

な感じで

f:id:igara1119:20190303171445p:plain

SpreadSheet上に描画ツールで作成した更新ボタンにGoogle Apps Scriptのスクリプトを割り当てできるようにします。

Google Apps Script

Google Apps Script経由でSpreadSheetの内容取得、Firestoreに書込みができるように マニフェストファイルを編集します

appsscript.json

{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
    "libraries": []
  },
  "exceptionLogging": "STACKDRIVER",
  "oauthScopes": [
    "https://www.googleapis.com/auth/firebase.database",
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/spreadsheets.currentonly",
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/datastore"
  ]
}

肝心なのがoauthScopesで

で追加してます。

あとはスクリプトの追加で

blogs.gs

function setBlogs() {
  // 列の扱うデータの配置
  const columnNumbers = {
    id: 0,
    title: 1,
    url: 2,
    tags: 3,
    created_at: 4,
    document_id: 5
  }

  const rowNumbers = {
    // 列名がある箇所の配置
    scheme: 0
  }
  const apiUrl = "https://firestore.googleapis.com/v1/projects/(project id)/databases/(default)/documents/blogs"
  // 後にdocument_idを取得するために使用
  const removeString = "projects/(project id)/databases/(default)/documents/blogs/"

  // マニフェストファイル(appsscript.json)にあるoauthScopesで許可されたoauth tokenを取得
  const token = ScriptApp.getOAuthToken()
  const headers = {
    authorization: "Bearer " + token
  }

  // 対象のSpreadSheetのURL
  const url = "https://docs.google.com/spreadsheets/d/(SpreadSheet ID)"
  const spreadsheet = SpreadsheetApp.openByUrl(url)
  // SpreadSheetの読み込みたいシート
  const blogsSheet = spreadsheet.getSheetByName('blogs')
  const blogsData = blogsSheet.getDataRange().getValues()
  // IDの逆の順番にする(最新のものを先に登録させるため Firestore REST APIのorderBy asc があれば...)
  blogsData.reverse().pop()

  blogsData.forEach(function(blog, rowIndex) {
    const id = blog[columnNumbers.id]
    const title = blog[columnNumbers.title]
    const url = blog[columnNumbers.url]
    const tags = JSON.parse(blog[columnNumbers.tags])
    const created_at = new Date(blog[columnNumbers.created_at]).toISOString()

    if (blog[columnNumbers.document_id]) {
      const targetDocumentId = blog[columnNumbers.document_id]
      const deleteOptions = {
        method: "delete",
        contentType: "application/json",
        headers: headers,
        muteHttpExceptions: true
      }
      const deleteResponse = UrlFetchApp.fetch(apiUrl + "/" + targetDocumentId , deleteOptions)
      const deleteResponseCode = deleteResponse.getResponseCode()
      const deleteResponseBody = deleteResponse.getContentText()

      if (deleteResponseCode === 200) {
        const deleteResponseJson = JSON.parse(deleteResponseBody)
      } else {
        Logger.log(Utilities.formatString("Request failed. Expected 200, got %d: %s", deleteResponseCode, deleteResponseBody))
      }
    }

    if (id && title && url && tags && created_at) {
      const createPayload = {
        fields: {
          id: {
            integerValue: rowIndex + 1
          },
          title: {
            stringValue: title
          },
          url: {
            stringValue: url
          },
          tags: {
            arrayValue: {
              values: tags.map(function(tag) {
                return {stringValue: tag}
              })
            }
          },
          created_at: {
            timestampValue: created_at
          }
        }
      }

      const createOptions = {
        method: "post",
        contentType: "application/json",
        payload: JSON.stringify(createPayload),
        headers: headers,
        muteHttpExceptions: true
      }
      const createResponse = UrlFetchApp.fetch(apiUrl, createOptions)
      const createResponseCode = createResponse.getResponseCode()
      const createResponseBody = createResponse.getContentText()

      if (createResponseCode === 200) {
        const createResponseJson = JSON.parse(createResponseBody)
        const documentId = createResponseJson.name.replace(removeString, "")
        blogsSheet.getRange(id + 1, columnNumbers.document_id + 1).setValue(documentId)
      } else {
        Logger.log(Utilities.formatString("Request failed. Expected 200, got %d: %s", createResponseCode, createResponseBody))
      }
    }
  })
}

な感じで雑にSpreadSheetにあるデータを正とするためにFirestore上のデータ全消しした後で再度追加するようにしてます。

このスクリプトに先ほどSpreadSheetのボタンにスクリプトの割り当てをすることでボタンから更新することが可能になります。

フロント実装

github.com

がっつり変更入れてるので現在(2018/03/03時点)でもPR中にしてます。

昔ながらのpublic_html に 自身で編集したindex.html 置くような運用辞めた

どう言ったことというと以前はindex.html, css, jsを直接変更していたのを辞めたということです。 いわゆるジェネレータやbundlerのようなものを用いていなかったため新しいページを作成するときはディレクトリ切ってindex.htmlを設置し、共通で変更必要なものを都度都度index.htmlを直すようなことを辞めました。 なぜ今までこのような運用していたのかというとサークルのみんながどのようなの得意としているのか理解していなかったのもあり一番共通な認識でできるだろうと当時思っていたのですが、結果としていじる側にしんどみが出てしまっていたという風に感じています。

ジェネレータとしてGatsbyJSにした

GatsbyJSのベースとなるReactJSを用いるため別の学習コストが発生してしまっているのですがここは僕が率先してできるので無理やりに選定しました。 Vueベースなものを扱うというのもありましたがTypeScriptで補完が効くようにすぐに作れるのが僕の中でReactだったのでGatsbyJSにしたというのがあります。 知り合いでよくJekyllで作ったGitHub Pagesを見てたりしてたのですが僕個人としてフロントで動的に見させたい需要が強かったのでNodeJSよりのものを選びました。

デプロイ周りの変更

GitHub Pagesのホスティングの仕組みとしてmasterブランチのものが静的サイトとして公開されるようになっていて 以前は様々な対応したものをPR出してmasterブランチに反映されて初めて公開されるような運用していました。

しかし今はmasterブランチとは別でdevelopブランチをデフォルトブランチとし、 yarn deployを実行することでmasterブランチ以外でもすぐにmasterブランチにあげて GitHub Pagesに反映されたものを確認するようにしました。 yarn deploy と言っても厳密には gatsby build と NodeJSライブラリのgh-pagesによるmasterブランチ反映をさせています。

型システム & コンポーネント思考 & できるだけすぐに捨てられるように対応

CSSの運用はCSS Modulesでやるようにしました。 Reactならstyled-componentsでやる手段もありますがelementとstyleが密結合すぎると感じて基本的なhtmlとcssで運用した方が良いと感じてCSS Modulesでやるようにしてます。 CSSのやつもNodeJSライブラリのtyped-css-modulesを用いれば型定義化され補完が効くようにできるのでそうしてます。 あと型導入によってAPIとして使用しているFirestoreが返してくるJSONの形(これとか)であったり、 よく困惑するコンポーネントが持つ独自の状態(これとかやってることとして背景画像が読み込まれたことを保証して背景画像のアニメーションをするようにするためのstate変更をさせる実装してます) というのも型化されて見通しいいように?しました。

jest導入

Reduxによる状態変更テスト以外に独自のコンポーネントのテストも行うようにして作成したテストファイルに対して必ずカバレッジが100%になるように頑張ってます。

f:id:igara1119:20190303183634p:plain

API周りはモック使用したり(これとか)、 デフォルトの値をモックしたりとか(これとか)、 画像の読み込み完了時にstate変更されるようなテストをjest.spyOn使用したとか(これとか)あります。

UI周りはBlogs一覧でリンク何個あるかのテストとかはやってますがどういったスタイル当たるかというかまでやってません。 (そこに関してはjestによるテストというか自動でスクリーンショットとるかstorybookによるもので担保するのが疲弊しないんじゃないかと個人的に思っています

しんどみの技術書典5 中間報告的な何か

この記事の内容

宣伝とかはバードくんやかんずさんが行っているので

ultrabirdtech.hatenablog.com

k-anz.hatenablog.com

僕からは入稿までにどういったことを行ったのかという報告をしたいと思います。

行ったこと

サークル名の決定

サークル名の候補としてメンバーの共通点から決めようというのがありました。
色々あったのですが身内ネタが濃すぎ・喧嘩を売り過ぎてるネーミングだったという自粛の点があり、生き残った候補のshin・DO・meeeeにしたというのがあります。

みんな なんらかの業(カルマ)とかしんどみあるでしょ?

意味とかは宣伝のブログに記載しているので割愛します。

気付かれている方々もいるかもしれませんが、
技術書典5のサークルリスト上でshinじゃなくてsinになってるあれは
スペルまで明確に決めていない時に先に登録してしまった名残で、
間違えるのもsin(罪)だなというの感じて残したまましております。

サークルカットの作成

techbookfest.org

技術書典5の詳細にある画像は別のプロのもきちくんに作成していだたきました。
なんでああなったのかの図にするとこれしかない案からデフォルメされた画像を作ってくれました。

f:id:igara1119:20180923174130p:plain
爆誕

PSDデータのレイヤー見ると細かい下書きの非表示レイヤーが残ってたり、
画像作成してと依頼したのも直近なのにも関わらずに対応していただいて
本当にありがとうございました。

先駆者の意見を聞いてみること

初めて出展する人向けの勉強会があったので参加しました。

techbookfest.connpass.com

その中で知ったこととして

  • 確定申告方法
  • 印刷所
  • 作成方法
  • ロイヤリティフリー素材の扱いについて (レビューの時にいらすとやの絵があるなぁと思ったけど規約内だったのでOKそうとか気付きがあった
  • 過去の現場の声

など他のサークルの人から聞けたというのがあるので
感謝だなぁと思っています。

本の作成手順的なこと

本のタイトルの決定

みんな別々のテーマで書いていたので汎用性あるようなタイトルにならざるを得なかったです。

執筆内容

モチベーション保って書けることを重視し各自自由にテーマを決めて書くようにしました。
途中でテーマ変えたりとかもありました。

執筆環境

これ完全に僕の圧でRe:VIEWで作成しましょうという風にしてしまったなぁと負い目を感じてますが、Re:VIEWで作成しました。
ただRe:VIEWにしたことによるCIとの連携や、
GitLab上でのMerge Requestのレビューが上手くいきましたので結果的によかったと思います。

今回の環境のサンプルとしてこんなものを作成しました。

gitlab.com

CI上で行わせたこととして

  • textlintによる簡単な文章構文の静的解析
  • 電子書籍用のPDF作成
  • 印刷用のPDF作成(本文のみのPDFにして隠し(通し)ノンブルを追加、ページ総数が奇数の時は空白ページをうめる
  • ビルド結果をDiscordに通知

Merge Requestのレビューついては
今までこのメンバーで議論をするということがあまりなかったので楽しかったです。
(途中、議論したがり欲でようわからん難癖議論をしてすみませんでした。

勉強会でも議論になっていたことですが、
レビューしてくれる人がいなくてどうしようというのがある中で
僕達のサークルでは相互にレビューしてくれる人がいるというのはありがたいことだと思いました。

しんどみあったとこ

  • マージン調整とかでLaTexの知見が必要になったところ
    CSSやっていき組だったらまた別の世界があったのかもしれない
  • textlintででたerrorをignoreするか議論
    (固有名詞として存在するけど長い漢字の単語なのでerrorが出るとか
  • PDF出力すると半角の_(アンダースコア)がなんかでかくみえる (未解決

入稿手順的なこと

印刷所の決定

日光企画さんにしました。
理由としてはhttp://www.nikko-pc.com/only-event/backup.htmlとかのページを見ると入稿から本が納品される日の目安が見れて良いというのと
技術書典のバックアップ印刷所でもあるので本の受け取りが会場でできて楽そうだったからです。

入稿日の決定

早めに印刷所に入稿すると割引が適応されるので当初は50%引きの日に入稿しようとしていました。

入稿の仕方・添削をしてもらう

日光企画さんにお伺いし、いただいた指摘として

  • 出力された本文がB5サイズのものではない
    (今回はB5想定ではない本文の中心から無理やりB5サイズに適応した
    Re:VIEWの設定でtexdocumentclass: ["jsbook", "uplatex,oneside,b5j"] を忘れていた
  • 本文全体に通しノンブルがない
  • 本の厚さに適応した表紙の設置がされていない
    (もきちくんに依頼してたけど細かい指示内容なしで 日光企画さんのテンプレートに表紙載っけてとしか言わなかったので反省してます。 あとB5で入稿できそうな雰囲気がなかったので別のフォーマットの提示とか かわいそうな事させたなぁと思っています。

入稿申し込み・入金

申し込みした結果

  • B5
  • 34ページ(表紙4ページ + 本文30ページ
  • 50冊
  • 左綴じ
  • 平綴じ
  • クリアPP(表紙の加工
  • 本文用紙 上質90
  • オンデマンド スミ
  • 割引適応40%

計15,180円 になりました。

さいごに

様々な方の知見を借りる事で進める事ができたなぁと思います。

当日寝坊しないようにがんばろう