RoadMovie

write down memos or something I found about tech things

bundle installでmysql2(5.7)のインストールに失敗する問題

mac OS MojaveでRails開発中に gem install mysql2 がなかなか成功しなかったのでメモ。

Don't know how to set rpath on your system, if MySQL libraries are not in path mysql2 may not load
...
ld: library not found for -lssl

みたいなログが出てます。まずは、opensslがインストールされているか確認します。

$ brew install openssl

インストール済みであれば、あとはここへのpathをうまく通すだけです。ちなみに $ brew reinstall openssl をやると、最後の方に注意書きでこういうふうにexportしなさいという記述があります。

アプリroot directoryで、下記コマンドを実行し、

$ bundle config --local build.mysql2 "--with-cppflags=-I/usr/local/opt/openssl/include"

さらに、コンソール上で

$ export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/opt/openssl/lib/

最後に bundle install しましょう。


ちなみに、ldflagsとcppflagsのオプションを両方つけると下記のようなエラーが出てしまうので注意。

clang: error: unsupported option '--with-cppflags=-I/usr/local/opt/openssl/include'

現在使用中のオプションは、アプリroot directoryで下記のコマンドを打てば確認できます。

$ cat .bundle/config

Androidアプリを定期的に再起動してメモリをリセットする

C向けのサービスではあまりこういうことはしないと思いますが、B向けのサービスではこうした定期的にアプリを再起動するニーズがあるかもしれません。

アクティビティの再起動とアプリの再起動の二種類あるかと思うのですが、アクティビティ再起動はググればすぐ出てくるので、今回はアプリの再起動について書きます。これの利点としては、アプリ自体を一旦落として立ち上げ直すのでメモリがリセットされるということです。

さて、実際のコードはProcessPhoenixというライブラリを使えばいいだけなのでとても簡単です。 github.com

gradleで下記をインストール

implementation 'com.jakewharton:process-phoenix:2.0.0'

AndroidManifest.xmlに下記を追加

<!-- for Pheonix -->
<category android:name="android.intent.category.DEFAULT" />

あとは下記のコードをどこかに埋め込んだらリスタートできます。

ProcessPhoenix.triggerRebirth(context)


ついでに、こうした処理は定期的に行いたいと思うので、そのコードも紹介します。単純に数時間後とか数分おきにであればTimerを使ったりでできますが、私の要件ではcronで実行するような感じで深夜帯に一度再起動したかったので、android-jobというライブラリを使いました。 JobScheduler でもできるのですが、使い勝手がいいのと、彼らがブログで書いてるように学習コストが高いのでこちらを使っています。 github.com

こちらもgradleでインストール

implementation 'com.evernote:android-job:1.2.6'

JobCreatorを継承したクラスを作ります。ここでは MyJobCreator.kt とします。

import com.evernote.android.job.DailyJob
import com.evernote.android.job.JobCreator

 class MyJobCreator : JobCreator {
     override fun create(tag: String): DailyJob? {
        when (tag) {
            DailyJobScheduler.TAG -> return DailyJobScheduler()
            else -> return null
        }
    }
} 

Schedulerの実装体です。下記のような形で MyJobScheduler.kt を用意します。

import com.evernote.android.job.JobRequest
import com.evernote.android.job.DailyJob
import com.evernote.android.job.JobManager
import com.jakewharton.processphoenix.ProcessPhoenix
import java.util.concurrent.TimeUnit

 class DailyJobScheduler : DailyJob() {
     companion object {
        val TAG = "DailyJob"

         fun schedule() {
             // アプリ自体の再起動を行う関係で、Jobの実行が完了しないため、キューされた状態でアプリが起動されてしまいます。
             // そうすると、無限ループ的に再起動が繰り返されるので、キューに入れる前にすべてのキューをキャンセルして、
             // 実行されるジョブがひとつになることを担保しています。
            JobManager.instance().cancelAll()

             // https://github.com/evernote/android-job/wiki/FAQ
             // 午前3時に実行されます。(TAG, startTime, endTime)という引数のとり方をするので、
             // 3時~3時2秒まで実行可能にしています。ここに幅を持たせないと実行されないので注意。
            DailyJob.schedule(
                    JobRequest.Builder(TAG),
                    (TimeUnit.HOURS.toMillis(3)),
                    (TimeUnit.HOURS.toMillis(3) + TimeUnit.SECONDS.toMillis(2))
            )
        }
    }

     override fun onRunDailyJob(params: Params): DailyJobResult {
        ProcessPhoenix.triggerRebirth(context);  // 前述したアプリの再起動です。
        return DailyJobResult.SUCCESS;
    }
} 

そして、下記をApplicationを継承したクラスのonCreateなどで呼んでおきます。

JobManager.create(this).addJobCreator(MyJobCreator())
DailyJobScheduler.schedule()

これで午前3時に一度メモリリセットのためにアプリが自動で再起動されるようになりました。 いい感じですね。

AndroidアプリのonResumeとonPause

かなりラフな記事になるけど、Androidでアプリがforeground(起動中)の時とbackgroundにある時とでの挙動に手を加えたかったり、そうなるタイミングで何か処理を行いたいということがあると思う。

Lifecycleを調べると、onResumeonPuase がそれぞれforegroundとbackgroundに対応しているように思われるが、これは必ずしも100%保証された動作ではない。

これらの検知をどう正確に実現するのかは下記のStackOverflowが役に立つのでそちらに譲ることにする。

How to detect when an Android app goes to the background and come back to the foreground - Stack Overflow


私が手こずらされた原因になったのは下記。知っていればなんてことないが、あまりAndroidアプリの経験がないと困る可能性がある。

android - OnPause is called right after OnResume - Stack Overflow

Note: When the system calls your activity's onPause() method, the system may be signaling that the activity will be paused for a moment and the user may return focus to your activity, or that the app is running in multi-window mode. However, this method call may also be the first indication that the user is leaving your activity.

ここにあるように、ユーザーがactivityを離れるときにもonPauseは呼ばれる。要はあなたのアプリでactivityを複数使っている場合(多くはそうだと思われるが)、そのactivity間の遷移でもonPauseは呼ばれる。

Keep in mind that onResume is not the best indicator that your activity is visible to the user; a system window such as the keyguard may be in front. Use onWindowFocusChanged(boolean) to know for certain that your activity is visible to the user

また、上記もある特定のユースケース(私が関わっているアプリはそうだった)では面倒な事になるので、正確に onResumeonPause の挙動を理解する必要がある人は頭に入れておきたい内容。

Kotlinで知ってると便利なTips

もともとはRubyをメインで書いていたのですが、最近Kotlinを使うようになって便利だと思ったTipsをいくつか紹介します。

data class

Ref: https://kotlinlang.org/docs/reference/data-classes.html

何もしないけどデータだけ持たせたい時に使えるクラスです。equals, toStringなどいくつかの簡単なメソッドは使えるようにしてくれています。

sealed class

Ref: https://kotlinlang.org/docs/reference/sealed-classes.html

簡単に言うとenumの拡張型です。継承に制限があって、同じファイル内でしか継承できません。

例えばこんなふうに使っています。

sealed class Result { 
    data class Success(val a: A, val b: B): Result() 
    data class Failure(val a: A, val b: B): Result() 
} 

enumだとこのようにdataクラスを持つことができないので、便利に使えます。

custom getter(setter)

Ref: https://kotlinlang.org/docs/reference/properties.html#getters-and-setters

ちょっと上の2つとは毛色が違いますが、getter, setterのoverrideをこんな風にかけます。知ってると意外と便利です。

var stringRepresentation: String 
    get() = this.toString() 
    set(value) { 

        setDataFromString(value) // parses the string and assigns values to other properties 

    } 

また何かおもしろいTipsを見つけたら追記していきます。

Kotlinで新たに使えるようになったCoroutineで非同期処理

Coroutines Overview - Kotlin Programming Language

2019/01/16現在、まだexperimentalなのですが、使い勝手が良いので私が手伝っている会社でも取り入れ始めている、というより全面的にcoroutineで書き直しています。 基本的に launch の使い方と async, await の使い方を理解できれば良いだけなのでとっつきやすいと思います。

launch — This builder simply launches a new coroutine and returns a reference to it as a Job object which does not have a result. If you intend to block the current thread, instead of launch you may use runBlockingbuilder instead.

訳: 新しいcoroutineを作ってJob objectとしてそのcoroutineへのリファレンスを返してくれる。現在動かしているスレッドをブロックしたければrunBlockingbuilderを使うことで可能になる。

これだけで非同期処理ができるのでラクですよね。

async — This builder launches new coroutine and returns a reference to it as a Deferred type object which may have a result. It is usually used along with await which is a suspending function which can wait for a result without blocking the current thread.

訳: 新しいcoroutineを作ってDeferred typeとしてリファレンスを返す。awaitと併用されるのが通常。併用すると現在のスレッドをブロックせずに結果を待つことができる。


要はasyncを呼んでDeferredを受け取っておくと自分の好きなタイミングで後でawaitを呼ぶことで実行して結果を受け取れる、という感じです。この2つを使いこなすだけでいわゆるコールバック地獄に陥らずにすむので便利ですね!

redashでデータドリブンなチームを構築

こんにちは。

あなたの会社では分析ツールやダッシュボードなどを使っていますか?もしまだ使ってない、あるいは現状のツールに満足していないのであれば、redashが代案になるかもしれません。redashはとてもシンプルで簡単ですし、分析に必要十分なツールだといえるでしょう。この記事ではAWS EC2を使ったビルド方法を紹介します。



https://redash.io/

Make Your Company Data Driven Connect to any data source, easily visualize and share your data

お金を払えば一切何もしないでredashを利用することもできますが、あなたがエンジニアであれば(あるいはエンジニアに手伝ってもらえば)すべての機能を無料で使うことができます。

documentは下記になります。

https://redash.io/help-onpremise/setup/setting-up-redash-instance.html


Steps

1. EC2をredash AMIを使って起動

2. データソースに接続

3. SESを使ってEmailシステムを構築

First Step: EC2をredash AMIを使って起動

redashはビルド方法として2つ用意しています。ひとつはDockerを用いた方法で、もうひとつがAWSのAMIを使った方法です。 ここではAMIを使った方法を紹介します。

EC2のダッシュボードで、"Launch Instance"をクリックします。そして、左側の"Community AMIs"をクリック。次に、検索窓から先程のdocumentにあったAMIの名前を探します。インスタンスサイズはsmallで十分なようです。起動が完了するとpublic DNSでアクセス可能になります。もちろん独自ドメインの設定も可能です。


Second Step: データソースに接続

最初のステップはとても簡単でした。次のステップは少し難しくなるかもです。PostgreSQLをデータソースに選んだ場合の方法を示します。

難しいポイントはポート指定です。autosshを使うと比較的簡単に設定が可能になります。もしAWS RDSを使っていれば簡単に接続できますが、そうでない場合はこの手段を用いるのが良いでしょう。

ubuntuにautosshをインストールします。

$ sudo apt-get install autossh

コネクションの設定例です。

$ autossh -M 0 -f -N -L 127.0.0.1:15432:127.0.0.1:5432 ubuntu@example.com -p 5082

上記のケースでは,

1. ubuntu userとしてsshexample.comにport 5082で接続

2. example.comのport 127.0.0.1:5432をhost(autosshを使っている)のport 127.0.0.1:15432に接続

autosshのプロセスを確認しておきましょう。

ubuntu@redash-production:~$ ps ax | grep autossh
 5746 pts/0    S+     0:00 grep --color=auto autossh
14346 ?        Ss     0:00 /usr/lib/autossh/autossh -M 0    -N -L 127.0.0.1:15432:127.0.0.1:5432 ubuntu@example.com -p 5082

ところで、実際はredashユーザーを作成して、read-onlyの権限を与えて運用するのが良いでしょう。

Third Step: SESを使ってEmailシステムを構築

もしredashにログイン可能なユーザーを追加したければ、Email送信機能を構築する必要があります。これは、redashでユーザーを追加した際、登録したメールアドレスに確認URLが書かれたメールが届くからです。ここではAWSを使っているのでSESを利用するのが簡単で良いでしょう。

先程のEC2内の /opt/redash にredashの設定があります。ここに.envという隠しファイルがあるので、SESの設定をここに書いていきましょう。

下記は設定例です。

# .env

export REDASH_LOG_LEVEL="INFO"
export REDASH_REDIS_URL=redis://localhost:6379/0
export REDASH_DATABASE_URL="postgresql:///redash"
export REDASH_COOKIE_SECRET=SECRET_KEY // set by default

export REDASH_MAIL_SERVER="email-smtp.eu-west-1.amazonaws.com" # default: localhost
export REDASH_MAIL_PORT="587" # default: 25
export REDASH_MAIL_USE_TLS="true" # default: false
export REDASH_MAIL_USE_SSL="false" # default: false
export REDASH_MAIL_USERNAME="USERNAME_OF_SES" # default: None
export REDASH_MAIL_PASSWORD="PASSWORD_OF_SES" # default: None
export REDASH_MAIL_DEFAULT_SENDER="email@email.com" # Email address to send from

export REDASH_HOST="https://redashhost.com" # base address of your Redash instance, for example: "https://demo.redash.io"

SESのusernameとpasswordがわからなければ下記のdocumentをチェックしてみてください。 https://docs.aws.amazon.com/ses/latest/DeveloperGuide/smtp-credentials.html


これで完了になります。クエリを作成してダッシュボードを作ってみましょう。

Happy life with redash! :D


ReactNativeでプッシュ通知を実装する方法

この記事ではReactNativeでのプッシュ通知の実装方法を紹介したいと思います。


まず最初に全体の設計はこんな感じです。

Application

  • identifierをAPIサーバ(Backend)に送ります
  • AWS SNSから通知を受け取ります

Backend(ここでは例としてRailsを想定しています)

  • identifierをアプリから受け取ります
  • DBにidentifierを保存します ( endpoint_arn を保存しましょう)
  • endpoint_arn を登録して、トピックをサブスクライブします
  • いつ通知を送るのかをAWS SNSに教えます(cronなどでトリガーを用意するという意味です)

AWS SNS

  • endpoint_arn を保存します
  • トピックを作ります(ユーザーグループのようなものです)
  • 通知を送ります
    • 例えばAndroidアプリではFMC(firebase message cloud)を利用します

ライブラリ群

in ReactNative

アプリからidentifierを送る必要があります。ReactNativeではindex.android.jsindex.ios.jsに実装コードを書くのがよいのではないでしょうか。OS毎に設定が多少違うので、ここだとその差異を吸収しやすいと思います。

例としてスクリプトを載せておきます。

in Rails(Backend)

まず、aws-sdk-rails用にinitializerを用意するのが良いでしょう。


Controller内でidentifierを受け取るためのアクションを定義しておきましょう。もしそれが新規identifierであればAWS SNSに登録します。AWS SNSにリクエストを送るとendpoint_arnを受け取ることができます。DBに保存しておきましょう。


上記の Aws::Sns::DeviceTokenRegisterer.register(device_token) はサービス層に用意しています。endpoint_arn を登録するために呼んでいます。

ここでは、2つのトピックをendpointとしてサブスクライブしています。この記事の始めに書いた通り、トピックとはユーザーグループのようなものです。ここでは2つのトピックを作りました。ひとつはすべてのデバイスに対して、もうひとつはAndroidiOSのどちらかのみというものです。AWS SNSの設定に関してはAWSのdocumentを参照してみてください。


最後に、プッシュ通知イベントを発火します。下記、サンプルコードです。


これで全てです。それほど複雑ではないですよね。AWS SNSは便利ですし、比較的値段も安いです。ぜひ利用してみましょう。もし質問や意見、提案などございましたらぜひコメントに書いていただければと思います。

それでは。