2010/06/29

RubyCocoa

先日<http://ashplanning.blogspot.com/2010/06/google.html>のコメントの中でちゃらんさんに
RubyCocoa 入門を教えていただきました。
こちらを経由し
Programming - RubyCocoa wiki
ここを読みながら遊んでみました。

# サンプル通りに警告音を鳴らす。
% irb
>> require 'osx/cocoa'
=> true
>> names = Dir['/System/Library/Sounds/*.aiff'].grep(/([^\/]+)\.aiff/){ |i| $1 }
=> ["Basso", "Blow", "Bottle", "Frog", "Funk", "Glass", "Hero", "Morse", "Ping", "Pop", "Purr", "Sosumi", "Submarine", "Tink"]
>> OSX::NSSound.soundNamed(names[0]).play
=> true

# OSX::NSSound のpublic メソッドをみてみた。
>> OSX::NSSound.methods
=> ["objc_class_method", "_ns_enable_override?", "kvc_wrapper_writer", "objc_instance_method_type", 
... 略

# OSX::NSSound を管理している OSX モジュールのメソッドをみてみた。
# ものすごい量に驚いてしまった!
>> OSX::methods
=> ["CFRelease", "CGColorSpaceRelease", "CGRectIntersectsRect", "frozen?", "CFNumberGetTypeID", "CGAffineTransformConcat", "launch_data_dict_lookup", "CGPaletteCreateWithByteSamples", "CGPDFDocumentAllowsCopying", "CFLocaleCreateCanonicalLocaleIdentifierFromScriptManagerCodes", "to_a"
... 略

# サンプルにあるテキストクラスの生成
>> str = OSX::NSString.stringWithString 'hello'
=> #<NSCFString "hello">

# 'hello'.methods と同じことをしたつもり
>> str.methods
=> ["upcase!", "to_f", "to_yaml", "lines", "sub", "methods", "send", "replace", "empty?", "squeeze", "crypt", "chr", "gsub!", 
... 略

# RubyのString と同じように扱えるようだ。
>> str.upcase
=> #<NSCFString "HELLO">
>> str.reverse
=> #<NSCFString "olleh">

# class String は OSX::NSCFString 
>> str.class
=> OSX::NSCFString

>> str.object_id
=> 216018122

# OSX::NSCFString を str2 に代入
# さっきの str は OSX::NSString.stringWithString だった。
>> str2 = OSX::NSString
=> OSX::NSString

# メソッドの表示
>> str2.methods
=> ["objc_class_method", "_ns_enable_override?", "kvc_wrapper_writer", "objc_instance_method_type", 
... 略

# send でstringWithStringを設定してみる
>> str2.send(:stringWithString, "hi")
=> #<NSCFString "hi">
# ちょっと感動!

# OSX::NSCFString のクラスは?
>> str2.class
=> Class


あ? Class なんだ。

% irb
>> str = String
=> String
>> str.class
=> Class

これと同等てことかな。

+++ memo +++
% sw_vers
ProductName: Mac OS X
ProductVersion: 10.6.4
BuildVersion: 10F569

% ruby -v
ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]

2010/06/27

Blogger Preview

 デザイン->HTMLの編集 画面を表示し
何も編集しないで「プレビュー」をクリックすると
about:blank
になってしまった。まっしろけの画面。はっこれは!? なぜだろう。

Safari の設定を変えて(ポップアップ開く、サイト固有のハックを無効にしない、Runawy JavaScript タイマーを無効にしない、クッキーを常に受け入れる)みたが、やはり about:blank になった。

編集して保存はできたのでよかった。

2010/06/23

launchd.db

一般ユーザが

% launchctl unload -w /System/Library/LaunchAgents/com.apple.midiserver.plist

を実行したとするとその記録はどこにあるんだろうか?

/private/var/db/launchd.db/com.apple.launchd.peruser.XXX/overrides.plist
だった。(XXX は userid)

% cat /private/var/db/launchd.db/com.apple.launchd.peruser.XXX/overrides.plist


一般ユーザの launchctl list の中には
% launchctl unload してもなぜかエラーがかえってきてしまって unload させることができないような agent がたまにある。
そんな場合 overrides.plist に書きこむと確実に unload させることができる。
# 再度ログインしなおすことが必要

+++

as general user

% launchctl list | grep UI

# unload ScreenReaderUI agent
% launchctl unload -w /System/Library/LaunchAgents/com.apple.ScreenReaderUIServer.plist
# => returned error message

# user's id 
% id
uid=XXX(foo) ...

# change directory
% cd /private/var/db/launchd.db/com.apple.launchd.peruser.XXX

# files
% ls
overrides.plist

# look
% cat overrides.plist

# add element
% vi overrides.plit

# logout and login as general user.
% launchctl list | grep UI

vi -i

OS X 10.6.4 で

$ su admin-user
$ vi foo.txt

として保存終了しようとすると

E138: Can't write viminfo file /Users/foo/.viminfo!
Press ENTER or type command to continue

と言われるようになってしまった。いつからこうなったのかはよくわからない。

$ vi -i "NONE" foo.txt
-i オプションをつけて起動するとすんなり終了できた。

GoogleCL post file

GoogleCL から

$ google blogger post --draft --title "test-01" post.txt 

で post する場合

Blogger にログインして
設定->フォーマット->改行の変換を[はい]にしておく必要がある。

こうしておかないと改行のない本文になってしまうようだ。

また --title オプションをつけないでファイルの1行目をタイトルにさせた場合、
実際1行目がタイトルになるんだけど本文にも1行目が残ってしまう...ようだ。

2010/06/21

googleCL

Google のコマンドラインツールを使ってみる をよみまして GoogleCL を試してみました。

(~/home 以下にインストールしたので)

% export PATH=$PATH:/Users/foo/path/to/bin  

投稿してみました。
% google blogger post --title "try-googleCL-1" "command line posting." --tags="google,blogger"
成功しました。

削除してみました。

% google blogger delete --title "try-googleCL-1"
Are you SURE you want to delete post "try-googleCL-1"? (y/N): y
ほんとに削除していいのですか?と訊ねられて y を入力すると投稿は削除されました。

下書きで投稿するには、--draft をつければいいらしい

% google blogger post --draft --title "test-try-googleCL" "command line posting." --tags="test,blogger"
ファイルを指定してみる

% google blogger post post.txt

2010/06/16

refe.rb を読む

これの続き。

たとえば

$ rurema String 

とすると

bitclust の

bitclust/bin/refe.rb String -d db_path

を実行させているってことがわかったので↓
rurema/myrurema/bin/rurema

 92     sh "#{bitclust_path/'bin/refe.rb'}" +
 93           " #{query} -d #{db_path(ver)}", :silent => true


それでふと refe.rb は "String" などの文字列うけとった後どうしているのだろう?と思っておいかけてみた。
(デバッガーみたいなものを使い倒せる人じゃないので p self;exit を付け足すといった原始的な方法)

bitclust/bin/refe.rb

def _main
  refe = BitClust::Searcher.new


BitClust::Searcher をnew して、class Searcher の method parse に "String" という引数を渡しているらしきことがわかったので
class Searcher を探す...

% grep Searcher lib/bitclust
class Searcher は searcher.rb ファイルに納められていることがわかった。

bitclust/lib/bitclust/searcher.rb

 20 module BitClust
 21 
 22   class Searcher


public method parse は80行あたりにあった。

 81     def parse(argv)
 82       @parser.parse! argv


引数としてうけとった argv の中身は
["String", "-d", "/path/to/data/db/1.9.2"]
配列になっていて "/path/to/data/db/1.9.2" は、myrurema でいうところの db_path がわたされていた。

そして条件分岐した後に
refe_mode_check argv
を実行していた。この時 refe_mode_check に渡している argv には ["String"] がおさめられていた。

refe_mode_check をみてみると、(line 118あたり)
@target_type によって条件分岐していて、@target_type が :class だった場合とそうでない場合の処理があった。

でも
% rurema String を実行した場合 @target_type は nil になるので case 中の条件のどれにもあてはまらない。
case がおわった後の箇所から実行されることになる。
(line 135 あたり)

       @view = TerminalView.new(compiler,
...略


class TerminalView を new して @view にそれを代入していることがわかった。
それでこのあとの続きはどこにあるのだろうか?
@view には class TerminalView が代入されているだけで、ほかにメソッドが与えられていない。

これまで辿ってきた道を引き返していくほかなさそうだ... 。
最初までもどってみたら続きがあった。

bitclust/bin/refe.rb

def _main
  refe = BitClust::Searcher.new
  refe.parse ARGV
  refe.exec nil, ARGV

要は
1, _main は ローカル変数 refe に class Searcher を代入する。
2, class Searcher のパブリックメソッドである parse を実行し@view にTerminalView.new を代入する。
3, class Searcher のパブリックメソッドである exec を実行。

ということのようだ。

では早速続きにあたる3番目の箇所 class Searcher の method exec をみてみよう。

 90     def exec(db, argv)
 91       if @listen_url


@listen_url 変数に何か値がはいっていたら spawn_server を、そうでなければ search_pattern を実行する、と。
% rurema String を実行した時 @listen_url は nil なので
line 94 の search_pattern
が実行される。

def search_pattern みてみよう。

200     def search_pattern(db, argv)
201       db ||= new_database()
202       case @target_type || db
203       when :class
...略
214       else


引数として "String" を渡した時は @target_type は nil なので line 214 以降に移動する。移動すると argv のサイズによっての条件分岐が始まる。サイズが 1 の場合は

219         when 1
220           find_class_or_method db, argv[0]

find_class_or_method を実行する...

262     def find_class_or_method(db, pattern)
263       case pattern


pattern の値によって処理を分けている。
pattern には "String" がおさまっている。
db には#<BitClust::MethodDatabase>
 
"String" という文字列受け取った場合は when /\A[A-Z]/ に該当するのでそれに続く begin以降にジャンプする。

270       when /\A[A-Z]/   # Method name or class name, but class name is better.
271         begin
272           find_class db, pattern

次に探すべきメソッドは find_class 。

find_class

241     def find_class(db, c)
242       @view.show_class db.search_classes(c)
243     end


をーここで @view と出会いました。やっと話がつながった感じがします。
つまり

1, _main は ローカル変数 refe に class Searcher を代入する。
2, class Searcher のパブリックメソッドである parse を実行し@view にTerminalView.new を代入する。
3, class Searcher のパブリックメソッドである exec を実行。


この3番目の処理が line 242line 90 にあたると。
2番目の処理で「あらかじめ」Searcher のインスタンス変数である @view に class TerminalView が代入されているからここ(line 242 )で@view.show_class を使うことができるわけね、と。

一応、確認してみると

def find_class(db, c)
  p @view.class, c ; exit
end

% rurema String
BitClust::TerminalView
"String"

ちゃんと TerminalView が代入されている。

ではでは
class TerminalView の methods show_class をみてみよう。

324     def show_class(cs)
325       if cs.size == 1

def show_class は cs をうけとって そのサイズが1だった場合とそうじゃなかった場合とで処理を分岐させている。

% rurema String
の場合、cs は [#<class String>]

% rurema Hash 
の場合、cs は [#<class Hash>]
と変化する。@line が false の場合で、csのサイズが1ならば describe_class を実行する。

なので
% rurema String
した時は

line. 329 describe_class cs.first
を実行するのだとわかった。
では def describe_class はどこだろう? line 411 あたりにあった。

describe_class

411     def describe_class(c)
...略
416       puts "#{c.type} #{c.name}#{c.superclass ? " < #{c.superclass.name}" : ''}"
417       p c.class.name; p c; exit


line. 417 を上のようにしてみると

% rurema String
class String < Object
"BitClust::ClassEntry"
#<class String>

と返ってきたのでローカル変数c には ClassEntry が代入されていてそのクラスは type や name といったメソッドを持っているとわかった。
つづいて

417       unless c.included.empty?
...略
423       unless c.source.strip.empty?

とあって c (すなわち BitClust::ClassEntry)のincluded が empty じゃなかったら空行を1行出力し
c.included.each ...
を実行し
同様に、c.source.strip.empty? がempty じゃなかったら空行を1行出力し
@compiler.compile(c.source.strip)
を実行する。
ようだ。

% rurema String
を実行した場合は何行目の処理が実行されるのか?

line 417 のunless c.included.empty? にキャッチされるとわかった。従ってこの部分

419         c.included.each do |mod|
420           puts "include #{mod.name}"
421         end

を実行していることになる。

変数c には class ClassEntry が代入されているので c.included は、ClassEntryクラスの included メソッド。included メソッドはブロックを受け取ることができるのだなぁ、と。

では class ClassEntry がどうなっているかをみてみよう...。 classentry.rb を開いてみると

bitclust/lib/bitclust/classentry.rb

class ClassEntry < Entry

Entry クラスを継承していた。searcher.rb が使っていた included はどこだろ?継承元の Entry の方なのかな?いや無い。てことは ClassEntry が自分でもっている?ないみたい。

うむむ、どうやら何かにくるまれているようだ...。

hash のキーとしてならあるんだけども...

bitclust/lib/bitclust/classentry.rb

 77       property :included,   '[ClassEntry]'


included のゆくえがわからなくなってしまった。

こういう時は最初に initialize がどうなっているかをみてみよう。

 16   class ClassEntry < Entry
 ... 略 
 36       init_properties


init_properties って何かな?classentry.rb ファイル中にその実態は無いようだ。
継承元のクラスのファイルを開いてみると
% vim entry.rb
init_properties は module_eval によって定義されていた...。

もう一度頭を整理して考えてみよう。呼び出した側は下記のようになっていて
bitclust/lib/bitclust/searcher.rb

419         c.included.each do |mod|
420           puts "include #{mod.name}"
421         end


ClassEntryクラスのパブリックメソッドの中に included という名前のメソッドはない。
classentry.rb ファイル中に included という名前で存在しているのはハッシュのキーのみ。

最終的なところまで近づいてきた感じがしつつもこの先はちょっと難しそうだな。読み解けるかなぁ...
今日はここまで。

2010/06/08

install-rurema

Rubyの新リファレンスマニュアルをコマンドラインから簡単に引くためのツールを書いた

うわっこれいいな。早速 install

http://github.com/yhara/myrurema

README を読むと gem を使ってインストールできるとあったんですが、ちょっと gem 苦手な為 gem を使わないで動せないものかな?と、try してみました。 (OS X 10.6.3)

$ git clone git://github.com/yhara/myrurema  rurema
$ cd rurema/myrurema/bin
$ vim rurema
# shebang を自分の ruby 1.9.2 のパスに修正。#!/usr/bin/env ruby #  changed
$ pwd
/path/to/rurema/myrurema/bin

# とりあえず

$ ./rurema
Usage: rurema [options]
         --init                       initialize rurema
         --update                     update documents and database
...(略)

# README を読みながら
# Init

$ ./rurema --init --ruremadir=/path/to/rurema/data

# これを実行すると ~/home に.subversion ディレクトリができた。

try

$ ./rurema Array#index
You don't have a database for ruby 1.9.2.
109 Make it now? [y/n]
...略

y としたけど ~/home/.rurema を作っていないのだからアップデートできない。

$ ln -s /path/to/rurema/data ~/.rurema

シンボリックはってみた

# Update

$ ./rurema --update --ruremadir=/path/to/rurema/data

# この時勢いよくファンが回りはじめてちょっと不安になった。$ top してみると ruby(OS X に標準インストールされてる方) 一時的に50% 台になったりしつつも基本的には10% 台で動いてた。ずっとみていたら急に90% 近くなってあ”と思ったとたんに終了してほっとした。

# try

$ pwd
$ /path/to/rurema/myrurema/bin
$ ./rurema Array#index
Array#index
--- index(val)           -> Integer | nil 
--- index {|item| ...}   -> Integer | nil 

指定された val と == で等しい最初の要素の位置を返します。
等しい要素がひとつもなかった時には nil を返します。
...(略)


わい
動かすことができたのでエイリアスにしちゃう。

$ vim ~/.bashrc
# alias 'rurema'='/path/to/myrurema/bin/rurema'

$ source ~/.bashrc
$ rurema String#size
String#size
--- length -> Integer
--- size -> Integer

文字列の文字数を返します。バイト数を知りたいときは bytesize メソッドを使ってください。

@see [[m:String#bytesize]]


コマンドラインからるりまをみることができました。うれしいな。便利だなー。

+++ まとめ +++

# Download rurema 
$ git clone git://github.com/yhara/myrurema rurema
$ cd rurema/myrurema/bin

# shebang を自分がインストールした ruby 1.9.2 のパスに修正。#!/usr/bin/env ruby #  changed
$ vim rurema

# 確認 & ヘルプ
$ ./rurema

# database directory
$ mkdir /path/to/rurema/data

# リンク作成
$ ln -s /path/to/rurema/data ~/.rurema

# init and update
$ ./rurema --init --rubyver=1.9.2 --ruremadir=/path/to/rurema/data
$ ./rurema --update --rubyver=1.9.2 --ruremadir=/path/to/rurema/data

# みてみる
$ ./rurema String#size

# alias
# $ vim ~/.bashrc 
# $ source ~/.bashrc


参照
http://ja.wikipedia.org/wiki/Subversion
SnowLeopard には標準でSubversion入っていた。知らなかった。

2010/06/05

plutil

無駄な試みかもしれないけれども下記のヘルプをもう2度とみたくないために書いてみる...。

% plutil -h
unrecognized option: -h
plutil: [command_option] [other_options] file...
  (略)


[ -lint: シンタックスエラーがないかどうかみてくれる! ]

% plutil -lint /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist 
/System/Library/LaunchDaemons/com.apple.mDNSResponder.plist: OK

% plutil -lint ~/.MacOSX/environment.plist 
/Users/foo/.MacOSX/environment.plist: OK


-s で silent mode。OK ならば黙っている。

% plutil -lint -s ~/.MacOSX/environment.plist
%


[ -convert: binary を text にしたり逆にbinary にしたり ]

cat とかで読めない plist を読めるようにする為には convert する。
convert したファイルをいきなり上書きするのがいやな時は -o オプションをつける。

binary plist to text plist

% plutil -convert xml1 ~/.MacOSX/environment.plist  -o ~/test-dot-environment.plist
% 

失敗すると ... file does not exist or is not readable or is not a regular file とかなんとか言われる。

成功すると

% more ~/test-dot-environment.plist

読めるようになる。

今つくったファイルをbinary にするテスト
text plist to binary plist

% plutil -convert binary1 ~/test-dot-environment.plist -o ~/test-dot-bin-environment.plist

% more test-dot-bin-environment.plist 
"test-dot-bin-environment.plist" may be a binary file.  See it anyway? 

% defaults read ~/test-dot-bin-environment


-e オプションについてはわからず。

+++ memo +++

defaults コマンドを使うと設定内容を閲覧できる。
こんなかんじで。
% defaults read /Library/Preferences/SystemConfiguration/com.apple.PowerManagement
/Library 配下の plist ファイルを指定する時は必ずフルパスで指定する必要がある。

で、おもむろに
% defaults read com.apple.Finder
などとうってみたりするとすんなり返事をかえしてくれる。
(たとえ cd /Volumes していて ~/home にいなかったとしても)
ログインユーザ所有の plist を指定した場合は admin 所有のplist 等とは異なり
必ずしもフルパスを要求されない。

以前は、こうじゃなかったような気もするんだけど
(つまりユーザplist もフルパスを毎度毎度要求されていたような記憶があるんだけども)
おもいっきり記憶違いかもしれない。とりあえず OS X 10.6.3 では今このようになってる。

2010/06/02

install-ruby-1.9.2-preview3

さっそく

% autoconf
% ./configure --prefix=/Volumes/foo/ruby192 --program-suffix=192 --with-x=no
% make
% make test
% make install


% make test の時

PASS all 932 tests
 ./miniruby -I./lib -I.ext/common -I./- -r./ext/purelib.rb  ./tool/runruby.rb --extout=.ext  -- "./bootstraptest/runner.rb" --ruby="ruby1    92"  ./KNOWNBUGS.rb

Driver is ruby 1.9.2dev (2010-05-31 revision 28117) [x86_64-darwin10.3.0]
Target is ruby 1.9.2dev (2010-05-31 revision 28117) [x86_64-darwin10.3.0]


KNOWNBUGS.rb .
PASS all 1 tests

となりましたが KNOWNBUGS とあったので気にしないで install してみました。

github.com/midore/mblogger
をruby 1.9.2 preview3 でもって「この投稿」を投稿してみました。どうかな?
=> 成功しました。
put はどうかな?
=> OK.

github.com/midore/mdiary
こっちも、検索できなくなったかな?と思ったけどできてるみたい。

github.com/midore/2010-amazon-jp-api
は動かない!と思ったらruby 1.9.2 はとはぜんぜーん関係ない問題でした。ショック。ずっと気づいてなかった。

2010/06/01

Mac OS X Security Configuration Guides

Mac OS X にはファイル共有機能があるので、例えば自宅にある複数のコンピュータ間でデータをやりとりできるし、iTunes 共有機能を使えば メインのコンピュータのiTunes を複数のコンピュータで共有できます。
また、sshd など 使えば外出先からでも自宅のコンピュータにアクセスできます。
そういった様々な OS X に付随してるサービス機能をふる活用させている方も世の中にはいらっしゃるのでしょう。

けれども、いまどき自宅コンピュータをサーバ化させたい人のうち Mac OS X を選択する人ってどれだけの割合なのでしょうか?もし仮に私がそういう必要に迫られたとしたら迷わず Ubuntu を使うと思います。

多くの人が、OS X をクライアントとして使用しているにすぎなくて、たまに iPodやiPhoneやiPad と同期させるくらい。
だとするならば...
下記のドキュメントはより多くの Mac OS X Users に読まれるべき文章なんじゃないかと思います。

Mac OS X Security Configuration Guides

ただ、日本語化されていません。
過去の Leopard 時代の文書ですら未だに日本語化されていないようです。

で、英語ですがコマンドに慣れてる人なら最後の index 眺めてみるだけでもポイントを押さえられます。

コマンドを使ったことない方でも、
手始めにターミナル(Applications->Utilities->Terminal.app)のヘルプの中の「UNIX の基礎」に目を通し、興味を持てそうであればUNIX関連の書籍や、コマンドリファレンス的な本やそもそも shell って何?的な本を眺めつつ、簡単なコマンドの練習などしながら、これを機会にコマンドに慣れていけば大丈夫かと思います。

+++ 追記:上記ドキュメントを読む読まない以前の最低限のセキュリティ +++

1, たとえ自宅にたった1台しかコンピュータがない場合でも ルータ を使用する。
2, 無線ルータの WEP の使用を避ける。
3, ウイルス対策用のアプリケーションを購入する。また、そのアップデートを怠らない。
4, OS X のソフトウエア•アップデートは定期的に必ず行うか自動アップデート(システム環境設定->ソフトウエア•アップデート->アップデートを自動的にダウンロード)にしておく。
5, ファイアウォール機能を有効にする。(システム環境設定->セキュリティ->ファイアウォール)
6, 自分ユーザにファイル共有させる必要がないのであれば FileValut を 入 にする。
7, システム環境設定->共有 不必要なサービスを起動しない。
8, 必要がないかぎり、システム環境設定->アカウント->ゲストアカウント は無効にしておく。