The Pragmatic Ball boy

iOSを中心にやってる万年球拾いの老害エンジニアメモ

Jestで環境変数を設定する

JestでテストするときにwebpackのDefinePluginなどで環境変数をセットしている場合、テスト時にもセットしたいときがあります。

package.jsonのjestのところにglobalsを追加することで設定できます。

Configuring Jest · Jest

EXAMPLE

package.json

"jest": {
  "globals": {
    "ENDPOINT1": "http://api.example.com",
  }
}

ルートVueインスタンスのプロパティ

こんな感じにroot Vue instanceのpropsを使って初期値とかを与えたい場合があったりします。

  • html
<div id='root' props='initial-value'>
const vm = new Vue({
    el: '#root',
    props: ['initialValue'],
    data: {
        value: '',
    },
    beforeMount: function() {
        this.value = this.initialValue;
    }
});

が、これだとthis.initialValueはundefinedになります。

原因は、propsは親から子にデータを渡すためのもので、親のないroot vue instanceでは使えないようでした

Passing props to root instance · Issue #6440 · vuejs/vue · GitHub

対策1

真っ当な対策としてはroot vlue instanceのpropsを使って値を渡すのではなく、 rootに子コンポーネントを作って、子コンポーネントのpropsを使うです。

対策2

どうしてもrootで渡したい場合は、強引なやり方ですが、elementのattributesから取ってくれば一応とれます

const vm = new Vue({
    el: '#root',
    data: {
        value: '',
    }
    beforeMount: function() {
        this.value = this.$el.attributes['initial-value'].value;
    }
});

slackのbotでmentionが飛ばせなくなった対応

9/11からslackのusername指定が使えなくなりました。

A lingering farewell to the username | Slack

これにより、botなどでユーザーにメンション飛ばしていた場合にusernameを使っているとmetionが飛ばなくなります。

対応方法

<@username><@userID>に変更するだけです。

userIDを取得方法

以下からユーザーリストを取得してIDを取ってきます。

https://slack.com/api/users.list?token=<token>

Markdown Night 2017 Summer 資料まとめ

Markdown Night 2017 Summerの発表資料のまとめです。

イベントページ

connpass.com

ハッシュタグ

twitter.com

資料

なぜMarkdownは拡張されるのか

Markdownはなぜ拡張され続けるのか | bitjourney Kibela

esa Markdownの思想とデザイン

esa-pages.io

Markdownを拡張する話

speakerdeck.com

Qiita/Qiita:TeamにおけるMarkdownレンダリングの歴史

speakerdeck.com

TBD in Markdown Night

speakerdeck.com

MARKDOWNの本を一緒に作りたい

gitpitch.com

Vagrantでwebpackのwatchが動かない

Vagrant上でwebpackのwatchオプションを使ってファイルの変更を監視しても、変更を検知しないという現象に出くわしました。

こちらに(Webpack Watch in Vagrant/Docker )書いてあるように

webpackのconfigファイルに以下のようにpollingするようにする必要があります。

watchOptions: {
  poll: true
}

Ubuntu16.04にyarnを入れる

Ubuntu16.04にNode.jsの6系とyarnをインストー

sudo curl -sL https://deb.nodesource.com/setup_6.x | sudo bash -

sudo apt-get install -y nodejs

sudo curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 0.24.4

Ubuntu 16.04にDocker環境を作る

Ubuntu 16.04上でDocker動かすのにちょっと手こずったのでメモ。

とりあえずこれを実行すれば、Docker CEとdocker composeがインストールされるはず。

yes | sudo apt-get install \
           apt-transport-https \
           ca-certificates \
           curl \
           software-properties-common

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

sudo add-apt-repository \
        "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

sudo apt-get update

sudo apt-get install -y docker-ce

apt-cache madison docker-ce

sudo curl -o /usr/local/bin/docker-compose -L https://github.com/docker/compose/releases/download/1.13.0/docker-compose-`uname -s`-`uname -m`

sudo usermod -aG docker $USER

sudo chmod +x /usr/local/bin/docker-compose

Fetch APIを使ってRailsのAPIを叩く

JavaScript(es2015)でFetch APIを使ってRailsAPIを呼ぶときの方法。

ポイントとしては、'credentials: same-origin'をつけることと、 CSRFを有効にしている場合はheaderにX-CSRF-Tokenをつける点です。

    const getCsrfToken = () => {
        const metas = document.getElementsByTagName('meta');
        for (let meta of metas) {
            if (meta.getAttribute('name') === 'csrf-token') {
                console.log(meta.getAttribute('content'));
                return meta.getAttribute('content');
            }
        }
        return '';
    }

    const postFoo = () => {
        return fetch('/api/foo', {
            method: 'POST',
            credentials: 'same-origin',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-Token': getCsrfToken()
            },
            body: JSON.stringify({
                bar: "bar",
            })
        }).then((response) => {
            if (response.status >= 200 && response.status < 300) {
                return response.json();
            } else {
                const error = new Error(response.statusText);
                throw error;
            }
        });
    }

iOSのFrameworkのVersionについて

Frameworkにはversionがいくつかあってそれらの違いの雑な説明です。

その1

ひとつはお馴染みのinfo.plistに書くやつ これはちょっと省きます

その2

Umbrella Headerに書いてあるやつ

FOUNDATION_EXPORT double FwVersionNumber;
FOUNDATION_EXPORT const unsigned char FwVersionString[];

Build SettingsのCurrent Project Versionがこれにあたります

Frameworkをコンパイルするとわかるんですが、 (ここでは例としてFwというframeworkをつくります)

Compile Fw_vers.c

というcのファイルが勝手に生成されてコンパイルされてます。 中身を見てみると

 extern const unsigned char FwVersionString[];
 extern const double FwVersionNumber;

 const unsigned char FwVersionString[] __attribute__ ((used)) = "@(#)PROGRAM:Fw  PROJECT:Fw-10" "\n";
 const double FwVersionNumber __attribute__ ((used)) = (double)10.;

とこんな感じでxxVersionNumberとxxVersionStringが定義されてます。

Umbrella Headerではこれらをexternしているので、Frameworkを利用しているプログラムはこれらの変数を使うことができます。

その3

Framework VersionのAとかはFrameworkの中身のフォルダ構成を見ての通りでCurrentさしているディレクトリになります

Anatomy of Framework Bundles

その4

current library versionとcompatibility versionについてですが、

Link時のビルドログをみるとわかるようにこれらの値はclangのオプションとして渡されて使われる感じになります。

clang ...  -compatibility_version 1 -current_version 1

詳しくはこの辺を読むと良いのではないでしょうか

developer.apple.com

現場からは以上です。

AutolayoutでレイアウトしたViewを外して元に戻す

InterfaceBuilderやStoryboardでAutolayoutを使って配置したViewをremoveFromSuperviewしてから、 再度addSubviewしたい!ということがたまにあります。

普通にremoveFromSuperviewしてaddSubviewすると元には戻りません。 なぜならremoveFromSuperviewした時点で親と消された子のViewのAutolayoutの制約は消えてしまうからです。

どうすればよいかというremoveFromSuperviewする前に制約をどこかに保存しておき、 addSubview後にそれらの制約を追加すればよいです。

view以下にbuttonが配置されていたのを取ったりつけたりする場合です。

private var buttonConstraints = [NSLayoutConstraint]()

...

// removeFromSuperviewするとき
view.constraints.forEach {
    if $0.firstItem is UIButton || $0.secondItem is UIButton {
        self.buttonConstraints.append($0)
    }
}
button.removeFromSuperview()

...
// addSubviewするとき
view.addSubview(button)
buttonConstraints.forEach {
    view.addConstraint($0)
}

独自のNotification名を追加

独自のNotification名を追加する場合はこんな書き方がよいような気がします。

extension Notification.Name {
    struct AppName {
        public static let DidLogin = Notification.Name(rawValue: "com.example.appname.didLogin")
    }
}

通知名の文字列には通知名の衝突を避けるために独自のprefixをつけています。

com.example.appnameのところはBundle.main.bundleIdentifierでbundle identifierをとってきて 使ったりするのがよいかもしれません。

CircleCIでipaファイルを作ろうとするとExport Failedする

ローカルではarchiveからipaにexportできるのにCircleCIだとExport Failedになる場合の対処法です。

原因としてはCircleCIの環境変数が悪さをしているようで、以下のをunsetすればなおりました

unset BUNDLE_BIN_PATH
unset BUNDLE_GEMFILE
unset BUNDLE_ORIG_PATH
unset GEM_HOME
unset GEM_PATH
unset RUBYLIB
unset RUBYOPT

面倒な場合は、このxcode-safe.shを使うと良いです。

xcode-safe.sh

https://gist.github.com/claybridges/cea5d4afd24eda268164

Visual Studio Codeをemacs keybindingにする

Visual Studio Codeを使うときにEmacs Keybindingにする方法です。

Emacs Keymap(Emacs Keymap - Visual Studio Marketplace)というのが存在するので、これを使ってみます。

  1. Shift + Command + Xを押して拡張機能を開きます
  2. 検索窓でemacsといれるとEmacs Keymapというのがでてきますので、インストールを押せばOKです。

Macで出たエラー

MacC-x C-fでファイルを開こうとすると以下のようなエラーがでました

command 'workbench.action.files.openFolder' not found

↓を見た感じ、Macだと'openFileFolder'じゃなきゃアカン的なことが書いてありました・・

bind command(workbench.action.files.openFile) failed · Issue #5437 · Microsoft/vscode · GitHub

メニューから Code->基本設定->キーボード ショートカット を選択し、 以下のようにキーバインドを上書きするとなおりました。

// 既定値を上書きするには、このファイル内にキー バインドを挿入します
[
    {
        "key": "ctrl+x ctrl+f",
        "command": "workbench.action.files.openFileFolder"
    }
]