Vimのプラグイン管理Vundleにてrails.vimを入れるとautoload周りでエラーが出る件について

vimプラグイン管理

vimプラグイン管理に関して、以前はvim-pathogenを使ってbundleディレクトリ以下にプラグインファイルをまとめる管理をしていた。標準のvimのやり方だと、プラグインのファイルが混ざってしまい、特定のプラグインを削除したり、更新したりをするためにファイルを1つ1つ確認せねばならず面倒になるからだ。

最近はvim-pathogenを一歩進めたvundleというプラグインを使っている。これは設定ファイルを書いておけば、vim上からプラグインを自動でダウンロード、更新してbundleディレクトリ以下で管理してくれる。vundleはRailsのgem管理に使われているBundleを参考にしているらしい。

ということでvundleを使っている。

rails.vimでautoload周りのエラーが出る

ところが最近、rails.vimの読み込みで失敗してしまう。調べてみたところ、以下の記事が見つかった。

" Bundle 'rails.vim'
" Fetch 'rails.vim' plugin from github.
Bundle 'tpope/vim-rails'
としたところ、ちゃんと使えるようになりました。

http://d.hatena.ne.jp/Umeyashiki/20111001/1317488217

上記のとおり、rails.vimからtpope/vim-railsに変更すると上手く動作する。

これはどういうことなのか。そもそもvundleの動作原理を知らなかったのでソースを参照することにした。

vundleの仕組みを調べる

https://github.com/gmarik/vundle/blob/master/autoload/vundle/config.vim

これを見ると、parse_nameで何処にレポジトリを見に行くのか決定している。
例えばレポジトリの設定を

Bundle 'rails.vim'

のように書いていくのだが、この書き方によって変わる。

  • https://github.com/以下を見に行く
    • 先頭がgh:もしくはgithub:
    • [文字列]/[文字列]であった場合
  • そのままアクセスする
    • git://から始まる
    • file://もしくはhttps://から始まる
    • 最後が.gitである
  • https://github.com/vim-scripts/以下を見に行く
    • 上記以外の場合

よって、

Bundle 'tpope/vim-fugitive'
Bundle 'git://git.wincent.com/command-t.git'
Bundle 'rails.vim'

のような指定があった場合、tpope/vim-fugitiveは[文字列]/[文字列]なので、https://github.com/tpope/vim-fugitive.gitを、git://git.wincent.com/command-t.gitはそのまま、rails.vimhttps://github.com/vim-scripts/rails.vimを見に行く。

https://github.com/vim-scripts/rails.vim のレポジトリの中を見ると、autoloadディレクトリはなく、2011/8/31の更新のままであり、かつミラーであると記されている。本家のhttps://github.com/tpope/vim-railsは5日前に変更されたばかりのファイルがあり、前述のミラーは多少古いようだ。

結論

rails.vimのように、単純に書いただけだと割と古い https://github.com/vim-scripts/スクリプトを掴んでしまうので、多少面倒でもgithubプラグインの執筆者を調べてtpope/vim-railsのように書いたほうが最新のプラグインを使えるので楽しいかと思う。

Ruby1.9のCGI.unescapeHTMLにて実体参照の戻しを行う場合には変換元の文字列のエンコーディングを考慮する必要がある

Ruby1.9で遊び始めた。WebのAPI叩いていたら、実体参照が含まれる文字列をunescapeHTMLしようとしても上手くいかない。例えば、"&#xffff"のような形式の実体参照

Ruby1.8の場合には問題とならなかったケースだった。cgi.rbのソースコードを読んで、ようやく原因が分かった。

cgi/util.rb@1.9.2p180に含まれる、一節を抜粋する。

  def CGI::unescapeHTML(string)
    enc = string.encoding
#(中略)
    string.gsub(/&(amp|quot|gt|lt|\#[0-9]+|\#x[0-9A-Fa-f]+);/) do
      match = $1.dup
      case match
#(中略)
      when /\A#x([0-9a-f]+)\z/i
        n = $1.hex
        if enc == Encoding::UTF_8 or
          enc == Encoding::ISO_8859_1 && n < 256 or
          asciicompat && n < 128
          n.chr(enc)
        else
          "&#x#{$1};"
        end
      else

上記のwhen節は正規表現で"&#x0000;"形式を抜き出したあとの処理になる。encとは、引数stringのencodingを示し、この入ってくるstringのエンコーディングUTF-8か、それ以外で処理が異なる。

Ruby1.9ではそれぞれの文字列にそれぞれのエンコーディングが関連付けられる。よって引数としてのstringにASCIIがエンコディングとなっている文字列が入っているのではないか、と考えられる。

#!/usr/bin/env ruby
require 'cgi'

print "RUBY_VERSION ", RUBY_VERSION,"\n\n"
word = "&#x901A;&#x5E38;"
print "encoding: ", word.encoding, "\n"
print "word: ", CGI.unescapeHTML(word), "\n\n"

word_utf8 = word.encode("UTF-8")
print "encoding: ", word_utf8.encoding, "\n"
print "word: ", CGI.unescapeHTML(word_utf8), "\n\n"

上記に検証コードを示す。下記に実行結果を示す。

$ ruby unescape.rb
RUBY_VERSION 1.9.2

encoding: US-ASCII
word: &#x901A;&#x5E38;

encoding: UTF-8
word: 通常

推測のとおり、US-ASCIIを入れてしまっていたようだ。文字コードを利用して整数から文字に変換するためには、当然、変換先のエンコーディングが必要になる。US-ASCIIのようなエンコーディング指定では、無理に変換しようとせずに、無視される。

Ruby1.9ではプログラムの冒頭にてリテラル文字列のエンコーディングに何を使うかの指定が行える。そのため通常は問題にならなそうだ。

# -*- encoding: utf-8 -*-

今回のケースでは、WebのAPIを叩く過程のどこかでASCIIエンコーディングの文字列が返ってきたようだ。どこで入っていたのかについては確認をしていない。

あまりにもアホなミスなのか、ネット上にunescapeHTMLについての事例の情報がなかった。恥ずかしいが、書いておこうと思う。

vimでruby,railsのインストールをUbuntu 10.4で

結論としては素直にvim-railsを入れましょう

Ubuntu 10.4が出たのでvimrailsのコードを編集しやすい環境を構築中。オムニ補完が欲しいので、rubyライブラリにリンクしたvimをインストールする。

sudo aptitude install vim-nox

当初は vim-basic あたりで十分かと思っていた。が、
vim --version | grep ruby
コマンドでrubyがリンクされているかどうか確認してみたところ、 +ruby ではなく -ruby なんぞが出てきた。面倒なので vim の再コンパイルをしようとしたが、尋常ではない依存関係のパッケージの多さに諦めた。debian/rulesを読むと、vim-gnome や vim-nox なんかは ruby ライブラリを有効にしてある設定だったので、それを入れることにした。

が、次は rubygems がない。

sudo aptitude install rubygems

ruby-enterprise を入れていたので、Ubunturuby は入れていなかった。混在するのは得策ではないと考えたからだ。諦めた。素直に rubygems を入れよう。

次は rails が入っていないと...

sudo /usr/bin/gem install rails

うん。もう既に vim-rails を入れない理由はない。素直に vim-rails を入れよう。

sudo aptitude install vim-rails
vim-ruby-install.rb
vim-rails-setup

Ruby on Railsのプラグイン(gem)まとめ

2009年11月7日の時点で、google先生に時々教えてもらう程度で知っているプラグイン(プラギン?)。誰もまとめてくれないので書く。

認証プラグイン

auth_logic

AuthlogicはBen Johnson@Binary Logic氏が作成した「キレイで、シンプルで、控えめな」認証プラグインだ。Railsの認証機能としては、Rails自身の簡便な認証機構や、acts_as_authentication / restful_authenticationを使うことが多いが、このプラグインも少しずつ人気が出てきているようだ
http://d.hatena.ne.jp/mothprog/20090331/1238494900

他には acts_as_authenticated や restful_authentication などの認証のためのプラグインが存在している。authlogic で具体的にどういう controller と view を書けばよいのか分からない場合は後述の groundwork を使うと実装された状態のテンプレートを作ることができる。

ページネーションプラグイン

mislav-will_paginate

The name of the will_paginate gem is now “mislav-will_paginate” because it’s being built by GitHub. If you have a gem named “will_paginate”, it is old and you are advised to remove it from your system.
http://wiki.github.com/mislav/will_paginate

だってさ。<> を作るのがダルいときにやってくれる。

HTMLテンプレートプラグイン

Haml
<div id="profile">
  <div class="left column">
    <div id="date"><%= print_date %></div>
    <div id="address"><%= current_user.address %></div>
  </div>
  <div class="right column">
    <div id="email"><%= current_user.email %></div>
    <div id="bio"><%= current_user.bio %></div>
  </div>
</div>

#profile
  .left.column
    #date= print_date
    #address= current_user.address
  .right.column
    #email= current_user.email
    #bio= current_user.bio

のように書ける。nested なレイアウトエンジン。
erb の代わりに使うので index.html.erb ではなく index.html.haml とする。

html から haml が生成できる html2haml が標準で付属することはあまり知られていない。

CSSテンプレートプラグイン

Sass

Haml と同様に

table.hl {
  margin: 2em 0;
}
table.hl td.ln {
  text-align: right;
}

li {
  font-family: serif;
  font-weight: bold;
  font-size: 1.2em;
}

table.hl
  margin: 2em 0
  td.ln
    text-align: right

li
  font:
    family: serif
    weight: bold
    size: 1.2em

のように書ける。変数にも対応する。

public/stylesheets ディレクトリに sass ディレクトリを作成して default.sass なんぞを置くと、stylesheets/default.css にアクセスされたときに自動で sass から css のファイルが作成され、配置される。変更があっても自動で反映される。

これも css2sass が標準で添付することはあまり知られていない。

テスト

cucumber, webrat

features テストを標準語に近い形で書ける。らしい。
BDD 駆動のテストができるなんたらかんたら。

glowl

マカー御用達。ちなみに Windows 版の glowl も出た。

Rails Webアプリケーションのテンプレート

Groundwork

で、以上に述べたプラグイン(からwill_paginateを除く)が全て実装された Rails Web アプリケーションの雛形を作ることのできるアプリケーションが Groundwork である。

Groundwork is a set of rails templates that has the goal to jump-start the development of rails applications by helping in the configuration of development environment and with the implementation of features present in the majority of web applications:

Authentication
Authorization
Internationalization
Basic Administration
Basic Layout
In the best case it will permit a developer to focus in the domain specific features of his application.
http://github.com/hectoregm/groundwork

rails アプリの git の初期設定は設定ファイルをよけるなどの作業がダルいのだが、この雛形作成アプリケーションでは自動で処理してくれる。国際化にも対応しているのでそのようなコードを書きたい場合は非常にいい(その逆の場合は大変だ)。

認証まわり、レイアウトまわり、自動テストまわりの今年4月時点の動向まで追いつきたい場合は、このコードを読むと一通りが分かってよさそうだ。

それ以降のトレンドは知らない。

その他

使うときは使うよねプラグイン

  • active_scaffold
    • もしかして ajax-scaffold
    • controllerとview書かずにmodelの値を編集できるページを作ってくれる
    • 管理者ホイホイ
  • auto_complete
    • 自動補完・サジェスト
    • 動的にデータベース問い合わせしない場合はsuggest.js使った方が幸せになれるかも
  • acts_as_ordered_tree
    • 順序つき木
    • sortable_elementと連携することで木+ajaxD&Dの荒業が可能
  • super_inplace_controls
    • in_place_editingの派生。
    • 文字列クリックしたら編集できるアレ
  • acts_as_versioned
    • 版管理
  • crummy
  • acts_as_paranoid
    • 論理削除
    • アカウント消してくださいと要望が来るけれども消えたように見えて内部的には「削除された日時」を書き込んで削除されたように見えるようにしただけで、実はデータベース上にデータは残ってたりするんですよ的なアレ
  • mimetype-fu
    • MIMETYPEを別のに設定したいときあるよね
  • search_do
    • HE(Hyper Estraier)な前文検索を簡単に
    • もしかして acts_as_searchable
  • html5jp_graphs
    • html5なグラフっしょ
  • google_charts_on_rails
    • google chartで十分っしょ
  • acts_as_commentable
    • モデルにコメントをつけたい
  • acts_as_rated
    • モデルに評価(レイティング)したい
  • ajaxful-rating
    • モデルに5つ星でajaxな評価をしたい
  • background-fu
    • DBを介して背景仕事を渡す
      • 非同期な長時間な仕事に向く?
  • backgrounDRb
    • Drubyを介して背景仕事を渡す
      • 同期な短時間の並列仕事に向く?
  • jpmobile
    • ケータイ
  • calendar_date_select
    • カレンダーの選択をグラフィカルに
  • jRails
  • web-app-theme
    • デフォルトのテーマ
    • 定型なのでCSSが色々と返られるし出てくる(はず)
    • 管理者画面でデザインを投げたいときー
    • jqueryのテーマがもっさりしていたときー
  • InlineRuby
  • aasm
    • !?
  • capistrano
    • アプリケーションの本番環境へのデプロイ(設置・更新・メンテナンスモード移行)をコマンド1つで
  • Sinatra
    • 節子、それプラギンちゃう

節子、なんでrailsのプラギンたくさんあるちゅうのに日本語解説サイトがすくないんやろか。

...にいちゃん、メリケン語...読めないからそう思うだけや。

netcommons2の動画配信モジュールが上手く動かなかった件について

netcommonsとは、PCにそれほど詳しくない一般利用者でも使いやすいように管理されているCMSであり、XOOPSベースのPHPコードによって構成されている。


このnetcommonsにて動画配信モジュールが公開されている。この動画配信モジュールを入れてみたのだが、なかなか動かない。コードを見てみると、環境によって注意すべき点がいくつかあり、やっとの思いで動作させることができた。この動画配信モジュールが目的としている所はnetcommons利用者のレベルに合わせた作りとなっており、簡単で使いやすいものになっている点が評価できる。


そこで動作しなかった場合の解決方法を書いておこうと思う。

ffmpegffmpeg-phpコンパイル

このモジュールはffmpegffmpeg-phpを利用する。手動コンパイルマニアでなければ、当然パッケージ管理システムのffmpegffmpeg-phpを利用したほうが良い。


ちなみに、ffmpeg-phpに関してはGDライブラリを利用できる状態でコンパイルする必要がある。


debianubuntuの特定バージョンによってはffmpegそのものが存在しないこともある。
そのような手動コンパイルしたいという気骨のある方は、
ffmpegの設定は

./configure --enable-gpl --enable-nonfree --enable-pthreads --enable-shared --prefix=/usr/local

ffmpeg-phpの設定は

./configure --with-php-config=/usr/local/php/bin/php-config --enable-skip-gd-check

にて上手くいったということをお伝えしたい。


尚、ffmpeg-phpのenable-skip-gd-checkとはgdライブラリを自動認識できなかった場合にgdの組み込みを強制的にenableにするものであり、gd-checkをskipすることをenableするものではないことに注意されたい。

動画配信モジュールのインストール

圧縮ファイルから展開したmultimediaディレクトリをwebapp以下moduleディレクトリの中に移動し、netcommonsに管理者権限でログインして、管理パネルからインストールを行う。

フレーム取得処理における修正

自分が検証を行ったとき、動画配信モジュールのパネルを表示し、動画をアップロードするところまでは行えたが、その後、レスポンスが発生せず、「動画がアップロードしています」という表示ダイアログで停止する状況に出会った。(ダイアログの日本語がおかしいのは気にしない)

コードを調べてみたところ、multimedia/validator/Validator_ItemUpload.class.php内の

:
$duration = $mov->getDuration();
$frame = $mov->getFrame(ceil($mov->getFrameCount()/2));
$image = $frame->toGDImage();
:

にてエラーが発生していることがわかった。


エラーの内容は、3行目の$frame->toGDImage()にて、$frameがnullであり、メソッドを解決できないということであった。そこで2行目の$mov->getFrameについて、様々な動画をアップロードして検証してみたところ、動画ファイルによっては正しくフレーム情報を取れていないことが分かった。


そもそも、この箇所は何をやっているのかというと、動画のフレーム数の取得を行い、その半分あたりのフレームを取得するという処理を行っている。

:
// $frame = $mov->getFrame(ceil($mov->getFrameCount()/2));
$frame = $mov->getFrame(1);
:

どうやら上記のコードのように最初のフレームであれば安定して取得できるようだ。動画の真ん中のフレームをサムネイルにしたいという要求はそれほど強くは無かったので、上記のコードに修正することで解決を行った。

動画変換における修正


その後、flvファイル、wmvファイル、mp4ファイルにて動画アップロードの検証を行った所、flvファイルは無事に投稿できるが、wmvファイルとmp4ファイルの投稿は失敗する事態に遭遇した。


この動画配信モジュールはflashを利用して閲覧する形式であり、flvファイルは無変換で表示する仕様になっている。それ以外のファイルはmultimedia/validator/Validator_ItemUpload.class.phpファイルに記述されているように、

$str_cmd = "/usr/local/bin/ffmpeg -y -i ".$file_dir.$file['physical_file_name']." -ar 44100 ".$file_dir.$item_name.".flv 2>&1";

によって動画変換を行う。


自分の環境では、LD_LIBRARY_PATHが/usr/local/libを指し示していなかったので、

$str_cmd = "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib /usr/local/bin/ffmpeg -y -i ".$file_dir.$file['physical_file_name']." -ar 44100 ".$file_dir.$item_name.".flv 2>&1";

と修正することで正しく動作した。


この箇所が上手くいかない場合は、ターミナル上で

/usr/local/bin/ffmpeg -i [入力ファイル名] [出力ファイル名].flv

で上手くいくかどうか確認してみると良いかもしれない。

なお、linux系ではffmpegの実行ファイルは/usr/bin/ffmpegにあることが多いため、パッケージ管理システムなどでffmpegffmpeg-phpのインストールを済ませた方は、

$str_cmd = "/usr/bin/ffmpeg -y -i ".$file_dir.$file['physical_file_name']." -ar 44100 ".$file_dir.$item_name.".flv 2>&1";

に修正すると良い。

$ which ffmpeg
/usr/bin/ffmpeg

上記のようにwhichコマンドで探しても良い。(ffmpegの前にディレクトリつけなくてもいいのになぁ)

アップロードサイズの制限

この点については実際に効くのかどうか確認をしていないので、参考程度に。意味のない設定かもしれない。


この一連のシステムにはアップロードできるファイルのサイズを制限する設定がいくつかあり、php、netcommmons、そしてmultimediaモジュールの3つにて制限を行っているように見える。この制限を修正する。

webapp/module/multimedia/config/main.ini

define:MULTIMEDIA_UPLOAD_MAX_SIZE_MEDIA = 100000000
webapp/config/global-config.ini

_UPLOAD_MAX_SIZE_IMAGE = 100000000
_UPLOAD_MAX_SIZE_ATTACHMENT = 100000000
php.ini

upload_max_filesize = 100M
post_max_size = 100M

まとめ

もし、この動画配信モジュールにて、動画アップロードの際にエラーが発生する場合は、主にmultimedia/validator/Validator_ItemUpload.class.phpを確認することで解決できた。アップロードされたファイルと生成されたサムネイルは、webapp/uploads/multimedia内にある。もしファイルのみしか存在しない状況であれば、それはサムネイルの生成に失敗しているということであり、getFrameからtoGDImage()の流れに失敗している可能性が高い。


この情報が少しでも他のnetcommonsで動画配信を行おうとしている利用者の助けになれば幸いである。

おまけ

ffmpeg-phpのINSTALLファイルにて

TROUBLESHOOTING
---------------

(省略)

Q: Can I just pay you to install it?

A: Yes. I will install ffmpeg + codecs, ffmpeg-php and flvtool2 on your server for $75 USD. This
   is everything you need to convert just about any movie type to flash.
   Shoot me an email at ffmpeg.php AT gmail.com if you are interested.

おぉぅ、オープンソースビジネス...!!!

rubygemのgooglereaderでフィードURLに引数的な何かが入っているために記事を引っ張れない件について

Google ReaderというRSSリーダーを提供するサービスが存在する。このRSSリーダーではサーバにGoogleがアクセスした個別のフィード情報(記事情報)を保存して、多くのユーザーに提供するというモデルを取っている。このため、今まで購読していなかったサイトのフィードを登録すると過去の記事のRSSまで確認することが出来たり、そのフィードをGoogle Reader内で何人が購読しているのかを知ったりできる。


このうち、「何人が購読しているのか」、「過去の記事はなんだったのか」という情報を得て活用したい場合、隠しAPIであるGoogle Reader APIを叩く事になる。隠しAPIなので仕様は公開されていないのだが、rubygemsgooglereaderプラグインを利用すると簡単にアクセスすることが出来る。

url = "http://www.google.com/reader/atom/feed/http://example.com/feed%3Fq%3Drails"
Google::Reader::Base.get_entries(url)

などのように、GoogleReaderAPIまとめサイトの情報を参考にして叩くのだが、上手くいかない。ブラウザからのアクセスは上手くいく。URLエンコードの問題だろうと予想する。隠しAPIだけあって他のAPI利用記事でも苦労している旨が伝えられている。

module Google
  class Base
    private
    def self.request(method, url, o={})
      :
      url  = URI.parse(url)
      #url  = URI.parse(URI.escape(url))
      :
    end
  end
end

googlebaseの中にどうもuriエスケープを強制する部分があったので外す。これにて上手く取得できるようになった。

正直、誰得な記事なわけだが。

ロリポップの生ログをrubyとmechanizeで取得するコード

gemでmechanizeをインストールしている必要あり。mechanizeを使えば簡単にbotが作れるという1例。
debugとか何も考えていないので、必要があれば適宜追加で。

#!/usr/bin/env ruby

require 'rubygems'
require 'mechanize'

sld = "diffshare"
tld = "com"
pass = "pass"
domain = "www.diffshare.com"

# login form
agent = WWW::Mechanize.new
login = agent.get "https://user.lolipop.jp/"
form = login.form_with "frm"
domain_plan = form.radiobutton_with "domain_plan"
domain_plan.value = 1
login = form.submit

# domain login form
form = login.form_with "frm"
domain_name_2 = form.field_with "domain_name_2"
domain_name_2.value = sld
domain_name_3 = form.field_with "domain_name_3"
domain_name_3.value = tld
passwd = form.field_with "passwd"
passwd.value = pass
form.action = form.action + "?mode=login&exec=1"
logined = form.submit

# log access form
aclog = agent.get "https://user.lolipop.jp/?mode=aclog"
form = aclog.form_with "frm"
form.field_with("select_domain_name").value = domain
form.action = "http://logs5.lolipop.jp/index.php?login=1"
log = form.submit

# log download form
form = log.form_with "frm2"
sltDate = form.field "sltDate"
sltDate.options.last.select
data = form.submit

data.save_as(data.filename)