imagemagickをインストール

公式のFAQや、似たような記事は多数あるのですが、それでも嵌ったのでメモ残しときます。

事前準備

Install XQuartz

結果を出力するときに、ウィンドウ(X11)に出す場合は、先にXQuartzをインストールしておく必要があります。

display: delegate library support not built-in `' (X11) @ error/display.c/DisplayImageCommand/1909.
Unsatisfied dependency: XQuartz·
Homebrew does not package XQuartz. Installers may be found at:
  https://xquartz.macosforge.org

こんなエラーが出る場合は、XQuartzが入ってないです。
エラーが教えてくれている通りに、 https://xquartz.macosforge.org からインストールします。

Install libtool

なかったら入れておきます。

sudo brew install libtool

動かしたときに、以下のエラーが出る場合はリンク張りましょう

Library not loaded: /usr/local/lib/libltdl.7.dylib (LoadError)
sudo brew link --force libtool

set display

表示先を指定しておきます

export DISPLAY=:0.0

Install imagemagick

デフォルトだと、X11(XQuartz)利用しないので、オプションに指定してインストールします。

sudo brew install imagemagick --with-x11

お疲れ様です。インストール完了です。

動かしたときに、以下のエラーが出る場合は、X11が起動してません。

`display': unable to open X server `' @ error/display.c/DisplayImages/1666 (Magick::ImageMagickError)

/Applications/Utilities/XQuartz をダブルクリックして起動してやりましょう。


やれやれ、インストールで力つきちゃったよ。。。

バイナリ形式にするだけでメモリ節約プログラミング

比較条件に関係しない値なら、バイナリにする価値あり

小さい数値を多く扱う場合は、バイナリ(可変長表現)にすると結構小さくできます。

データ量が小さくなれば、DBからの取得速度が向上し、アプリのメモリ上にもより多くのデータを保持できるようになります。

ただ、バイナリ形式のままだと大小比較などの計算ができないので、必要に応じて数値型に戻してあげる必要があります。

コード

今回比較に利用したコードは、githubに上げてあります。

https://github.com/TakiTake/seiseki

数値とバイナリの速度比較

学校の生徒100人毎日テストを5科目受けたときの成績管理用DBを想定

バイナリ形式のDBは、科目カラムに5科目分のデータを保持させます。

テストデータなんで、土日祝日お構いなしにテスト受けてますw

ノーマルなDB

カラム名
日付 text
学籍番号 text
科目1 integer
科目2 integer
科目3 integer
科目4 integer
科目5 integer

バイナリ形式のDB

カラム名
日付 text
学籍番号 text
科目 blob

結果

1年分のデータ

size

normal  1.9MB
binary  1.7MB

insert

user     system      total        real
normal  4.740000  11.920000  16.660000 ( 94.378284)
binary  4.660000  11.920000  16.580000 ( 93.093927)

select

user     system      total        real
normal  0.210000   0.010000   0.220000 (  0.221661)
binary  0.190000   0.010000   0.200000 (  0.201906)
5年分のデータ

size

normal  9.6MB
binary  8.8MB

insert

user     system      total        real
normal 23.860000  58.860000  82.720000 (476.608330)
binary 23.530000  58.400000  81.930000 (507.330652)

select

user     system      total        real
normal  1.190000   0.070000   1.260000 (  1.300539)
binary  0.980000   0.070000   1.050000 (  1.078891)
10年分のデータ

size

normal  19MB
binary  18MB

insert

user     system      total        real
normal 47.350000 118.030000 165.380000 (920.600014)
binary 46.610000 118.380000 164.990000 (935.340207)

select

user     system      total        real
normal  2.140000   0.100000   2.240000 (  2.236974)
binary  1.790000   0.100000   1.890000 (  1.891564)

なぜ、サイズが小さくなるのか?

小さい数値を表すのに、8 byte もいらないよね

環境差分はありますが、自分の環境ではRubyのFixnumは、8 byte でした。

0~255までの数値なら 1byte で表すことができるので、固定長ではなく可変長で数値を扱えば、 7byte 削減できというわけです。

どうやって変換すればいいの?

数列 -> バイナリ文字列

Array#pack のテンプレートに w(BER-compressed integer) を指定するだけです。

http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-pack

バイナリ文字列 -> 数列

String#unpack テンプレートに w(BER-compressed integer) を指定するだけです。

http://www.ruby-doc.org/core-1.9.3/String.html#method-i-unpack

応用編

ただの文字列なので、文字列結合もできます

  [33, 49, 300, 99, 1000].pack('w5') // "!1\x82,c\x87h"
  'abc' + [33, 49, 300, 99, 1000].pack('w5') // "abc!1\x82,c\x87h"

また、こういった文字列処理には、StringScannerが便利です。

http://www.ruby-doc.org/stdlib-1.9.3/libdoc/strscan/rdoc/StringScanner.html

先頭から、3 byte 取得

  str = 'abc' + [33, 49, 300, 99, 1000].pack('w5')
  ss = StringScanner.new(str)
  ss.peek(3) // "abc"

3 byte 分ポインタを進める

  ss.pos += 3

ここから後ろは、可変長なので何byte分取得すればいいかわかりません。

そんなときは、文字列中にbyte数を持たせてあげれば、そこから判断できます。

サイズを含む文字列

  packed = [33, 49, 300, 99, 1000].pack('w5')
  
  # packした文字列のサイズをさらに、packする
  # C(8-bit unsigned)にすることで、この1byteを読めば続く文字列のサイズが分かる
  # 文字列のサイズが、8bitで表しきれない場合は、テンプレートにS, L, Qを適宜使う
  packed_size = [packed.size].pack('C')
  
  str = 'abc' + packed_size + packed
  ss = StringScanner.new(str)
  
  # "abc"の3byte分進める
  ss.pos += 3
  
  # 1byte取得して、unpackすることで保存しておいたサイズを取得
  # unpackは、配列で返ってくるので、最初の要素を取得している
  ss.peek(1).unpack('C').first // 7
  
  # サイズを格納している、1byte分進める
  ss.pos += 1
  
  # 最後に、先ほど判明した7byte分取得して、unpackすると
  # めでたく最初の数列が取得できる
  ss.peek(7).unpack('w5') // [33, 49, 300, 99, 1000]

heroku上にsinatraで作ったアプリ上げるときのハマりどころ

herokuもsinatraも初心者な自分がハマった所と、対処法書いときます。

基本は、ユーザ登録をして
https://devcenter.heroku.com/articles/ruby
の通りに進めていけば良いのですが

HTMLのテンプレート使いたい

風の噂で、hamlがサクサク書けて良いと聞いたので、hamlを選択

Gemfileにhamlを追加
gem 'haml'
bundle installでhamlをインストール
bundle install
APPファイル(web.rb)でhaml読み込み
require 'haml'


これで、準備OK。

hamlテンプレートを指定する
get '/' do
  haml :index
end

と書いてやれば、index.hamlファイルをテンプレートとして利用可能。

# -*- coding: utf-8 -*-

require 'rubygems'
require 'sinatra'
require 'haml'

# ここだと、渡せないよ
@hoge = "この位置で宣言しても渡せません"

get '/' do
  # ここで、宣言すれば
  # 変数の受け渡しも可能
  @foo = "bar"
  haml :index
end
テンプレートはどこに置けば良いの?

sinatraのデフォルトだと、viewsディレクトリを見に行くらしいので

mkdir views

ディレクトリ作成。
viewsの"s"をお忘れなく。

cssとかは?

静的ファイルは、publicディレクトリ

mkdir public

同じようなコードで毎回テンプレート名指定するのがめんどくさい

パスと同じテンプレートファイル名ならば、これでいけた。

get '/:path' do
  path = params[:path]
  haml path.intern
end

hamlでちょっと凝ったことしたい

HTML5のフォーマットで書きたい

hamlのrequire文の下辺りに

set:haml, :format => :html5

で、フォーマットを指定する。

HTMLエスケープも自動でしたい

エスケープ設定も追加で

set:haml, :format => :html5, :escape_html => true
共通部分はレイアウトとしてまとめたい

layout.hamlという名前でレイアウトファイルを作成すれば
自動的にレイアウトとして認識してくれます。
ビューファイルは、= yieldで呼べる。
以下、body宣言までのレイアウトが、ビューを呼ぶ例。

!!!
%html{:lang => "ja"}
  %head
    %meta{:charset => "UTF-8"}
  %body
    / ここでビューを呼ぶ
    / =ではなく、!=にしないとエスケープされるので注意
    != yield

呼ばれるビューファイル側のインデントは、インデント無しの状態から始める。
bodyの子要素だからと、インデント2つ分から始めたら怒られた。

要素の属性値がたくさんあって、1行が長くなるのがいや

rubyのハッシュに詰めてから渡すと多少改善される

:ruby
  opts = {
    :src => "https://www.google.com/calendar/embed?src=XXX
    :style => "border: 0",
    :width => "800",
    :height => "600",
    :frameborder => "0",
    :scrolling => "no"
  }
%iframe{opts}

herokuでアプリの名前変えたらコミットできなくなっちゃった

# 現在の設定確認
% git remote -v 
heroku     git@heroku.com:strong-day-3354.git (fetch)
heroku     git@heroku.com:strong-day-3354.git (push)
# 変更
# アプリ名をAAAに変えた場合
% git remote set-url heroku git@heroku.com:AAA.git
# 反映確認
% git remote -v                                    
heroku     git@heroku.com:AAA.git (fetch)
heroku     git@heroku.com:AAA.git (push)

感想

使ってみての感想は、sinatraは記述量が少なくて便利。
命名規則は知らないとさっぱり。


hamlは、噂通りサクサクかける。
<、>と閉じタグ書かなくて良いのが楽。
rubyコードと記法が似ているから、覚えやすい。


裏側は、サクサクだな。
後は、表のデザインセンスがあれば、、、

社内の開発環境とローカルの開発環境の連携方法

どんな時に便利か?

全てのサービスをローカル環境として構築できれば良いのですが
それがあまり現実的ではなく、基本は外部のアプリを利用して
一部アプリだけローカルのアプリを利用したいとい時に便利です。

ローカル環境構成図

PAC(Proxy Access-Control)ファイルを利用して
まずは、ローカルとそれ以外へのアクセスを振り分けています。


ローカルでは、nginxがアクセスを集約しています。
これは、80番や443番ポートを複数のアプリで利用するためで
ホスト名によって各アプリへリクエストの振り分けをしています。


PACファイル

開発環境用に社内プロキシが無い場合は、設定不要です。
JavaScriptの文法で、proxyの設定が書けるファイルです。


Firefoxだと、下記の場所からから設定できます。
オプション > ネットワーク > 接続設定 > 自動プロキシ設定スクリプト URL


proxyを通さない場合(今回はローカル環境)は、"DIRECT"という文字列を返しておきます。
ドメイン名に"hatena.ne.jp"が入る場合は、開発環境用のproxyを通したい。という場合は、
組み込みのshExpMatch関数を使い、host名とマッチングをかけ、PROXYのIPとポートを返します。
セミコロンでproxyもしくは、"DIRECT"をつなげると、順番に接続を試みてくれます。

function FindProxyForURL(url, host) {
  if (isInNet(host, "127.0.0.1", "255.255.255.255")) {
    return "DIRECT";
  } else if (isDevHost(host)) {
    // dev.proxy:9999
    return "PROXY 172.16.0.2:9999";
  }

  // default.proxy:9999
  return "PROXY 172.16.0.3:9999; DIRECT";
}

function isDevHost(host) {
  return shExpMatch(host, "*.hatena.ne.jp");
}

hostsの設定

isInNet(host, "127.0.0.1", "255.255.255.255")でローカル環境として判定をさせるために
/etc/hosts で、ローカルアプリのドメインのIPを、127.0.0.1として登録しておきます。

# local env
127.0.0.1          d.hatena.ne.jp


これで、http://d.hatena.ne.jp/とアクセスした場合は、127.0.0.1:80へのアクセスに
それ以外のはてなドメインは、172.16.0.2:9999のproxy経由でのアクセスが可能になりました。

nginxの設定

ほぼ、デフォルトの設定のままですが、肝はinclude文です。
これで、各アプリに振り分けるようの設定を読み込んでいます。

error_log  /usr/local/var/logs/nginx/error.log;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       params/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /usr/local/var/logs/nginx/access.log  main;

    proxy_set_header Host              $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;

    keepalive_timeout  65;
    sendfile on;
    gzip on;

    include    /usr/local/var/nginx/conf/extra/backends/*.conf;
}
/usr/local/var/nginx/conf/extra/backends/d-hatena-ne-jp.confの場合

d.hatena.ne.jp用のtomcatアプリが、8080ポートで動いている場合は
以下のような設定になります。

server {
    listen      80;
    server_name d.hatena.ne.jp;

    location / {
        proxy_pass http://127.0.0.1:8080;
    }
}


アプリを増やしたい場合は、hostsファイルへの追記とconfファイルの新規作成で簡単に行えます。

魔術でデプロイ

1文字に飽きたら、詠唱でも

準備

# ホームディレクトリへ移動
function I(){ cd ~/ }
# バックアップ
function Steel(){ cp /usr/local/tomcat/webapps/UBW.war ./UBW.war }
# リリースファイルをSTGサーバから取得
function Unknown(){ scp stg:UBW.war ~/UBW.war }
# tomcat停止
function Nor(){ sudo -u tomcat /usr/local/tomcat/bin/shutdown -force }
# アプリ削除
function Have(){ sudo -u tomcat rm -rf /usr/local/tomcat/webapps/UBW }
# warファイル入れ替え
function Yet,(){ sudo -u tomcat cp ./UBW.war /usr/local/tomcat/webapps/UBW.war }
# tomcat開始
function So(){ sudo -u tomcat /bin/sh usr/local/tomcat/bin/startup.sh }

詠唱

I am the bone of my sword.
Steel is my body, and fire is my blood.
I have created over a thousand blades.
Unknown to Death.
Nor known to Life.
Have withstood pain to create many weapons.
Yet, those hands will never hold anything.
So as I pray, unlimited blade works.

1文字シリーズ

毎日、毎日タイプするコマンドならば、タイプ数を減らすに限るよね。
抜けてるところには、何を入れよう?

1文字alias

alias a="sudo aptitude"
alias b=""
alias c="cat"
alias d="diff"
alias e="echo"
alias f="find . -type f -name"
alias g="grep"
alias h="head"
alias i="/sbin/ifconfig"
alias j=""
alias k=""
alias l="ls -ltrah"
alias m=""
alias n="node -e"
alias o=""
alias p="perl -wnl -e"
alias q=""
alias r="reset"
alias s="sqlplus user_name/password@host[:port][/service_name]"
alias t="tail -f"
alias u=""
alias v="view"
alias x="xargs"
alias y=""
alias z=""

1文字変数

aliasよりさらにネタが無いな

export a="access_`date +%Y%m%d`.log"
export b=""
export c=""
export d="`date +%d`"
export e="error_`date +%Y%m%d`.log"
export f=""
export g=""
export h=""
export i=""
export j=""
export k=""
export l=""
export m="`date +%m`"
export n=""
export o=""
export p=""
export q=""
export r=""
export s=""
export t=""
export u=""
export v=""
export w=""
export x=""
export y="`date +%Y`"
export z=""

node.jsでsocket通信

みんな大好きエコーサーバ

Install websocket server module

 % npm install websocket-server

Server side

httpとwsプロトコルで、返す値を変えられる。
サーバの監視用にする、クライアント側のコードを返すなどの利用方法が考えられる。

var http = require('http');
var ws   = require('websocket-server');

// http
server = http.createServer(function(req, res){
  res.writeHead(200, {'Content-Type': 'text/html'});    
  res.end("<html><body><h1>It's Works!</h1></body></html>");
});
server.listen(10003);

// WebSocket
var socket = ws.createServer({'server': server});
socket.on('connection', function(conn){
  conn.on('message', function(msg){
    conn.send(msg);
  }); 
  conn.on('close', function(){
    console.log('closed');  
  })  
});

Client side

今回は、サーバのコードとは分けてあるので、httpでアクセスできる場所に適当に置く。
ローカルファイル(file://)だと、セキュリティに引っかかりWebSocket通信ができない。

<script>
  var ws = new WebSocket('ws://127.0.0.1:10003/');
  ws.onopen = function() {
    ws.send('hello world'); 
  }
  ws.onmessage = function(evt) {
    console.log(evt.data);
  }
  ws.onclose = function() {
    console.log('closed');  
  }
</script>