ゆるふわ技術日誌

エンジニア見習いの悪戦苦闘日記

Macのdefaultsコマンドで半角カナの入力を有効化する

周回遅れ感は否めないのですが、アップルのオタクとしては過渡期のAppleSiliconのMacに触っておくという経験をしておきたくてM1搭載のMac miniを買いました。

もともとはWWDC2021でMacBookProの新型が発表されたら買う気マンマンだったのですが、半導体不足の影響か?どうかは知りませんが発表されずに終わってしまったので、衝動買いです。

これでもし年内にMBP出たらそれはそれで買っちゃうのかもしれませんが、その時は売るなり自宅でサーバーにするなりしようと思います。

どうでもいい話はさておき、せっかくまっさらな状態のMacを手に入れたこともあり、前々からやろうやろうと思って中途半端になっていた環境構築のスクリプト化をやりながらセットアップを進めました。

Macには defaults コマンドというのが用意されていて、これを使うことでシステム環境設定とかから設定するような項目をスクリプトから設定変更することができます。

こちらの記事には大変お世話になったのでぜひ参考にしてみてください。

amasuda.xyz

…で、たいていの設定項目は誰かがやっていて調べればどの値を書き換えればいいか出てくるのですが、残念ながら半角カナ有効化のやり方だけどうしても見つけることができず、調べたのでここにメモっておきます。

結論から言うと、これでいけます。(実行後要再起動)

defaults write com.apple.HIToolbox AppleEnabledInputSources -array-add "
<dict>
  <key>Bundle ID</key>
  <string>com.apple.inputmethod.Kotoeri.RomajiTyping</string>
  <key>Input Mode</key>
  <string>com.apple.inputmethod.Japanese.HalfWidthKana</string>
  <key>InputSourceKind</key>
  <string>Input Mode</string>
</dict>
"

動作確認した環境は macOS 11.4 (Big Sur)です。冪等性があるかとかはちょっと確かめられてないので、もしかすると複数回実行したらおかしくなったりする可能性もあるので自己責任で。

ここから先は余談というか、どうやって設定値を見つけ出したか、という話。

どうやって見つけたか

前述の記事で現在の設定値の確かめ方が載っていたので、

  • 設定値を defaults read で出力したものを適当にファイルにリダイレクトして保存
  • GUIから設定変更
  • defaults readで再び設定値出力してファイルにリダイレクト
  • 変更前と変更後でdiffを取ってそれっぽい値に目星をつける

みたいなことをやりました。もうちょっとスマートにできて欲しいけどまあ致し方なし。

そういえば、WWDCMacにもショートカットアプリが来るみたいなこと言っていたので、簡単な設定変更くらいだったらショートカットにまとめたりできる未来があるかもしれないですね。(スクリプトの方がエンジニアにとっては便利なことに変わりはないかもしれないけど)

Mac2台持ちになったので片方はBetaのOSとか入れて遊んでもいいなあとか思ったりした。おわり。

社員を選択できるフォームをつくってよ!と言われたのでAirtableでハックしてみた話

会社で全社イベントの運営っぽいことをしているのですが、その事務作業の一環で社員一覧から一人を選択でき、かつその社員に紐づく情報を入力するフォームみたいなものを作りたいという要望が出てきました。

フォームを作るというと、思いつくのはOffice365やGoogleのフォームなのですが、残念ながら大量の選択肢から何か一つを選ぶというフォーム部品は用意されておらず実現するのは難しそうということでどうしたものかちょっと悩んでおりました。

いろいろ考えた末、Airtableというサービスを使ってそれっぽいことをできたのでやり方を共有。ちなみに、「社員を選択する」という超具体的なケースについて書いてますが、大量の選択肢が用意されたフォームみたいなものを作りたい全ての場合に応用できると思うので、ぜひ。

(注意: この記事は将来同じことしたくなった時に思い出すためのメモ書きなのでちょいちょい端折ってる箇所があるかもしれません。)


Airtableについて

airtable.com

最近流行りのサービスで、一言で言うとだれでも簡単に扱えるデータベース的なものをイメージしてもらうと良さそうです。
Notionを使ったことがある人にとっては、Notionのデータベース・テーブル機能を抜き出して高機能化したもの、というとわかりやすいかも。

ノーコードの文脈でよく触れられたりする気がします。今回はこのサービスを使っていきます。ちなみにFreeプランありで、この記事の内容程度の使い方であれば無料のままでいけます。

できあがったもの

f:id:uutarou:20210518225426p:plain

f:id:uutarou:20210518225442p:plain
「対象者」をクリックすると社員名簿が出てきて一人を選択できる

例として社員名の他に、文字列を入力できる箱を用意しましたが、他にもGoogleFormsでサポートしているような部品は一通りあります。

つくりかた

社員名簿を手に入れる

いきなりですが、フォームの選択肢の基になる社員名簿を手に入れてください。

Airtableは標準でCSVからのimportに対応しているのでExcel形式とかで存在していれば、CSVとして書き出したりしてください。

自分は社員名簿手に入れるのが面倒だったので(?)SlackAPIを使って社員が所属しているチャンネルのメンバー情報を引っこ抜きました。SlackのAPI的に、チャンネルに所属するメンバーのIDだけを取ってくるAPI(conversations.members)と、メンバーのIDからメンバー情報を取得するAPI(users.info)の二つを組み合わせる必要があるので、適当にコードを書いて引っこ抜きます。(自分はGolangを使って書きましたがシェル芸がお得意な方は多分curlとjqとかで同じようなことができるんじゃなかろうかと思います。僕はチャレンジして2分で諦めてGo書きました)

だいたいこんな感じ。

package main

import (
    "fmt"
    "github.com/slack-go/slack"
    "sync"
)

const (
    token = "insert your api token"
    channelId = "insert target channel id"
)

func main () {
    api := slack.New(token)

    members, _, _ := api.GetUsersInConversation(&slack.GetUsersInConversationParameters{
        ChannelID: channelId,
        Cursor:    "",
        Limit:     1000,
    })

    wg := sync.WaitGroup{}

    fmt.Printf("\"%s\",\"%s\",\"%s\"\n", "name", "slackId", "email") // ヘッダー行を出力しておく。Airtableに取り込んだ時に使われる。
    for _, memberId := range members {
        wg.Add(1)
        go func(id string) {
            user, _ := api.GetUserInfo(id)
            if !user.IsBot {
                fmt.Printf("\"%s\",\"%s\",\"%s\"\n", user.RealName, user.ID, user.Profile.Email)
            }
            wg.Done()
        }(memberId)
    }

    wg.Wait()
}

ちなみに上記コードは極めて雑です。エラー処理をしていないのはもちろん、rate limitやメンバーが1000人以上いた時の考慮(conversations.membersは1回で1000人までしか取得できない。それ超える場合は、cursorを載せて再度リクエストして続きを取得する必要がある。)なども一切していないのでガチ用途の時は気をつけてください。

で、これを実行するとカンマ区切りで名前・SlackのID・メアドが吐き出されます。ファイルにリダイレクトするとかしてCSVとして保存しておいてください。

ちなみに1列目を名前などフォームで表示したい値にしておいた方が良いっぽいです。あとは適宜必要な項目があれば取得する感じで。(自分は一意に特定して何かしたくなったりした時のためになんとなくSlackのIDとメアドを取るようにしてみた。それ以上の理由は特にないです)

Airtableに名簿を取り込む

無事に名簿を手に入れたら、Airtableに登録してもらって適当にbaseをつくります。base = Excelでいうブックだと思います。(たぶん。雰囲気でAirtable触っているので詳しくはわかってない)

baseを作ったら、上の方のタブ?のところにある「Add or import」を開き「CSV file」を選択します

f:id:uutarou:20210518231401p:plain

さっき錬成したCSVファイルをアップロードするとこんな感じになるので、データの切れ目がおかしくなってるところがないかなどをざっと確認しつつImport

f:id:uutarou:20210518231632p:plain

これで新たテーブル(Excelでいうところのシート)が作られて、名簿が取り込まれます。ちなみにテーブル名ダブルクリックで名前変更できるので、わかりやすい名前に変えておきましょう。

フォームの入力を受け付けるテーブルをつくる

次にフォームの入力が溜まっていくテーブルを作ります。

さっきの「Add or import」のところを再びクリックし、今度は「Create empty table」というのを選択します。名前は適当に。

f:id:uutarou:20210518232223p:plain

するとこんな感じになります。最終的にフォームを作った時、列が回答項目になります。(Googleフォームの結果をスプレッドシートに吐き出した時をイメージしてもらうとわかりやすいです。結果を入れるスプレッドシートを先に作って、それに対応するフォームが後から作られるイメージ)

列は「+」ボタンから作れるので任意の項目を足してあげてください。(たとえば文字列での入力が必要ならSingle line textやLong textなど)

f:id:uutarou:20210518232526p:plain
これだけでもすでにGoogleフォームとかよりいろんな選択肢があっていい感じ。

で、肝心の社員を選択する項目はどう作るかというと、↑の図一番上にある「Link to another record」をクリック。

するとさっきCSVから取り込んだテーブルがあるはずなので選択。

f:id:uutarou:20210518232743p:plain
ちょっとテストで色々作ったりしてたのでとっ散らかってますが気にせず。

f:id:uutarou:20210518232825p:plain

「Allow linking to multiple records」を選んでおくと複数人が選択できるようになり、外しておくと単一の選択になります。

設定が済んだら「Create field」を。

f:id:uutarou:20210518232956p:plain

こんなやつも出てきますがあとから足せるので一旦スキップで大丈夫かと思います。(選ばれた人に紐づく情報を横に出すかどうかが選べる)

フォームをつくる

ここまできたらフォームの形にするだけです。先程と同じテーブルで左下にある「Form」をクリックしてForm viewをつくります。(Airtableにはviewという概念があって、同じテーブルを複数の見方で見られる。さっき作った表をFormという見方で見るよ、という意味でviewです)

f:id:uutarou:20210518233259p:plain

f:id:uutarou:20210518233348p:plain

この時点ですでにあるフィールドは全て含まれた状態のフォームが出来上がってます。Googleのやつと同じようにマウスのドラッグ&ドロップで順番を入れ替えたりもできますし、タイトルの変更などもできます。無料プランでは✨マークのついている項目のカスタマイズはできません。それが嫌ならお金を払うかフルスクラッチでフォームを作りましょう😇

ちなみにいらない項目(フォームで収集しない項目)があれば、フォーム部品をクリックして右上のボタンから消しちゃうこともできます。

f:id:uutarou:20210518233604p:plain

フォームを共有する

最後にフォームを実際に配って書いてもらうためには、左上にある「Share form」を。

f:id:uutarou:20210518233729p:plain

こんな感じの画面で共有用のURLを取得できます。

そればかりではなく、「Restrict access with a password」を選ぶと事前に決めておいたパスワードを知っている人でないとフォームを開けないようにすることもできたりします。これはなかなか嬉しい。

おわり

という感じでサクッとメモ書きするつもりが、結構ガッツリになってしまった。

Airtable、有料プランだと結構いいお値段しちゃうので個人でゴリゴリ使っていくイメージはあんまりもてなくて触ってなかったのですが、実は結構いろんなことができて楽しいなと思いました。

NotionのDatabaseにも言えることなのですが、英語なのと、若干DB設計に対する理解みたいなものを必要とするので、普段そういうのをやらない事業職の人たちとかからはもしかするととっつきづらさがあるのかもしれないなとは思いつつ、みんなで使いこなせたら幸せになれるんじゃないかとも思いましたとさ。おしまい。

【超小ネタ】Gitリポジトリ内にいるときにリポジトリのrootまで一発で戻りたい

Gitリポジトリの深い階層にいるときにリポジトリのルートまでさくっと戻りたいことってないでしょうか?

自分は cd でホームディレクトリまで戻ってからリポジトリのルートまで移動し直したり、 cd ../../../.. みたいなかんじで戻ったり(そして戻りすぎたり)してました。

gitコマンド使うとリポジトリのルートの絶対パスを取得できることを知ったのでリポジトリのルートまで戻るコマンドをエイリアス貼ってみました。

rootをとるコマンドは git rev-parse --show-toplevel 、これの結果をcdに渡してあげたコマンドをalias貼るだけでOKです。すばら。

rev-parseコマンド、結構いろいろできるみたいですね。

qiita.com

おしまい。

Golang インタフェース型のポインタを扱おうとしてハマった【たすけて】

注:いまいちすっきり解決してないので、誰か知っていたら教えてください。


年末クリーンアーキテクチャの本を読んだので、Golangで実践してみようと思ったら思わぬところでハマったメモ。(クリーンアーキテクチャするならオブジェクト指向の言語でするべきなのでは?というのはあるがGolangの勉強も一緒にしたかったというのが大きい)

簡略化するとこんなコードを書いていた。

package main

// 実際は引数受け取ったり値を返したりする
type SomeRepository interface {
    Create()
    Find()
}

// DBとかの技術的詳細に関わる値を持っているイメージ
type SomeRepositoryImpl struct {
    CollectionName string
}

func (repo *SomeRepositoryImpl) Create() {
    return
}

func (repo *SomeRepositoryImpl) Find() {
    return
}

func CreateSomeRepository() *SomeRepository {
    return &SomeRepositoryImpl{
        CollectionName: "Something",
    }
}

SomeRepositoryという抽象を実装したSomeRepositoryImplというのを作りたい、というあるあるなRepositoryのコード。Repositoryを使う側は技術的詳細であるDBに対する操作がどうなっているかということを知らなくて済む、というやつ。

ところが上記コードはコンパイルエラーになる。

エラーになるのは

func CreateSomeRepository() *SomeRepository {
    return &SomeRepositoryImpl{
        CollectionName: "Something",
    }
}

この関数のSomeRepositoryImplを作っているところで、Cannot use '&SomeRepositoryImpl{ CollectionName: "Something", }' (type *SomeRepositoryImpl) as type *SomeRepositoryというエラーになる。

いろいろ試行錯誤して、散々ハマったが、結局のところ答えはこう。

func CreateSomeRepository() SomeRepository {
    return &SomeRepositoryImpl{
        CollectionName: "Something",
    }
}

関数の返り値の型がポインタ型ではなくなった。どうやらインタフェース型のポインタというのはない?らしい。とりあえずうまくいってしまったので続きをやっていこうかなと思いつつ、これがなぜなのか?というのにすっきりとした答えが出せずにモヤモヤしている。

2020年反省会会場

紅白見ながら一気に振り返る。

https://yurufuwa-tech.hatenablog.com/entry/2020/01/06/091359

この記事の答え合わせ。


月2回以上の技術アウトプット

これは完全にできなかった。なんなら1月で崩壊してた気がする。

昇級する

これも叶わず。

そもそも自分のWillってなんなんだろうなあということに悩み続けた一年だった気がする。現在進行形で悩んでいるが、来年は新卒3年目、流石にそうも言っていられないのでバチっと決めていきたい。

アプリ以外の技術領域をやる

これはやった。一応Elmという言語を使ったWebフロントエンドの開発や12月にはGolangを使ったサーバーサイド開発もやったりした。

一人の開発者として、求められるクオリティのモノを作ることはできるというレベル感。

フルスタックになっていくとしても、得意技を持っておきたいと思う今日この頃。

100名以上くるイベントへCfPを出す

これは叶わず。

というかそんなイベントもなかったが。(ただの言い訳)

冬キャンプをしたいので貯金する

冬キャンプは叶わなかったが、貯金はした。結構貯めたと思う。

マネーフォワードで可視化してそれを眺めることでモチベーションにつなげたり、持株会を利用してそもそも手元に入ってこない形で資産形成していったのがよかったかな。

生活の質を一定に保つ

これはどうだろう?そもそも「普段の生活」の定義が揺れまくっていた2020年、それなりに健全な人間の生活はできていたとは思う。

他人に優しくする

これもだめだなあ。

余裕がなくなるとすぐ対人コミュニケーションが雑になってしまって良くない空気にしてしまう。特にオンラインのコミュニケーションだと、相手の気持ちを感じ取りづらいのでより丁寧なコミュニケーションをするべきだったのかもと思うことも多々。

人間的な成長をしないとだめですね。

まとめ

典型的に宣言だけしてやった気になる1年を過ごしてしまった。

コロナもあって環境の変化が激しかったというのはありつつも、怠けすぎた一年だった。

業務でGolangを書き始めた。最初の週にやったこと雑記 + (少しだけ)思ったこと

新卒2年目も終わろうとしております、もがみです。

ここまでフロントエンド一筋でReact Nativeを使ったアプリ開発とか、Elmを使ったWebフロントエンド開発をやってきましたが、運良くサーバーサイドにチャレンジする機会を得ることができました。チームのタスクがサーバーサイドに偏重している間のお助けとしてやっている部分も大きいので、いつまで続くかは現状未定ですが、せっかくなのでやったこととか、感じたことをだらだら書いておきます。

前提

  • Tour of goは一通りやった(全部完璧に理解しているとは言っていない)というレベル感。
  • 普段はTypeScriptとかElmとか書いているひと。フロント寄り。
  • サーバーサイドは学生の頃趣味で書いてたことがあるくらいの知識レベル。

環境構築

なにはともあれ環境がないと開発できないので環境構築をしました。 普段のフロントエンド開発はWebStormを使っているので、Golandを選択。とりあえず体験版を入れつつ、特に問題がなければ、WebStormと統合してIntellJ IDEA Ultimateのライセンスを購入しようかなと思います。

知らなかったんですがIntelliJJava系の言語のためのIDEというわけではなく、JetBrains系の様々なIDEの機能を内包しているようです。また、すでに持っているWebStormのライセンスをアップデートすることもできるらしい。いくらになるのかよくわかりませんが買うことになったら調べてみようと思います。

support.samuraism.com


Goland自体は公式から適当にダウンロードして起動すればOK。個人的必須プラグインであるIdeaVimもインストール。(大事)

システムにGoを入れないといけないので、普段使っているanyenvでgoenvを入れて、開発するプロジェクトのgo.modに書いてあったバージョンのGoをインストールした。

改めて、Golandでプロジェクトを立ち上げると go: github.com/example/example-repo@v1.6.0: reading github.com/example/example-repo/go.mod at revision v1.6.0: unknown revision v1.6.0 と言われた。(example/example-repoは実際には会社のプライベートリポジトリ)

これはプロジェクト内で使っているプライベートなライブラリが取得できずに怒っているっぽかった。SSH鍵は設定してあるしどうしたら…?と思い調べたら、GOPRIVATEという環境変数を指定してやる必要があるということがわかった。

syfm.hatenablog.com

他にもいくつかの記事をみて、

  • sumdbというGolangパッケージのチェックサムを返すサーバーがある
  • そいつのおかげでproxyからパッケージを取ってきても改竄が行われていないことを証明できるという仕組み
  • sumdbにチェックサムが存在しなかった時、proxyの他のサーバーにパッケージ名が流出しないように処理を止めるという仕組みがある
  • GOPROXYに指定したパッケージを取りに行くときはproxyではなくオリジン(GitHubとか)に直接取りに行くようになる

みたいな理解をした。多分あってる?

f:id:uutarou:20201220235949p:plain

Golandは設定画面からイイ感じに環境変数を入れられるので入れてあげた。(わかりづらいがインプットの右端にあるボタンを押すと、イイ感じに入力できるフォームみたいなのが出てくる。イイ感じに。)

さっきのエラーが出たリポジトリ以外にもいくつかのプライベートリポジトリに依存しているようだったのでワイルドカードで指定した。

ここまできたら、適当にソースコード開いて赤くなってるimport文の上でOpt + Enterを叩いてSync dependenciesみたいな項目を選ぶと依存ライブラリの取得が走る。規模にもよると思うが、ちょい時間かかった。

思ったこと

ポインタ

他言語からきて一番戸惑うのはやっぱりポインタのような気がする。理解はしているつもりだったがいくつか致命的なバグを埋め込みそうになった。

Goを始める僕に言いたいことがあるとすれば*&を使う時は慎重になれ」ということかなと。(Go普段から書いてる人からしたら当たり前体操だと思うが)

TypeScript(他、最近のモダン & 高機能な言語)になれていると、基本的にしっかり型が当ててあって、コンパイラが怒りさえしなければ動くのが普通だと思ってしまうところなんだが、Goも型がある言語という思いが先行して、ポインタは常にnilである可能性を含んでいるということを忘れて、IDEが怒っているからという理由だけで&やら*やらを書いて本当に痛い目をみた。(デプロイしてウキウキしながらリクエストを叩くと500が返り、Loggingにはpanicの5文字が。。。)

それを繰り返して、「nilをチェックするのはプログラマの責任なんだぜ」というメッセージに気づくことができた。

フロントエンドエンジニアにGoを書かせる機会があったら是非「TSでいえばasとか!とかああいうのを使う時と同じ気持ちでポインタは扱わないといけないものなんだ」ということを伝えてあげてほしい。(ちょっと意味合いとしては違うと思うが、ポインタとの正しい向き合い方に気づいた後の僕の気分としてはそんな感じだった)

1週間で理解できたこととしてはこんなところだが、いまだに関数の戻り値や引数にはポインタ型を使うべきなのか、そうではないのかというところの判断がつかない。飲みの席での話なので100%理解できていないと思うが、Goは言語の仕組み上、ある程度の大きさまでは値渡しの方がパフォーマンス的にも有利であると聞いた。コピーのコストをとってもパフォーマンスで勝つらしい。不思議だ。

文法が少ないのは良い

Goの良いところとして、1個のことやる書き方は1つしか存在しないというのがよく挙げられると思うが初学者にとってそれは本当に嬉しいことだと感じた。

Tour of goの知識があれば、使われているライブラリや、他のメンバーが書いたコードも時間をかけて読めば何をしているか理解するには十分であるというのはめっちゃいい。

テストもわけわからんテストフレームワークの使い方とか覚えなくてもいいし最高。


他にも思うところはあるんだが、社内事情がだんだん濃くなっていきそうなのでブログはこの辺で。

PrettierとLinterを共存させるときの公式オススメが変わったらしいという話

個人でやっているプロジェクトにPrettierとESLintを入れようとしたときの話。

ESLintにもコーディングスタイルを整えるための機能があるので、何も考えずにそのまま両方を入れると、お互いのルールが競合してしまうという問題が発生します。
これを防ぐために、Prettierの公式ドキュメントに共存させるためのオススメ手順が書いているのですが、最近この内容に少し変化があったっぽいということに偶然気がついたので記事にしておきます。


prettier.io

これが公式のドキュメント。書いてあることを雑に要約すると

  • コードフォーマットのことはPrettierを、コード品質に関することはLintを使ってくれ
  • ただLintツールのルールと競合することがあるからeslint-config-prettierのようなコンフィグを入れてくれ

と書いています。つまり、Linter側のコードフォーマットに関するルールを全て無効化して、その上でPrettierを使ってコードフォーマットをするというのが推奨されています。

blog.ojisan.io

(こちらの記事が大変わかりやすく参考になりました)

ここまでの話は昔から同じようなことが書いていたのですが、このあとの部分がここ最近変わったみたいです。

この記事を書いている時点のドキュメントを引用すると以下のような注意書きがあります。

When searching for both Prettier and your linter on the Internet you’ll probably find more related projects. These are generally not recommended, but can be useful in certain circumstances.

First, we have plugins that let you run Prettier as if it was a linter rule:

・eslint-plugin-prettier
・tslint-plugin-prettier
・stylelint-prettier
These plugins were especially useful when Prettier was new. By running Prettier inside your linters, you didn’t have to set up any new infrastructure and you could re-use your editor integrations for the linters. But these days you can run prettier --check . and most editors have Prettier support.

これまた雑に要約すると、

  • PrettierをLinterルールのように実行するツール(eslint-plugin-prettierなど)があるが、これはPrettierがまだ新しいツールだった時には役に立っていたが、現在はオススメしない。
  • なぜなら、現在はprettier --check .(dry run的なやつ)があったり、多くのエディターがPrettierをサポートしているからである

また、これらのツールを使うデメリットとして以下が挙げられています。

  • Linterとして実行すると、スタイルに関するエラーがエディター上で赤線で表示されたりする。Prettierはコーディングスタイルを忘れるためにあるのに。
  • 普通に実行するより遅い
  • 1レイヤー増えるので壊れるリスクが増える

こちらアーカイブを見てもわかるように、過去には公式でeslint-plugin-prettierのようなプラグインをオススメしていたみたいですが、公式オススメではなくなったっぽいという話でした。

ちなみに、LintかけたあとにPrettierをかける手段としてはprettier-eslintというのがリコメンドされていました。ちなみに、prettier-eslintはCLI機能を有していないので、CLIから実行したい人はprettier-eslint-cliというパッケージがまた別で用意されているのでそっちを使うといいかもです。