俺、サービス売って家買うんだ

Swift, Kotlin, Vue.js, 統計, GCP / このペースで作ってればいつか2-3億で売れるのがポっと出来るんじゃなかろうか

async/awaitが手になじむまで色々書いてみるよ〜

f:id:ie-kau:20170930235521p:plain:w500


現在運用中のプロジェクトのNodeのバージョンを7から8にあげたのでPromiseで書いていた箇所を徐々にasync/awaitに置き換えています。

簡単に書ける一方でPromsieに慣れすぎていたため、「これ並列処理になるんだっけ」とか、「てか、これ動く?」見たいなレベルで手になじまなかったので色々ためして馴染もうと思います。

利用するタイマー関数

function timer(ms, name) {
  console.log(`name: ${name} start!`)
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(name), ms)
  })
}

Case.1

  • awaitを書いて非同期処理の関数を並べて書く
(async () => {
  const result1 = await timer(10000, 'a')
  const result2 = await timer(10000, 'b')
  console.log(result1, result2)
})()

// name: a start!
// name: b start!
// a b
// 経過時間: 20000ms

Case.2

  • awaitを書いて非同期処理の関数の戻り値でデストラクチャリング
(async () => {
  const [result1, result2] = [await timer(10000,'a'), await timer(10000, 'b')]
  console.log(result1, result2)
})()

// name: a start!
// name: b start!
// a b
// 経過時間: 20000ms

Case.3

  • Promise.allをawaitしてデストラクチャリング
(async () => {
  const [result1, result2] = await Promise.all([timer(10000, 'a'), timer(10000, 'b')])
  console.log(result1, result2)
})()

// name: a start!
// name: b start!
// a b
// 経過時間: 10000ms

Case.4

  • 先にpromiseを作ってデストラクチャリング
(async () => {
  const p1 = timer(10000, 'a')
  const p2 = timer(10000, 'b')
  const [result1, result2] = [await p1, await p2]
  console.log(result1, result2)
})()

// name: a start!
// name: b start!
// a b
// 経過時間: 10000ms

これは並列処理になる!

Case.5

  • for文の中でawaitさせる
(async () => {
    for (let {ms, name} of [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]) {
      const result = await timer(ms, name)
      console.log(result)
    }
})()

// name: a start!
// a
// name: b start!
// b
// 経過時間: 20000ms

直列処理になる。

Case.5

  • forEach内のラムダで実行する
(async () => {
  [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
    .forEach(async ({ms, name}) => {
      const result = await timer(ms, name)
      console.log(result)
    })
  console.log('c')

})()

// name: a start!
// name: b start!
// c
// a
// b

並列処理になるがcはラムダ内のawaitを待つことはない。

Case.6

  • mapの戻りでawaitさせてみる
(async () => {
  const results = [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
    .map(async ({ms, name}) =>  await timer(ms, name))
  console.log(results)
})()

// name: a start!
// name: b start!
// [ Promise { <pending> }, Promise { <pending> } ]
// 経過時間: (ほぼ)0ms

ただのpromiseの配列が帰ってくる。 当然だけど↓と同じ。

(async () => {
  const results = [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
    .map(({ms, name}) => timer(ms, name))
  console.log(results)

  // name: a start!
  // name: b start!
  // [ Promise { <pending> }, Promise { <pending> } ]
})()

Case.8

(async () => {
  [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
    .map(({ms, name}) => timer(ms, name))
    .forEach(async promise => {
      const result = await promise
      console.log(result)
    })
  console.log('c')
})()

// name: a start!
// name: b start!
// c
// a
// b

cは即時出力。timerは並列で実行されるが、forEachのラムダ内はawaitできる。

Case.9

  • promiseの配列をPromise.allの引数に与える
(async () => {
  const sample = [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
  const results = await Promise.all(sample.map(({ms, name}) => timer(ms, name))
  console.log(results)
})()

// name: a start!
// name: b start!
// [ 'a', 'b' ]
// 経過時間:10000ms

これが一番シンプルに並列処理の結果を直列に受け取れる気がする。

Case.10

  • mapに渡すラムダ内でasync/awaitをする
(async () => {
  const sample = [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
  const results = await Promise.all(sample.map(async ({ms, name}) => await timer(ms, name)))
  console.log(results)
})()

// name: a start!
// name: b start!
// [ 'a', 'b' ]
// 経過時間:10000ms

そもそもtimerもasync functionもpromiseを返すので二重定義になって意味ない。

完全に理解した!!!!!

GAEにデプロイしたRailsからGoogle Cloud SQL に疎通できない時に確認すること

f:id:ie-kau:20170806002855p:plain


概要

さてさて、去年 Google App Engine に Ruby の runtimeが追加されたことにより、GAE上でRailsを稼働させることが出来るようになりました。

ちょっと時間が空いてしまいましたが絶賛いじり中です。
今日は、GAEに上げたRailsからGoogle Cloud SQLに接続しようとした結果、少しハマってしまったので対処法のメモを残して置こうと思います。

本来の Google Cloud SQLへのつなぎ方

GAE上から Google Cloud SQL に繋ぐ設定は簡単です。 コンテナ上で mysql ソケットは以下のパスに展開されるので Rails の設定で Path を指すように記述します。

/cloudsql/コネクション名

app.yaml

GAE の設定

runtime: ruby
env: flex
entrypoint: bundle exec rackup -p 8080 -E production config.ru
env_variables:
  SECRET_KEY_BASE: 'xxxxxxxxxxxx'
beta_settings:
  cloud_sql_instances: [PROJECT_NAME]:[REGION_NAME]:[INSTANCE_NAME] # Cloud SQL のコネクション名

database.yml

Rails - MySQLの設定

production:
  <<: *default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: USER_NAME
  password: PASSSWORD
  database: DATABASE_NAME
  socket: /cloudsql/[PROJECT_NAME]:[REGION_NAME]:[INSTANCE_NAME] # Cloud SQL のコネクション名

しかし、この設定のみで通信できると考えていたですが、何故か疎通できませんでした。。。

GAE上のstderrログを見る

問題切り分けのためにログを見ます。 ログは Stackdriver に流されているので、検索条件を絞ることで閲覧可能です。

ドロワーメニューから Stackdriver を選んで Logging -> Logs

f:id:ie-kau:20170805194743p:plain

デフォルトでは以下のログが見れるようになっています。

  • stdout
  • stderr
  • nginx.request

f:id:ie-kau:20170805194902p:plain

何が悪かったか??

で、stderr を追っていくと・・・ Rails を起動する前にこんなエラーが・・・

payload: " googleapi: Error 403: Access Not Configured. Cloud SQL Administration API has not been used in project XXXXXX before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/sqladmin.googleapis.com/overview?project=XXXXXX then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry., accessNotConfigured

とのこと。どうやら、Cloud SQL の API を有効にする必要があるらしいです。ログに流れていたリンク先に飛ぶと API 設定画面が開くので、 disable の項目を enable にします。

f:id:ie-kau:20170806003101p:plain

これで疎通完了です。

まとめ

ローカルで cloud_sql_proxy を入れて使う場合には問題なく疎通できるのですが、GAE 上では API の設定を変更する必要があるようでした。
これで GAE / Ruby on Rails の環境が整いましたね。

UFW (Uncomplicated Firewall) を理解しておく

f:id:ie-kau:20170717174929p:plain


UFWとは?

コマンドラインで簡単に設定出来るfirewall。 Ubuntu 8.04 LTSより新しいバージョンならデフォルトでインストールされているので、Ubuntuを利用している場合はこれを利用してfirewallの設定をするのが一般的。

解説

例えば以下の設定があったとする。

$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22                         ALLOW IN    Anywhere
Anywhere                   DENY IN     192.168.1.1
80/tcp                     ALLOW IN    Anywhere
22 (v6)                    ALLOW IN    Anywhere (v6)
80/tcp (v6)                ALLOW IN    Anywhere (v6)

初期設定は、

  • 起動中
  • 内向きのパケットは全て禁止
  • 外向きのパケットは全て許可

フィルタリングルールは上から順に適応されているくので、

  • port 22(ssh目的)は許可
  • 192.168.1.1からのアクセスは禁止
  • port 80(http目的)は許可

という読み方になる。

よく使うコマンド

確認

ルール表示

sudo ufw status

ルール + ステータス

sudo ufw status

設定

ポートで許可 / 禁止

# sudo ufw allow <port>/<optional: protocol>
sudo ufw allow 80/tcp
sudo ufw deny 80/tcp

IP adressで許可 / 禁止

# sudo ufw allow from <ip address>
sudo ufw allow 192.168.1.1
sudo ufw deny  192.168.1.1

編集

連番付きルール表示

sudo ufw status numbered

特定の番号が付いたルールを消す

sudo ufw delete 4

番号指定でルールを追加する

sudo ufw insert 1 allow from <ip address>

ネットワークアドレスでフィルタリングする

sudo ufw deny  192.168.1.0/24

HOST部分を変えながらDOS攻撃してくる場合などの対応のためネットワークアドレスでフィルタリングすることも出来る。

To                         Action      From
--                         ------      ----
Anywhere                   DENY IN     192.168.1.0/24

※サブネットマスクで言うと255.255.255.0なので192.168.1.0〜192.168.1.255

設定手順

sudo ufw allow 22
sudo ufw enable
sudo ufw deny from 192.168.1.1
sudo ufw allow 80/tcp

22番ポートが閉じたまま接続が切れたらマジ厄介なので先に許可設定したまま ufw を enable にする。

参考

NodeでFCMを利用してAndroidにプッシュ通知を送る

f:id:ie-kau:20170628160920p:plain


すいません!すいません!
めちゃくちゃブログの更新滞っちゃいましたねorz

概要

サーバーサイドのNodeからFCM経由でAndroidアプリにプッシュ通知を送る際の実装メモです。

FCMとは?

Firebase Cloud Messagingのことで、Googleが提供するAndroidの通知サービスです。
これまでは Google Cloud Messaging (GCM)が一般的だったのですが、GoogleがFirebaseを買収したことにより、今後はFCMをメインで利用していくことになります。

事前準備

FCMの認証のための秘密鍵を取得します。

設定のサービスアカウントからいけます。 f:id:ie-kau:20170628161547p:plain

利用するライブラリ

自作するのは面倒なので、さすがに誰か作ってるだろーと思って探したらありました。

github.com

コード

初期設定

const fcm = require('fcm-node')
const fcmProvider = new fcm(require('../keys/privatekey.json'))

送信関数

function sendFcm(title, body, token, payload = {}) {
  const message = {
    to: token,   // register token

    notification: {
      title: title,  // プッシュ通知として表示されるタイトル
      body: body  // プッシュ通知本文
    },

    // アプリに送信するペイロード
    // Number型は送れないので文字列にしておく
    data: Object
        .keys(payload)
        .reduce((accum, current) => {
          accum[current] = String(payload[current])
          return accum
        }, {})
  }

  fcmProvider.send(message, (err, response) => {
    if (err) {
      logger.system.error(`sending fcm is failure : ${err}`)
    } else {
      logger.system.info(`sending fcm is success : ${response}`)
    }
  })
}

APNsにも送信するため個別で関数を用意しています。

ちなみに

Firebaseのコンソールからも通知を送信できるのでプッシュが届かない場合にアプリ側の問題か、サーバー側の問題か切り分けることが出来ます。

Have a happy Notification!

ES2015でどこまでクイックソートを短くかけるか

f:id:ie-kau:20151019153301j:plain


やってみた。

function quick(arr) {
  if (arr.length === 0) return []
  const [pivot, ...tail] = arr
  return [...quick(tail.filter( x => x <= pivot )),  pivot , ...quick(tail.filter( x => x > pivot ))]
}

const sorted = quick([8, 1, 10, 5, 4, 5, 1, 7, 9])
console.log(sorted) // [ 1, 1, 4, 5, 5, 7, 8, 9, 10 ]

実行環境

Node >= v7.x

ポイント

filterを一行で書く場合returnも {}もいらない

これmapだけだと思っていましたがfilterでも使えるようです。

tail.filter( x => { return x <= pivot } )
// ↓
tail.filter( x => x <= pivot)

スプレッド演算子大活躍

  • Haskellのパターンマッチでリストのheadとtailを分割する方法を似せて書こうとすることが出来る
    • デストラクチャリング + スプレッド演算子
 const [pivot, ...tail] = arr
  • 配列の結合はスプレッド演算子でやる
[...quick(tail.filter( x => x <= pivot )),  pivot , ...quick(tail.filter( x => x > pivot ))]

もっとエレガントに出来るところあるか?

関連書籍

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

Twitter APIで取得したプロフィール画像のサイズを変更する

f:id:ie-kau:20170418002246p:plain


Twitter認証でプロフィール画像を引っ張ってきた時にそのサイズを変更したいことって結構ありますよね。 なにも考えなければuserオブジェクトのprofile_image_urlというプロパティから48px x 48pxの画像が取得できます。

画像が小さい

ただ、48px x 48px というサイズなので、そのままユーザーアバターとして利用するには少し小さいサイズです。
画像のサイズを変更したい場合は画像URLサフィックス部分の _normal を _bigger にしてやることで73px x 73pxにすることが出来ます。

これを https://pbs.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_normal.png https://pbs.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_normal.png

こうする https://pbs.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_bigger.png https://pbs.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_bigger.png

APIにパラメータを渡してサイズを変更できるようなものでもないので、今のところ文字列置換で対応する他ないようです。

プロフィール画像を設定してない場合

ユーザーがプロフィール画像を設定してない場合はnullが返ってきて、アプリケーション側で面倒を見てあげないとエラーになっちゃわないかと心配だったのですが、どうやらtwitter側でデフォルトの画像を用意してくれているようです。

これ↓

f:id:ie-kau:20170417235018p:plain

当然こちらも _normal , _biggerのサフィックスをつければサイズを変更することが可能です。

参考

詳細ドキュメントでは、より小さいサイズ(_mini)やオリジナルの画像を取得する方法も書いてありますので是非ご確認あれ。

1時間で終わる、圧倒的「確定申告」の方法

f:id:ie-kau:20170411113715p:plain


10代の頃からウェブサイトの広告収益をちまちま貰っては、確定申告で地獄を見てきたHayatoです。 今回は、最近よく聞かれる確定申告の流れをまとめておこうと思います。

サラリーマンとして普通に働きつつ副業で収入を得ている人が初めての確定申告をするところを想定して書いてます。

Index

  • 確定申告とは何か?
  • 対象者は?
  • 年間スケジュール
  • やっておくと便利な下準備
  • 経費の面倒じゃない集め方
  • 確定申告書を作って提出しよう
  • よく聞かれるQ&A

確定申告とは

ざっくりいうと、どれくらい収入があったかを国に伝えることです。 サラリーマンの場合は会社がやってくれるのですが、自分で稼いだ場合は自分で申告する必要があります。
稼ぐのに必要だった経費(交通費・交際費など)を稼いだ額(売上)から引くことで、収入を計算します。

確定申告の対象者は?

年間20万円以上の収益が、勤めている会社以外からある人は全員確定申告の義務があります。年間500万円を超えたあたりくらいからは税理士にお願いした方がいいような気もしますが、それより少なければ自分でやっても問題ないと思います。

確定申告の年間スケジュール

確定申告は2月15 - 3月15日の間に、税務署に収支を証明する確定申告書を提出することで完了します。期間は前年の1-12月の1年間です。 基本的なサイクルはわりかし簡単で僕の場合は、

  • 1~12月に5000円以上の領収書をとっておく(適宜)
  • 2月にマネーフォワードで帳簿を作成して提出する(1時間くらい)

と言った感じです。

確定申告前の下準備

確定申告が初めての方は、下準備から始めましょう。これをやっておけば2月の帳簿作成が1時間で終わりますが、やっておかないと100時間かかるか税理士にお願いするハメになります。

1.青色申告申請書を税務署に提出

税務署に事前に承認を受けないと青色申告はできません。
といっても5分で書き終わる書類を、自分が住んでいる地域の税務署に提出するだけです。国税庁のHPからPDFをダウンロードして、記入すればOKです。
半休を使ってなるべく早めに申請しておきましょう。

2. 専用の口座とクレジットカードを用意しておく

プライベートの口座・クレジットカードを使うのもよいのですが、分けておくと帳簿を作る時に便利です。
最近は会計ソフトの無料化&便利化しているので、半自動で帳簿や確定申告書を作成できます。が、プライベートの口座と混ざっていると仕分けが非常に面倒なので、出来れば専用の口座とクレジットカードを用意しておくと便利です。
僕はANAのSuicaオートチャージ付きクレジットカードと、SBI銀行を使っています。(後述する旅費交通費を仕分けする時に楽するため)

3. 無料で使えるMFクラウドにアカウント登録しておく

マネーフォワードクラウドを使えば自動で確定申告書と帳簿をつけてくれます。無料で提出書類まで作成できるのでおすすめです。 アカウント登録したら、上記で作成した口座とクレジットカードを紐付けておけば、あとは半自動で帳簿作成が完了します。1時間くらいで終わるので、放置しておいて申告期間中に帳簿を作成しましょう。 *ちなみにFreeeは個人事業主の場合あんまり使えない感じです。

経費の面倒じゃない集め方

最初は経費をどのように申請したり集めればいいか、どのように仕分けしてよいかわからないと思います。そんなときは、細かく全部の領収書をとっておくよりも、5000円以上の領収書だけ取っておく、とすると仕分け時の手間が激減します。 また、実際に経費として扱うのは

  1. 旅費交通費:電車賃とかタクシーとか飛行機とか
  2. 交際費:会食などから発生する飲食店での費用
  3. 図書新聞費:Amazonとかで買った本とか

の3つくらいに絞るのが良いかと思います。ウェブサイトを運営しているときのサーバー代やドメイン代は、少額で面倒なので経費申請してないです。 また、 専用のクレジットカードで支払うことを徹底すると、後の作業がめちゃめちゃ楽になります。Suicaのオートチャージを使えばもっと楽になります。 ちなみに一つ一つの経費に理由を書いたりとかはしなくて大丈夫ですw(なぜかこれ結構聞かれる)

毎年2月に確定申告しよう

さあ、1年間ためてきた領収書(レシート)の出番ですね。マネーフォワードで確定申告書と帳簿を作成しましょう。

1. 源泉徴収票を用意する

確定申告時は会社からの給与+副収入を足して、合計収入を税務署に報告します。「会社からの給与」を証明するのに、毎年12月にもらう源泉徴収票を用意しましょう。なくしたら会社に再発行してもらえます。

2. クレカと口座の履歴を仕分けする

口座の履歴は主に収入かと思います。こちらを「事業所得」に振り分けましょう。 クレカの方は主に支出可と思います。こちらを「旅費交通費・交際費・図書新聞」に振り分けていきましょう。 この「振り分け」はやる前にはピンとこないですが、ラベルを付ける程度なので非常に簡単です。マネーフォワードに感謝しましょう。

3. 税務署に行って提出する

仕分けが終わったら、マネーフォワードから確定申告書類をPDFでダウンロードしましょう。そちらを印刷して、源泉徴収票と一緒に税務署に持っていけば完了です。半休の申請を忘れずに。

以上です。割りと雑に確定申告をすること、出来るだけ帳簿作成を自動化すること、が楽するコツかと思います。
売上をごまかすのは犯罪ですが、微妙に経費かどうかわからないものや、経費なんだけど金額が大したことないものを自腹にしちゃうことでだいぶ楽になります。
あとは、マネーフォワード&専用クレカ&口座は割りと完璧なコンボだと自画自賛ですね。

よく聞かれるQ&A

青色申告が良いの?白色申告が良いの?

今や手間はあんま変わらないので青色申告しましょう。

家賃とか光熱費やインターネット費用を経費にできる?

できます!しかし100%ってのはなかなかイメージわかないので20%くらいで申請するのが良いのではないでしょうか。(例:家賃10万円だったら、2万円*12ヶ月)。しかし「旅費交通費・交際費・図書新聞」で賄えるのであればそれに越したことはないかと思います。

確定申告したらお金を払わなきゃいけないの?

場合によります。極端な話、売上 - 経費がマイナスだった場合は払う必要はないですし、割と個人事業主の人はそういう努力をしているイメージがあります(あくまでイメージ)

会社にバレない確定申告方法

残念ながら絶対バレない、というのは無いです。が、確定申告時に住民税の徴収方法の選択という項目があり、「給与から天引き」または「自分で納付」を選択できます。「自分を納付」を選択することで、少なくとも住民税の増減による会社バレを防ぐことはできます。確定申告時に忘れずに「自分で納付」を選択しましょう。

マネーフォワードの回し者ですか?

いえ、違います。

オラッ!オラオラオラ!