書誌情報を取得するAPI(openBD)のラッパーのgemを作成した

個人で読書管理サイトを作成しており、書誌の情報が欲しいと思い、利用できる場所を探していた時に出会いました。

openBD | 書誌情報・書影を自由に

調べたところ、goのライブラリはあったのですが、ruby用がなかったので作成しました。

isbnを送信するだけで、結果が返ってくるので楽です。

あとは、その結果を利用しやすいようにラッパーを作成しました。(title/contributorsなどで値を取得したかった)

GitHub - ryosuke-endo/open_bd

ライブラリの使い方

READMEから抜粋しました。

client = OpenBD::Client.new
# Pass the value of isbn to isbns
res = client.search(isbns: [])

res.empty?
res.body
res.status

# [OpenBD::Resource]
res.resources.each do |resource|
  res.content
  res.content_detail
  res.contributors       # [OpenBD::Contributor]
  res.cover_image
  res.height
  res.isbn
  res.main_title
  res.paper_size
  res.paper_size_detail
  res.publisher
  res.release_date
  res.sub_title
  res.table_of_contents
  res.title
  res.width
end

こんな素晴らしいデータを自由に利用させてくれるなんて、感謝しかない。

openBDプロジェクト株式会社カーリルさんと版元ドットコム さん、ありがとうございます!

webのスマホサイトとアプリのUIの壁

webのスマホサイトも段々アプリのようなUIをする動きが増えてきました。

ユーザーからしてみると、普段はアプリを利用しているので、できるだけアプリの動きに近い方が馴染みやすいです。

アプリのようなUIに合わせるのは当たり前の流れなのかもしれません。

下は自分がツイートしたものですが、検索UIにおいては、上部にそのまま検索できるようにしておくことが増えてきました。

しかし、どうしても真似をすることのできない壁があります。

webのスマホサイトは検索からくる人を想定しないといけません

これがどこに影響してくるのかです。

  • サイト名
  • SEOを意識したワード表示

主にこの二つかなと思います。

これは大体のサイトにおいてファーストビューの上部に表示されます。

前者については、どんなサイトかわからないので、必然的に表示することになります。

後者については、google先生にどんなページかを伝えないといけないため、どうしてもSEOワードになりがちです。

ファーストビューで見せないといけない情報になるので、アプリだとその二つを削れるってのは大きい気がします。

食べログの例

食べログで二つの差を見てます。

スマホサイトは「渋谷駅 ランチ」で検索した結果を表示しています。

アプリは位置を「渋谷駅」と営業時間を「ランチ」で条件検索しました。

スマホサイト

f:id:frozen_faithjp:20180204230404p:plain

アプリ

f:id:frozen_faithjp:20180204224507p:plain

アプリだと、タイトルはいらないので、削って代わりに並び順を意識させています。

また、SEOを意識した表示しなくていいので、「グルメ・レストラン」というワードを削り、表示箇所を分けています。

アプリはサイト内にいる前提で作成できる

完全にユーザーにとって必要な情報だけを渡すことができます。

なので使い勝手を追求した作りにするのがベターだと思います。

webスマホサイトの場合は、UIはできるだけアプリに近づけつつ、SEOを意識した作りになるので、どうしても使い勝手では不利になります。

ただ、この話の前提は検索結果で来るサイトならではの話なので、webでも完全に閉じた世界(検索いらないサイト)で行動できるなら、思い切った見せ方をしてもいいかもしれないですね。

Intersection Observerを使ってみた

JavaScriptのIntersection Observerが便利じゃないのか?と気づき、ちょっと利用しました。

Intersection Observerとは

直訳すると、「交差点の監視」という意味でしょうか。

Intersection Observerの何が便利かというと、対象物が画面に入る時に処理を実行することができます。

この辺の処理はスクロールイベントで頑張ることが多かったのではないでしょうか?

そこが楽になります。

できることとして、例によく出るのですが、

  • 画像の遅延読み込み
  • 無限スクロール

の実装が楽になります。

なぜなら、これ系の処理は画面に入るまでは実行しなくてもいいからです。

今まではライブラリに頼ることが多かったと思いますが、自前でも簡単に作ることができます。

画像の遅延読み込みのライブラリで、layzr.jsはIntersection Observerを利用して、コード数を削減しています。

実装のサンプルコードとして、Vueで無限スクロールを実装したコードを置いておきます。

余談

この作者のライブラリには、他にもIntersection Observerを利用しているので、好きなのか、便利さにはまっているのかなって気持ちになりました。

使い方

const io = new IntersectionObserver((entries) => {
  for (let entry of entries) {
    console.log(entry);
  }
});
io.observe(target);

これでターゲットが画面に表示するかどうかで判定処理が動きます。

実装のサンプルコード

検索結果の無限スクロールです。

loadingとかはないので、形だけです。

export default class SearchScroll {
  constructor(list) {
    this.list = list
    this.page = 2

    const parser = document.createElement('a')
    parser.href = location.href
    this.parser = parser
  }

  path() {
    const params = {
      page: this.page
    }
    const query = $.param(params)
    return `${this.parser.pathname}.json${this.parser.search}&${query}`
  }

  target() {
    return this.list.children[this.list.children.length - 1]
  }
}
import axios from 'axios'
import SearchScroll from './search_scroll.js'

  mounted() {
    const self = this
    const list = document.querySelectorAll(`[data-sentinel]`)[0])
    const searchScroll = new SearchScroll(list)

    const sentinelListener = (entries) => {
      const entry = entries[0]
      if(entry.intersectionRatio > 0) {
        io.unobserve(entry.target)←対象に入ったら前の監視をやめる
        axios.get(searchScroll.path(), {
          headers: {
            'Content-Type': 'application/json'
          }
        })
        .then((response) => {
          searchScroll.page += 1
          self.lists.push(...response.data)
          io.observe(searchScroll.target())←新しい監視を始める
          console.log("success")
        })
        .catch((error) => {
          console.log("fail")
        })
      }
    }

    const io = new IntersectionObserver(sentinelListener)
    io.observe(searchScroll.target())
  }

感想

画面に表示しているかどうかってのいうのは、今後フロントエンドで処理をする上で何かで使いそうなので、覚えておきたい。

細かい制御もできるので、その辺の理解をきちんとしたい。

参考

Intersection Observer を用いた要素出現検出の最適化

Intersection Observer

Intersection Observer が良さそうなので試してみた

JavaScriptのdebuggerでデバッグが楽になる

やっとconsole.logから脱出できそうな気がしてきました。

今までずっとconsole.logを利用してきました。

これからも使っていくでしょう。

ただし、debuggerの存在に気づいたので、debuggerを利用していきたいと思います。

何がいいかっていうと、実行時にブレイクポイントで止まってくれる。

これに尽きます。

利用方法

document.addEventListener('DOMContentLoaded', () => {
  document.getElementById("foo").addEventListener('click', () => {
    debugger←差し込む
  })
});

https://gyazo.com/e2a44d5c27b3cee45a0cc4f7fe6c8d69

上の画像でクリックすると、差した場所でブレイクポイントで止まっていると思います。

これが便利ですね。

これで少しは楽になりそう。

インターフェースデザインの心理学を読んだ

デザインによって、人は使いやすいかどうかを判断するので、役に立つかなと思い読んでみました。

正直内容が心理学的の話をしており、インタフェースの話(ホームページのデザイン)じゃないの?って思ったのが正直な感想です。

微妙に騙されたな・・・って思ったのですが、印象に残っている話があります。

データより物語の方が説得力があると言う話です。

インズタグラムもストーリーっていう機能をリリースしたし、妙に印象に残ったんですよね。

データの提示だけでは感情に訴えづらい

子持ち世帯が中古マンションを購入しようとします。

条件は人それぞれですので、簡単なデータだけにします。

  • 中古マンションを購入した人の80%は満足
  • 満足した理由の一位は子供が喜んでいる

これだけのデータでも購入する人はすると思います。

つっこみどころは置いといてくださいw

では、自分の信頼する人が中古マンションを買った時のよかった感想を伝えてきたらどうなりますか?

あの人が言うのなら・・・っていう感情は出てくるのではないでしょうか。

データより物語に説得力がある理由

本に書いてある理由です。

  • 物語は感情的に訴える力が強い
  • 物語は聞き手の共感を呼び、それにより情緒的な反応をする
  • 感情は記憶中枢にも働きかける

要するに、印象に残るってことですね。

最近は動画でものを紹介することが増えていますね

  • youtuberの宣伝動画
  • メルカリチャンネル

youtuberの宣伝動画なんて、企業からお金もらっていることが多いから、宣伝の方が多いでしょう。

そんなことさておいて、youtuberの人が楽しく、便利なことを紹介してきたら、親近感があるので購入すると思います。

そもそも、宣伝動画なんていうフィルターをかけるのは、業界の人だけでしょう。

感想

僕はブログでもストーリー性がある話は好きです。

それは、感情に訴えてくる部分があるからでしょう。

ストーリー性をうまく持たせる機能を作ることができたら、ソーシャルで広まりそうなサービスが生まれる気はしています。

ただ、難しいのは、それをユーザー側にやってもらうことです。

動画は自然に登場人物が出てくくるので、ユーザー側でもできやすいのかなって思ったりしています。

ストーリーは何かにうまく組み込みたいなって感じています。

以上。

インタフェースデザインの心理学 ―ウェブやアプリに新たな視点をもたらす100の指針

インタフェースデザインの心理学 ―ウェブやアプリに新たな視点をもたらす100の指針

vueを使っている時のディレクトリ構成

みなさん、ディレクトリ構成は気になったりしませんか?

自分は割とディレクトリ構成を気にします。

どこにどういうコードが書いてあるのかすぐに探すことができるからです。

自分は普段railsでコードを書いているので、railsのレールに則って書くとディレクトリ構成で悩むことは少ないです。

そして、vueを使っている場合は、レールがないので、自分で考えなければいけません。

今回は初期構成から現在に至る構成を伝えます。

これがベストプラクティスかどうかは自分もわかりません。

現在の構成はgitlabhqを見ていて、納得することが多かったので、それに従っています。

余談ですが、大規模なプロダクションレベルのコードを見るのはオススメです!

初期構成

javascripts
├── application.js
├── components
│   ├── comment
│   │   │── form.vue
│   │   └── icon.vue
│   └── common
│       └── form
│           └── modal.vue
├── comment.js
├── mixins
│   └── icon.js
└── store
 └── index.js

こんな感じです。

内容

  • 起点となるディレクトリ(javascripts)にファイルを置いていく
  • compoments単位・store単位に区切る
  • 共通のcomponentsはcommonを利用する

vuexなどのサンプルにあるやり方です。

一番最後だけは付け足しました。

小規模のサイトならいいのでしょうが、大規模のサイトになってくるとちょっとcomponents内が膨らんでいきます。

また、起点となるファイルもディレクトリを作成することになるでしょう。

現在の構成

javascripts
├── application.js
├── icon
│   ├── components
│   │   ├── form.vue
│   │   └── icon.vue
│   ├── icon_bundle.js
│   └── store
│       └── index.js
├── initializers
│   └── axios.js
└── vue_shared
    └── components
        └── form
            └── modal.vue

内容

  • 起点となるのはディレクトリ内の**_bundle.jsで管理します。
  • 起点となるディクレトリ単位でcomponentsstoreを作成
  • 共通処理はvue_sharedに移す
  • 初期設定はinitializersで設定する

一番最後のはWeb アプリの JavaScript の初期化処理をどうまとめるかを見て取り入れました。

普段railsと一緒に使用しているので、ディレクトリ単位の管理だと、位置を合わせることができるので、便利です。

componentsstoreもどこで一緒に利用されているのかがわかります。

このディレクトリ構成が何かしっくりきました。

もっとこうしたほうがいい!などの意見があれば教えてください。

以上です。

参考

gitlabhq

Vue(GitLab Documentation )

Web アプリの JavaScript の初期化処理をどうまとめるか

Deviseのログイン後のページ先をユーザーフレンドリーにする

せっかくログインしたのに、トップページに戻ると、なんだこれ?ってなってしまいます。

それを回避する方法です。

class ApplicationController < ActionController::Base
  before_action :store_user_location!, if: :storable_location?

  private

  def storable_location?
    request.get? && is_navigational_format? && !devise_controller? && !request.xhr?
  end

  def store_user_location!
    # :user is the scope we are authenticating
    store_location_for(:user, request.fullpath)
  end

storable_actionでGET/Devise/Ajaxじゃないなどの判定をします。

その場合のみ、sessionでurlを保存しています。

class Users::SessionsController < Devise::SessionsController
  include Cookie

  protected

  def after_sign_in_path_for(resource_or_scope)
    stored_location_for(resource_or_scope) || super
  end
end

あとはsession後にそれがあるかどうかを判定します。

参考

https://github.com/plataformatec/devise/wiki/How-To:-Redirect-back-to-current-page-after-sign-in,-sign-out,-sign-up,-update

wikiが充実していて、本当に助かる。

ここから何かをカスタムする場合は、storable_location?をいじるか、after_sign_in_path_forで条件分岐でいじるかになると思います。

railsで今月・先月の期間を取得する方法

all_monthを使いましょう。

今月の場合

[1] pry(main)> Time.current.all_month
=> Fri, 01 Dec 2017 00:00:00 JST +09:00..Sun, 31 Dec 2017 23:59:59 JST +09:00

先月の場合

[2] pry(main)> Time.current.last_month.all_month
=> Wed, 01 Nov 2017 00:00:00 JST +09:00..Thu, 30 Nov 2017 23:59:59 JST +09:00

これは便利すぎる

参考

https://qiita.com/whitefox_105/items/7c1d409ebd863fab5cb5

railsの本番サイトのデータベースを別サーバーで行うための方法

今回やりたいことです。

  • app/dbサーバーを分けて運用する

そんなの当たり前だろ!って感じですが、デプロイするときに詰まったので、忘れないために書いておきます。

なお、今回のお話はmysqlでの運用にしておりますが、他のデータベースでも同じ感じでいけると思います。

mysqlでリモートの接続を許可する

こんな設定あるとは知りませんでした。

言われてみれば、どこからでもデフォルトでアクセスできるのは、危険としか言いようがありません。

/etc/mysql/mysql.conf.d/mysqld.cnfファイルから、mysqlの設定を変更します。

# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

[mysqld]
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysql
log-error       = /var/log/mysql/error.log
# By default we only accept connections from localhost
# bind-address  = 127.0.0.1
bind-address    = xxx ←ここをmysqlがあるサーバーのIPアドレスにする
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

これでmysql -h IPアドレス -u hoge -pでアクセスできるようになります。

補足

ただし、この場合は全世界に解放されている状態なので、最低でもDBサーバーにアクセスする場所からしか入れないようにしましょう。

AWSの場合はセキュリティグループがあるので、port:3306へのアクセスはIPアドレスで制限しましょう。

ファイアーウォールでもそういう設定はできるでしょう。

database.ymlの設定

production:
  <<: *default
  host: xxx ←IPアドレスを環境変数で設定しておきましょう
  ...

mysql userの作成

deployするとmysqlのユーザーはhoge@localhostではなく、hoge@IPアドレスでアクセスします。

なので、それに沿ってユーザーを作成しましょう。

  • ユーザー作成
  • 権限設定
create user {user名}@IPアドレス identified by {パスワード} -- ユーザー作成

grant all on foo_db.* to {user名}@IPアドレス identified by {パスワード};

この辺を注意して作成します。

以上です。

やってる内容は普通なのですが、意外に詰まってしまった。

chromeで簡易的にパフォーマンスチェックする方法

「推測するな、計測せよ!」ってよく言われるけど、どうやって計測すればいいんだよ!ってパターンはあります。

自分はサーバーサイドをメインでやることが多いのですが、最近話題のフロント側のパフォーマンスについては、どうやって計測するのかわかっておりません。

サーバーサイドだと、New relicで計測することが多いでしょう。

では、サーバーからレスポンス後の速度はどのようにチェックすれば良いのでしょうか?

その時のために、google先生がパフォーマンス計測する方法を用意してくれていました!

Developer toolsのAuditsを利用する

まずはchromeを用意しましょう。

 chromeでページを表示した状態ででF12キーWindowsの場合。Macの場合はCommand+Option+Iキー)を押しましょう。

chromeのdeveloper toolが開きます。

タブのAuditsを選択しましょう。

https://gyazo.com/cd1f9b5ece05b1e45fbe3369af8bef49

上記のような画面になります。

ここから単純にボタンを押して、実行をします。

https://gyazo.com/db9b9c8547754ad6d35dfdd12b584266

パフォーマンス測定をしてくれます。

これはひどいですね・・・

内容は今風のことが多いです。

  • Service Workerを利用しろ!
  • 画像は遅延読み込みしてくれ
  • ファイルは圧縮して配信してくれ(オフラインなので、勘弁してくれ・・・)

とりあえず、google先生のいうところから、少しづつ改善していきましょう。

追記

画像はwebp使えばこれだけ軽くなるぞって教えてくれます。

https://gyazo.com/b25fc9885e49edb8d0815842d41db351

google先生優しすぎだろ・・・