pig's diary

何でも忘れるので万年初心者ね

ArchLinux の Wi-Fi 設定

Mac mini (Late 2012) にパーティション切ってArchLinux を入れている。Wi-Fi 設定の過程で試したことをメモしておく。-- 起動時に自動的にWi-Fi を探すよう加筆。

まず前提として、pacstrap などは Ethernet 経由で入れ、ネットに繋がるようにはしていた。aur パッケージを入れるための yaourt も持っていた。

その上で以下モジュールを入れる。

$ yaourt -S b43-fwcutter b43-firmware crda wireless-regdb
# reboot

再起動する(たぶん b43 のロードに必要)。

続いてwifi-menu設定のためのパッケージを入れる(/etc/netctl/xxx さえ作れればなくてもいいんだろうけど)。

$ yaourt -S dialog wpa_supplicant

必要なものは揃っているので、繋げるか確認。

# modprobe ath5k
# modprobe b43
# ip link  # デバイスIDは wlp2s0 だった
# ip link set wlp2s0 up
# wifi-menu  # これにより /etc/netctl/wlp2s0-networkname が作成される
# ping google.com  # 繋がっていたらOK

b43 はロード済みのはずだが、ath5k のあとに再度ロードしないと動かなかった。ロードの順番があるようだ。

接続が確認できたら、起動時に自動的にWi-Fiを探すよう設定する。起動時に必要なモジュールをロードさせ、Wi-Fiを探すデーモンを自動起動させる。

# echo ath5k > /etc/modules-load.d/ath5k.conf
# echo b43 > /etc/modules-load.d/b43.conf
# pacman -S wpa_actiond                    # netctl-auto のため
# systemctl enable netctl-auto@wlp2s0

これにより、先ほどwifi-menuで作った設定が自動的に使われるはず。reboot して起動後、すぐネットに繋がるだろうか?

# reboot

繋がれば成功。

参考:

Solr を導入

Solr を導入しました。インストール先のサーバに合わせたディレクトリ構成です。

このようなディレクトリ構成でインストールしていきます。

# ライブラリ本体(solr.war、jetty-*.jar、その他もろもろの jar 置き場所)
/usr/local/lib/solr-4.6.0

# シンボリックリンク
/usr/local/lib/solr -> /usr/local/lib/solr-4.6.0

# Solr Home ディレクトリ
/var/solr

# ログ(Solr Home からのデフォルト設定)
/var/solr/logs

# 各Solr コアのディレクトリ(Solr Home からのデフォルト設定)
/var/solr/solr/CORE_NAME

# コアごとのインデックスデータ(Solr Home からのデフォルト設定)
/var/solr/solr/YOUR_CORE_DIRECTORY/data

最新の Solr を落とします。24 November 2013 に 4.6.0 がリリースされたようです。

$ su -
$ cd /usr/local/lib
$ wget http://ftp.kddilabs.jp/infosystems/apache/lucene/solr/4.6.0/solr-4.6.0.tgz -O - | tar zxvf -
$ ln -s solr-4.6.0 solr

Solr の読み書き・実行は安全のため、root でなくに常に特定のユーザーにやらせます。ここでは www-data というユーザーを使います。

$ chown -R www-data:www-data solr solr-4.6.0

続いて、コアごとの設定ファイルを増やしていくためのディレクトリを用意します。

$ cd /var
$ mkdir solr

Solr のサンプルの中から、最もベーシックな設定例をコピーして利用します。

$ cp -R /usr/local/lib/solr-4.6.0/example/solr /var/solr/
$ chown -R www-data:www-data /var/solr
$ cd solr

コピーした設定では、collection1 というコアが 1個だけ宣言されています。まずはこれを起動してみます。

/var/solr/solr/collection1/conf/solrconfig.xml を編集し、ライブラリのパスを書き換えます。

75,76c75,76
<   <lib dir="../../../contrib/extraction/lib" regex=".*\.jar" />
<   <lib dir="../../../dist/" regex="solr-cell-\d.*\.jar" />
---
>   <lib dir="${solr.solr.lib}/contrib/extraction/lib" regex=".*\.jar" />
>   <lib dir="${solr.solr.lib}/dist/" regex="solr-cell-\d.*\.jar" />
78,79c78,79
<   <lib dir="../../../contrib/clustering/lib/" regex=".*\.jar" />
<   <lib dir="../../../dist/" regex="solr-clustering-\d.*\.jar" />
---
>   <lib dir="${solr.solr.lib}/contrib/clustering/lib/" regex=".*\.jar" />
>   <lib dir="${solr.solr.lib}/dist/" regex="solr-clustering-\d.*\.jar" />
81,82c81,82
<   <lib dir="../../../contrib/langid/lib/" regex=".*\.jar" />
<   <lib dir="../../../dist/" regex="solr-langid-\d.*\.jar" />
---
>   <lib dir="${solr.solr.lib}/contrib/langid/lib/" regex=".*\.jar" />
>   <lib dir="${solr.solr.lib}/dist/" regex="solr-langid-\d.*\.jar" />
84,85c84,85
<   <lib dir="../../../contrib/velocity/lib" regex=".*\.jar" />
<   <lib dir="../../../dist/" regex="solr-velocity-\d.*\.jar" />
---
>   <lib dir="${solr.solr.lib}/contrib/velocity/lib" regex=".*\.jar" />
>   <lib dir="${solr.solr.lib}/dist/" regex="solr-velocity-\d.*\.jar" />

${solr.solr.lib} という変数は私が適当につけた変数で、あとで Jetty を起動するときに引数を与えることで値が入ります。

Jetty は tomcat のようなサーブレットコンテナの1つで、他のプロジェクトに埋め込みやすいのが特徴のようです。今回は Jetty も含め "Solr サーバ" として扱いたいと思います。

Solr をサービスとして起動するための service スクリプトを書きます。以下のスクリプトは http://stackoverflow.com/a/8189312/804314 を元に作りました。あくまで「Solr 付属の Jetty で Solr を起動する」という書き方になっています。適宜書き換えてご利用ください。

それでは起動します。

$ sudo service solr start

Webブラウザで http://localhost:8983/solr にアクセスし、管理画面を確認してください。

f:id:piglovesyou:20131125160554p:plain

左下のプルダウンから、宣言されているコアを選択することができます。

次に、コアを増やしてみます。既存のコアをフルコピーし、data フォルダを削除してください。

$ cd /var/solr/solr
$ cp -R collection1 yasai
$ rm -rf yasai/data  # 既存のデータが大きいと大変かも・・
$ vi core.properties       # コアの名前を変更します。

コアの名前を書き換えます。

1c1
< name=collection1
---
> name=yasai

Solr を再起動します。

$ sudo service solr restart

新しいコア yasai が確認できます。

f:id:piglovesyou:20131125161920p:plain

以上で、Solr でコアを増やしていく準備ができました。あとは使うアプリに従って、コアごとに主に schema.xmlスキーマ定義) 、 data-config.xml(データインポート時のカラム対応やSQLなど) をいじるだけです。

ところで、各コアのディレクトリ内に共通化できそうなファイルがたくさんあります。これを外に出す方法がわかりませんでした。もし知っている方がいたら教えてください。

sed でマッチした行だけ標準出力する

マッチした行だけを出力するためには -n オプションと p コマンドの両方が必要。

$ sed -n -e '/nobody/p' /etc/passwd
nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false

そもそも sed は、取り込んだ全てのデータを標準出力するデフォルト挙動がある。それを -n オプションによって抑制することができる。これとは別に、p コマンドはマッチした行を出力する。

参考: SoftwareDesign 2013年9月号

arch, gnome, terminal, vim, tmux, clipboard setup

arch 入れた。gnome 入れた。ターミナルで vim 使う。* にヤンクする。クリップボードにコピーされない。悲しい。

$ vim --version
...
-clipboard
-xterm_clipboard
...

コピーされそうな気配が無い。オプションを足して、コンパイルしなおす。

クリップボードオプション有りで Vim をコンパイルする

$ sudo abs extra/vim
$ cd /var/abs/extra/vim  # 私は /var/abs 配下で一般ユーザーに編集権限を持たせてます

abs 最高。tarball 触りたくない。

.. vim-build .. ./configure らへんにコンパイルオプションを追加する。

$ vi PKGBUILD
...
    --with-x \      # これを追記しました
    --with-gnome \  # これを追記しました

makepkg でコンパイル。-s は依存解決を含める。-i はそのままインストールの「イ」。

$ makepkg -s -i

古いバイナリをきれいさっぱり置き換えてくれる感じがすごくいい・・

$ vim --version
...
+clipboard
+xterm_clipboard

ヨッシャー。

ここまでで、vim の * レジスタが X のクリップボードと仲良くなってるはず。(vim の clipboard オプションに unnamed が必要)

tmux のコピーモードを Vi にし、コピーしたものをクリップボードにつっこむ

つづいて tmux で vim みたいなコピーができるようにもする。tmux は最近使い始めたターミナル系便利ツール。べんり。

要るもの。

$ sudo pacman -S tmux xclip

tmux が 1.8 以上であることを確認。でも arch を使っているとパッケージが古いと感じることが無い。debian とは違う。

$ tmux -V
tmux 1.8

.tmux.conf を編集。

$ vi ~/.tmux.conf

# emacs じゃなくて vi モードを使います
set-window-option -g mode-keys vi

# コピー開始を Vim のヴィジュアルモード開始みたいにします
bind-key -t vi-copy 'v' begin-selection

# コピー終了を Vim のヴィジュアルモードヤンクみたいにします ついでに内容を X のクリップボードに突っ込みます
bind-key -t vi-copy 'y' copy-pipe "xclip -i -selection clipboard"

# スペースキーのコピー開始を unbind します
unbind -t vi-copy Space

# お好みで。
# unbind -t vi-copy Enter

tmux で config をリロードする(PREFIX + :source-file ~/.tmux.conf)。 するとコピーモード開始後(PREFIX + [ )、画面をVim のように動き回れるではないか。/ で検索もできるし、v で選択開始、y でコピーもできるではないか。ヨッシャーもうこれ。

iTerm, Vim, Solarized, command-t

MacVim.app と Terminal.app を交互に切り替えて使っていたけど、iTerm を使うと全画面でターミナル、Vim を表示できるらしい。

  1. iTerm2 をインストール
  2. さて Vim を起動
    • :colorscheme solarized
    • 色がなんか変。iterm 側に色のプリセットを入れる必要があるらしい。
  3. iterm2-colors-solarized
  4. さて Vim
    • そういえば command-t が使えない(MacVim.app では動いていた、という前提です)
    • mac デフォルトの /usr/bin/vim は --enable-rubyinterp (ruby support) でコンパイルされてない。
    • $ brew install vim -f
      • brewmac のコマンドを大切に思っているので --force オプションを付けないと Vim をインストールできない
      • brew で入る Vim には ruby surrpot が付いている。他には必要ないのでデフォルトのままで行く。
    • /Applications/MacVim.app を消しておく
    • 欲しい Vim が入った。
  5. $ which vim
    • /usr/bin/vim 。古いまんまだ
    • $PATH の順番を変えたい
    • $ sudo vim /etc/paths
      • /usr/local/bin を上に持って来て保存
    • シェルを立ち上げ直せば /usr/local/bin/vim を先に見られるようになってる


Command-d で縦分割、Command-] でペイン移動できるようになった。"mouseless copy" とか便利そう。


参考:

マージャンのやつ JavaScript

あなたのスキルで飯は食えるか? 史上最大のコーディングスキル判定
http://www.itmedia.co.jp/enterprise/articles/1004/03/news002_2.html

※トータルで1週間以上かかっているorz

var assert = require('assert');



var test = function(results, answers) {
    if (results == null && answers == null) return;
    assert.equal(results.length, answers.length);
    answers.forEach(function(answer) {
        assert(~results.indexOf(answer));
    });
};



var machi = (function() {
        
    var format = function(answer) {
        var s = '';
        answer.grouped.sort().forEach(function(g) {
            s += '(' + g + ')';
        });
        return s + (answer.rest ? '[' + answer.rest + ']' : '');
    };

    var collectFlattens = function(input, enough) {
        var regexp = new RegExp('(.)\\1{' + (enough - 1) +'}', 'g');
        return input.match(regexp);
    };
    test(collectFlattens('112224588899', 2), ['11', '22', '88', '99']);
    test(collectFlattens('12345', 2), null);
    test(collectFlattens('1112224588899', 3), ['111', '222', '888']);
    test(collectFlattens('124589', 3), null);

    var collectAscendings = function(input, enough) {
        var results = [];
        var collected;
        var curr;
        var foundIndex;
        var isEnough = function() {
            return collected && collected.length >= enough;
        };
        while (input.length >= 2) {
            collected = '';
            curr = 0;
            foundIndex = -1;
            while (~(foundIndex = input.indexOf(+input[curr] + 1, curr + 1))) {
                if (!collected) collected = input[curr];
                collected += input[foundIndex];
                if (isEnough()) break;
                curr = foundIndex;
            }
            if (isEnough()) results.push(collected);
            input = input.slice(1);
        }
        return results.length >= 1 ? results : null;
    };
    test(collectAscendings('1122335556799', 3), ['123', '123', '567', '567', '567']);
    test(collectAscendings('112255578', 3), null);

    var getRest = function(collected, original) {
        collected.split('').forEach(function(letter) {
            original = original.replace(letter, '');
        });
        return original;
    };
    assert.equal(getRest('123', '1122335556799'), '1235556799');
    assert.equal(getRest('567', '11225556799'), '11225599');
    assert.equal(getRest('789', '1122555789'), '1122555');

    var hasHead = function(grouped) {
        return grouped.some(function(group) {
            return group.length == 2;
        });
    };
    assert.equal(hasHead(['234', '555', '888']), false);
    assert.equal(hasHead(['234', '555', '88']), true);



    return function(original) {
        var answers = [];
        (function collect(grouped, input) {

            var finalizer = function(candidates) {
                if (!candidates) return;
                candidates.forEach(function(newGroup) {
                    var newGrouped = grouped.slice(0);
                    newGrouped.push(newGroup);
                    var rest = getRest(newGroup, input);
                    if (rest && (
                            collectFlattens(rest, 3) ||
                            collectAscendings(rest, 3))) {
                        collect(newGrouped.slice(0), rest);
                    } else if (rest.length <= 3) {
                        var heads = collectFlattens(rest, 2);
                        if (rest.length == 1 ||
                                (heads && heads.length >= 1) ||
                                collectAscendings(rest, 2)) {
                            var a = format({
                                grouped: newGrouped,
                                rest: rest
                            });
                            if (!~answers.indexOf(a)) answers.push(a);
                        }
                    }
                });
            };

            if (!hasHead(grouped)) {
                finalizer(collectFlattens(input, 2));
            }
            finalizer(collectFlattens(input, 3));
            finalizer(collectAscendings(input, 3));

        })([], original);
        return answers;
    };

})();

test(machi('1112224588899'), [
    '(111)(222)(888)(99)[45]'
]);
test(machi('1122335556799'), [
    '(123)(123)(55)(567)[99]',
    '(123)(123)(555)(99)[67]',
    '(123)(123)(567)(99)[55]'
]);
test(machi('1112223335559'), [
    '(123)(123)(123)(555)[9]',
    '(111)(222)(333)(555)[9]'
]);
test(machi('1223344888999'), [
    '(123)(44)(888)(999)[23]',
    '(123)(234)(888)(999)[4]',
    '(234)(234)(888)(999)[1]'
]);
test(machi('1112345678999'), [
    '(11)(123)(456)(999)[78]',
    '(11)(123)(678)(999)[45]',
    '(11)(345)(678)(999)[12]',
    '(11)(123)(456)(789)[99]',
    '(111)(234)(567)(99)[89]',
    '(111)(234)(789)(99)[56]',
    '(111)(456)(789)(99)[23]',
    '(123)(456)(789)(99)[11]',
    '(111)(234)(567)(999)[8]',
    '(111)(234)(678)(999)[5]',
    '(111)(345)(678)(999)[2]'
]);

Ti ヘッダ 取りにくい

検索クエリ「Ti ヘッダ 取りにくい」の方にのみお送りしております。

私の場合 Titanium でHTTP 通信したくなった結果こういうことになりました。

気づいたことなど

  • iPhone だと、getAllResponseHeaders できないらしい。(動きとドキュメント見た限り)だから、ヘッダは個別に取得する必要がある。
  • iPhone シミュレータで、 req.getStatus() するとどうしてもエラーになる。req.status すると素直に取れた。ドキュメントの嘘つき・・。
  • Titanium Studio についてる Ajax Monitor は、ブラウザの通信しか教えてくれない。Ti.Network.createHTTPClient でやるなら、WireShark とか使う必要がありあそう。
  • いろいろとたまに動かなくなって、JavaScript がミスってるのか、不可解なTitanium のエラーなのかが分からなくなってきてしんどい。慣れてきたけど。
  • 非同期管理モジュール q を使ってみた。node 系のモジュールがそのまま使えるからありがたい。
  • 追記)cookie は勝手に管理してくれているようだ。
  • 追記)Titanium Studio を開くたびに「アップデートがあるよ」と言われる。
// 使用例

var http = require('http');

// リクエストを送る
http.post('http://localhost:3000/post', {
    title: 'タイトル',
    body: '本文'
})
.then(function(res) {
    console.log(res.getStatus()); // 200 とか
    console.log(res.getJson()); // responseText の JSON.parse 結果
    console.log(res.getHeader('Content-Type')); // 'application/json;charset=UTF8'
})
.fail(function(reason) {
    console.log(reason); // 失敗した理由
});

http.js の中身

var Q = require('../libs/q');
var Response = require('./response');
var _ = require('../libs/underscore');
var URL = require('../libs/url');
var querystring = require('../libs/querystring');



/**
 * @param {string} url .
 * @param {Object} params .
 */
var buildUrlForGet = function(url, params) {
  url = URL.parse(url);
  url.query = _.extend(url.query, querystring.parse(params));
  return url.toString();
};



/**
 * @param {string} method .
 * @param {string} url .
 * @param {Object} params .
 * @return {Object} Promise to response.
 */
var request = function(method, url, params) {
  var defer = Q.defer(),
      usePost = method === 'POST',
      req = Ti.Network.createHTTPClient();

  if (!usePost && params) {
    url = buildUrlForGet(url, params);
  }

  if (usePost) {
    req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  }
  req.setAutoRedirect(false);

  req.onload = function() {
    defer.resolve(new Response(req));
  };
  req.onerror = function(reason) {
    defer.reject({ reason: reason });
  };

  req.open(method, url);
  req.send(usePost && params);

  return defer.promise;
};



/**
 * @param {string} url .
 * @param {Object} query .
 * @return {Object} Promise to response.
 */
var get = function(url, query) {
  return request('GET', url, query);
};

/**
 * @param {string} url .
 * @param {Object} body .
 * @return {Object} Promise to response.
 */
var post = function(url, body) {
  return request('POST', url, body);
};

module.exports.get = get;
module.exports.post = post;

更にクッキーを保存・送信する仕組みも必要だなー。大変だ。

追記)クッキーは自動的に保存しているようだった。2度目に作った HTTPClient には、1度目の responseHeader の Set-Cookie がちゃんと載っている。