| @技術/プログラミング

DSC_4022.jpeg

Amazon Product Advertising API ( PA API )が 5.0 になるらしい。 4.0 は 3 月で廃止になるそうだ(当初は 2 月 11 日と言われていたが、 3 月まで伸びたみたい)。

最近 4.0 に対応させたのにな、と思って調べてみたら何と 4.0 対応したのは 10 年以上前だった。

PA API 4.0 までは AWS アカウントで利用する感じ1だったが、 PA API 5.0 では AWS から独立して Product Advertising API 専用のアカウントを登録しなければならないようだ。

AWS は US Amazon でアカウントを作るのに PA API のレポート画面へのログインでは Amazon Japan のアカウントを使うのは変だなと思っていたけど、 PA API 専用アカウントを設けることでその辺のねじれも解消されるだろう。

クライアントライブラリにはこれまで ecs という gem を使ってきたが、 PA API 5.0 対応はされてなくて、別の人が作った vacuum という gem に乗り換えた。

ecs という gem の名前に違和感をもつ人がいるかもしれない。いまでは Amazon で ECS といえば AWS の ECS ( Elastic Container Service ) のことを指すが、昔は Amazon 自身が PA API という名前ではなく Amazon ECS という名前でアフィリエイト用のシステムを提供していた。 Amazon は命名が色々紛らわしい。

PA API 5.0 は RESTful API ではなく GraphQL のような感じで、欲しいフィールド名を指定して API リクエストする感じになっている。レスポンスのサイズが小さくなって便利になった。

ちなみに移行ガイドは以下にあります。

DSC_4119.jpeg


  1. 10 年前にペパボの面接を受けに行ったときに「 AWS 使えますか?」と聞かれて「はい、使えます。アフィリエイトで小銭を稼いでいます」と答えてしまった。 

| @ブログ

background-image random pick up

写真や画像があると文章だけよりも記憶に定着しやすいらしい。なのでヘッダー画像のバリエーションを増やしてみることにした。これまで自分が撮ってきた写真の中で気に入ってるもの、ヘッダー画像にちょうど良さそうなやつを 14 枚選んで、これまで表示していた門司の「平民食堂」のやつと合わせて 15 枚をランダムピックアップして表示させている。本当ならランダムではなく記事ごとにイメージに合う画像を選定すべきなのだろうけどそこまではやってない。「このブログ主はこういう写真の場所に行ったりこういう写真をいいと思ってるんだな」ということが伝われば良いと思ってる。

ランダム表示に関して、最初は JavaScript で CSS の background-image を書き換える方式でやっていたが、 DOM の読み込み完了時に動く都合上、かくつきが出てしまう。それで画像の選定は Ruby で行って data-attribute として指定し、予め CSS にそれぞれの data-attribute に応じた background-image を書いておくことにした。これでかくつきはほぼほぼなくなった。

<div id="header" data-background-image="heimin">
</div>
#header[data-background-image="heimin"] {
  background-image: url(header-bg-heimin.jpg);
}

このほかにも CSS 周りを結構いじってて、パンくずリストが載る部分は文字が読みにくくなるのでグラデーションでシャドーを掛けてる。これまで float でやってた配置の制御を flexbox に置き換えたりもやった。今のデザインのベースは 10 年前に作ったので、当時はグラデーションなんかは CSS で実現できず横幅 1px の画像をリピートする方法を使ったが、これで画像を捨てて全て CSS で実現できそう。良い世の中になってきてる。

| @技術/プログラミング

lokka/lokka 、 Pull Request を出す度に Hound CI のチェックが走って bot にコードレビューでぼこぼこにされるので、この bot を黙らせるべくガチャガチャやってた。 Hound CI のチェックルールは Rubocop に準拠しているようで、 2011 年からある Rack アプリを Rubocop のチェックにかけるのは面白かった。

Lokka 、意外と Hacky なコードが多く、条件式内での代入とか、ヨーダ記法とか、後置の until とか、スコープが広い一文字変数とか、めっちゃ長いメソッドとか、 if 文のネスト、代入したものの使われてない変数なんかを修正した。 method_missing はカスタムフィールドを定義できるという Lokka の仕様上根絶できなかったけど、 .rubocop.yml に最低限の除外ルールを追加して Rubocop のチェックはパスするようにできた。

Lokka 、 ORM が ActiveRecord じゃないことが問題だと思ってたけど、真の問題は lib/lokka/helpers/{helers,render_helper}.rb にビジネスロジックが詰め込まれてることだと思った。しかもこのあたりのコードの可読性がよくなく、触るのが怖い感じの複雑なやつが多い。この辺のコードをもうちょいクラス化して分割し、ユニットテストも手厚くしていかないと ORM を変えても F/E を今風にしてもウェブアプリケーションとして生存していくことは厳しいと思う。

前に進んでいくためにも Rubocop のチェックを入れる&パスさせるのはプラスになると思う。 頑張ってメンテしていくぞ。

追記

この辺のコードをもうちょいクラス化して分割し

と書いたけど、 Rails と違って手軽にサクッと作れるのが Sinatra の良い所なわけではあって、仕事で作る Rails アプリのノリでクラスやファイルを分割したりするのは違うのかもしれないと思った。 Rails で作られたオープソースの CMS やブログツールに長生きし続けるものがないのも、 Rails の場合、個人が偶発的に始めてメンテ出来るようなものになりにくいからかも知れない。

とはいえヘルパーがビジネスロジックを所持しているのはテスタビリティやメンテナビリティが良くないので Lokka と心中する覚悟でやっていくぞ!!!、!

| @技術/プログラミング

Pinboard にブックマークしたらはてなブックマークに同期するやつを作った。

なんで Pinboard を使うのか、どうしてはてブに一本化しないのかというと、洋物のサービスを使うときに Pinboard の方が使いやすいから。 IFTTT に Pinboard 連携機能はあってもはてブ連携機能はないし、 Delibar や Reeder や ReadKit も Pinboard には対応しているけどはてブには対応してない。あと Pinboard は広告出ないしホッテントリ的なものもないので気がついたら Amazon で買い物してたとかホッテントリの海に溺れてた、ということも起こらない。とはいえはてブのコメントでキャッキャッウフフはしたい。なのでブックマークするときは両方にしたい。パソコンの Chrome からブックマークするときは Taberareloo でクロスポストできるのだけど、最近 iPhone からブックマークすることが増えて(iPhone 進化し過ぎて仕事するときしかパホコン使わなくなった) Pinboard とはてブにそれぞれブックマークするのがだるかった。そういうわけで Pinboard をメインにしつつはてブに同期を試みた。

はてなスタッフの aereal さんが作ってるはてブ API 用の gem (aereal/hatena-bookmark-restful: A client library for Hatena::Bookmark RESTful API)あって使わせてもらったのだけど、これはそのままだと使えない。タグが複数あるケースに対応してない& User-Agent を送らないので API 側から 401 Unauthorized が返ってくる。なので雑にモンキーパッチした。

はてなブックマークの API は tag を 10 個まで設定できるけど、以下のように Request Body が Encode されるのを期待しているっぽい。

"comment=&tags=ruby&tags=http&url=https%3A%2F%2Fgithub.com%2Flostisland%2Ffaraday"

この様な Request パラメーターを Ruby で表現すると以下のような Hash になると思う。

params = {
  comment: '',
  tags: ['ruby', 'http'],
  url: 'https://github.com/lostisland/faraday'
}

Hash は当然のことながら同じキーを複数持つことはできないから、 tags は配列として表現される。これをこのまま Faraday (Ruby の HTTP クライアント。上述の gem でも使われてる)に渡して Request Body を生成するとエラーになってしまうのだった。

最近の Faraday には encode option が追加されて FlatParamsEncoder というのを選べるようになってた。こいつを使うと配列を value に持つ Hash をシリアライズしたときに tags=foo&tags=bar みたいな形式にしてくれる。加えて User-Agent も載せるようにもした。こんな感じ。

class Hatena::Bookmark::Restful::V1
  def create_bookmark(bookmark_params)
    res = connection.post("/#{api_version}/my/bookmark") {|req|
      req.params = bookmark_params
    }
    attrs = JSON.parse(res.body)
    bookmark = Bookmark.new_from_response(attrs)
  end

  private

  def connection
    @connection ||= Faraday.new(url: 'http://api.b.hatena.ne.jp/') do |conn|
      conn.request :url_encoded
      conn.options.params_encoder = Faraday::FlatParamsEncoder
      conn.request     :oauth, {
        consumer_key:    @credentials.consumer_key,
        consumer_secret: @credentials.consumer_secret,
        token:           @credentials.access_token,
        token_secret:    @credentials.access_token_secret
      }
      conn.headers['User-Agent'] = 'Hatena::Bookmark::Restful Client'
      conn.adapter Faraday.default_adapter
    end
  end
end

なおはてブの API はリクエストパラメーターが不正なとき 400 Bad Request を返すのではなく 401 Unauthorized を返す。のみならず User-Agent なしのリクエストに対しても 401 を返す。一方で OAuth ヘッダーが不正なときは 400 を返す。原因の切り分けがむずかしくなるので、リクエストパラメータが不正なときは 400 を返して欲しいし認証できないときは 401 を返して欲しい。

追記 2017-05-22 18:34:38

作者の aereal さんに気づいてもらってパッチを取り込んでもらったんだけど、なんかやっぱりタグありの記事をブックマークしようとするとエラーになるっぽい。相変わらず 401 Unauthorized が返ってくる…。

| @技術/プログラミング

Archives ページは部分的に React.js を使ってる。これを全部 React にして SPA 化してみたい。そのためには Server Side Rendering が必要。

Ruby で Server Side Rendering するライブラリでは react-rails が有名だけど、 Rails 以外で Server Side Rendering したい。

有名なやつでは airbnb の hypernova というのがあった。これ自体は Node.js で、 hypernova-ruby というクライアントからサーバーを呼び出して Server Side Rendering するというもの。なのでサーバー側に一個プロセスが増える。開発環境でも Node.js でプロセス起動したりしないといけない。仕事で Microservice には食傷気味になってきてるので趣味では Majestic Monolith™ で行きたい。

名前似てるけど Hyper-Ract というやつもあった。これは過激で JavaScript とか HTML を排除して Ruby で全部書こうぜという Opal が React 対応したもの。良いんだけど F/E の進化早すぎ問題に対応できなくなる気がする。数年後には CoffeeScript みたいになりそうな予感がある。スタンダードでシンプルなものを使いたい。

もう一個、スター全然付いてないしメンテナンスもされてなさそうなんだけど、 V8 で React 読み込んで出力するだけという簡単なのもあった。react-ruby-v8 という直球な名前。こんなので十分かもしれない。

| @技術/プログラミング

BitBar で私家版通勤タイマー (1) - portal shit! の続き。

JSON から情報を読み取って出力する奴は Ruby で書こうかなと思っていたんだけど、先日新しい上司と 1 on 1 をしていてどんな言語を使えるかの話になったときに Ruby と PHP とコールドフュ〜ジョン(と片手間 JavaScript )くらいしか使える言語がないということが判明してダメだと思ったので無理矢理 Go 言語で書いてみた。クソコードなので恥ずかしい。

package main

import (
    "encoding/json"
    "fmt"
    "os"
    "strconv"
    "strings"
    "time"
)

type Hour struct {
    Hour    string
    Minutes []string
}

func printLefts(m int, minutes []string, hour string) {
    for i := 0; i < len(minutes); i++ {
        mindes := strings.Split(minutes[i], " ")
        min := mindes[0]
        var Dest string
        if len(mindes) > 1 {
            Dest = mindes[1]
        }
        _m, _ := strconv.Atoi(min)
        left := _m - m
        if left > 0 && left < 30 {
            leftS := strconv.Itoa(left)
            takeM := fmt.Sprintf("%02d", _m)
            Str := leftS + " minutes left to " + hour + ":" + takeM
            if Dest != "" {
                Str = Str + " " + Dest
            }
            fmt.Println(Str)
        }
    }
}

func main() {
    decoder := json.NewDecoder(os.Stdin)
    timetable := make([]Hour, 0)
    decoder.Decode(&timetable)
    t := time.Now()
    h := t.Hour()
    m := t.Minute()
    var CurrentHour, NextHour Hour
    for i := 0; i < len(timetable); i++ {
        hour := timetable[i]
        _h, _ := strconv.Atoi(hour.Hour)
        if _h == h {
            CurrentHour = hour
            NextHour = timetable[i+1]
            break
        }
    }
    minutes := CurrentHour.Minutes
    printLefts(m, minutes, CurrentHour.Hour)
    if m > 44 {
        nextMinutes := NextHour.Minutes
        printLefts(m-60, nextMinutes, NextHour.Hour)
    }
}

こんな感じに表示される。

私家版通勤タイマー

あとはこれを BitBar で良い感じに読めるように出力内容を調整していけば良い。

Go 言語、 2 年くらい前に gyowitter っての作って Yo が来たら Twitter に晒すってのやってた以来に触った。変数のスコープとかがわかりづらかったし、 PHP っぽい雰囲気もある。やっぱり Ruby の方が書きやすいし好きだけど歯を食いしばって使ってみる。

| @技術/プログラミング

以前は電車の本数多いところに住んでたので帰りの電車の時間とか気にしたことなかったけど、いまは郊外に住んでるので電車の本数少なくて、一本乗り遅れると駅で 20 分待たされたりする。特に遅くまで仕事してたりすると電車の本数がみるみる少なくなる。なので乗るべき電車に乗り遅れないように Yahoo! 乗換案内の通勤タイマーをよく使ってる。家まで帰る電車の発車時刻まであと何分かがカウントダウン表示されて便利。

ただ iPhone のウィジェットで見るとちょっともっさりしててだるい。 Pebble とかでサクッと確認できないかと思っているけど Pebble 入手して 10 ヶ月以上経つにもかかわらず Pebble 用のソフトを自分で作るという機運が高まらず今日に至る。

ある晩ふと、これって BitBar でできるんじゃねと思った。 BitBar は Mac のメニューバーに CLI で出力される情報を表示されることができるソフトで、 Netatmo Weather Station の計測値を表示するのに使っている。 Mac のメニューバーから二酸化炭素濃度や湿度確認できて便利。こんな感じ。これの通勤タイマー版が欲しい。

取り急ぎ時刻表の情報必要だけどちょっと探した感じでは簡単に時刻表取れるような API なかった。ので Yahoo! 乗換案内の時刻表をダウンロードして pup というコマンドラインの HTML パーサーで JSON 化し、それを jq に食わせて整形して良い感じの JSON にするという風にした。

# curl して HTML 取得
curl -s http://transit.yahoo.co.jp/station/time/28236/?gid=6400 | \
# HTML から必要な情報取り出して JSON 化
pup 'table.tblDiaDetail [id*="hh_"] json{}' | \
# JSON を良い感じに整形
jq '[.[] | { hour: .children[0].text, minutes: [.children[1].children[].children[].children[].children[].children | map(.text) | join(" ") ] }]'

得られる JSON はこんなのになる。(福岡市地下鉄空港線天神駅の姪浜方面の時刻一覧)

[
  {
    "hour": "5",
    "minutes": [
      "36",
      "42",
      "56 筑"
    ]
  },
  {
    "hour": "6",
    "minutes": [
      "8",
      "12 筑",
      "17",
      "26 筑",
      "34",
      "39",
      "43 筑",
      "56 筑"
    ]
  },
  ...,
  {
    "hour": "24",
    "minutes": [
      "6",
      "12 筑"
    ]
  }
]

これをコマンドラインで良い感じに表示するスクリプトを Ruby なり Shell Script なりで書いて BitBar に登録すればメニューバーから次の電車を確認できるようになり便利。