コマンドライン出力のハンドリング

ふと標準エラー出力の表示を抑制したくなった (Windows7)

 c:\><コマンド類> 2> NUL

「2>」が標準エラー出力「1>」なら標準出力のリダイレクト、「NUL」は NULデバイスで入出力の抑制になる。また、「2>&1」とすれば標準エラー出力を標準出力にリダイレクトする。

Windosで、CD(DVD)トレイを開閉する

結構悩んでしまった、
CD/DVDトレイ開閉スクリプト - IT生活向上ブログ

wmp = WIN32OLE.new 'WMPlayer.OCX'
f = wmp.cdromcollection.getByDriveSpecifier('F:')
f.eject
f.eject

「F:」は光学ドライブのドライブレター、手元ではそうでした。
同じドライブオブジェクトに対し ejectメソッドを繰り返すとトレイが閉じます、オブジェクトを作り直してしまうと閉じなくなるの注意。そのときは二度 eject呼ぶと閉じます。

一行スクリプトで閉じるならこんな感じか

WIN32OLE.new('WMPlayer.OCX').cdromCollection.item(0).tap{|f|f.eject}.eject

そして WIN32OLE.new('WMPlayer.OCX')オブジェクトや、WIN32OLE.new('WMPlayer.OCX').cdromCollectionオブジェクトが捕まえてる間はドライブの電源を切れなかったり。

http://www.microsoft.com/japan/technet/scriptcenter/resources/qanda/apr06/hey0417.mspx

そしてトレイを開いたり閉じたりする(一行)スクリプト、もうドライブ指定しなくていいじゃん、それっぽいの最初で。

光学ドライブトレイを開く

eject.rb

require 'win32ole'
WIN32OLE.new('WMPlayer.OCX').cdromCollection.item(0).eject

光学ドライブトレイを閉じる

tceje.rb

require 'win32ole'
WIN32OLE.new('WMPlayer.OCX').cdromCollection.item(0).tap{|cd| cd.eject}.eject

PuTTYで生成した公開鍵を使う

どこで見たんだっけな、PuTTYgen で生成したキーペアについて。公開鍵を Linux機に仕込む。

ssh-keygen -i -f id_rsa.pub >> authorized_keys

ちなみに -iオプションは

     -i      This option will read an unencrypted private (or public) key file
             in SSH2-compatible format and print an OpenSSH compatible private
             (or public) key to stdout.  ssh-keygen also reads the RFC 4716
             SSH Public Key File Format.  This option allows importing keys
             from several commercial SSH implementations.

経過分秒表示(続)

前に同じような事もあった(time.rb 経過分秒表示 - Rubyとか Illustratorとか SFとか折紙とか)のだが、やはりライトニングトーク絡みで時間表示を作ってみた(申し込んだLT自体は reject)。取敢えずソース

LT_timer.rb (このソースはその後更新されています、11/1/2)

#coding:Windows-31J
# $Id: LT_timer.rb 3582 2010-07-04 09:32:14Z hs9587 $
# $Date: 2010-07-04 18:32:14 +0900 (譌・, 04 7 2010) $
# $Name$ $Author: hs9587 $ $Revision: 3582 $
Version = "0.0 ($Revision: 3582 $)"
require 'vr/vruby'
require 'vr/vrcontrol'
require 'vr/vrtimer'
require 'vr/vrlayout'
require 'time'
Min = Time.parse('00:01')-Time.parse('00:00')

class TimerControl < VRPanel
  include VRTimerFeasible
  include VRDrawable
  SrartResetStruct= Struct.new :name, :button, :body
  StartReset = [
    SrartResetStruct.new('start', '!' \
      , lambda{@start_time = Time.now; def anchor_time; @start_time; end}),
    SrartResetStruct.new('reset', '0' \
      , lambda{def anchor_time; Time.now; end}),
    SrartResetStruct.new('exit', '.', lambda{exit}),
    ]
  attr_writer :time_out, :time_color
  
  def construct
    self.caption = 'LTTimer'
    #addTimer 1000
    addTimer 50
    @font = @screen.factory.newfont 'MS ゴシック', -72
    #@font = @screen.factory.newfont 'HG ゴシックE', -72
    #@font = @screen.factory.newfont 'HGS ゴシックE', -72
    #@font = @screen.factory.newfont 'Tahoma', -72, 400, 0, 0, 0, 34, 0
    #@font = @screen.factory.newfont 'Tahoma', -72
    @time_out = 5.0
    @time_color = 3.0
  end # def construct
  
  def self_paint
    elapse = Time.at((Time.now - anchor_time).abs)
    self.textColor = RGB elapsed(elapse), 0, 0
    mm_ss = elapse.strftime '%M:%S'
    setFont @font
    #p textExtent(mm_ss)
    drawText mm_ss, 0,0, *textExtent(mm_ss)
  end # def self_paint
  
  def self_timer
    refresh
  end
  
  StartReset.each do |start_reset|
    define_method start_reset.name, start_reset.body
  end # StartReset.each do |start_reset|
  
  private
  def anchor_time
    Time.now
  end # def anchor_time
  
  def elapsed(sec)
    sec = sec.to_i
    case sec
      when 0 ... Min*@time_color then
        0
      when Min*@time_color ... Min*@time_out then
        256 * (sec - Min*@time_color) / (Min*(@time_out - @time_color))
      else
        sec < 0 ? 0 : 255
    end # case sec
  end # def elapsed(sec)
end # class TimerControl < VRPanel

class StartResetControl < VRPanel
  include VRVertLayoutManager
  include VRStdControlContainer
  
  def construct
    TimerControl::StartReset.each do |start_reset|
      addControl VRButton, start_reset.name, start_reset.button
      send_parent start_reset.name, 'clicked'
    end # TimerControl::StartReset.each do |start_reset|
  end # def construct
end # class StartResetControl < VRPanel

module LTTimer
  attr_reader :timer
  def construct
    self.caption = 'LTTimer'
    move 0,0, 198,104
    addControl TimerControl, 'timer', 'Timer', 0,0, 180,72
    addControl StartResetControl, 'buttons', 'Buttons', 181,0, 10,72
    top 0xffffffff
  end # def construct
  
  TimerControl::StartReset.each do |start_reset|
    define_method("buttons_#{start_reset.name}_clicked") do
      @timer.send start_reset.name
    end # define_method("buttons_#{start_reset.name}_clicked") do
  end # TimerControl::StartReset.each do |start_reset|
end # module LTTimer

time_out, time_color, start = 5, 3, false
require 'optparse'
ARGV.options do |opt|
  opt.on('-o', '--timeout=VAL', 'default: 5(min)'){|v| time_out = v.to_f}
  opt.on('-c', '--timecolor=VAL', 'default: 3(min)'){|v| time_color = v.to_f}
  opt.on('-s', '--[no-]start', 'default: no'){|v| start = v}
  opt.banner << "\nLeft upper topmost timer"
  begin # rescue OptionParser::ParseError => evar
    opt.parse!
  rescue OptionParser::ParseError => evar
    evar.display $stderr; "\n".display $stderr
    opt.help.display $stderr
    exit 1
  end # rescue OptionParser::ParseError => evar
end # ARGV.options do |opt|

begin # rescue SystemStackError => evar
  #VRLocalScreen.start LTTimer
  lt = VRLocalScreen.showForm LTTimer
  lt.timer.time_out = time_out
  lt.timer.time_color = time_color
  lt.timer.start if start
  VRLocalScreen.messageloop
rescue SystemStackError => evar
end # rescue SystemStackError => evar

WindowsVisualuRuby を使って時間(分秒)表示する GUI時計を作ります。一応 5分計で、途中 3分から表示色を赤っぽくし始め 5分で赤くします。コマンドラインオプションからその辺のタイミングを設定するのはOptionParser (optparse) です。

その表示色なんですが 3分から RGBの値を変えていくのに、実際のところ 3分では全然分かりません。3分半でもしかしてら色変えてますかという程度。4分でそれは褐色かもと見えるようになり、4分半でかなり赤く、そして4分45秒くらいでもう真っ赤、そのまま 5分以降も赤いです。

以下、順次メモ書き。

Version とか require

#coding:Windows-31J
# $Id: LT_timer.rb 3582 2010-07-04 09:32:14Z hs9587 $
# $Date: 2010-07-04 18:32:14 +0900 (譌・, 04 7 2010) $
# $Name$ $Author: hs9587 $ $Revision: 3582 $
Version = "0.0 ($Revision: 3582 $)"
require 'vr/vruby'
require 'vr/vrcontrol'
require 'vr/vrtimer'
require 'vr/vrlayout'
require 'time'
Min = Time.parse('00:01')-Time.parse('00:00')

Version定数は後で OptionParser (optparse) の --version 表示に拾って貰う用、subversion の更新文字列を混ぜてる。
そして VisuzluRuby関係の require、vruby は全体的なもの、vrcontrol は釦とか使うので、vrtimer はタイマーの自動画面更新用、vrlayout は釦を配置するときのレイアウトマネージャー。
time はそして Min = 60 の計算用、一分が60秒ってのは即値で書いちゃってもいいのかな。定数 Min の名前はちょっと迷う、最小とか誤解しないか、でもあんまり長いのもなんだし。

タイマー表示のVRコントロール

class TimerControl < VRPanel
タイマー表示の導入
class TimerControl < VRPanel
  include VRTimerFeasible
  include VRDrawable
  SrartResetStruct= Struct.new :name, :button, :body
  StartReset = [
    SrartResetStruct.new('start', '!' \
      , lambda{@start_time = Time.now; def anchor_time; @start_time; end}),
    SrartResetStruct.new('reset', '0' \
      , lambda{def anchor_time; Time.now; end}),
    SrartResetStruct.new('exit', '.', lambda{exit}),
    ]
  attr_writer :time_out, :time_color

include はまあそんな感じ。Struct構造体を定義しての StartReset配列にはこの後のコントロール釦の名前や表示、動作を登録する。このコントロールを含む三つのクラス(モジュール)定義で共用するものなので一箇所に登録する。lambdaブロックの動作の為か、このクラス内で定義しないとうまく動かなかったのでここに。
釦の動作は、exitはまあ良いとして、あとの二つは後ろの方で定義するプライベートメソッド anchor_time の再定義。釦押下時点を記録してその時間を返すか、現在時刻を返すか。
そして :time_out, :time_color はコマンドラインオプションからやってきたりする文字色変更のタイミング、というかタイマー期限。

コンストラクタとフォントの事
  def construct
    self.caption = 'LTTimer'
    #addTimer 1000
    addTimer 50
    @font = @screen.factory.newfont 'MS ゴシック', -72
    #@font = @screen.factory.newfont 'HG ゴシックE', -72
    #@font = @screen.factory.newfont 'HGS ゴシックE', -72
    #@font = @screen.factory.newfont 'Tahoma', -72, 400, 0, 0, 0, 34, 0
    #@font = @screen.factory.newfont 'Tahoma', -72
    @time_out = 5.0
    @time_color = 3.0
  end # def construct

コンストラクタの意義としては addTimer かな。あんまり長いと秒表示のずれがひどいし短いとちらつく。もう少しだけ長くてもいいかも。コメントアウトしてる 1000 は参考にした「フォントを選べる時計」での値。
とはいえそれを参考にしてもフォントの指定方法は良く分からなかったので 'MS ゴシック' 72サイズ。本当は Tahoma とか使いたかったんだけどどうも位置がずれる、大体「-72」の「-(マイナス)」って何だ。

分秒の表示
  def self_paint
    elapse = Time.at((Time.now - anchor_time).abs)
    self.textColor = RGB elapsed(elapse), 0, 0
    mm_ss = elapse.strftime '%M:%S'
    setFont @font
    #p textExtent(mm_ss)
    drawText mm_ss, 0,0, *textExtent(mm_ss)
  end # def self_paint
  
  def self_timer
    refresh
  end

実際の表示と再表示、メソッド名は VisualuRuby に従うもの。
表示時間は achor_timeプライベートメソッドと現在時刻の差、achor_timeメソッドの動作によってタイマーが進むのか止まってるのか決まる。そしてそれを elapsedプライベートメソッドに渡して文字色を調整する。

コントロール系のメソッド
  StartReset.each do |start_reset|
    define_method start_reset.name, start_reset.body
  end # StartReset.each do |start_reset|

釦押下時の動作(メソッド)を定義、前掲配列を当たって三つ。

プライベートメソッド
  private
  def anchor_time
    Time.now
  end # def anchor_time
  
  def elapsed(sec)
    sec = sec.to_i
    case sec
      when 0 ... Min*@time_color then
        0
      when Min*@time_color ... Min*@time_out then
        256 * (sec - Min*@time_color) / (Min*(@time_out - @time_color))
      else
        sec < 0 ? 0 : 255
    end # case sec
  end # def elapsed(sec)
end # class TimerControl < VRPanel

タイマー開始時を返す anchor_time と、タイマー経過時間に対して文字色を返す elapsed。色の計算には起動時にセットされるインスタンス変数 @time_out, @time_color を用いる

操作用釦のVRコントロール

class StartResetControl < VRPanel
  include VRVertLayoutManager
  include VRStdControlContainer
  
  def construct
    TimerControl::StartReset.each do |start_reset|
      addControl VRButton, start_reset.name, start_reset.button
      send_parent start_reset.name, 'clicked'
    end # TimerControl::StartReset.each do |start_reset|
  end # def construct
end # class StartResetControl < VRPanel

レイアウトマネージャを使って釦を配置、釦定義には前掲配列を当たる。

タイマー本体

module LTTimer
  attr_reader :timer
  def construct
    self.caption = 'LTTimer'
    move 0,0, 198,104
    addControl TimerControl, 'timer', 'Timer', 0,0, 180,72
    addControl StartResetControl, 'buttons', 'Buttons', 181,0, 10,72
    top 0xffffffff
  end # def construct
  
  TimerControl::StartReset.each do |start_reset|
    define_method("buttons_#{start_reset.name}_clicked") do
      @timer.send start_reset.name
    end # define_method("buttons_#{start_reset.name}_clicked") do
  end # TimerControl::StartReset.each do |start_reset|
end # module LTTimer

本体の定義、既に定義したタイマー表示部分と釦配置の二つのコントロールを配置する。サイズは即値でここに書く、表示してみないと表示領域の大きさが分からないのでいろいろやってみた。大きさ動的にとるの難しい。あと、top の指定でタイマーを最前面表示する。
それとやはり前掲配列を当たって、釦のコントローラから釦押下を受け取ってタイマー表示のコントローラに受け渡すメソッドを定義。タイマー表示のコントローラは @timerインスタンス変数にいれてる。

コマンドラインとオプション変数

time_out, time_color, start = 5, 3, false
require 'optparse'
ARGV.options do |opt|
  opt.on('-o', '--timeout=VAL', 'default: 5(min)'){|v| time_out = v.to_f}
  opt.on('-c', '--timecolor=VAL', 'default: 3(min)'){|v| time_color = v.to_f}
  opt.on('-s', '--[no-]start', 'default: no'){|v| start = v}
  opt.banner << "\nLeft upper topmost timer"
  begin # rescue OptionParser::ParseError => evar
    opt.parse!
  rescue OptionParser::ParseError => evar
    evar.display $stderr; "\n".display $stderr
    opt.help.display $stderr
    exit 1
  end # rescue OptionParser::ParseError => evar
end # ARGV.options do |opt|

optparse を使ってコマンドラインオプションを受け取る。文字色を変えるタイミングと起動時にタイマーをスタートさせてるか止めてるかの選択。opt.banner にて Usage文字列に説明を付加。

実行

begin # rescue SystemStackError => evar
  #VRLocalScreen.start LTTimer
  lt = VRLocalScreen.showForm LTTimer
  lt.timer.time_out = time_out
  lt.timer.time_color = time_color
  lt.timer.start if start
  VRLocalScreen.messageloop
rescue SystemStackError => evar
end # rescue SystemStackError => evar

セットされたコマンドオプションを設定して .messageloopを起動。オプションを設定する必要があるのでコメントアウトしてるような .startで一気に実行というのはない。
exit を呼ぶとちょっと階層深くなってるのか SystemStackError が発生するので begin rescue で囲ってある。

VisualuRuby

GUI表示には、Windows という事で VisualuRuby(VisualuRuby計画(仮称))を用いた。なかなか説明とか資料が見付からなくて苦労する。上記プロジェクトのページと、そこにも記載のあるサンプル(VisualuRuby計画(仮称) サンプル)を参考に考える。

SystemStackErrorのこと(/7/25 追記)

メイン(実行)パートの SystemStackError だけど単純な僕の間違いだった。
StartReset定数の3項目

SrartResetStruct.new('exit', '.', lambda{exit}),

これは TimerControlコントローラの中ではメソッド定義に用いる。その実行ブロックでは「Kernel.#exit」メソッドを呼んでプログラム全体を終了しようとするつもりのに、メソッド名自体を「exit」にしてるので自身を呼ぶ事になって循環してしまう。そしてこのエラーになる。

ということで StartReset定数は書き換え、メソッド名は「close」にでもしましょう。

  StartReset = [
    SrartResetStruct.new('start', '!' \
      , lambda{@start_time = Time.now; def anchor_time; @start_time; end}),
    SrartResetStruct.new('reset', '0' \
      , lambda{def anchor_time; Time.now; end}),
    SrartResetStruct.new('close', '.', lambda{exit}),
    ]

そしてメイン(実行)ブロックは begin, rescue節で囲う事もなく。

#VRLocalScreen.start LTTimer
lt = VRLocalScreen.showForm LTTimer
lt.timer.time_out = time_out
lt.timer.time_color = time_color
lt.timer.start if start
VRLocalScreen.messageloop

Windows7 での細々した設定

Windows 7 Professional (64bit版) を使い始めた。で、初期設定とか細々した辺り、先週志村 (@hs9587) | Twitterしたものから転載(誤字ちょっと修正)。

ログオン時の背景画像の変更、C:\windows\System32\oobe\info\BACKGROUNDS\BACKGROUNDDEFAULT.JPG を同名の別 jpeg画像ファイルに差し替える。大きさ制限 250Kまで、とか、かも。 Windows7 
2:29 PM Nov 27th webで 
ファイル拡張子の表示。コントロールパネル-デスクトップのカスタマイズ-フォルダーオプション-表示-詳細設定-登録されている拡張子は表示しない、のチェックを外す。 Windows7 
8:16 PM Nov 26th webで 

他にも隠しファイルの表示非表示とかその辺りはこの辺に。

ログイン時のユーザ名表示の抑制。コントロールパネル-システムとセキュリティ-管理ツール-ローカルセキュリティポリシー-ローカルポリシー-セキュリティオプション-対話型ログオン: 最後のユーザー名を表示しない-プロパティ、で有効に設定。140字以内で書けた! Windows7 
5:35 PM Nov 26th webで 

パスワードの長さ難さとかその辺りもこの辺。

取り敢えずマウス調整、早くする。ポインター画像、待ちは最初からアニメーションだった、ドーナツぐるぐる、Windows7 
3:30 PM Nov 26th webで 
Windows7(64bit)始めました。でもなんだかよく分かりません、混乱しています。 
2:59 PM Nov 26th webで 

PGconn#quote_ident の有無

昨日 PGconn#quote_ident - Rubyとか Illustratorとか SFとか折紙とか の続き。
大事な事、Debian lenny で libpgsql-ruby を使ってた方は大丈夫だったです、他でも新しめの環境なら大丈夫なのでしょう。

  1. Rails(ActiveRecord) 2.3.3 <- 2.3.2 のヴァージョンアップで、activerecord-2.3.#/lib/active_record/connection_adapters/postgresql_adapter.rb の中の quote_column_name(name)メソッドの実装が変わった
    • 単に「%("#{name}")」としていたものから「PGconn.quote_ident(name.to_s)」へ
  2. Debian の libpgsql-ruby1.8 の提供する Rubyライブラリは lenny では pg.so だが、etch では postgres.so たった
    • postgres.so の提供する PGconnクラスに所要の quote_ident は定義されていない
  3. それはそれとして上記 postgresql_adapter.rb 冒頭ではまずライブラリでかジェムでか知らず 'pg' を requireしようとし、駄目だったら 'postgres' を requireする(そしてメソッド名とかちょこっと調整する)

という訳で、未だに Debian etch を引きずってるのが駄目だった模様。

etchの場合

そういうわけで、ActiveRecord(postgresql_adapter.rb) のライブラリ切換えを利用することにして、site_ruby にちょっと pg.rb とか置いて誤摩化す。これで Railsアプリケーションレベルでの昨日みたいなちょっとなんとかはせずに済むようになる。

/usr/local/lib/site_ruby/pg.rb

require 'postgres'

class PGconn
  unless self.respond_to? :quote_ident then
    def self.quote_ident(name)
      '"' + name + '"'
    end # def self.quote_ident(name)
  end # unless self.respond_to? :quote_ident
end # class PGconn

class PGresult
  alias_method :nfields, :num_fields unless self.method_defined?(:nfields)
  alias_method :ntuples, :num_tuples unless self.method_defined?(:ntuples)
  alias_method :ftype, :type unless self.method_defined?(:ftype)
  alias_method :cmd_tuples, :cmdtuples unless self.method_defined?(:cmd_tuples)
end # class PGresult

site_ruby か site_ruby/1.8 か、或いは vender_ruby以下とかは大変迷う。 PGresult方面は上記 postgresql_adapter.rb 冒頭での調整事項、ここが繰り返しになるのはちょっとやなんだけどね

Windowsの場合

こっちの方ではポスグレのドライバーとしては gem の postgres-pr を使っている (PostgreSQLサーバ自体は近在の Linux機)。そして gem の postgres はコンパイル不能でやっぱりインストール出来ない。「Could not find PostgreSQL build environment (libraries & headers): Makefile not created」

上記 etch での措置と同じやり方でも誤魔化せそうなんだけど折角 postgres-pr というちょっとだけ違う名前が使えるのでなんかやってみる。

C:\Program Files\ruby-1.8\lib\ruby\site_ruby\postgres.rb

require 'rubygems'
require 'postgres-pr/postgres-compat'

class PGconn
  unless self.respond_to? :quote_ident then
    def self.quote_ident(name)
      '"' + name + '"'
    end # def self.quote_ident(name)
  end # unless self.respond_to? :quote_ident
end # class PGconn

postgres-prジェムの入れる postgres.rb は乗っ取ってしまうがその中でやってるのは postgres-pr/postgres-compat の呼び出しなのでそれを代行。rubygems を require しておくべきかどうかは微妙だが、いつもいつも Rails方面からの require だけでもあるまいと考えて。
pg として振舞うわけではだいので etch の時のようなメソッド名調整は無くて良い、そこはちょっとすっきりした。

関係ないけど rake cucumber:all

関係ないけどそんなことしてる間に「rake features」は時代遅れになっていた。「rake cucumber:all」だって、あと「ok」「wip」(work in progress 絶賛開発中)モードとか。