Akata Works

東京エンジニア。主にRuby,Go,たまにAWSとiOS。ゲーム音楽が好きです。連絡はTwitterかakata.onen@gmail.comまで

NodeJSとnodejsコマンド・nodeコマンドの違い

前々からちょこちょこ気になってたことだったので調べてみた。

RubyイメージのDockerコンテナ内とかUbuntuでNodeJSを使おうと思って調べると、すぐにapt-get install nodejsに辿り着くんですが、このコマンドでインストールされたバイナリはnodeじゃなくてnodejsになるのでいろんな所でちょっとメンドイ。例えばwebpacker実行したら/usr/bin/env: ‘node’: No such file or directoryとなる。

ということで調べてみたところ、こちらもすぐに辿り着いた。

There is a naming conflict with the node package (Amateur Packet Radio Node Program), and the nodejs binary has been renamed from node to nodejs. You'll need to symlink /usr/bin/node to /usr/bin/nodejs or you could uninstall the Amateur Packet Radio Node Program to avoid that conflict.

要するに単に名前が被ってたからnode使えなかったってことみたい。iPhoneのカタカナ表記が「アイフォーン」になってるノリですね。

手元にあったUbuntu 16.04.1 LTSサーバにもRubyイメージにもnodeコマンドは入ってなかったので単純にリンクを通せば大丈夫です。

sudo ln -s `which nodejs` /usr/bin/node

備考

Dockerならrubyとnodeでコンテナ分けたら解決するし、責務も明確になるしその方がいいかも。
webpacker使うならrailsに依存するから、コンテナ分けずにmulti-stage buildした方がいいかも。

参考

DockerHubにあるイメージのタグリストを出力するスクリプト

DockerHubにあるイメージのタグを検索するだけのスクリプト。docker searchだとそこまで分からないから便利。

curl -s -S "https://registry.hub.docker.com/v1/repositories/#{image_name}/tags" | jq '.[]["name"]'

jqコマンドに依存するので、便利だからこの際入れよう。

関数化してbashrc(zshrc)に追加していればもっと便利。

function docker-search-tags() {
  curl -s -S "https://registry.hub.docker.com/v1/repositories/$1/tags" | jq '.[]["name"]'
}
$ docker-search-tags ruby | head
"latest"
"1"
"1-onbuild"
"1-slim"
"1-wheezy"
"1.9"
"1.9-onbuild"
"1.9-slim"
"1.9-wheezy"
"1.9.3"

短いけど終わり。

スクリプトからgsutilを使用するときの認証などのポイント

はじめに

RubyからGoogle Cloud StorageのAPIを叩くにはgoogle-cloud-storage gemを使うのが一般的なので、この記事はどうしてもスクリプトからgsutilを実行したい人向けです。

今回はGoogle Cloud SDKの一部としてgsutilをインストールした前提で書いていますが、スタンドアローンのgsutilを使用している方も同様に処理できると思います。

gsutilのインストールがまだな人はここからどうぞ!(Pythonバージョンには注意ね)

認証

gcloudだと別々の認証情報を使ったプロセスが並列したときに問題になりますし、.botoファイルだと細かい制御が難しいのでトップレベルの-oオプションを使うといいです。

-oオプション使用することで.botoで設定できるような項目をコマンド単位で上書きできます。

gsutil -o 'Credentials:gs_service_key_file=etc/credential.json' cp gs://hoge.huga/foo/bar .

その他

並列処理させる場合は、-mオプションに加えて、GSUtil:parallel_process_count,GSUtil:parallel_thread_countを上書きしてください。

gsutil -m -o 'Credentials:gs_service_key_file=etc/credential.json' -o 'GSUtil:parallel_process_count=8' -o 'GSUtil:parallel_thread_count=8' cp gs://hoge.huga/foo/bar .

-qオプションを指定することでアップロード中などのプログレスなどを非表示にできます。crontabなどで実行するときはこれも付けるといいです。

gsutil -q -o 'Credentials:gs_service_key_file=etc/credential.json' cp gs://hoge.huga/foo/bar .

探せばいろいろありますがgcsはまだまだawsに比べて日本語情報が少ないですね。

参考

Top-Level Command-Line Options  |  Cloud Storage Documentation  |  Google Cloud

Railsでオンメモリにキャッシュするとハマるので注意

Railsのフラグメントキャッシュでオンメモリを指定してたら、外部ミドルウェアを使うのと挙動が違ってハマったのでメモ。
開発環境デフォルトのオンメモリは準備が楽ですが、本番でDalliとか使ってると混乱するので統一しておきたいですね。

検証

恐らく最もよく使われると思われるRails.cache.fetchの結果をそれぞれ見てみます。

Dalli(Memcached)

設定値config.cache_store = :mem_cache_store

[1] pry(main)> @tmp = Rails.cache.fetch('hoge'){puts 'no cache'; {hoge: 'huga'}}
Dalli::Server#connect localhost:11211
no cache
=> {:hoge=>"huga"}
[2] pry(main)> @tmp[:foo] = 'bar'
=> "bar"
[3] pry(main)> @tmp = Rails.cache.fetch('hoge'){puts 'no cache'; {hoge: 'huga'}}
=> {:hoge=>"huga"}
[4] pry(main)> @tmp[:foo] = 'bar'
=> "bar"
[5] pry(main)> @tmp = Rails.cache.fetch('hoge'){puts 'no cache'; {hoge: 'huga'}}
=> {:hoge=>"huga"}

on Memory

設定値config.cache_store = :memory_store

[1] pry(main)> @tmp = Rails.cache.fetch('hoge'){puts 'no cache'; {hoge: 'huga'}}
no cache
=> {:hoge=>"huga"}
[2] pry(main)> @tmp[:foo] = 'bar'
=> "bar"
[3] pry(main)> @tmp = Rails.cache.fetch('hoge'){puts 'no cache'; {hoge: 'huga'}}
=> {:hoge=>"huga"}
[4] pry(main)> @tmp[:foo] = 'bar'
=> "bar"
[5] pry(main)> @tmp = Rails.cache.fetch('hoge'){puts 'no cache'; {hoge: 'huga'}}
=> {:hoge=>"huga", :foo=>"bar"}

このようにオンメモリではキャッシュされた値に、その後の変更が適応されています。

原因

恐らく、オンメモリだとrailsプロセスと同一のメモリ空間に保存されるため、ポインタのその後の変更も適応されているんだと思います。三回目の呼び出しまで適応されない理由はわかりませんが......(ドキュメントとかコードちゃんと読んだ人教えて!)

結論

あんまりオンメモリストア使うもんじゃないね。

Deployment TargetとBase SDKと互換性について

XcodeのDeployment TargetとBase SDKがややこしかったのでまとめてみました。なるべくわかりやすく書いたつもりです。「間違っているぞー」とい点があればご指摘いただけると幸いです。

前提

そもそもの話として、Appleは各種OS(Mac OSiOS)とそのバージョンごとに異なるSDKを用意しており、それぞれの端末には対応するSDKが事前に組み込まれています。これにより以下のようなメリットがあります。

  • 同一実行ファイルでありながら、機能が組み込まれていればそれを活用し、組み込まれていくてもそれなりに対処できる
  • Base SDKからある程度の上位・下位バージョンへの互換性が担保できる

で、この「組み込まれているか、組み込まれていないか」を判断する必要があるSDKの範囲を決めるのがDeployment TargetとBase SDKです。

Deployment Target(配布ターゲット)

開発者がソフトウェアの動作を保証する最も古いSDKのバージョンを表します。つまり、これ以上のSDK上では動作を保証する必要があるけど、これより古いSDK上ではクラッシュしても知りませんってことです。

動作を保証する必要がないため、これ以下のバージョンで実装された機能にはがっつりと強リンク(機能がなければクラッシュする)が張られます。

Base SDK

ソフトウェアのコンパイル時に基準とするSDKのバージョンを表します。つまり、これより新しいSDK上で定義された機能は使えません(コンパイルエラー)。

また、このバージョンで廃止予定とされている機能はこれ以降のバージョンでは存在しない可能性があるためさっさと移行したほうがいいです。多分警告が出ると思います。

廃止予定の機能さえ使用していなければ上位互換性はほぼ保証されます。なぜなら、コンパイル時により新しいSDKで定義された機能は使ってないし、廃止予定でない機能が急に消されることはない(はず)だからです。

Deployment Target〜Base SDK

前項までで、Deployment Target以前の機能は気にする必要がないので無視して問題がないこと、Base SDK以上の機能は廃止予定のものを使っていなければほぼ安全であるということがわかりました。

ただ、これらのバージョン間で実装(変更)された機能に関してはユーザの端末に定義されているとは限りません。メソッドが追加されたり、挙動が変更されたりといろいろ考えられます。

故に、これらの機能にはソースコード上で動的に変更が可能なように弱リンク(機能がなければヌルポになる)が張られ、機能が定義されているか事前に確認する必要があります。

具体的な実装方法に関しては省きますが、参考URLにあるAppleの公式ドキュメントが一番まとまっていると思います。

まとめ

まとめると、このあたりをきちんと守っていれば互換性に関しては問題ないと思います。

f:id:akatakun:20180614194429p:plain (参考URL: Appleの公式ドキュメントから)

  • Deployment Target以前: 動作を保証をしない
  • Deployment Target以上〜Base SDK以下: 動作を保証する。追加・変更された機能はフレームワーク読み込み設定やソースコードで順応させる必要がある
  • Base SDK以降: 動作を保証する。廃止予定の機能は極力使わない

おわり。

参考URL

https://developer.apple.com/jp/documentation/cross_development.pdf

Twitter OAuthが403 Forbiddenを返すようになったので対応した

2018/06/13夕方頃からコードを変更していないのに急にTwitter OAuth APIが403 Forbiddenを返すようになりました。Twitterにて同じような報告を確認し、調べてみたところ、結構前から言われてたみたいですが、いやー気づかなかったっすね(^ ^ ;)

コールバックロックを有効にしていなくてもCallback URLsに設定済みのcallback_urlパラメータ以外はエラー返すよってことらしいです。変更理由としてはセキュリティのためとか。Enable Callback Lockingの設定項目はそのうち消えるらしいです。

対応策

対応は簡単で、https://apps.twitter.com/のアプリSettingsのCallback URLsに認証時に使うcallback_urlパラメータを設定してやればいいです。

f:id:akatakun:20180614115540p:plain

ちなみに、最低二つ以上設定する必要があります(ちょっと理由はわからなかったです)

f:id:akatakun:20180614115413p:plain

(はじめは出なかったが一度Callback URLsを二つ指定してからはこのエラーが出るようにりました)

また開発環境でテストする際、localhostのCallback URLは指定できませんが、127.0.0.1は指定できます。同様にhttp://localhost:3000でアクセスしたら403エラーが返ってくるのでhttp://127.0.0.1:3000でアクセスする必要があります。


いろいろ見てると「できない」という声が結構ありましたが、僕の場合はこれでいけたのでご参考になれば幸いです。

補足

ワイルドカードは使えない

Callback URLsにはワイルドカードが使えないっぽいので、動的URLを指定することはできないです。その場合は下記みたいにCallback用のページを用意してCookieとか使ってリダイレクトさせればいいんじゃないでしょうか。

なんでワイルドカード指定できねえんだよっ!調べたらちゃんと理由ありました。

参考

CocoaPodsクイックリメンバー用【ほぼメモ】

About CocoaPods

iOSのライブラリ管理ツール。他言語のツールだとBundler(Ruby)とかYarn(JS)とかが近い

How to Start

Rubyで動作するためGemからインストール

gem install cocoapods

Podfileを作成

pod setup

Commands

Podfile.lock > Podfileの優先順位でライブラリを追加(更新)。lockを見るので追加済みのものはバージョン固定。ライブラリ追加時はこれ

pod install

Podfileでライブラリを追加(更新)。lockを見ないのでバージョン変動。アップデート時はこれ

pod update ${lib_name} # 特定のライブラリに限定
pod update # 怖い

インストールバージョンと最新バージョンを調べる。updateしても最新バージョンにならないときは大体Podfileのplatform制限のせい

pod outdated

Podfile

platform :ios, '8'

target :testTests do
  pod 'Kiwi', '>=2.3.0' # Bug Fix https://github.com/kiwi-bdd/Kiwi/pull/542
  pod 'OCMock'
end

target: ライブラリをビルドする際のDeployment target(最低SDK Version)とかアーキテクチャを指定