2011年9月12日 #ruby #metaprogramming

下記ソースコードに英語コメントで書いてますが、日本語で解釈してみます。

定義

まずinstance_evalはObjectのインスタンスメソッドです。なので全てのオブジェクトがこのinstance_evalを呼び出すことができます。instance_evalはブロックを受け取ります。

機能

  • 呼び出すオブジェクト(receiver)のスコープ内でブロックを評価(実行)する、つまりreceiverのプライベートメソッドやインスタンス変数にアクセスできること
  • 現在スコープの変数などBindingにアクセスできる

サンプルコードではPersonというclassに@nameのインスタンス変数、credit_cardというプライベートメソッドを定義しました。

someoneはPersonのインスタンスで、someoneからinstance_evalをコールすると、そのブロック内ではselfがsomeoneとなり、@nameやcredit_cardにアクセスできます。

さらにinstance_evalの外で定義した変数outter_valにもアクセスできます。

Real-life example

さて、原理は分かったとして、実際はどのように使われているのか、Railsのソースコードでgrepしてこんなコードが見つかりました。

in ActiveRecord

まずマイグレーションでまあよく使うchange_columnメソッドのソースコードです。

definition[column_name]がinstance_evalを呼び出したのですが、definition[column_name]はActiveRecord::ConnectionAdapters::ColumnDefinitionクラスのオブジェクトで、ColumnDefinitionの実態はStructを継承し、カラムの名前、種類、limit、precision、デフォルト値などの情報が格納されているオブジェクトです。

definition[column_name].instance_evalを使うことで、そのオブジェクト(テーブルカラム)の種類、limit、default値とnull可否を変更することが分かりましたね。
optionsはinstance_evalブロックの外の変数ですが、普通にアクセスできます。

in ActiveSupport

上記ActiveRecordのコードが分かればここは分かりやすいと思います。
実際の意味はさておき、instance_evalを利用して@marshal_with_utc_coercionというインスタンス変数が定義されていれば、それを削除する使い方ですね。

まとめ

instance_evalを使ってオブジェクトの構造(インスタンス変数の値の変更、インスタンス変数の追加削除など)を実行時に変更するのが使い道、というのが個人的な感想です。

最後に注記:この記事はRails3.1.0のソースコードを使ってます。

現象

SQL# Plusで出力したcsvファイルなどをExcelで開いたら日本語の部分が全部文字化けになっちゃいました。

原因

出力ファイルはUTF8ですが、ExcelではShiftJisじゃないと文字化けするらしいです。

解決策

秀丸などのエディターで出力ファイルを開き、エンコードUTF8で名前をつけて保存。
そうすればExcelで開くときに文字化けは治る。

2011年9月 9日 #ruby #metaprogramming

Rubyのクラスにメソッドを追加する方法です。ここではStringを例にしました。

solution_1.rbはOpen Classというテクニックです。classキーワードで既存クラスを定義するときは上書きするのではなく、拡張した感じで、他のStringメソッドに影響がないです。

solution_2.rbはただclass_evalの理論を試したかっただけです。class_evalはスコープ内のselfcurrent classをreceiverのStringクラスに変更するんです。だからclass_evalのブロック内に定義したメソッドはStringのinstance methodになります。

solution_mistake.rbはStringのクラスメソッド(Singleton methods)を定義したもので、instance methodではないです。

最近「Metaprogramming Ruby」という本を読んでます。よくわからなかったこととか、曖昧だっだことがすっきりした感じです。お勧めです!

2011年8月20日 #textmate #textmate #tips

課題

最後の編集した箇所に遷移する機能はTextMateデフォルトではないですが、多分みんなUndo/Redoで実現していたと思います。
でも⌘Z(Undo)、⇧⌘Z(Redo)はやはり手間がかかってしまいますので、一発でできるかをググッてみたところ、TMGoToLastEditを見つかりました。

インストール

ダウンロード、解凍、ダブルクリックでインストール完了です。

使い方

⌥⌘J ( Option + Command + J )
あるいは
Navigationのメニューからクリック

Go_to_Last_Edit

2011年8月19日 #mac #textmate #textmate #tips

従来の⌘T(Command + T)Go to Fileの問題:パスが効かない

⌘TはTextMateで速やかにファイルを開ける機能ですが、例えばこんなファイルがあるとします。
-blogs/index.html.erb
-posts/index.html.erb
-categories/index.html.erb
-tags/index.html.erb
-users/index.html.erb

で⌘Tで"blogs/index"を叩くと何も表示されない、つまりパスが効かないのが一番の問題です。それを解消するのがgotofile.tmbundle

インストール

cd ~/Library/Application\ Support/TextMate/Bundles/
git clone git://github.com/stevenchanin/gotofile.tmbundle.git GoToFile.tmbundle

で、textmateを再起動

使い方

⇧⌘K ( Command + Shift + K )で新しいGo To Fileダイアログを開いて"ar/in"を叩くことでarticles/index.htmlが選択されます!

他にもspaceキーでQuickLookモードでファイルを見たり、⌥(option)キーでデフォルトのアプリでファイルを開いたり、⇧(shift)キーでフォーカスに選択したファイルのパスを入力することができます。

参考

http://how-i-work.com/workbenches/2-keyboard-based-no-mouse-rails-development-with-textmate

2011年8月19日 #linux #date #server
# stop it first if it's running, 
# or you will get a warning like: 
# the NTP socket is in use, exiting
$ /etc/init.d/ntpd stop

# ntp.nict.jp: 日本標準時プロジェクト 公開NTP
$ ntpdate ntp.nict.jp
19 Aug 15:30:18 ntpdate[27156]: step time server 133.243.238.243 offset -856.532277 sec

# start ntp again
$ /etc/init.d/ntpd start

# confirm
$ date
2011年  8月 19日 金曜日 15:32:15 JST

NPTについて

ここでは、ネットワーク上のサーバー及びクライアントマシンの時刻同期をとるためのプロトコルであるNTP(Network Time Protocol )について解説していきます。PCの時刻はマザーボード上のリアルタイムクロック(RTC)によって時間が表示されていますが、この時間はあまり正確ではなく、毎日少しづつ時間が狂っていきます(電池の消耗度にもよりますが、年間約10分程度)。NTPサーバーは、タイムサーバーに接続されたラジオ・クロックや原子時計から時刻を取得し、UTC (協定世界時)に同期させることができます。PCの時刻が狂っていると、メールの送受信時刻が狂ったり、こまめにとっているログの取得時間なども狂ってくることになります。Linuxでは標準でNTPサーバーが付属しているので是非、活用して時刻同期を行いましょう。

2011年8月17日 #mysql #sql #oracle

これが既存データで、同じ日に複数のデータがある。

Example data

期望結果はこれ、つまり日付別で集計したい場合

Expected result

MySQLではDATE関数を使う

mysql> SELECT DATE(create_datetime), COUNT(*) FROM user_m GROUP BY DATE(create_datetime);

Oracleではto_char関数を使う

SELECT to_char(create_datetime, 'YYYY-MM-DD'), COUNT(*) FROM user_m GROUP BY to_char(create_datetime, 'YYYY-MM-DD');

参考