CentOS+Rails4+Apache2.2でPumaを設定する

備忘を兼ねて、CentOS 6.6, Apache 2.2.15, Ruby 2.1.5-p273, Rails 4.2.0, Puma 2.10.2 でサービスを動かす際の設定や実行方法について書いておく。
ApacheやRailsのインストールは完了している前提なので注意。

環境情報

Ruby, Rails, RubyGems

% rake about
About your application's environment
Rails version        4.2.0
Ruby version         2.1.5-p273 (x86_64-linux)
RubyGems version     2.4.4
Rack version         1.5

Puma

% puma --version
puma version 2.10.2

Apache

% /usr/sbin/httpd -v
Server version: Apache/2.2.15 (Unix)
Server built:   Oct 16 2014 14:48:21

CentOS

% inxi -SM
System:    Host: hakone.vps.sakura.ne.jp Kernel: 2.6.32-431.1.2.0.1.el6.x86_64 x86_64 (64 bit)
           Console: tty 1 Distro: CentOS release 6.6 (Final)
Machine:   System: Red Hat product: KVM v: RHEL 6.4.0 PC
           Mobo: N/A model: N/A Bios: Sea v: 0.5.1 date: 01/01/2007

Railsアプリの作成からPumaのインストール

通常通りRailsアプリを作成し、GemfileへPumaのgemをインストールするよう記載。

Railsアプリ作成

% rails new MYAPP
% cd MYAPP
% bundle exec rails runner 'puts Rails.root'
/PATH/TO/MYAPP

Pumaインストール

% echo "gem 'puma', group: :development" >> Gemfile
% bundle install

Pumaの初期設定と設定ファイル作成

Pumaのpid等ファイルを置く場所を作る

% mkdir -p tmp/pids
% mkdir -p tmp/sockets

config/puma.rb

以下の内容でconfig/puma.rbを作成する。

require 'active_support'
require 'active_support/core_ext'

rails_root = Dir.pwd

unless ENV['RACK_ENV'] == 'production'
  environment ENV['RACK_ENV'] || 'development'
  daemonize true  # デーモン化

  workers 0  # 1以上を指定するとCluster化する
  threads 0,2  # スレッド数(最小, 最大)

  bind 'unix://' + File.join(rails_root, 'tmp', 'sockets', 'puma.sock')  # /PATH/TO/MYAPP/tmp/sockets/puma.sock
  port 9293  # Port番号

  pidfile File.join(rails_root, 'tmp', 'pids', 'puma.pid')  # /PATH/TO/MYAPP/tmp/pids/puma.pid
  state_path File.join(rails_root, 'tmp', 'puma.state')  # /PATH/TO/MYAPP/tmp/puma.state

  stdout_redirect(  # pumaのログをlog/以下に出力する。trueは追記モード。
    File.join(rails_root, 'log', 'puma.log'),
    File.join(rails_root, 'log', 'puma-error.log'),
    true
  )

  # pumactlのトークンを指定 via. http://qiita.com/takkkun/items/ecdee7e7dec1bcc9a5b5
  activate_control_app('auto', auth_token: rails_root.camelize.parameterize)
end

起動できるか試してみる

% pumactl --config-file config/puma.rb start
Puma starting in single mode...
* Version 2.10.2 (ruby 2.1.5-p273), codename: Robots on Comets
* Min threads: 0, max threads: 2
* Environment: development
* Daemonizing...

起動できた。

ApacheをWebサーバとしてPumaへアクセスできるよう設定

バーチャルホストを利用している。virtualhost_MYAPP.confはドメイン(Railsアプリ)毎に作成し、puma-confは共通なので一度作成すれば良い。なお、puma-confpuma.confではないので注意。これは置いてあるディレクトリの性質上.confとすると自動的にIncludeされてしまうためだ。(他の良い方法あればコメントほしい)

大元の設定ファイルを含めて3ファイルのInclude関係は以下の通り。ここでは大元のhttpd.confの内容は割愛するが、バーチャルホストを利用すること以外は通常のものと変わりない。

conf/httpd.conf
  -(include)-> conf.d/virtualhost_MYAPP.conf
    -(include)-> conf.d/puma-conf

virtualhost_MYAPP.conf

Railsアプリのアプリケーションサーバ(Puma)へ接続するためのリバースプロキシ設定をバーチャルホストとして設定する。

# -*- coding: utf-8; mode: conf; -*-

##
# MYAPP
#
<virtualhost *:80>
    DocumentRoot "/PATH/TO/MYAPP/public" # Railsアプリのpublicディレクトリを指定
    ServerName myapp.example.com  # Railsアプリのドメインを指定

    RewriteLog logs/MYAPP-rewrite_log
    RewriteLogLevel 1
    ErrorLog logs/MYAPP-error_log
    CustomLog logs/MYAPP-access_log combined env=!no_log

    SetEnv RACK_ENV developmen  # Railsアプリの実行環境(development/test/production)

    # for puma config
    include conf.d/puma-conf  # ここでPuma共通設定ファイルを読み込んでいる
    RewriteRule ^/(.*)$ http://0.0.0.0:9293%{REQUEST_URI} [P]  # '9293'のところにはconf/puma.rbで指定したPort番号を設定

    <directory "/PATH/TO/MYAPP/public">
        AllowOverride All
        Options FollowSymLinks -MultiViews
        Order deny,allow
        Allow from all
    </directory>

</virtualhost>

puma-conf

共通で使うpuma-confファイル。パスの変換や静的ファイルはApacheで処理することなどが書かれている。

# -*- coding: utf-8; mode: conf; -*-

    # Redirect all requests that don't match a file on disk
    # under DocumentRoot get proxied to Puma
    RewriteEngine On
    RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f

    # Don't allow client to fool Puma into thinking connection is secure
    RequestHeader unset X-Forwarded-Proto

    # Disable ETags (https://github.com/h5bp/server-configs-apache/tree/master/doc#configure-etags)
    # Set Expiration date for all assets to one year in the future
    <locationmatch "^/assets/.*$">
        Header unset ETag
        FileETag None

        ExpiresActive On
        ExpiresDefault "access plus 1 year"
    </locationmatch>

    # Rewrite requests for js and css to gzipped versions
    # if client and server support it
    <locationmatch "^/assets/.*\.(css|js)$">
        RewriteEngine on
        RewriteCond %{HTTP:Accept-Encoding} \b(x-)?gzip\b
        RewriteCond %{REQUEST_FILENAME}.gz -s
        RewriteRule ^(.+)$ $1.gz
    </locationmatch>

    # Set type and headers for gzipped css
    <locationmatch "^/assets/.*\.css\.gz$">
        ForceType text/css
        Header set Content-Encoding gzip
        Header add Vary Accept-Encoding
    </locationmatch>

    # Set type and headers for gzipped js
    <locationmatch "^/assets/.*\.js\.gz$">
        ForceType application/javascript
        Header set Content-Encoding gzip
        Header add Vary Accept-Encoding
    </locationmatch>

    # Compress HTML on the fly
    AddOutputFilterByType DEFLATE text/html

Apacheを再起動してアクセスしてみる

これら設定ファイルが用意できたら、設定ファイルをテストして問題無ければApacheを再起動する。

% sudo /etc/init.d/httpd configtest
Syntax OK
% sudo /etc/init.d/httpd restart
httpd を停止中:                                            [  OK  ]
httpd を起動中:                                            [  OK  ]

これで、ServerNameに設定したmyapp.example.comへブラウザでアクセスしてみて見慣れたRailsアプリのTOPページが表示されればOKだ。

まとめ

CentOS, Apache2.2, Rails4の環境でアプリケーションサーバPumaを利用するための一連の手順を書いてみた。
config/puma.rbは同じサーバで動く別アプリとPort番号が被らないように設定すれば使い回しができるようにしてあるので、テンプレートにしてみても良いかもしれない。もっと良い方法などあればコメント等で教えてもらえると嬉しい。

では、Enjoy Ruby! Enjoy Rails!

参考リンク

スポンサード

IDCフロンティアのクラウドを使ってみた(登録〜仮想マシン作成〜SSHログイン)


追記(2014/10/16 10:00):念のためですが追記です。私はヤフーの中の人ですが直接IDCフロンティアと関わっていないため、この記事はインターネット(Yahoo!検索)で確認できる情報を参照して書いてます。ですので、もし誤りなどがあればぜひご指摘ください。また、使ってみた感想や使いづらい点・分かりづらい点などを教えてもらえると嬉しいです。いただいたものは社内から必ず担当へ伝えます(お返事はできないかもです、すみません)。

AWSより安い500円/月で、ミニマム構成のクラウドが作れるというIDCFクラウドを利用してみた。
登録から仮想マシン作成までは比較的簡単にできたが、いざSSHでログインしようとすると少しつまずいた(Web画面でファイアウォールとポートフォワードの設定が必要だった)のでメモがわりに記事にしてみる。

日経でも記事になってた。

ヤフーのデータセンター子会社、IDCフロンティア(東京・新宿)は企業向けの低価格クラウドサービスを始める。米アマゾン・ドット・コムのクラウド「アマゾン・ウェブ・サービス(AWS)」より性能が高いうえ、価格を4分の1に抑えた。
CPU1コアとメモリー1ギガバイトからシステムを作れる。月額使用料は500円(税別)からで、使用状況で変動する。
ヤフー系、格安クラウド アマゾンの4分の1、高性能 :日本経済新聞

特徴としては以下のようだ。

  • 世界最速クラス、サーバーの並列作成時間は1台約2秒
  • 国内クラウド初採用、ioMemoryと40コアCPU/128GBメモリ搭載タイプの提供
  • 国内最安、月額500円の利用プランと従来比28~65%の大幅な料金引き下げ
  • 国内初、はてな社のクラウドパフォーマンス管理サービス「Mackerel」(マカレル)に対応

プレスリリース2014年 | IDCフロンティア、よりパワフルな新クラウドサービス「IDCFクラウド」を提供開始 | IDCフロンティア

アカウント登録

IDCFクラウドのアカウント登録が必要だが以下のビデオの通り進めて問題無く完了。スマートフォンなどでSMSまたは自動音声で認証が必要なので注意。

登録後、ログインするとユーザ情報登録と支払い方法の登録が画面に表示されるのでこれも入力。
クレジットカード、銀行振込、口座振替が利用できる。

新しいクラウドサービス「IDCFクラウド」リリース記念! IDCFクラウドの1万円分無料クーポンを、期間限定ですべてのお客さまにプレゼントいたします。
トピックス2014年 | 「IDCFクラウド」1万円分無料クーポンプレゼントキャンペーン!11月14日まで。 | IDCフロンティア

また、11/14まではキャンペーンとして1万円分のクーポンがあるので、上記のページ内に記載されているクーポンコードをアカウント情報のページから入力すれば適用される。有効期限が6ヶ月なので、ミニマムスペックの仮想マシンを3台構成(Web, DB*2)にしても500円/月×3台×6ヶ月=9,000円と使い切れないくらい。

仮想マシン作成

IDCFクラウドコンソール画面から、左上にある仮想マシン作成をクリックして作成していく。
上記ビデオのように画面内で選んでいく方式なのでこれも簡単に進むだろう。今回はビデオとは少し違い最小構成で行くことにし、仮想マシンタイプにlight.S1、OSイメージはCentOS 7.0 64-bit、ボリュームは変更せず初期値のルートディスクを15GBを選択。

SSH Key(SSH公開鍵)の設定

SSH Key(SSH公開鍵)はセキュリティを高めるためにも設定した方が良い。

自分の秘密鍵・公開鍵ペアがあれば、公開鍵を「アップロード」のところから入力する。
ちなみに、RSA(ssh-rsa)形式のみの対応なので注意。

SSH鍵ペアの作成

もし無ければ、上記のようにボタン一つで新規に秘密鍵と公開鍵のセットを作ってくれるので、(4)の秘密鍵を~/.ssh/id_rsa_idcfcloudなどに保存する。(パーミッションを400にするのを忘れないように。詳しくはWeb検索などで調べること)

今回は、自分のGitHubアカウントの鍵情報(https://github.com/USERNAME.keys)からSSHキーを「アップロード」のところで入力した。

残りは、台数は1台、仮想マシン名は任意で構わないのでマシン名をTest01、グループをTestとした。プライベートIPアドレスは自動設定で問題無い。

確認画面から作成して完了。

SSHログインする前の設定

ここで引っかかったが、SSHでログインするためにファイアウォールとポートフォワードの設定が必要になる。
IDCFクラウドは文字通りクラウドサービスとして仮想的なデータセンターを構築でき、上記で作成した仮想マシンが所属するネットワークの仮想ルータが存在するため、SSHログインも仮想ルータを経由して仮想マシンにログインすることになるためだ。

具体的な設定方法は、クラウドコンソールのIPアドレス設定にアクセスすると、最初から無料で使えるグローバルIPアドレスが1つ登録されているので、これに対してファイアウォールとポートフォワードの設定をしていく。ちなみに下記画面に表示されているものが割り当てられたIPアドレスだ(下記画面では210.140.168.169)。

idcf_ipaddress

ポートフォワード設定

最初から無料で使えるIPアドレスは仮想ルータに割り当てられている。
この仮想ルータに特定のポート番号でアクセスしたとき、ルータ配下のネットワークに所属する仮想マシンへそのアクセスを中継(フォワーディング)することができる機能がポートフォワードだ。

コンソールのIPアドレス設定画面から、IPアドレス名をクリックすると基本設定画面が表示されるので、ポートフォワードをクリック。
idcf_ipaddress_setting

下記画像のようにSSHアクセスのための設定を行う。
コメントは分かりやすい名前を入力し、プライベートポートはセレクトボックスからSSHを選択、パブリックポートは他サービスと重複していないポート番号(自由に使用できるポート番号である49152番〜65535番が好ましい)を指定し、右側にある+ボタンをクリックすると設定が完了する。
idcf_ipaddress_portfoward

今回は例えば49222を指定してポートフォワードを設定した。
これで、仮想ルータの49222ポートへのアクセスが仮想マシンの22ポートへ転送されるようになる。

ファイアウォール設定

続いて、ファイアウォール設定画面から上記フォワード設定したポート番号(今回は49222)へのアクセスを許可するように設定する。

同じく、コンソールのIPアドレス基本設定画面からファイアウォールをクリック。
コメントは分かりやすい名前を入力し、ソースCIDRはAnyを指定、タイプはCustom TCPを指定する。ポートレンジには先ほどポートフォワード設定で決めたパブリックポート番号を入力し、右側の+ボタンをクリックすると設定完了だ。
idcf_ipaddress_firewall

今回は先ほどポートフォワードへ設定した49222へのアクセスを許可する設定にした。

SSHでログイン

ここまでの設定が完了すれば、作成した仮想マシンへSSHログインができる。SSH Keyの設定時に作成した(または既存の)秘密鍵ファイルが~/.ssh/id_rsa_idcfcloudだとすると以下のコマンドでssh接続できる。ちなみに、実際のIP_ADDRESSはIPアドレス設定画面で確認できる。

$ ssh -l root -p 49222 -i ~/.ssh/id_rsa_idcfcloud IP_ADDRESS
Last login: Thu Oct 16 05:52:11 2014 from ***********
    ________  ______   ______                 __  _
   /  _/ __ \/ ____/  / ____/________  ____  / /_(_)__  _____
   / // / / / /      / /_  / ___/ __ \/ __ \/ __/ / _ \/ ___/
 _/ // /_/ / /___   / __/ / /  / /_/ / / / / /_/ /  __/ /
/___/_____/\____/  /_/   /_/   \____/_/ /_/\__/_/\___/_/

[root@Test01 ~]#

ログインできた。OS情報を確認してみる。

[root@Test01 ~]# uname -a
Linux Test01.cs6cidcfcloud.internal 3.10.0-123.6.3.el7.x86_64 #1 SMP Wed Aug 6 21:12:36 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
[root@Test01 ~]# cat /etc/redhat-release
CentOS Linux release 7.0.1406 (Core)

CPUやメモリ情報は下記のコマンドで見られる(結果は割愛)。

[root@Test01 ~]# cat /proc/cpuinfo
[root@Test01 ~]# cat /proc/meminfo

ログイン後は(先ほど設定した)クラウドのファイアウォールがあるがセキュリティ等の設定をしておく方が良いだろう。

スポンサード

東京メトロが公開予定のオープンデータAPIが楽しみなのでアイデア考えてみた

東京メトロが10周年を記念して、いろいろなAPIが公開されるようだ。

オープンデータ活用コンテスト(2014.9.12-2014.11.17)
オープンデータを活用し、東京メトロをご利用になるお客様の生活がより便利でより快適になるようなアプリを募集します。
東京メトロ10周年オープンデータ活用コンテスト

具体的な公開データ内容は未だ不明だが、東京メトロのプレスリリースに一部記載があった。

すでにホームページで公開している列車時刻表、駅別乗降人員、バリアフリー等の情報に加え、今回初めて東京メトロ全線の列車位置、遅延時間等に係る情報(方向、列車番号、列車種別(普通、急行、快速等)、始発駅・行先駅、車両の所属会社、在線位置(ホーム、駅間の2区分)、遅延時間(5分以上の遅延を「遅延」として表示))をオープンデータ化
東京メトロプレスリリース

また、INTERNET Watch記事でもう少し詳細に書かれていた。

列車の方向(どこ方面行きか)、列車番号、普通・特急・急行・快速・臨時といった列車種別(試運転・回送の非営業列車は非公開)、始発駅・行先駅、所属会社(どの鉄道事業者の車両か)、在線位置(ホーム・駅間の2区分)、遅延時間(5分以上の遅れを「遅延」として表示)といったデータが含まれ、1分ごとに配信される。
東京メトロ、列車の在線位置など全線オープンデータ化、車両の所属会社も -INTERNET Watch

まとめると、以下の情報が1分毎に取得できるようだ。

  • 列車時刻表
  • 駅別乗降人員
  • 駅毎のバリアフリー等の情報
  • 東京メトロ全線の列車位置
    • 方向(どこ方面行きか)
    • 列車番号
    • 列車種別(普通・特急・急行・快速・臨時等)
    • 始発駅・先行駅
    • 車両の所属会社
    • 在線位置(現在位置:ホームに停車中か線路を走行中かの2種)
    • 遅延時間(5分以上遅れがあれば遅延)

オープンデータ活用コンテストの応募対象は、Webアプリ、Androidアプリ、iOSアプリ、Windowsストアアプリで、グランプリ1点に100万円など豪華。しかし、なにより通常取得できない情報を利活用できるのが嬉しい。

(実行されない限り)アイデアに価値はない、という考え方に賛成なのでパッと思いついたアイデアを書き留めておくことにしよう。ちないに、すでにあるかどうかは調べていないので、ご存じのサービスがあればツッコミ歓迎。

  • 乗り過ごし防止アラーム
  • 乗り換え時の歩くスピード教えてくれる
  • 外国人観光客向けおすすめ観光ルート生成
  • トイレまでのダッシュシミュレーター(あと何分我慢すればいいのか)
  • 乗ってる電車内でつながるSNS
  • 乗車時間に最適な音楽プレイリスト作成・ニュース記事ピックアップ

挙げてみたが特に良いものがあるか自信も無いので、もし参考にされても連絡などは不要です。

スポンサード

Rails4+Paperclip(ImageMagick)のサムネイルサイズ一覧

20140102

必要だったのでまとめたシリーズ。

Ruby on Railsに画像アップロード機能を実装する際のデファクトスタンダードであるPaperclipには、ImageMagickと連携してサムネイル画像を自動生成する機能がある。

サムネイル画像のサイズは、Modelクラスに簡単なスタイル記述を追加することで指定可能になるが、使える記号と加工された画像サイズが分かりづらかったので調べてみた。

下記の表にまとめてあるので参考になれば。なお、元の画像は横長(400x200px)と縦長(200x400px)の2種類を用意し、それぞれ同じスタイル指定でサムネイル加工している。

スタイル指定とサムネイル加工の動作対応表

スタイル指定 400×200 200×400 備考
100×100 100×50 50×100 100×100>と同じ
100×100! 100×100 100×100 アスペクト比を無視して強制的にサイズを合わせる
100×100> 100×50 50×100 長辺が指定サイズに合うように縮小しアスペクト比は維持する
100×100^ 200×100 100×200 短辺が指定サイズに合うように縮小しアスペクト比は維持する
100×100# 100×100 100×100 短辺が指定サイズに合うように縮小し中央を切り取ってもう一辺も合わせる

実際にサムネイル化された画像を含めて示すと以下。

元画像とサムネイル画像加工後対応表

スタイル指定 元画像(400×200)
400x200
元画像(200×400)
200x400
100×100 400x200_square 200x400_square
100×100! 400x200_square_ignore_aspect 200x400_square_ignore_aspect
100×100> 400x200_square_adjust_longer 200x400_square_adjust_longer
100×100^ 400x200_square_adjust_shorter 200x400_square_adjust_shorter
100×100# 400x200_square_croped_center 200x400_square_croped_center

スポンサード

コード解説:Gunosyは多くのユーザに同じ記事を配信しているのか検証してみた

Gunosyは多くのユーザに同じ記事を配信しているのか検証してみた

はじめに

昨日、Gunosyは多くのユーザに同じ記事を配信しているのか検証してみたという記事を書いた。

ランダムにピックアップしたGunosyユーザの特定の日の配信記事リストから、記事URLを使って重複をカウントし、本当に重複記事数が著しく多かったり、ユーザ間の重複率が批判の通りなのかを検証したものだ。

こちらでは、技術ブログなので検証に使ったコードを公開して簡単に解説しよう。

技術的な方針

今回は、素早く分析するためにフレームワークなどは使わず、捨てる前提のスクリプトを以下の方針で書いた。

  1. Google Custom Search APIによりGunosyの公開ユーザ名を取得
  2. (1)で取得したユーザに特定の日(今回は5/5)に配信された記事のURLとタイトルを取得
  3. ユーザ間の重複数を記事毎に数えてファイルに書き出す

事前準備

Google Custom Search APIにより、gunosy.com内の公開ユーザ名をリストアップする。
ここはコードにしても良かったが、Google APIの制約上、上位100件までしか取得できないことが分かっていたため、ブラウザからAPIにパラメータを変えて10回アクセスし、それぞれの結果を1.jsonから10.jsonとしてローカルに保存した。(これを後述のimport.rbから読み込む)

Google Custom Search APIの使い方は、Getting Started with the API – Custom Search — Google Developersに書いてある順番通りに作業すれば使えるようになると思うので試してみてほしい。

以下のINSERT-YOUR-KEYの部分を自身のKEYに変えてリクエストすると試せると思う。
なお、2ページ目以降を取得する際に変更するパラメータはstartだ。

https://www.googleapis.com/customsearch/v1?key=INSERT-YOUR-KEY&cx=016434714500670441010:kxhgwdwqsza&q=site:gunosy.com&start=1

今回は、1.jsonから10.jsonを含むファイル群を後述のRubyファイルと共にGithubに上げてあるので、試す人はそちらを使うと良いと思う。

noriaki/validate-gunosy at 20130506 · GitHub

モデル (ORM)

データベースには、MongoDBを利用した。ローカルにMongoDBがインストールされていて、ポート27017(デフォルト設定)で起動していることを前提にしている。

user.rb

class User
  include Mongoid::Document
  field :name

  has_many :recommendeds, dependent: :destroy

  def url(date=Date.yesterday)
    "http://gunosy.com/#{name}/#{date.strftime("%Y/%m/%d")}"
  end
end

ユーザ名をnameカラムとして保持する。
また、urlメソッドにより、特定の日にユーザへ配信されたGunosy上の記事一覧ページURLを作っている。

article.rb

class Article
  include Mongoid::Document
  field :url, type: String
  field :title, type: String

  has_many :recommendeds, dependent: :destroy
  has_many :pickings, dependent: :destroy
end

こちらもUserモデルと同様の形式で、配信された記事URLとタイトルを保持している。

recommended.rb

これは、UserモデルとArticleモデルを多対多でつなぐためのモデルで、両モデルのIDだけを保持している。本来のKVSの使い方とは違うかもしれないが、自分としては分かりやすいので良くこういった書き方をする。

class Recommended
  include Mongoid::Document
  field :user_id
  field :article_id

  belongs_to :user
  belongs_to :article
end

事前準備したデータを読み込む (Import)

import.rb

このファイルは以下のように単体で実行すると、既存DBをクリアして事前準備したデータを読み込む。

% ruby import.rb
require 'json'
require 'open-uri'
require 'nokogiri'
require 'mongoid'
require 'csv'
require 'kconv'

$LOAD_PATH.push '.'
require 'user'
require 'article'
require 'recommended'

Mongoid.configure do |conf|
  conf.master = Mongo::Connection.new('localhost', 27017).db('validate_gunosy')
end

if __FILE__ == $0
  Article.destroy_all
  User.destroy_all

  search_results = (1..10).map{ |i|
    JSON.parse(File.open("g/#{i}.json").read)['items'].map do |t|
      t['link']
   end
  }.flatten
  search_results.each do |l|
    name = l[/[^g]\/([^\/]*?)\/?$/,1]
    if name && !%w(gunosy.com signup login iphone).include?(name)
      User.create name: name
    end
  end

  links = User.all.each do |user|
    Nokogiri(
      (open(user.url(Date.parse('2013/5/5'))) rescue StringIO.new).read
      ).css('article h1').each do |e|
      article = Article.find_or_initialize_by url: e.parent['href']
      article.title = e.text.strip
      article.save
      Recommended.create user_id: user.id, article_id: article.id
    end
  end

end

そんなに複雑なことはやっていないが、GitHubにも上げた先述の1~10.jsonを順番に読み込み、ユーザ名を抽出して、それぞれの5/5に配信された記事一覧をスクレイピングして取得している。

irb -r”./import”

最後にirbを以下のようにimport.rbを読み込んで起動し、各種検証用に数字を確認した。

% irb -r"./import"

TSVファイルへ結果を書き出す

上記の通り起動したirb上で以下のコードを実行すると、articles.tsvにデータが出力される。Excel以外で見るときなどは、.tosjis部分を削除すれば文字コードがUTF-8になるはずだ。

CSV.open('articles.tsv', 'w', col_sep: "\t") do |row|
  Article.all.each do |a|
    row << [a.url,a.title.tosjis,a.recommendeds.count]
  end
end

なお、最後になったがRubyのバージョンは以下である。

% ruby -v
ruby 1.9.3p392 (2013-02-22 revision 39386) [x86_64-linux]

さて、はてブのホットエントリーデータも入手できたことなので、次回はさらにはてブとGunosyが似ているという点についても調べてみよう。

スポンサード

名刺管理サービス「Eight」から連絡先情報をエクスポートする

名刺管理サービス「Eight」

はじめに

日々増え続ける名刺の管理には、もうずいぶん前からEightを利用している。
データも正確で検索もし易く相手も利用していれば名刺情報の更新が届くなど非常に便利に使っているが、唯一あったら良いなと思っているのがデータのエクスポート機能だ。

自分が退職するときにも、関わりのあった皆に退職報告をしようと名刺情報をダウンロードしようと思ったが見つからなかった。
登録した名刺情報をエクスポートしたいという要望は有るようだが、まだ機能は実装されていないようだ。

そこで、無ければ自分で何とかするということで、自分の管理している名刺情報をエクスポートするためのJavascriptを書いた。ログインしている画面 (Google Chrome) の Console から実行する簡易コードだ。

2014/03/23追記:Eightサービスがリニューアルして現在は動かなくなっています。対応したものを名刺情報をiPhone連絡先へインポートする方法(またはEightデータをvCard形式でエクスポートするブックマークレットを作りました) – noriaki blog はてな出張所としてエントリ書きました。こちらをご覧ください。

2014/03/23追記:また、コメント欄でMauriceさんからEight / 8card エクスポート Chromeエクステンションを作ったとご連絡いただきました。

ご注意点

  • あくまでも自分の管理している名刺情報(名前、会社、電話番号、メールアドレス)が取得できるだけ
  • 使い方をよく読み、Javascriptコードの内容が理解できる方のみ実行することをオススメする
  • 動作は無保証であり何らかの問題が起こっても責任は取れない
  • Eightサービス側の仕様変更等により使えなくなることもある
  • 利用規約はざっと読んで問題無いと思われるがEightサービスから怒られたら謝る

使い方

  1. EightへGoogle Chromeを使ってログイン
  2. Ctrl + Shift + JなどでConsole画面を開く
  3. 下記のJavascriptコード1を実行する
  4. 100件ずつ名刺情報を取得するので全件取得完了したらJavascriptコード2を実行する
  5. 結果として表示された部分をコピーしてExcel等にペーストする

Javascriptコード1

var res = [];
function recursive_get_data(res, page) {
    $.ajax({
        type: "GET",
        url: request_url({ page: page }),
        dataType: "json",
        success: function(data) {
            var data_array = data.result;
            $.each(data_array, function(i,obj) {
                $.each(obj, function(month,cards) {
                    if(cards.length === 0) { return }
                    $.each(cards, function(j, card) {
                        var person = card.person;
                        var info = person.personal_cards[0].eight_card || person.personal_cards[0].friend_card;
                        var r = [person.id];
                        r.push(info.front_company_name === "" ? info.back_company_name : info.front_company_name);
                        r.push(info.front_full_name === "" ? info.back_full_name : info.front_full_name);
                        r.push(info.front_full_name_reading === "" ? info.back_full_name_reading : info.front_full_name_reading);
                        r.push(info.front_tel1 === "" ? info.back_tel1 : info.front_tel1);
                        r.push(info.front_email === "" ? info.back_email : info.front_email);
                        r.push(month);
                        res.push(r);
                    });
                });
            });
            console.log(data.context, page);
            if(page < data.context.total_pages) {
                recursive_get_data(res, page + 1);
            }
        }
    });
}
recursive_get_data(res, 1);

function request_url(option) {
    return "https://8card.net/people/display_personal_cards.json?0.13670155755244195&_method=get&sort=5&transcribing=0&per_page=100&web=1&use_paginate=1&tags=&index=&keyword=&page=" + option.page;
}

Javascriptコード2

console.log($.map(res, function(array, i) { return array.join("\t"); }).join("\n"));

最後に

Eightさん、いつもありがたく使わせてもらってます。有料機能でも良いのでエクスポート機能を早く提供してください。

スポンサード

WordPress on Herokuでパーマリンクをカスタマイズする方法(あるいはnginxのrewriteルール設定方法)

WordPress on Heroku (nginx) でカスタムパーマリンクを設定する方法を説明する

WordPress on Herokuなら無料でWordPressブログを開設できる

紆余曲折を経て、ブログをWordPressに戻した。
ホスティングはHerokuを利用して無料で運営する魂胆だ。

インストールやデプロイはWordPressの日本語版をHerokuに設置する方法を参考にすると問題無く完了したが、過去の記事へのリンクをNot Foundにしないようにパーマリンク構造を引き継ごうとすると上手くいかなかった。

問題点はカスタムパーマリンクを設定すると記事ページが見られない

記事のリンクURLが
/?p=111といったパラメータ形式であれば問題無くHerokuでもWordPressが動いたのだが、
/category/year/monthnum/day/postnameなどのカスタムパーマリンクを設定するとトップページ以外が404 Not Foundになってしまった。

nginxのrewriteルールは.htaccessでは変更できない?

WordPress on HerokuのGitHubで指定されているように.htaccessを修正しても状況は変わらず。

.htaccess

# BEGIN WordPress

<ifmodule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</ifmodule>

# END WordPress

そもそも.htaccessのrewriteルールが効いてないのでは?と思い、HTTPサーバのnginxの設定ファイルを確認してみることに。

conf/nginx.conf.erbにそのものズバリのコメントが書いてあった

冒頭のWordPress on Herokuでは、Herokuのbuildpackとしてiphoting/heroku-buildpack-php-tylerを利用しているが、ここで読み込まれているnginx用の設定ファイルは自分のgitリポジトリに適切にコピーして書き換えると上書きできる。

方法は、自分のgitリポジトリに
confディレクトリを作り、そこに設定ファイル (
nginx.conf.erb) を置く。

設定ファイルのオリジナルを取得してディレクトリに置く

% mkdir conf
% cd conf
% wget https://raw.github.com/iphoting/heroku-buildpack-php-tyler/master/conf/nginx.conf.erb

nginx.conf.erbでコメントアウトされている部分を有効化

rewriteルールが書かれている部分を探し、コメントアウトされている箇所を以下のように直して保存。Herokuへデプロイしたら404 Not Foundは解消されてちゃんと見られるようになった。

    location / {
      # wordpress fancy rewrites
      if (-f $request_filename) {
        break;
      }
      if (-d $request_filename) {
        break;
      }

      rewrite         ^(.+)$ /index.php?q=$1 last;

      # Add trailing slash to */wp-admin requests.
      rewrite         /wp-admin$ $scheme://$host$uri/ permanent;

    #  # redirect to feedburner.
    #  # if ($http_user_agent !~ FeedBurner) {
    #  #   rewrite ^/feed/?$ http://feeds.feedburner.com/feedburner-feed-id last;
    #  # }
    }