rvmの環境でのRubyスクリプトのcron実行

Ruby-1.9.3のrvm環境だとcronにスクリプトを組み込んでも問題なく実行できていたのに、rvmで2.2.1に更新するとなぜかcron時にgemが見つからないとのエラーが出るようになってしまった…(↓):

/usr/local/rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require': cannot load such file -- mechanize (LoadError)
        from /usr/local/rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from /home/oshiro/sbiclient.rb:2:in `<top (required)>'
        from /usr/local/rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from /usr/local/rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'

種々やって、よくわからんのだが、他でも色々と苦労されている模様:

直接rvm上のrubyバイナリをバージョン番号付きで呼び出せとか、rvm側を呼び出すよう別のシェルスクリプトを組めだとか、何やら訳のわからない解決策がぞろぞろ。

次のページには「rvm cron setup」を使えとの記述が:

これをやってみると、自身のcrontabファイルの冒頭に下記のようなrvm環境の設定が付け加わるようになり、実行も問題なく行なえるようになった。

#sm start rvm
PATH="/usr/local/rvm/gems/ruby-2.2.1/bin:/usr/local/rvm/gems/ruby-2.2.1@global/bin:/usr/local/rvm/rubies/ruby-2.2.1/bin:/usr/local/rvm/gems/ruby-2.2.1/bin:/usr/local/rvm/gems/ruby-2.2.1@global/bin:/usr/local/rvm/rubies/ruby-2.2.1/bin:/usr/local/rvm/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:"
GEM_HOME='/usr/local/rvm/gems/ruby-2.2.1'
GEM_PATH='/usr/local/rvm/gems/ruby-2.2.1:/usr/local/rvm/gems/ruby-2.2.1@global'
MY_RUBY_HOME='/usr/local/rvm/rubies/ruby-2.2.1'
IRBRC='/usr/local/rvm/rubies/ruby-2.2.1/.irbrc'
RUBY_VERSION='ruby-2.2.1'
#sm end rvm
...

[追記]
上記の環境変数をコメントアウトして、ひとつずつ有効にして試してみたら、とりあえず自分の使い方であれば以下でも動作した:

#sm start rvm
PATH="/usr/local/rvm/rubies/ruby-2.2.1/bin"
GEM_HOME='/usr/local/rvm/gems/ruby-2.2.1'
#sm end rvm
...

PATHの設定は/usr/local/rvm/rubies/ruby-2.2.1/bin/rubyを呼び出すためのものだが、次のように/usr/local/binに’ln -s’しておいて、PATHには/usr/local/binを指定しても動作した。

ln -s /usr/local/rvm/rubies/ruby-2.2.1/bin/ruby /usr/local/bin/
#sm start rvm
PATH="/usr/local/bin"
GEM_HOME='/usr/local/rvm/gems/ruby-2.2.1'
#sm end rvm
...

また、GEM_HOMEの指定はGEM_PATHのどちらかでもいいらしい。

それと、cronに指定するのがスクリプトファイルで、その冒頭でrubyのパスを指定しているのであれば、環境変数のPATHはなくてもいいらしい。

船戸与一 死去

船戸与一氏死去(Google検索)。これまでありがとう。友達のリクエストで古本屋で大量入手したうち一冊の「非合法員」やら、雪山に車で遭難しかけた際に復活後の宿での手持無沙汰で本屋に出張って買った「砂のクロニクル」やら。今でも場面が目に浮かぶ「猛き箱舟」やら…。楽しませて貰いました。

Postfixでのエイリアス状況の確認

エイリアス読み込み(指定したエイリアスファイルを指定後に、リロード)

postalias aliases; postfix reload

エイリアス状況の確認
キー(例:XXX)を指定

postmap -q XXX aliases

指定状況を一覧表示

postmap -s aliases

上記の方法だと単一のaliasesファイルの状況確認しかできない。postfix上に読み込まれた全体の設定一覧の表示はできんのか?

Ruby/Tk:キャンバス上のアイテム作成

Ruby/Tkでキャンバス上へのアイテム作成で、以下のように書く例がよく紹介されている:

#!ruby
require "tk"
c = TkCanvas.new {pack}
TkcLine.new(c, 0, 0, 50, 50)
Tk.mainloop

だが、これだとアイテム生成毎にTkcLine.new(…やら、TkcRectangle.new(…やらと常にTkcを付けて、またアイテム種をキャピタライズして書かなければならず面倒で、アイテムを生成するキャンバス情報もいちいち持ち回る必要がある。

他の言語の呼び出しはもっと簡単(気分的に?)になっていて、Tclでのサブコマンドの利用とほぼ同様なのでそれほど違和感がない:

  • TkDocs – Tk Tutorial – Canvas

    • Tcl: .canvas create line 10 10 200 50
    • Ruby: TkcLine.new( canvas, 10, 10, 200, 50)
    • Perl: $canvas->create_line(10,10,200,50);
    • Python: canvas.create_line(10, 10, 200, 50)

そこで、これを他の言語(PerlやPython)と同様にできないのかと、以下のようなTkCanvasクラスへのアイテム生成のためのメソッド追加を考えてみた:

class TkCanvas
  def create_arc(*args); TkcArc.new(self, args); end
  def create_bitmap(*args); TkcBitmap.new(self, args); end
  def create_image(*args); TkcImage.new(self, args); end
  def create_line(*args); TkcLine.new(self, args); end
  def create_oval(*args); TkcOval.new(self, args); end
  def create_polygon(*args); TkcPolygon.new(self, args); end
  def create_rectangle(*args); TkcRectangle.new(self, args); end
  def create_text(*args); TkcText.new(self, args); end
  def create_window(*args); TkcWindow.new(self, args); end
end

(当初は、メソッド名をcreate_lineなどでなくlineだけ、とか、create_polygonは短くpolyとすることも考えたのだが、他の言語の状況にも合わせてみた)

これで呼び出しをすると記述が簡単になって便利だが、改めてRubyのライブラリファイルのtk/canvas.rb【注1】

【注1】

ファイルの場所はWindowsだと「C:/Ruby21-x64/lib/ruby/2.1.0/」など。また、このファイルは上位フォルダのtkcanvas.rbでrequireされている)

を見てみると、内部にcreateメソッドがあり、TkCanvasインスタンスへの以下のような指示で作成できることがわかった:

c = TkCanvas
c.create("line", 10, 10, 200, 50)

アイテム個別のTkcXxxxクラスを使ってアイテムを生成するより、こちらのほうが把握しやすいのでは。

さらに見ていると、このTkCanvas::createメソッドでは、以下のようにTkcItemクラスのtype2classメソッドを使って、アイテム文字列からクラスへのマッピングを行なっていた:

class Tk::Canvas<TkWindow
  include TkCanvasItemConfig
  ...
  def create(type, *args)
    if type.kind_of?(Class) && type <TkcItem
      # do nothing
    elsif TkcItem.type2class(type.to_s)
      type = TkcItem.type2class(type.to_s)
    else
      fail ArgumentError, "type must a subclass of TkcItem class, or a string in CItemTypeToClass"
    end
    type.create(self, *args)
  end
  ...
end

同ファイルの下部にあるTkcItemクラスを見ていくと、type2classメソッドではアイテム名からクラスへの対応表をCItemTypeToClassハッシュに保持している:

class TkcItem<TkObject
  ...
  def TkcItem.type2class(type)
    CItemTypeToClass[type]
  end
  ...
end

個々のアイテムクラスの情報は、さらに下部にあるクラス定義で行われていた:

class TkcLine<TkcItem
  CItemTypeName = 'line'.freeze
  CItemTypeToClass[CItemTypeName] = self
end
...

このCItemTypeToClassハッシュの情報を使うと、TkCanvasへのcreate_xxxxメソッドの登録は以下のようにひとまとめにできる(class_evalを使った):

class TkCanvas
  TkcItem::CItemTypeToClass.each {|k, v| class_eval("def create_#{k}(*args); #{v}.create(self, *args); end")}
end

上記を使うと以下のように記述できる:

c = TkCanvas
c.create_line(10, 10, 200, 50)

create_の入力は増えるわけだが、いちいちTkcXxxx.new(c, …)とせずこちらでいいんじゃないかなぁ。

リンク

class_eval

Ruby/Tk紹介

RubyのWindowsバイナリ選び

Windows版のバイナリでインストーラをダウンロードしようとしたら、新ためて見ると「ActiveScriptRuby」と「RubyInstaller」の2つがあるんだが、どちらも「(幾つかの|多数の)便利なライブラリを加えた」とだけ説明されていて、違いがよくわからーん…。(前にはRubyInstallerを使ったはず)

  • Windows版Rubyバイナリ – ダウンロード

    • ActiveScriptRuby 安定版に幾つかの便利なライブラリを加え、さらにRubyをCOMサーバとしても利用可能にしたもの。ベースはmswin32版
    • RubyInstaller (英語) 安定版に多数の便利なライブラリを加えたもの。ベースはmingw32版

ActiveScriptRubyを検索してもRuby-2.2.0版はあるものの、探し方が悪いのかどういう機能があるのかの情報が得られない。
RubyInstallerのほうは最新がRuby-2.1.5版で、こちらはgemの一覧があってRubyInstaller 1.9.1p243についてのものが35件挙げられている。

どちらもインストーラのサイズはほぼ同じ。

機能にもいろいろと違いはありそうだが、RubyInstallerにしとこうかな…。