vimperatorからKeySnailへの移行
やろうやろうとおもっていたけど特に不便でもなかったのでやってなかったvimperatorからKeySnailへの移行を果たしました。
まじでEmacs使っている人ならKeySnailおすすめです。
とりあえずVimpと比較して嬉しかったところは、以下のとおりです。
- Emacsキーバインドがテキスト編集時にフル活用できる。
- Anythingっぽい絞り込み機能がある。
- Yankが使える。
- Pluginマネージャがよく出来ていて、更新が非常に楽!
- 現在設定されているキーバインドが一覧化できる。
移行してまだ2日ぐらいですが、おそらくもうVimpには戻れない体になっています。
というわけで移行時にやったことをめも。
KeySnailのインストール
http://wiki.github.com/mooz/keysnail/keysnail-japaneseからxpiをダウンロードしてFirefoxにドラッグドロップでインストールします。
インストールが終わったら、上部メニューの
ツール→KeySanil→設定ファイルを作成
から新規で設定ファイルを作ります。
Pluginのインストール
プラグインは以下のページにまとめられています。
http://wiki.github.com/mooz/keysnail/plugin
自分が使っているPluginは以下の通り、
Pluginの設定
以下からPluginの説明と使い方を見ることができます。
ツール→KeySnail→プラグインマネージャを開く
だからプラグインマネージャの説明からコピペしたらOKです。
あと以下のtipsページで気になるものがあれば追加します。
(私はSuggestSearchを追加しました。)
http://wiki.github.com/mooz/keysnail/tips-japanese
とりあえず設定を変更したところを載せてみる。
//{{%PRESERVE% //bookmarkを開くときは新規タブで開く //http://d.hatena.ne.jp/mooz/20091213/p1 plugins.options["bmany.default_open_type"] = "tab"; //LocalKeymapの設定 var local = {}; plugins.options["site_local_keymap.local_keymap"] = local; function fake(k, i) function () { key.feed(k, i); }; function pass(k, i) [k, fake(k, i)]; function ignore(k, i) [k, null]; local["^http://www.google.(co.jp|com)/reader/view/"] = [ ["j", null], ["k", null], ["v", null] ]; //tanythinの設定 plugins.options["tanything_opt.keymap"] = { "C-z" : "prompt-toggle-edit-mode", "SPC" : "prompt-next-page", "b" : "prompt-previous-page", "j" : "prompt-next-completion", "k" : "prompt-previous-completion", "g" : "prompt-beginning-of-candidates", "G" : "prompt-end-of-candidates", "D" : "prompt-cancel", // Tanything specific actions "O" : "localOpen", "q" : "localClose", "p" : "localLeftclose", "n" : "localRightclose", "a" : "localAllclose", "d" : "localDomainclose", "c" : "localClipUT", "C" : "localClipU", "e" : "localMovetoend" }; //}}%PRESERVE% //最初から定義されているキーマップを変更 key.setViewKey('C-f', function () { getBrowser().mTabContainer.advanceSelectedTab(1, true); }, 'ひとつ右のタブへ', false); key.setViewKey('C-b', function () { getBrowser().mTabContainer.advanceSelectedTab(-1, true); }, 'ひとつ左のタブへ', false); key.setViewKey('r', function () { BrowserReload(); }, '更新', true); key.setViewKey('h', function () { BrowserBack(); }, '戻る', false); key.setViewKey('l', function () { BrowserForward(); }, '進む', false); //スクロールの幅を変更 key.setViewKey([["C-n"], ["j"]], function (aEvent) { for (var i = 0; i < 5; i ++){ key.generateKey(aEvent.originalTarget, KeyEvent.DOM_VK_DOWN, true); } }, '一行スクロールダウン', false); key.setViewKey([["C-p"], ["k"]], function (aEvent) { for (var i = 0; i < 5; i ++){ key.generateKey(aEvent.originalTarget, KeyEvent.DOM_VK_UP, true); } }, '一行スクロールアップ', false); //.keysnail.jsの末尾 //追加 //suggestSearch key.setViewKey('s', function (ev, arg) { let engines = util.suggest.getEngines(); // If you want to use all available suggest engines, // change suggestEngines value to util.suggest.filterEngines(engines); let suggestEngines = [util.suggest.ss.getEngineByName("Google")]; let collection = engines.map( function (engine) [(engine.iconURI || {spec:""}).spec, engine.name, engine.description] ); prompt.selector( { message : "engine:", collection : collection, flags : [ICON | IGNORE, 0, 0], header : ["Name", "Description"], keymap : { "s" : "prompt-decide", "j" : "prompt-next-completion", "k" : "prompt-previous-completion" }, callback : function (i) { if (i >= 0) util.suggest.searchWithSuggest(engines[i], suggestEngines, "tab"); } }); }, "Search With Suggest", true); //tab key.setViewKey("t", function (ev, arg) { ext.exec("tanything", arg); }, "タブを一覧表示", true); //hit a hint key.setViewKey('f', function (aEvent, aArg) { ext.exec("hok-start-background-mode", aArg); }, 'リンクをバックグラウンドで開く Hit a Hint を開始', true); //フォーカス操作 key.setGlobalKey(['C-c', 'b'], function (ev, arg) { let elem = document.commandDispatcher.focusedElement; if (elem) elem.blur(); gBrowser.focus(); _content.focus(); }, 'コンテンツにフォーカス', true); //C-gを強化 hook.setHook('KeyBoardQuit', function (aEvent) { // キーシーケンス入力中なら無視 if (key.currentKeySequence.length) return; // 検索バーを閉じる command.closeFindBar(); if (util.isCaretEnabled()) { let marked = aEvent.originalTarget.ksMarked; let type = typeof marked; if (type === "number" || type === "boolean" && marked) { // マークされてるときは、マークの解除だけ command.resetMark(aEvent); } else { // それ以外はフォーカスをコンテンツへ let elem = document.commandDispatcher.focusedElement; if (elem) elem.blur(); gBrowser.focus(); _content.focus(); } } else { // テキスト編集してなければ選択の解除だけ goDoCommand("cmd_selectNone"); } // ブラウザ画面なら ESC キーイベントも投げておく if (KeySnail.windowType == "navigator:browser") key.generateKey(aEvent.originalTarget, KeyEvent.DOM_VK_ESCAPE, true); }); key.setViewKey("d", function (ev) { BrowserCloseTabOrWindow(); }, 'タブ / ウィンドウを閉じる', false); //標準のコマンドが聞かないので代替(たぶん自分の環境のせい?)http://gist.github.com/287576 key.setGlobalKey(["C-x", "k"], function (ev) { BrowserCloseTabOrWindow(); }, 'タブ / ウィンドウを閉じる', false); key.setGlobalKey(["C-c", "u"], function (ev) { undoCloseTab(); }, '閉じたタブを元に戻す', false); key.setGlobalKey(["C-x", "C-c"], function (ev) { goQuitApplication(); }, 'Firefox を終了', true); //bookmark http://d.hatena.ne.jp/mooz/20091213/p1 key.setViewKey([':', 'b'], function (ev, arg) { ext.exec("bmany-list-all-bookmarks", arg, ev); }, "bmany - ブックマークを一覧表示"); key.setViewKey([':', 'B'], function (ev, arg) { ext.exec("bmany-list-bookmarklets", arg, ev); }, "bmany - ブックマークレットを一覧表示"); key.setViewKey([':', 'k'], function (ev, arg) { ext.exec("bmany-list-bookmarks-with-keyword", arg, ev); }, "bmany - キーワード付きブックマークを一覧表示"); key.setViewKey('g', function (aEvent) { getBrowser().selectedTab = getBrowser().addTab("http://google.co.jp/reader/view/"); }, 'GoogleReaderを開く');
objective-cのキー値コーディングでvalueにBOOLを使う方法
objective-cのキー値コーディングでBOOLをやり取りする方法がわからなくて少しはまったのでメモ。(iPhoneのuserDefaultの方法を使えると思い込んでた。。。)
そもそもsetValue forKeyでつかうvalueはid型。つまりオブジェクトじゃなきゃいけないけどBOOLはプリミティブ型なのでそのままじゃ出し入れできない。
そこでつまったんですが以下の方法でできました。わかってしまえば当たり前ですね。
//id paramsのプロパティhogeにBOOL(YES)を設定 [params setValue:[NSNumber numberWithBool:YES] forKey:@"hoge"]; //id paramsのプロパティhogeからBOOLを取得 NSNumber *i = (NSNumber *)[params valueForKey:@"hoge"]; BOOL b = [i boolValue];
BOOLはC言語のtrue,falseのエイリアスなので、0ならNO,0以外ならYESなのでNSNumberオブジェクトに変換してやり取りすればOKです。
mongoDBをrubyからmongo使う(mongo-acriverecord-ruby)
mongo-acriverecord-rubyを使うと、mongoDBでactiverecordチックにアクセスできる。
http://github.com/mongodb/mongo-activerecord-ruby:mongo-activerecord-ruby
本当のactiverecordから使うならhttp://github.com/mongodb/activerecord-mongo-adapter:activerecord-mongo-adapterがいいみたいです。
せっかくのドキュメント指向なのにRDB風に使うのはどうなの?とか思いますが。
インストール
前提としてhttp://d.hatena.ne.jp/amacou/20091205/1260035177:昨日の作業はやっていて、デーモンも上がっている状態です。
gemcutterすでにインストールしてるなら以下は省略。
$ sudo gem install gemcutter $ sudo gem tumble
でインストール
$ sudo gem install mongo_record
使ってみる
では、ありがちなblogっぽいDBの操作をやってみます。
まずはPostクラスを作ります。
#post.rb require "mongo" require "mongo_record" require File.join(File.dirname(__FILE__), 'comment') class Post < MongoRecord::Base collection_name :posts fields :title, :content has_many :comments, :class_name => "Comment" def to_s "title: #{self.title} content: #{self.content}" end end
つぎにCommentクラスを作ります。
#comment.rb require "mongo" require "mongo_record" class Comment < MongoRecord::Base collection_name :comments fields :name, :content belongs_to :post def to_s "name: #{self.name}, content: #{self.content}" end end
上記を使うMainファイルを作ります。
#blog.rb # -*- coding: utf-8 -*- require "mongo" require "mongo_record" require File.join(File.dirname(__FILE__), 'post') require File.join(File.dirname(__FILE__), 'comment') MongoRecord::Base.connection = Mongo::Connection.new.db('blog') #とりあえず空にしとく Post.destroy_all Comment.destroy_all def show puts "" puts "=" * 50 Post.find(:all).each do |pos| puts "日記" puts " #{pos}" pos.comments.each do |com| puts " コメント" puts " #{com}" end end puts "=" * 50 puts "" end puts "日記をつける" post = Post.new(:title => "うんこ", :content => "きょうはおおきなうんこがでた" ) post.save show puts "コメントをつける(belogs_to を利用)" comment = Comment.new(:name => "通りすがりさん", :content => "わたしもおおきなうんこでした", :post => post) comment.save show puts "コメントをつける2( << を利用)" comment2 = Comment.create(:name => "通りすがりさん", :content => "わたしはコロコロのうんこでした") post.comments << comment2 post.save show puts "自作自演日記をつける" comment3 = Comment.create(:name => "自作自演さん", :content => "まだだしちゃだめ") post2 = Post.new(:title => "うんこ", :content => "でそう", :date => Date.new, :comments => comment ) post2.save show
で実行
$ ruby blog.rb 日記をつける ================================================== 日記 title: うんこ content: きょうはおおきなうんこがでた ================================================== コメントをつける(belogs_to を利用) ================================================== 日記 title: うんこ content: きょうはおおきなうんこがでた ================================================== コメントをつける2( << を利用) ================================================== 日記 title: うんこ content: きょうはおおきなうんこがでた コメント name: 通りすがりさん, content: わたしはコロコロのうんこでした ================================================== 自作自演日記をつける ================================================== 日記 title: うんこ content: きょうはおおきなうんこがでた コメント name: 通りすがりさん, content: わたしはコロコロのうんこでした 日記 title: うんこ content: でそう コメント name: 通りすがりさん, content: わたしもおおきなうんこでした ==================================================
ソース見てみたら、belongs_toはまだ実装されていないみたいです。
でもhas_manyとか関連はちゃんと動いています。
実データはどうなっているのか確認。
mongoコマンドでインタラクティブにDBを操作できます。
$ mongo MongoDB shell version: 1.1.4 url: test connecting to: test type "help" for help > use blog switched to db blog > show collections comments posts system.indexes > db.posts.find() { "_id" : ObjectId("4b1bcc000b965d203b000001"), "created_at" : "Mon Dec 07 2009 00:21:36 GMT+0900 (JST)", "comments" : [ { "$ref" : "comments", "$id" : ObjectId("4b1bcc000b965d203b000003") } ], "created_on" : "Mon Dec 07 2009 00:00:00 GMT+0900 (JST)", "title" : "うんこ", "updated_at" : "Mon Dec 07 2009 00:21:36 GMT+0900 (JST)", "_ns" : "posts", "updated_on" : "Mon Dec 07 2009 00:00:00 GMT+0900 (JST)", "content" : "きょうはおおきなうんこがでた" } { "_id" : ObjectId("4b1bcc000b965d203b000005"), "created_at" : "Mon Dec 07 2009 00:21:36 GMT+0900 (JST)", "comments" : [ { "$ref" : "comments", "$id" : ObjectId("4b1bcc000b965d203b000002") } ], "created_on" : "Mon Dec 07 2009 00:00:00 GMT+0900 (JST)", "title" : "うんこ", "updated_at" : null, "_ns" : "posts", "updated_on" : null, "date" : /(?:)/, "content" : "でそう" }
commentsの中に配列で関連するcommentのIDを保存している。
ちなみにcommentsは以下のようになってました。
> db.comments.find() { "_id" : ObjectId("4b1bcc000b965d203b000002"), "name" : "通りすがりさん", "created_at" : "Mon Dec 07 2009 00:21:36 GMT+0900 (JST)", "post" : { "$ref" : "posts", "$id" : ObjectId("4b1bcc000b965d203b000001") }, "created_on" : "Mon Dec 07 2009 00:00:00 GMT+0900 (JST)", "_ns" : "comments", "content" : "わたしもおおきなうんこでした" } { "_id" : ObjectId("4b1bcc000b965d203b000003"), "name" : "通りすがりさん", "created_at" : "Mon Dec 07 2009 00:21:36 GMT+0900 (JST)", "created_on" : "Mon Dec 07 2009 00:00:00 GMT+0900 (JST)", "_ns" : "comments", "content" : "わたしはコロコロのうんこでした" } { "_id" : ObjectId("4b1bcc000b965d203b000004"), "name" : "自作自演さん", "created_at" : "Mon Dec 07 2009 00:21:36 GMT+0900 (JST)", "created_on" : "Mon Dec 07 2009 00:00:00 GMT+0900 (JST)", "_ns" : "comments", "content" : "まだだしちゃだめ" }
ドキュメント指向ってちゃんと勉強していないけど、上記のようなデータだと、postもcommentも同じコレクションにしちゃった方がいいのかな?
ちゃんとドキュメント指向を勉強してみたいです。
(参考になるサイトや参考書をご存知のかたは教えてください。)
mongoDBをmacにインストール
ドキュメント指向のDBってやつはどんな感じか知りたくてmongoDBを使ってみることにした。
http://www.mongodb.org/display/DOCS/Home:mongoDB
mongoDBの特徴は以下の通り
先に使ってみた感想を上げてみると、
一般的なRDBと比べてよかったところは
- インストールらくちん
- シンプル
- 柔軟
短所(?)は
- トランザクションがない(たぶん)
ということで、たぶん簡単なシステムだとmongoDBはかなり魅力的だとおもう。
あとオブジェクト指向やアジャイル開発と相性が良さそう。
でもトランザクションとかがガッツリ必要なものは無理っぽい。
rack+sinatra+mongoDBとか素敵な組み合わせに見える
インストール方法
まずダウンロード
http://www.mongodb.org/display/DOCS/Downloads:ここからMac64bitの1.1.4をダウンロードもしくは
wget http://downloads.mongodb.org/osx/mongodb-osx-x86_64-1.1.4.tgz
そんで解凍
tar zxvf mongodb-osx-x86_64-1.1.4.tgz
配置
cd mongodb-osx-x86_64-1.1.4 sudo cp bin/* /usr/local/bin/ sudo cp -r include/* /usr/local/include/ sudo cp lib/libmongoclient.a /usr/lib/
デーモンの起動
mkdir db mongod --dbpath ~/db
これで使えるようにになったはず。
rubyからmongoDBを使ってみる
まずライブラリをインストール
sudo gem install gemcutter sudo gem tumble sudo gem install mongo sudo gem install mongo_ext
そんでgithubにのってたコードを実行
http://github.com/mongodb/mongo-ruby-driver:mongo-ruby-driver
require 'rubygems' require 'mongo' include Mongo #dbにコネクションはる @db = Connection.new.db('sample-db') #コレクションをつくる(rdbでいうスキーマみたいなもんだと思う) @coll = db.collection('test') #コレクションの中身をとりあえず全削除 @coll.remove #insert処理 3.times do |i| @coll.insert({'a' => i+1}) end puts "There are #{@coll.count()} records. Here they are:" @coll.find().each { |doc| puts doc.inspect }
そしたら
$ ruby test.rb There are 3 records. Here they are: {"_id"=>4b1a99c40b965d1e66000001, "a"=>1} {"_id"=>4b1a99c40b965d1e66000002, "a"=>2} {"_id"=>4b1a99c40b965d1e66000003, "a"=>3}
ってなった。かんたん。
ためしに
require 'rubygems' require 'mongo' include Mongo @db = Connection.new.db('sample-db') @coll = @db.collection('test') @coll.remove 3.times do |i| @coll.insert('a' => i + 1) end #ここを追加 @coll.insert('unko' => "hoge") puts "There are #{@coll.count()} records. Here they are:" @coll.find().each { |doc| puts doc.inspect }
ってやったら
$ ruby test.rb There are 4 records. Here they are: {"_id"=>4b1a9a440b965d1e6f000001, "a"=>1} {"_id"=>4b1a9a440b965d1e6f000002, "a"=>2} {"_id"=>4b1a9a440b965d1e6f000003, "a"=>3} {"_id"=>4b1a9a440b965d1e6f000004, "unko"=>"hoge"}
ってなった。おもろい。
CocoaEmacsのコンパイルと設定
ソースの取得とコンパイル
cvs -z3 -d:pserver:anonymous@cvs.savannah.gnu.org:/sources/emacs co emacs cd emacs ./configure --with-ns --without-x make bootstrap make install
フォントとメタキーとかの設定
.emacsに以下を追加
;; for Emacs23 (when (>= emacs-major-version 23) ;;metaをコマンドに割り当てる (setq ns-command-modifier (quote meta)) ;;superをoptionに割り当てる (setq ns-alternate-modifier (quote super)) ;;システムにキーを渡さない (setq mac-pass-control-to-system nil) (setq mac-pass-command-to-system nil) (setq mac-pass-option-to-system nil) (define-key global-map [ns-drag-file] 'ns-find-file) ;;フォントの設定 ;;英字Monaco日本語ヒラギノ丸ゴを使う (setq fixed-width-use-QuickDraw-for-ascii t) (setq mac-allow-anti-aliasing t) (set-face-attribute 'default nil :family "Monaco" :height 120) (set-fontset-font (frame-parameter nil 'font) 'japanese-jisx0208 '("Hiragino Maru Gothic Pro" . "iso10646-1")) (set-fontset-font (frame-parameter nil 'font) 'japanese-jisx0212 '("Hiragino Maru Gothic Pro" . "iso10646-1")) (set-fontset-font (frame-parameter nil 'font) 'mule-unicode-0100-24ff '("Lucida Grande" . "iso10646-1")) ;;日本語と英語を2:1の大きさにするための設定 ;;こだわりがなければコメントアウト ;;ここから (setq face-font-rescale-alist '(("^-apple-Hiragino_Maru_Gothic_Pro.*" . 1.2) (".*osaka-bold.*" . 1.2) (".*osaka-medium.*" . 1.2) (".*courier-bold-.*-mac-roman" . 1.0) (".*monaco cy-bold-.*-mac-cyrillic" . 0.9) (".*monaco-bold-.*-mac-roman" . 0.9) ("-cdac$" . 1.3) )) ;;ここまで )
本、CD、DVD、ゲームなどのコレクションをWEBで管理するPabo-Tanaをリリースしました
Pabo-Tanaとは
Webで本やCDなどを管理するよくあるサービスです。
他のサービスと何が違うの?
管理のしやすさに特化しました。
例えば、
- 検索結果からワンクリックで登録可能
- ワンクリックで削除可能
- コメントや評価ができる
- アイテムを登録時に自動的に分類
- 分類ルールはカスタマイズ可能
ということが可能です。
また、登録したアイテムから新作がでたときに自動的にメールで通知します。
これから更新通知はTwitterと本ブログで行います。
もしよろしければお使いください
登録はこちらから
http://pabo-tana.net
Mysqlのレプリケーション設定メモ
環境
CentOS5
Mysql5
前提
マスターのDBはある程度稼働してデータがたまっている状態
マスター側
スレーブ用のユーザを作成
#mysql -u root -p mysql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO repl@"%" IDENTIFIED BY 'パスワードを記述'; mysql> exit
/etc/my.cnfに設定を追加
(省略) [mysqld] #以下を追加 log-bin=/var/lib/mysql/mysqld-bin server-id=1 (省略)
サーバをリスタート
# service mysqld restart
スナップショットを取得
コンソールを2つ開く
1つ目のコンソールでDBの更新を止める
# mysql -u root -p mysql> FLUSH TABLES WITH READ LOCK;
2つ目のコンソールでデータをtarに固める
# cd /var/lib/mysql # tar zcvf mysql_snap.tgz ./*
終わったらもう一度1つ目に戻ってバイナリログの位置情報をメモ
mysql> SHOW MASTER STATUS; +-------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +-------------------+----------+--------------+------------------+ | mysqld-bin.000015 | 346 | | | +-------------------+----------+--------------+------------------+
メモが終わったら止めていた更新を開始する
mysql> UNLOCK TABLES;
あとはさっき作ったスナップショットをsftpなどでスレーブ側に転送する。
でマスター側は終わり。
スレーブ側
mysqlを停止
# service mysqld stop
転送したスナップショットをmysqlのデータ置き場に解凍
# rm -rf /var/lib/mysql/* #いらないデータを消す(いるデータがある場合バックアップしとく # cp mysql_snap.tgz /var/lib/mysql # tar zxvf mysql_snap.tgz # rm mysql_snap.tgz # rm mysql-bin.*
/etc/my.cnfを編集
[mysqld] #以下を追加もしくは変更 server-id=2
mysqlを起動
# service mysqld start
スレーブの設定
#mysql -u root -p mysql> CHANGE MASTER TO -> MASTER_HOST = 'マスタのホスト名かIP', -> MASTER_USER = 'repl', -> MASTER_PASSWORD = 'さっき設定したパスワード', -> MASTER_LOG_FILE = 'mysql-bin.000015',#さっきメモしたバイナリログのファイル名 -> MASTER_LOG_POS = 346; #さっきメモしたバイナリログのポジション mysql> START SLAVE; mysql> SHOW SLAVE STATUS\G #スレーブがうまく動いているか確認、Slave_IO_Running: YesとSlave_SQL_Running: Yesがあれば一応安心だと思う
このままだとスレーブを再起動したときにlogにエラーが書かれるので起動ファイルを修正
# vi /etc/init.d/mysqld (省略) start(){ touch "$errlogfile" chown mysql:mysql "$errlogfile" chmod 0640 "$errlogfile" [ -x /sbin/restorecon ] && /sbin/restorecon "$errlogfile" if [ ! -d "$datadir/mysql" ] ; then action $"Initializing MySQL database: " /usr/bin/mysql_install_db ret=$? chown -R mysql:mysql "$datadir" if [ $ret -ne 0 ] ; then return $ret fi fi chown mysql:mysql "$datadir" chmod 0755 "$datadir" # Pass all the options determined above, to ensure consistent behavior. # In many cases mysqld_safe would arrive at the same conclusions anyway # but we need to be sure. /usr/bin/mysqld_safe --datadir="$datadir" --socket="$socketfile" \ --log-error="$errlogfile" --pid-file="$mypidfile" \ --relay-log=/var/run/mysqld/mysqld-relay-bin \ #この一行を追加 >/dev/null 2>&1 & ret=$?