メイン

Ruby アーカイブ

2005年06月24日

CGIKit2に挑戦する(1)

環境

Rubyのインストール手順

  1. Rubyのサイトからruby 1.8.2をダウンロードする。

  2. ダウンロードしたファイルを展開する。

    % tar xzvf ruby-1.8.2.tar.gz

  3. ファイルを展開すると、ruby-1.8.2のディレクトリができるので、移動する。

    % cd ruby-1.8.2

  4. ホームディレクトリにrubyディレクトリを作成する。
    このディレクトリにrubyをインストールします。

    % mkdir ~/ruby

  5. rubyをホームディレクトリにインストールする。

    % ./configure --prefix=$HOME/ruby
    % make
    % make install
    % make clean

  6. 動作を確認する。
    Rubyのバージョン番号が表示されたら、インストールは成功です。

    % ~/ruby/bin/ruby -v
    ruby 1.8.2 (2004-12-25) [i686-linux]

CGIKit2のインストール

インストールの方法は、CGIKitのサイトに詳しい説明があります。

  1. CGIKitのサイトから2.0.0-preview-1ダウンロードする。

  2. ダウンロードしたファイルを展開する。

    % tar xzvf cgikit-2.0.0-preview-1.tar.gz

  3. 展開するとcgikit-2.0.0-preview-1のディレクトリができるので、移動する。

    % cd cgikit-2.0.0-preview-1

  4. CGIKitをインストールする
    ホームディレクトリにインストールしたrubyにCGIKitをインストールします。

    % ~/ruby/bin/ruby setup.rb config
    % ~/ruby/bin/ruby setup.rb config

  5. 動作を確認する
    CGIKitがインストールされていることを確認します。

    % ~/ruby/bin/ruby -r cgikit -e 'p CGIKit;p CGIKit::VERSION'
    CGIKit
    "2.0.0"

2005年06月27日

CGIKit2に挑戦する(2)

Tutorialの動作を一通り確認する。
このTutorialは、本当によくできている。 ドキュメントの充実はライブラリの普及するための重要な要素だと思うが、今後CGIKitは広く使われていくのではないだろうか。

Tutorial7の最後に、

Tutorial8では認証について説明します。

とあるが、Tutorial8のページはまだないようだ。 配布ファイルにも含まれていない。 しばらく様子を見ようと思う。

CGIKit2には、CGIKit1.xにはなかった開発ツールが用意されていた。 便利かも。

2005年06月28日

Ruby on Railsでアクセスカウンタを作成する

Ruby on Railsの練習に、アクセスカウンタを作成してみる。

データベースの作成

MySQLを使用して、データベースとテーブルを作成する。

データベースを作成。

CREATE DATABASE `counter`;

テーブルを作成。

CREATE TABLE `counts` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `count` INT NOT NULL,
  PRIMARY KEY ( `id` )
);

レコードを登録する。

INSERT INTO `counts` VALUES(0);

プロジェクトの作成

C:\railsディレクトリを作成する。
C:\railsディレクトリに移動して、次のコマンドを実行する。

rails counter

C:\rails\counter\config\database.ymlのdatabaseの項目をcounterに編集する。

development:
  adapter: mysql
  database: counter
  host: localhost
  username: root
  password: 

test:
  adapter: mysql
  database: counter
  host: localhost
  username: root
  password:

production:
  adapter: mysql
  database: counter
  host: localhost
  username: root
  password:

モデルとコントローラを作成する。

ruby script/generate model Count

ruby script/generate controller count

コーディング

C:\rails\counter\app\views\count\index.rhtml ファイルを作成する。
index.rhtmlでは、@countの値を表示する。

<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja-JP" lang="ja-JP">
<head>
  <title>カウンタ</title>
</head>
<body>
  <h1>カウンタ</h1>
  <p>
    <%= @count %>
  </p>
</body>
</html>

C:\rails\counter\app\controllers\count_controller.rb を開く。
countsテーブルの先頭のレコードを取得し、countフィールドの値に1を加算して、保存する。 @countの値は、index.rhtmlで表示する。

class CountController < ApplicationController
  def index
    count = Count.find(:first)
    count.count += 1
    count.save
    @count = count.count
  end
end

WEBrick サーバーを起動する。

ruby script/server

http://localhost:3000/count/にアクセスする。
リロードするたびに数字が増えたら成功。

2005年06月29日

Ruby on Railsで一行掲示板を作成する

2007年4月29日追記:Ruby on Rails 1.2対応版を作成しました。

Ruby on Railsの練習に、簡単な掲示板を作成してみる。

データベースの作成

MySQLを使用して、データベースとテーブルを作成する。

データベースを作成。

CREATE DATABASE `bbs`;

テーブルを作成。

CREATE TABLE `items` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `name` TEXT NOT NULL,
  `body` TEXT NOT NULL,
  PRIMARY KEY ( `id` )
)

nameは投稿者名、bodyは本文。

プロジェクトの作成

C:\railsディレクトリを作成する。
C:\railsディレクトリに移動して、次のコマンドを実行する。

rails bbs

C:\rails\bbs\config\database.ymlのdatabaseの項目をbbsに編集する。

development:
  adapter: mysql
  database: bbs
  host: localhost
  username: root
  password: 

test:
  adapter: mysql
  database: bbs
  host: localhost
  username: root
  password:

production:
  adapter: mysql
  database: bbs
  host: localhost
  username: root
  password:

モデルとコントローラを作成する。

ruby script/generate model Item

ruby script/generate controller item

コーディング

C:\rails\bbs\app\views\item\index.rhtml ファイルを作成する。
index.rhtmlでは、投稿された内容が配列で @items に入り、 @items.each do ~ end で、投稿内容を出力する。
フォームで入力された内容は、コントローラの add_item メソッドで処理をする。

<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja-JP" lang="ja-JP">
<head>
  <title>一行掲示板</title>
</head>
<body>
  <h1>一行掲示板</h1>
  <dl>
<% @items.each do |@item| %>
    <dt><%=h @item.name %></dt>
    <dd><%=h @item.body %></dd>
<% end %>
  </dl>
  <hr />
  <%= form_tag :action=>"add_item" %>
    名前:<%= text_field("new_item", "name", "size"=>10) %><br />
    本文:<%= text_field("new_item", "body", "size"=>60) %><br />
    <input type="submit" value="Add" />
  </form>
</body>
</html>

C:\rails\bbs\app\controllers\item_controller.rb を開く。

class ItemController < ApplicationController

  def index
    @items = Item.find_all
  end

  def add_item
    item = Item.new
    item.attributes = @params["new_item"]
    if item.save
      redirect_to(:action => "index")
    else
      render_text "Couldn't add new item"
    end
  end
end

WEBrick サーバーを起動する。

ruby script/server

http://localhost:3000/item/にアクセスする。
掲示板が表示されたら、成功。

2005年06月30日

WindowsでCGIKit2のckprojectが使えない

WindowsでCGIKit2のckprojectを使用するとエラーになる。

[c:\cgikit]ruby C:\ruby\bin\ckproject --ja todo
C:/ruby/bin/ckproject:17:in ``': No such file or directory - which ruby (Errno::ENOENT)
    from C:/ruby/bin/ckproject:17:in `default_ruby_path'
    from C:/ruby/bin/ckproject:23

Windowsにはwhichコマンドがないのが原因のようだ。

def default_ruby_path
  %x(which ruby).chomp
end

whichコマンドを作るを参考にしてwhich.batを作成し、パスの通ったディレクトリにおけば、ckprojectが使用できるようになりました。

2005年07月04日

ファイルの拡張子を変更する

ファイルの拡張子を変更する

change_ext("sample.txt", ".html") #=> sample.html

# ファイル名(filename)の拡張子をextに変更する
def change_ext(filename, ext)
  return filename.gsub(/\.\w+$/, ext)
end

CGIKitでTODOリストを作成する。(1)

Ruby on Rails のTODOリストプログラムを参考にして、TODOリストプログラムを作成します。 CGIKitのインストール

CGIKit2をインストールします。

ActiveRecordのインストール

O/Rマッピングのライブラリ ActiveRecord をインストールします。

RubyGemsがインストールされていなければ、インストールします。

ActiveRecordをインストールします。

gem install activerecord

(参考)Rubyist Magazineの記事

データベースの作成

MySQLを使用して、データベースとテーブルを作成する。

データベースを作成。

-- Create the database
CREATE DATABASE `todo`;

テーブルを作成。

-- Create the table
CREATE TABLE `todos` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `description` VARCHAR( 100 ) NOT NULL ,
  `done` TINYINT DEFAULT 0 NOT NULL ,
  PRIMARY KEY ( `id` )
);

プロジェクトの作成

プロジェクト用のディレクトリを作成する。(C:\cgikit)

mkdir C:\cgikit

cgikitディレクトリに移動して、ckprojectコマンドを実行する。

※Windowsでckprojectを使うとエラーになる場合の対処法

cd c:\cgikit
ruby C:\ruby\bin\ckproject --ja TodoList

TodoListディレクトリに、アプリケーションのひな形が作成されました。

つづく

2005年07月05日

CGIKitでTODOリストを作成する。(2)

続き

Ruby on Rails のTODOリストプログラムを参考にして、TODOリストプログラムを作成します。

コーディング

はじめの一歩

さっそく、アプリケーションを起動してみます。 TodoListディレクトリのTodoList.rbを実行します。

ruby TodoList.rb

これで、開発モードのWEBRickサーバがポート8080で立ち上がりました。 ブラウザで、http://localhost:8080/ を開くと、タイトルが「MainPage」でBodyが空白のページが表示されます。

項目の表示 1

配列の値を表示する処理を実装します。

MainPage.htmlを編集します。

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
  <title>My todo list</title>
</head>
<body>
  <div ckid="items">
    <span ckid="item">item</span><br />
  </div>
</body>
</html>

<div ckid="items">~</div>の間を配列の数だけ繰り返します。
<span ckid="item">item</span>は、配列の値と置き換えます。

MainPage.ckdを編集します。

{
  :items => {
    :element => Repetition,
    :list => :items,
    :item => :todo
  },
  :item => {
    :element => String,
    :value => :todo,
  }
}

ckid="items" と ckid="item" にコンポーネントを割り当てます。 ckid="items" はMainPage#items()メソッドの返値を繰り返し処理します。 ckid="item" はMainPage#items()メソッドの返値の各要素に置き換えます。

MainPage.rbを編集します。

module TodoList
  class MainPage < CGIKit::Component
    def items
      (1..20).to_a
    end
  end
end

さあ、WEBRickサーバを再起動し、ブラウザをリロードしてみましょう。
MainPage#items()メソッドの返値を順番に表示します。

つづく

2005年07月27日

RubyでファイルのリストをとってEXCELに書き込むプログラム

pythonでファイルのリストをとってEXCELに書き込むプログラム

日経ソフトウエアの8月号にEXCELのVBAで、指定フォルダのファイルのリストを取ってEXCELに書き込むという記事が載っていた。 Pythonでやってみる。

Rubyで同じことをやってみる

#!ruby -Ks

#http://lightson.dip.jp/blog/seko/342
require 'win32ole'

PATH = 'C:/temp'

xlapp = WIN32OLE.new('Excel.Application')
xlapp.visible = true
xlapp.Workbooks.add()
sheet = xlapp.Sheets(1)

i = 1
Dir.foreach(PATH) do |file|
  filename = File.join(PATH, file)
  sheet.Cells(i, 1).Value = filename
  sheet.Cells(i, 2).Value = File.size(filename)
  sheet.Cells(i, 3).Value = File.mtime(filename)
  i += 1
end

2005年08月11日

ActiveRecord

インストールしなくても(インストールする権限が無くても)、ActiveRecordActiveSupportのファイルをコピーすればActiveRecordが使えるみたい。

2005年08月12日

CGIKitでTODOリストを作成する。(3)

前回の続き

Ruby on Rails のTODOリストプログラムを参考にして、TODOリストプログラムを作成します。

項目の表示 2

ActiveRecordを使用して、データベースのtodoテーブルのデータを取得します。

lib/todo.rbファイルを作成します。

require 'active_record'

ActiveRecord::Base.establish_connection(
  :adapter => 'mysql',
  :host => 'localhost',
  :username => 'root',
  :password => '',
  :database => 'todo'
)

class Todo < ActiveRecord::Base
end

MainPage.rbを編集します。

require 'lib/todo'
module TodoList
  class MainPage < CGIKit::Component
    def items
      Todo.find_all
    end
  end
end

MainPage.ckdを編集します。

{
  :items => {
    :element => Repetition,
    :list => :items,
    :item => :item
  },
  :item => {
    :element => String,
    :value => :"item.description",
  }
}

:item の :value を :todo から :"todo.description" に変更します。

WEBRickサーバを再起動し、ブラウザをリロードします。
todoテーブルには、まだデータがないので、何も表示されません。

データベースにデータを登録します。

INSERT INTO `todos` (description) VALUES ('Do my bed');

ブラウザをリロードすると、「Do my bed」が表示されます。

2005年08月16日

REXMLでノードの階層構造を出力する

REXMLでノードの階層構造を出力する

出力例

html[xmlns="http://www.w3.org/1999/xhtml"]
 head[]
  meta[content="text/html; charset=UTF-8",http-equiv="Content-Type"]
  title[]
 body[]
  h1[]
  h2[]
  div[]
   form[name="form1",id="form1"]
    p[]
     br[]
     input[name="textfield",type="text"]

ソースコード。

require 'rexml/document'

# 要素を階層表示する
# element 要素
# level 階層レベル
def show_element(element, level)
  # 空白
  sp = ' ' * level
  # 属性
  attr = []
  element.attributes.each do |name, value|
    attr << "#{name}=\"#{value}\""
  end
  # 出力
  puts "#{sp}#{element.name}[#{attr.join(',')}]"

  # 子要素
  element.each_element do |e|
    show_element(e, level + 1)
  end
end

if $0 == __FILE__
  if ARGV.size < 1
    puts "USAGE: ruby #{__FILE__} xmlfilepath"
    exit
  end
  doc = REXML::Document.new(File.new(ARGV[0]))
  show_element(doc.root, 0)
end

2005年08月17日

ckgeneratecode

一通り動作するようになりましたのでckgeneratecodeを公開します。

ckgeneratecodeは、CGIKit 2.x用テンプレートからバインディングファイルとコードを生成するプログラムです。

私のように、先にデザイナーさんからHTMLファイルをもらい、それを元にプログラムを作っていくスタイルだと、このようなスクリプトがあると楽になると思います。

2005年08月18日

goodic

goodicをRubyで作ってみました。ほとんど同じなのですが…。

Windowsで作ったので、入出力はShift_JISを前提にしました。

ruby goodic.rb ej python

py・thon

━━ n. 【動】ニシキヘビ; 大蛇.

ruby goodic.rb je 公園

こうえん 公園

a (public) park; 《小規模で四角形》a square.
国立[国定]公園 a national [quasi-national] park.
日比谷公園 Hibiya Park.

ruby goodic.rb jn おもむろに

おもむろに 0 【▽徐に】

(副)
落ち着いて、ゆっくりと事を始めるさま。ゆったりしたさま。
「―口を開く」

2005年08月19日

CGIKit2用日付選択コンポーネント

CGIKit2のサブコンポーネントの練習もかねて、日付選択コンポーネントを作成しました。

「日付」「日時」「時分」を選択する3種類を作成しました。

CGIKit2になって、簡単に作れるようになりましたね。

2005年08月20日

ckgeneratecode

ckgeneratecodeをPopupとBrowserの項目を配列で持つように変更しました。

CGIKit2のバインディングファイルは、解析はeval()でおこなっているので、Rubyの文法に従っていれば良いようです。 って、よく見たら書いてあった

書式はRubyスクリプトですが、ファイルをeval(load)した結果は Hashでなければなりません。

2005年08月22日

CGIKit2のコンポーネントとエレメント

CGIKit2のコンポーネントはHTMLに表示するためのもので、エレメントは値の表示と同期を行います。

だから、値の同期が必要ならサブコンポーネントを作るのではなくて、エレメントを作らないといけないことに気がつきました。

サブコンポーネントについてはTutorial5.5Tutorial6に詳しい解説がありますが、エレメントの作り方のドキュメントはないようです。ソースコードを探索しましょうか。

あと、CGIKitメーリングリスト - 760 で報告されている問題にはまってしまいました。

2.0.0-preview-1が公開されてから、たくさんの変更が行われているようですので、preview-2 を公開が待ち遠しいです。

2005年09月08日

CGIKit2の文字コードの処理について

るびま0009号Nvu と CGIKit2 による Web アプリケーション開発の記事を試しました。

Registration.rbに、

module Registration
  module UserInfo
    def to_utf8
      @name = Kconv.toutf8(@name)
      @age = Kconv.toutf8(@age)

というコード変換の処理を追加していますが、これは必要なんでしょうか。

CGIKit - 日本語処理 によると

CGIKit::Application#encoding

に文字コードを設定すればいいように思えます。

また、cgikitconf.rb の

def configure_localization
  # @encoding = nil

を設定してもいいように思えます。

試しに Kconv::UTF8 を指定してみたら、文字化けしました。あれ?

もうちょっと調べてみます。

追記

ちょうど同じ号にある標準添付ライブラリ紹介【第 3 回】Kconv/NKF/Iconv の記事を読むと、KconvのBUG に気になる記述が…。

試しに、cgikit/lang/ja.rb のKconvの処理をNKFに置き換えてみると、

class DynamicElement
  def encode_string( string, encoding )
    #Kconv.kconv(string, encoding)
    case encoding
    when Kconv::EUC
      return NKF.nkf('-e', string)
    when Kconv::SJIS
      return NKF.nkf('-s', string)
    when Kconv::JIS
      return NKF.nkf('-j', string)
    when Kconv::UTF8
      return NKF.nkf('-w', string)
    end
  end
end

文字化けの問題が解消しました。どうやら既知のバグだったようです。

でも、どうして Registration::UserInfo::to_utf8() はうまくいっているのだろう。

もうちょっと調べてみます。

続く

2005年09月10日

Windows用RDTool

Windows用のRDToolのバイナリがftp.ruby-lang.orgに用意されていることを知りました。

一緒に、rd2.batも作成すると便利。

@echo off
C:\ruby\bin\ruby C:\ruby\bin\rd2  %1 %2 %3 %4 %5 %6 %7 %8 %9

2005年09月12日

CGIKit 2.xを手動でインストールする

CGIKit 2.xを手動でインストールする

ローカルの開発環境ではCGIKit 2.xをインストールできても、実際に動作させるサーバではインストールできない場合もよくあります。

そういう場合のインストール方法。

ckproject でプロジェクトを作成した場合、次のようなディレクトリが作られます。

Sampleプロジェクトを作成すると、

ckproject Sample

ディレクトリ階層は、こうなる。

Sample/
  components/
  lib/
  resources/

ckprojectによって作られたソースコードには、libディレクトリを$LOAD_PATHに追加するコートがあります。

$LOAD_PATH.unshift('lib')

したがって、libディレクトリにライブラリを置けば、setup.rbでインストールしたときと同じように、ライブラリをロードできます。

cgikit-2.0.0-preview-1.tar.gz をダウンロードして、cgikit-2.0.0-preview-1フォルダに展開したとします。

Sample/lib/ ディレクトリに、cgikit-2.0.0-preview-1/lib/ディレクトリのファイルをすべてコピーすれば、完了です。

Sample/
  components/
  lib/
    cgikit/
      components/
        CKErrorPage/
      elements/
      lang/
      project/
        templates/
          en/
          ja/
    uconv/
  resources/

2005年09月13日

CGIKit2の文字コードの処理について(2)

前回の続き。

CGIKit 2.xでは、文字コードにUTF-8を推奨している。(CGIKit - 日本語処理)

しかし、ruby 1.8.2のKconvでは UTF-8 に変換できない。(るびま)

変換できないことを確認してみる。

require 'kconv'

# UTF-8 で表された 'Hello, るびま!'
str = "\x48\x65\x6c\x6c\x6f\x2c\x20\xe3\x82\x8b\xe3\x81\xb3\xe3\x81\xbe\xef\xbc\x81"

puts Kconv.guess(str) # => 6 == Kconv::UTF8 正しい
str = Kconv.kconv(str, Kconv::UTF8) # UTF-8をUTF-8に変換する
puts Kconv.guess(str) # => 1 == Kconv::JIS

# Shift_JIS で表された 'Hello, るびま!
str  = "\x48\x65\x6c\x6c\x6f\x2c\x20\x82\xe9\x82\xd1\x82\xdc\x81\x49"

puts Kconv.guess(str) # => 6 == Kconv::UTF8 正しい
str = Kconv.kconv(str, Kconv::UTF8) # Shift_JISをUTF-8に変換する
puts Kconv.guess(str) # => 1 == Kconv::JIS

対策としては、るびまにもあるように 修正されたkconv.rb を使用するのがよさそう。

修正されたkconv.rb をlibディレクトリにコピーすれば、使えるようになる(はず)。

2005年09月30日

Rubyを256倍使うための本 魔道編

RDについて書かれた、おそらく唯一の書籍。

RDの入門から始まり、HTMLの作成や、Rubyのリファレンスマニュアルの書き方、RDIndexや作表などの高度な使い方まで、幅広く解説している。著者はRDを活用しているだけあり、内容は実践的だ。

RDの構文は読みやすく、書きやすい。私もこの本を読んで以来、ドキュメントはRDで書くようになった。

ただ、残念な点は索引がないこと。知りたいことを探すのがちょっと大変だ。

2005年10月10日

MarkdownをHTMLに変換するエディタ

Ruby/TKの練習。

Markdownで記述したテキストをHTMLに変換するエディタ。

#!/usr/bin/env ruby
$KCODE = 'SJIS'
require 'tk'
require 'bluecloth'

class MarkdownEditor
  TITLE = 'Markdown Editor'

  def initialize

    Tk.root.title(TITLE)
    f = TkFrame.new.pack(:fill => :x)
    @button = TkButton.new(f, 
      'text'=>'Change Mode',
      'command' => proc{swich_mode()}
    ).pack('side' => 'left')
    @label = TkLabel.new(f).pack(:side => :left)

    scr_x = TkScrollbar.new.pack(:fill=>:x, :side=>:bottom)
    scr_y = TkScrollbar.new.pack(:fill=>:y, :side=>:right)
    @editor = TkText.new {
      xscrollbar(scr_x)
      yscrollbar(scr_y)
    }.pack(:fill => :both, :expand => true)

    to_text()
    Tk.mainloop
  end

  def to_html
    return if @state == :html
    begin
      Tk.root.cursor "watch"
      Tk.root.update
      @state = :html
      @label.text('HTML Mode')
      @source = @editor.value
      @editor.value = BlueCloth::new(@source).to_html
    ensure
      Tk.root.cursor ""
      Tk.root.update
    end
  end

  def to_text
    return if @state == :text
    @state = :text
    @label.text('Text Mode')
    @editor.value = @source
  end

  # モードを切り替える
  def swich_mode
    case @state
    when :text
      to_html()
    when :html
      to_text()
    end
  end
end

if __FILE__ == $0
  MarkdownEditor.new
end

追記
コメントで教えていただいた箇所を修正しました。

続く

2005年10月12日

Rubyist Magazine 0010 号

Rubyist Magazine 0010 号 が公開されました。

個人的に良かったのが、「あなたの Ruby コードを添削します」。

知らない便利なメソッドがたくさんありました。

このように、標準のメソッドをとことん使いたおしていけばコードを圧倒的に簡潔にできるのです。 String・Regexp・Array・Hash・Enumerable あたりだけで十分ですから、リファレンスマニュアルは熟読しておきましょう。

熟読します。

解説 Ruby Refactoring Browser - Emacs でリファクタリング は、噂に聞いていたRuby 用のリファクタリングブラウザ、「Ruby Refactoring Browser」の Emacs での使いかたについて解説記事です。これを読みながら試してみます。XYZZYでも使いたいな。

プログラマーのための YAML 入門 (中級編) は、YAMLの貴重な情報源です。YAMLを使うときにもう一度読みます。

シリーズ パッケージマネジメント 【第 2 回】 RubyGems (2) は、標準になりそうな勢いのRubyGemsの使い方の記事。使い方が一通りわかります。

とりあえず、ここまで読みました。

pdf-writer

pdf-writer は、PDFを作成するpure rubyなライブラリ。

[ruby-list:39016] ja font patch for pdf-writer-TP20030916 には、日本語を出力できるようにするパッチがある。最新版でも使えるかは未確認。

Creating Printable Documents with Ruby では、pdf-writer を使ってPDFを作成するチュートリアル。

2005年10月14日

MarkdownをHTMLに変換するエディタ(2)

前回 で作成したプログラムに、ポップアップメニューから「コピー」「切り取り」「貼り付け」の機能を追加する。

ポップアップメニューの作成は、「逆引き Ruby/Tk」の「ポップアップメニューを作る」で実装方法を発見した。

「コピー」「切り取り」「貼り付け」の実装方法がわからず、ソースコードを見ることに。

tk/text.rbを見ると、「text_cut」「text_copy」「text_paste」という、まさにそのままのメソッドを発見して、無事に完成。

ちなみに環境は、Windows + [One-Click Installer 1.8.2-15][4]

#!/usr/bin/env ruby
$KCODE = 'SJIS'
require 'tk'
require 'bluecloth'

class MarkdownEditor
  TITLE = 'Markdown Editor'

  def initialize
    Tk.root.title(TITLE)
    f = TkFrame.new.pack(:fill => :x)
    @button = TkButton.new(f, 
      'text'=>'Change Mode',
      'command' => proc{swich_mode()}
    ).pack('side' => 'left')
    @label = TkLabel.new(f).pack(:side => :left)

    scr_x = TkScrollbar.new.pack(:fill=>:x, :side=>:bottom)
    scr_y = TkScrollbar.new.pack(:fill=>:y, :side=>:right)
    @editor = TkText.new {
      xscrollbar(scr_x)
      yscrollbar(scr_y)
    }.pack(:fill => :both, :expand => true)

    menu = TkMenu.new { tearoff 'off' }
    menu.add('command', :label => 'Cut', :command => proc{ @editor.text_cut })
    menu.add('command', :label => 'Copy', :command => proc{ @editor.text_copy })
    menu.add('command', :label => 'Paste', :command => proc{ @editor.text_paste })
    @editor.bind('ButtonPress-3', proc { |x, y| menu.popup(x, y) }, "%X %Y")

    to_text()
    Tk.mainloop
  end

  def to_html
    return if @state == :html
    begin
      Tk.root.cursor "watch"
      Tk.root.update
      @state = :html
      @label.text('HTML Mode')
      @source = @editor.value
      @editor.value = BlueCloth::new(@source).to_html
    ensure
      Tk.root.cursor ""
      Tk.root.update
    end
  end

  def to_text
    return if @state == :text
    @state = :text
    @label.text('Text Mode')
    @editor.value = @source
  end

  # モードを切り替える
  def swich_mode
    case @state
    when :text
      to_html()
    when :html
      to_text()
    end
  end
end

if __FILE__ == $0
  MarkdownEditor.new
end

[4]; http://rubyforge.org/frs/?groupid=167&releaseid=2049

2005年10月15日

WebUnit

WebUnit がRuby1.8に対応していることに気がつきました。

ここのページも更新しなきゃ。

2005年10月23日

rake(Ruby版のmake)[

rake(Ruby版のmake) について調べてみる。

ビルド言語 では、Martin Fowler 氏がrakeの魅力について語っている。

活動日誌(2005-02-14) は、Rakefile Format を翻訳した記事だ。
Rakefileのフォーマットを日本語で読むことができる。

インストールは RubyGems を使って、簡単にできる。次のコマンドを入力するだけだ。

gem install rake

ちなみに、RubyGems については Rubyist Magazine の次の記事が詳しい。

rake に挑戦

About "Rake Tutorial"Introducing Rake を試してみる。

環境は、Windows2000、One-Click Installer 1.8.2-15Borland C++ Compiler 5.5

Borland C++ Compilerを使うのは初めてなので、まず、Borland C++ Compiler 5.5 の動作確認から。

hello.cpp を作成。

/* hello.cpp */
#include <iostream.h>
main()
{
  cout << "Hello, World!" << endl;
  return 0;
}

コンパイルして、

> bcc32 hello.cpp

実行する。

> hello.exe
Hello, World!

OK。

チュートリアル にしたがって、main.c、greet.h、greet.c、build.bat(チュートリアルではbuild.sh)を作成する。

main.c

#include "greet.h"
int main() {
  greet ("World");
  return 0;
}

greet.h

extern void greet(const char * who);

greet.c

#include <stdio.h>
void greet (const char * who) {
  printf ("Hello, %s\n", who);
}

build.bat

bcc32 -c -o main.obj main.c
bcc32 -c -o greet.obj greet.c
bcc32 -ehello main.obj greet.obj

build.batを実行する。

> build.bat
> hello.exe
Hello, World

コンパイルに成功して、hello.exeが作られる。
hello.exeを実行すると、Hello, World の文字が表示される。

main.c から main.obj が、greet.c から greet.obj が作られて、main.obj と greet.obj から hello.exe が作られる。

greet.c を変更したら greet.obj を更新しないと、hello.exe には、greet.c の変更は反映されないよ。 つまり、ファイル間に依存関係があるということ。

Rakefile では、依存関係は次のように記述する

file "main.obj" => ["main.c", "greet.h"]

Rakefileを作る。

Rakefile

file 'main.obj' => ["main.c", "greet.h"] do
  sh "bcc32 -c -o main.obj main.c"
end

file 'greet.obj' => ['greet.c'] do
  sh "bcc32 -c -o greet.obj greet.c"
end

file "hello" => ["main.obj", "greet.obj"] do
  sh "bcc32 -ehello main.obj greet.obj"
end

rake hello を実行する。

> rake hello
bcc32 -ehello main.obj greet.obj
> hello.exe
Hello, World

成功だ。

greet.c の Hello を Hi に変更する。

#include <stdio.h>
void greet (const char * who) {
  printf ("Hi, %s\n", who);
}

もう一度、rake hello。

> rake hello
bcc32 -c -o greet.obj greet.c
bcc32 -ehello main.obj greet.obj
> hello.exe
Hi, World

変更された greet.c はコンパイルした。
変更されていない main.c はコンパイルしない。

次回に続く。(たぶん)

2005年11月02日

Rubyで簡易HTTPサーバ

Rubyで簡単なHTTPサーバを作ろうと思って調べてみました。

webrickを使えば、あっというまにできました。

#!ruby
require 'webrick'

srv = WEBrick::HTTPServer.new({
  :DocumentRoot => './',
  :BindAddress => '127.0.0.1',
  :Port => 10080})
srv.start

2005年11月13日

ApolloでSQLiteを使う

ApolloでSQLiteが動かないというメールをもらったので、調べてみました。

以下の操作で、SQLiteが使えました。

  1. Apollo - Delphi Ruby interface のページから ap-840-bde60-ruby182.msi をダウンロードする。

  2. ダウンロードした ap-840-bde60-ruby182.msi をダブルクリックしてインストールする。

  3. RubyForge から sqlite-ruby-2.2.3-msvcrt-bin.zip をダウンロードする。

  4. ダウンロードした sqlite-ruby-2.2.3-msvcrt-bin.zip を展開して、install.rb をダブルクリックし、インストールする。

  5. SQLite Download Page から sqlitedll-2816.zip をダウンロードして展開する。

  6. test.rb を作成する。

    #!ruby -Ks
    require 'sqlite'
    begin
      db = SQLite::Database.new("data.db")
      sql = <<SQL
    create table 社員 (
      名前 varchar(10),
      年齢 integer,
      部署 varchar(200)
    );
    SQL
      db.execute( sql )
      sql = "insert into 社員 values ('橋本', 26, '広報部')"
      db.execute(sql)
      sql = "insert into 社員 values (?, ?, ?)"
      db.execute(sql, '小泉', 35, '営業部')
      db.execute(sql, '亀井', 40, '営業部')
      db.execute2('select * from 社員') do |row| 
        puts row.join("\t")
      end
    ensure
      db.close
    end
    
  7. test.rb と同じフォルダに sqlite.dll をコピーする。

  8. 「スタート」→「プログラム」→「Apollo」→「Apollo」から、Apolloを起動する。

  9. 「開く」ボタンから、test.rb を開く。

  10. 「実行」ボタンを押すと、操作卓に出力される。

    名前  年齢  部署
    橋本  26  広報部
    小泉  35  営業部
    亀井  40  営業部
    

最後に

sqlite.dll が見つからないとエラーになりますが、c:/program files/apollo/bin/ に sqlite.dll をおいてもエラーになりました。

スクリプトファイルと同じフォルダに sqlite.dll をおくと、うまく動作しました。

2006年02月13日

rubyscript2exe

どうしても安定しない Excel Book Search

ためしに rubyscript2exe をつかってみたら大成功。

落ちなくなりました。

実行速度やファイルサイズでは、exerb の方が優れてますが、安定度重視ということで。

exerb は、win32ole とは相性が悪いのかな?

2006年02月26日

Strutsの問題点

Strutsの問題点と改善案

  • コマンドパターンを採用したからアクションごとにクラスを作らなきゃいけなくて、無駄にクラス数が増えるし、重複するコードも増える。

この問題は、Strutsの開発者も認識していると思います。

Struts1.2で導入されたMappingDispatchActionは、「クラス数が増えすぎる」「重複したコードが多くなる」問題に対処するための機能です。

  • ページ遷移をXMLファイルに書かなければいけないから、手間がかかる。

同じくStruts1.2で導入された「Wildcard Mappings」は、「設定用のXMLファイルが複雑になる」問題の対処だと思います。

しかし、残念なことに後方互換性のために根本的な変更ができなかった。 だから今でも使いにくいまま。

Strutsの問題点

たかがデータ登録の画面を作るだけなのに、クラスを何個も作ってXMLファイルをかきかきして、それで動けばいいんだけど動かなくて、クラスやファイルが分散しているからどれが原因か突き止められなくて途方に暮れる。

だれもが一度は通る道でしょうか。(涙)

みんなが使っているのだから、Strutsを使えないのは自分の力不足のせいだと思ってしまうのだろうね。

Strutsなんてないほうがよかった。Strutsのせいで2,3年が失われてしまった。

Strutsのおかげで、Javaに見切りをつけ、Rubyを使うようになりました。感謝(笑)

でも、盲目的にJavaを指定するクライアントも少なくないんでしょうね。 ご愁傷様です。

2006年07月16日

BDS2006(C++Builder)からRubyを使う

Borland Developer Studio 2006(C++Builder)からRubyを使ってみます。

箏音の日記 - C++とRubyC#からRubyのコードを実行するテスト を参考にしました。

Rubyをコンパイルする手間を省くために、Ruby-mswin32 から 最新リリース版である ruby-1.8.4-i386-mswin32.zip をダウンロードします。

ダウンロードした ruby-1.8.4-i386-mswin32.zip を展開し、bin\msvcrt-ruby18.dll をC++Builderのプロジェクトを作成するディレクトリにコピーします。

C++Builderを起動し、VCLフォームアプリケーションを作成します。

まず、起動時にDLLを読み込み、終了時に解放します。

Unit1.hに次のコードを追加。

private:
  HINSTANCE hDll;

Unit1.cppに次のコードを追加。

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
  hDll = LoadLibrary("msvcrt-ruby18.dll");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
  if (hDll) FreeLibrary(hDll);
}

DLLが正しく読み込まれていることを確認します。

次は、Rubyプログラムを実行してみます。

Unit1.hに次のコードを追加。

private:
  __declspec(dllexport) void (*ruby_init)(void);
  __declspec(dllexport) unsigned long (*rb_eval_string)(const char*);
  __declspec(dllexport) int (*ruby_cleanup)(int);

Unit1.cppに次のコードを追加。

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
  hDll = LoadLibrary("msvcrt-ruby18.dll");
  if (hDll)
  {
    ruby_init = (void (*)(void))GetProcAddress(hDll,"ruby_init");
    rb_eval_string = (unsigned long (*)(const char*))GetProcAddress(hDll,"rb_eval_string");
    ruby_cleanup = (int (*)(int))GetProcAddress(hDll,"ruby_cleanup");
  }
}

フォームにボタンを追加し、ボタンを押したときのイベントを記述します。

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  if (!hDll)
  {
    Application->MessageBox("msvcrt-ruby18.dll が見つかりませんでした。",
                            "DLL エラー",
                            MB_ICONSTOP | MB_OK);
    return;
  }
  //Rubyインタプリタの初期化
  ruby_init();
  //スクリプトの実行
  rb_eval_string("File.open('a.txt', 'w' ) { |f| f.puts 'success' }");
  //Rubyインタプリタのクリーンアップ
  ruby_cleanup(0);
}

実行してみましょう。 ボタンを押すとsuccessと記述されたa.txtが作成されると成功。

次に、スクリプトをファイルから読み込んで実行します。

Unit1.hに次のコードを追加。

private:
  __declspec(dllexport) void (*ruby_init_loadpath)(void);
  __declspec(dllexport) void (*rb_load)(unsigned long, int);
  __declspec(dllexport) unsigned long (*rb_str_new2)(const char*);

Unit1.cppに次のコードを追加。

__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
  hDll = LoadLibrary("msvcrt-ruby18.dll");
  (中略)
  ruby_init_loadpath = (void (*)(void))GetProcAddress(hDll,"ruby_init_loadpath");
  rb_load = (void (*)(unsigned long, int))GetProcAddress(hDll,"rb_load");
  rb_str_new2 = (unsigned long (*)(const char*))GetProcAddress(hDll,"rb_str_new2");

フォームにもう一つボタンを作成し、ボタンを押したときのイベントを記述します。

//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
  if (!hDll)
  {
    Application->MessageBox("msvcrt-ruby18.dll が見つかりませんでした。",
                            "DLL エラー",
                            MB_ICONSTOP | MB_OK);
    return;
  }

  //Rubyインタプリタの初期化
  ruby_init();
  ruby_init_loadpath();
  // スクリプトをファイルから読み込んで実行
  rb_load(rb_str_new2("test.rb"), 0);
  //Rubyインタプリタのクリーンアップ
  ruby_cleanup(0);
}

test.rbファイルはこう書いてみました。

File.open('b.txt', 'w' ) { |f|
  f.puts 'success'
}

実行してみましょう。 ボタンを押すとsuccessと記述されたb.txtが作成されると成功。

続く

2006年08月09日

松江で勉強会

松江で勉強会があったら、是非参加したいです。

たとえば、東京や関西でやっているような勉強会(Rubyに限らず)を開催したらどのくらい集まるのかなあ。

「Rubyのメッカに」と松江市長,研究・交流拠点「オープンソースラボ」開設

松江でソフトウェア開発やってます。
なので、松江で勉強会があったら、是非参加したいです。

初心者向けのRuby講習会にも行きましたが、 やっぱりもっとレベルの高い話を聞きたい。

期待しています。

追記: トラックバックの送信が失敗する。後で調べる。

2006年08月16日

BDS2006(C++Builder)からRubyを使う(2)

前回の続き

メソッドの実行結果を受け取り、結果を表示します。

フォームにメモとボタンをを貼り付けます。

Unit1.h の private に次のメソッドを追加します。

__declspec(dllexport) char* (*rb_string_value_cstr)(volatile unsigned long*);

Unit1.cpp の TForm1::TForm1(TComponent* Owner) に次の行を追加します。

rb_string_value_cstr = (char* (*)(volatile unsigned long*))GetProcAddress(hDll,"rb_string_value_cstr");

ボタンのイベントを追加します。

void __fastcall TForm1::Button3Click(TObject *Sender)
{
  if (!hDll)
  {
    Application->MessageBox("msvcrt-ruby18.dll が見つかりませんでした。",
                            "DLL エラー",
                            MB_ICONSTOP | MB_OK);
    return;
  }
  //Rubyインタプリタの初期化
  ruby_init();

  //スクリプトの実行
  unsigned long value = rb_eval_string("'実行結果'");
  //実行結果の文字列を取得する
  AnsiString result = rb_string_value_cstr(&value);
  Memo1->Lines->Add(result);

  //スクリプトの実行
  value = rb_eval_string("File.open('test.rb', 'r' ).read");

  //実行結果の文字列を取得する
  TStringList* list = new TStringList();
  list->Text = rb_string_value_cstr(&value);
  Memo1->Lines->Add(list->Text);
  delete list;

  //Rubyインタプリタのクリーンアップ
  ruby_cleanup(0);

}

ボタンをクリックして、メモに「実行結果」と「test.rb」の内容が表示されたら成功です。

2006年08月17日

Windows用Rubyいろいろ

WindowsにRubyをインストールする場合、いろいろあって迷ってしまう。

2006年09月30日

BOOKLOGの本棚にある本のISBNを取得するRubyスクリプト

BOOKLOGの本棚にある本のISBNを取得するRubyスクリプト。

#!ruby -Ke
require 'open-uri'

#アカウント(本棚のURLが「http://booklog.jp/users/sample」なら「sample」)
ACCOUNT = 'sample'
#ページ数
PAGE = 2
#出力ファイル名
FILENAME = 'isbn.txt'

result = [] #重複を避けるための履歴
open(FILENAME, 'w') do |file|
  PAGE.times {|page|
    url = "http://booklog.jp/users/#{ACCOUNT}/spine/dm=&jm=&cate=&page=#{page}"
    open(url) {|f|
      html = f.read
      while token = html.slice(/\/asin\/(\w+)/, 1)
        if not result.include?(token)
          result << token
          file.puts token 
        end
        html = $'
      end
    }
  }
end

2006年11月10日

Ruby on Railsの統合開発環境

Ruby on Railsの統合開発環境,まつもと氏が在籍するNaClとOSJが発売
Ruby on Railsを業務システムに - NaClら日本語パッケージやサポート提供

RadRails を日本語化したもの、かな。

Ruby on Rails 開発環境"RadRails"

2007年01月06日

Ruby用画像編集ライブラリRMagickをVineLinux4.0にインストールする

Ruby用画像編集ライブラリRMagickをVineLinux4.0にインストールする方法。

あらかじめRubyGemsをインストールする

tar xzvf rubygems-0.9.0.tgz
cd rubygems-0.9.0
ruby setup.rb

次にRMagickをインストールするために必要以下のライブラリを apt-get でインストールする。(他に必要なものがあるかも)

ImageMagick
ImageMagick-devel
gcc
bzip2-devel
zlib-devel
libtiff-devel
libjpeg-devel
libwmf-devel
XOrg-devel
ruby-devel

RubyGemsでRMagickをインストールする。

gem install RMagick

使うときは、require 'rubygems' を忘れずに。

#!/usr/bin/ruby
require 'rubygems'
require 'RMagick'

RMagickで画像のサイズ(幅と高さ)を取得する

RMagickで画像のサイズ(幅と高さ)を取得する。

#!/usr/local/bin/ruby -Ke
require 'rubygems'
require 'RMagick'

filename = 'cat.jpg'
img = Magick::ImageList.new(filename)
p img.columns #幅
p img.rows #高さ

RMagickで画像のサイズを変更する

RMagickで画像のサイズを変更する。

#!/usr/local/bin/ruby -Ke
require 'rubygems'
require 'RMagick'

# RMagickで画像のサイズを変更する
filename = 'cat.jpg'
img = Magick::ImageList.new(filename)

width = 480
height = 320
img.resize(width, height)

2007年01月24日

自分でコンパイルしたRubyにはzlibがインストールされていない

自分でコンパイルしたRubyには、zlibがインストールされていない。
zlibは後からインストールする必要がある。

知らなかった。結構はまった。

DebianにRailsを入れる」のおかげで、何とかなりそう。
助かりました。

追記
リンク先が無くなっているのでメモ。

cd ext/zlib
ruby extconf.rb
make
make install

2007年01月25日

RMagickの使い方のメモ

RMagickの使い方のメモ

ライブラリのロード

require 'RMagick'

ファイルの読み込み

img = Magick::ImageList.new(path)

画像のサイズ

img.columns #=> 横幅
img.rows #=> 高さ

画像のサイズを変更する

width = 100 #横幅
height = 80 #高さ
image.resize(width, height)

縦横比を固定して画像のサイズを変更する

width = 100
height = (img.rows.to_f * width.to_f / img.columns.to_f).to_i
img.resize(width, height)

画像を保存する

img.write('/path/to/file')

画像を回転する

amount = 90 #角度(時計回り)
new_img = img.rotate(amount)

CentOS 4.4にRMagick をインストールする

CentOS 4.4にRMagick をインストールする。

最初に必要なファイルをインストールしておく。

yum install ImageMagick-devel

その後、普通にRubyGemsで普通にインストールしようと、

gem install RMagick

としたが、何故かエラーに。
エラーメッセージをよく見ると、

rmmain.c: In function `Magick_colors':
rmmain.c:90: error: too many arguments to function `GetColorInfoList'

関数の引数の数があっていないのがエラーの原因のようだ。
ImageMagickのバージョンが古いのか?
確かに古いようだ。バージョンは 6.0.7。
Vine Linux 4.0のImageMagickは6.3.0だから、かなりの開きがある。

ImageMagickの新しいバージョンをインストールする方法もあるが、面倒なので古いバージョンのRMagickをインストールしてみる。

gem install RMagick -v 1.13.0

すんなりとインストール成功。
ためしに動かしてみる。

#!/usr/bin/env ruby
require 'rubygems'
require 'RMagick'
img = Magick::ImageList.new('old.jpg')
img.resize(100, 100).write('new.jpg')

ちゃんと動いた。

2007年01月27日

ruby-fcgiをインストールする

ruby-fcgiをインストールするために、

gem install ruby-fcgi

をするが、撃退される。

ダウンロードサイトにある、ruby-fcgi-0.8.7.tar.gz をダウンロードして展開すると、lib/fcgi.rbというファイルを発見。

このfcgi.rbをCGIプログラムと同じフォルダにおいたら動いた。

C版よりも遅いかもしれないけど、簡単にインストールできるのはありがたい。

2007年02月07日

Rails用携帯電話ライブラリ mobile_view.rb を改良する

技術コラム : Rails で携帯用にビューを切り替えで公開されているmobile_view.rbを2箇所修正しました。

class ::ActionController::Base#is_mobile? で携帯電話からのアクセスか判定していますが、対象を増やしました。この処理はHikiを参考にしています。

# モバイルかどうかを判定
def is_mobile?
    ua = request.user_agent
    #ua =~ %r{^(DoCoMo|J-PHONE|Vodafone)/} || ua =~ %r{UP\.Browser/}
    %r[(DoCoMo|J-PHONE|Vodafone|WILLCOM|MOT-|UP\.Browser|DDIPOCKET|ASTEL|PDXGW|Palmscape|Xiino|sharp pda browser|Windows CE|L-mode)]i =~ ua
  rescue NoMethodError
    # テストのときは user_agent メソッドが存在しないので false を返しておく
    false
  end
end

次に、文字コードを変換するときに半角カタカナを全角カタカナに変換してしました。nkfは半角カタカナを自動的に全角カタカナに変換します。-x を指定して変換しないようにしました。

# レスポンスを Shift_JIS に変換
def sjis_response
  if is_mobile? && (!headers["Content-Type"] || headers["Content-Type"] =~ %r{^text/html})
    response.body = NKF.nkf('-Ws -m0 -x', response.body) if is_mobile?
    headers["Content-Type"] = "text/html; charset=Shift_JIS" if is_mobile?
  end
end

2007年03月09日

Delphi for PHP、Delphi for Ruby雑感

「Delphiライク」に、コンポーネントをマウスでペタペタとフォームに貼り付け、プロパティとイベントを設定してプログラムを作成します。

PHPでビジュアル開発 - つまりはマウス操作でWebアプリを開発

ASP.NETに近いイメージかな。ASP.NETは開発効率が低かったし、デザイナーとの共同作業が難しかった。
Webアプリケーションとは相性が悪い開発方式という印象があるけど、 Delphi for PHPはどこまでがんばれるかに期待したい。
自分ではPHPは使わないけど。

さらにDelphi for Rubyの話題も。

今のところ、Rubyプログラマーの開発環境はEmacsかViが主流だから、初心者は手が出しにくかった(と思う)。
統合開発環境がその敷居を下げてくれそうだ。

RubyからVCLライブラリを使うことができるApolloというRubyライブラリがあります。
Delphi for RubyがこのApolloを使ってGUIアプリケーションを作れるなら、おもしろくなりそう。
ただしDelphi for RubyはWebアプリケーションを想定しているようなので、Ruby on Railsが本命か。

2007年04月29日

Ruby on Railsで一行掲示板を作成する(Ruby on Rails 1.2対応版)

以前に書いた Ruby on Railsで一行掲示板を作成する をRuby on Rails 1.2用に書き直しました。

データベースの作成

MySQLを使用して、データベースとテーブルを作成する。

データベースを作成。

mysqladmin -uroot create bbs_development
mysqladmin -uroot create bbs_test
mysqladmin -uroot create bbs_production

ここでテーブルは作成しない。

プロジェクトの作成

C:\railsディレクトリを作成する。
C:\railsディレクトリに移動して、次のコマンドを実行する。

rails bbs
cd bbs

C:\rails\bbs\config\database.ymlのusernameやpasswordを編集する。
MySQLのバージョンが4.0以前の場合は「encoding: utf8」を削除する。

development:
  adapter: mysql
  database: bbs_development
  username: root
  password:
  host: localhost
  encoding: utf8

test:
  adapter: mysql
  database: bbs_test
  username: root
  password:
  host: localhost
  encoding: utf8

production:
  adapter: mysql
  database: bbs_production
  username: root
  password:
  host: localhost
  encoding: utf8

モデルとコントローラを作成する。

ruby script/generate model item  
ruby script/generate controller item

テーブルの設計

C:\rails\bbs\db\migrate\001_create_items.rb を編集する

class CreateItems < ActiveRecord::Migration
  def self.up
    create_table :items do |t|
      t.column :name, :text #投稿者名
      t.column :body, :text #本文
    end
  end

  def self.down
    drop_table :items
  end
end

マイグレーションの実行

rake db:migrate

itemsテーブルが作成される。

エラーが発生した場合は、

  • db/migrate/001_create_items.rbを確認する。
  • config/database.ymlを確認する。

コーディング

C:\rails\bbs\app\views\item\index.rhtml ファイルを作成する。
ファイルの文字コードはUTF-8。

index.rhtmlでは、投稿された内容が配列で @items に入り、 @items.each do ~ end で、投稿内容を出力する。 フォームで入力された内容は、コントローラの add_item メソッドで処理をする。

<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja-JP" lang="ja-JP">
<head>
  <title>一行掲示板</title>
</head>
<body>
  <h1>一行掲示板</h1>
  <dl>
<% @items.each do |@item| %>
    <dt><%=h @item.name %></dt>
    <dd><%=h @item.body %></dd>
<% end %>
  </dl>
  <hr />
  <%= form_tag :action=>"add_item" %>
    名前:<%= text_field("new_item", "name", "size"=>10) %><br />
    本文:<%= text_field("new_item", "body", "size"=>60) %><br />
    <input type="submit" value="Add" />
  </form>
</body>
</html>

C:\rails\bbs\app\controllers\item_controller.rb を開く。

class ItemController < ApplicationController

  def index
    @items = Item.find(:all)
  end

  def add_item
    item = Item.new
    item.attributes = @params["new_item"]
    if item.save
      redirect_to(:action => "index")
    else
      render_text "Couldn't add new item"
    end
  end
end

Webサーバーを起動する。

ruby script/server

http://localhost:3000/item/にアクセスする。
掲示板が表示されたら、成功。

2007年06月14日

Rubyのprotectedは、JavaやC++とは違う

RubyのprotectedはJavaやC++とは違うことは、意外と知られていないようです。

Ruby の protected は Java, C++ とは意味が違います。 Ruby の protected を使うと、サブクラスのインスタンスが「オブジェクトの外から」メソッドを呼べるようになります。

Rubyist Magazine - あなたの Ruby コードを添削します 【第 3 回】 dbf.rb

ということで、例として次のコードが示されています。

class A
  def m
    puts "OK"
  end
  protected :m

  def call_m(a)
    a.m
  end
end

A.new.call_m(A.new)   # これは大丈夫
A.new.m               # これはダメ

このコードを見ると一目瞭然ですね。

参考

追記

ちなみに、privateもJavaやC++とは意味が違います。

Rubyでは関数形式(レシーバを省略した形)でしか呼び出すことのできないメソッドのことをprivateなメソッドと呼んでいます。C++やJavaのprivateとは意味が違うので注意してください。

Rubyリファレンスマニュアル - FAQ::メソッド

ということで、Rubyではprivateに指定したメソッドをサブクラスから呼び出すことができます。

フレームワークを使っていると、知らないうちに基底クラスのメソッドをオーバーライドしてしまい、予期せぬバグに悩まされたりします。

2007年06月19日

Ruby on Rails 1.2対応作業

Ruby on Rails 1.1.6で作成したアプリケーションをRuby on Rails 1.2.3で動かすと、いくつかの警告が出たので修正作業を行った。

変更箇所は、

<%= start_form_tag %>
<%= end_form_tag %>

<% form_tag do %>
<% end %>

に書き直す。

Ruby on Railsの開発チームがこの変更を行った気持ちもわかる。
何といっても「end_form_tag」が気持ち悪い。

「end_form_tag」は</form>でも良く、</form>の方がタイプ数が少ない。
しかし、だからといって、

<%= start_form_tag %>
</form>

も受け入れがたい。

しかし、新しい「form_tag do ~ end」も良いかといわれると微妙なところ。
「do ~ end」の距離が離れると、対応がわかりにくくなる。
「start_form_tag ~ end_form_tag」の方が対応がわかりやすかった。

もう一つの変更箇所は、

Model.count(["priority=?", 1])

Model.count(:conditions => ["priority=?", 1])

に変更。
タイプ数が増えているけど、見やすさを重視したのかな。

2007年07月23日

影舞でSecurityError

インストールが簡単なバグトラッキングシステム「影舞」を、Ruby1.8.5の環境にインストールしたら、

Following errors occurred. Please contact administrator.
Insecure: can't intern tainted string (SecurityError)

というエラーメッセージが表示された。

影舞のホームページを見ると、動作環境がRuby 1.8.2となっている。
Rubyはバージョン間の互換性が低いから、Ruby1.8.5には対応いていないのかも。

とりあえず、 /kagemai/lib/kagemai/message_bundle.rb の69行目を、

@messages[key.intern] = message

から、

@messages[key.untaint.intern] = message

に変更して、動作することを確認した。

しかし、Rubyの後方互換性の低さは何とかならないものか。

追記
ほかにも修正する必要がありそう。
個人で使うのなら、セーフモードを変更した方が早いかも。
/kagemai/html/guest.cgi の29行目。

$SAFE = 1

この行をコメントアウトする。

#$SAFE = 1

影舞のデータを別サーバーに移行する

影舞のデータを別サーバーに移行するには、

  • データベースのデータ
  • /kagemai/project/ のデータ

を移行すれば良いようです。

追記
データの保存形式を「Kagemai::XMLFileStore」にしておくと、/kagemai/project/ のデータを移行するだけですむので簡単です。

2007年08月01日

ActiveRecordのbefore_saveメソッド

ActiveRecordのbefore_saveメソッドが真を返さない場合、 ActiveRecord::RecordNotSaveの例外を投げるようだ。

def before_save
  if ...
  end
  true #真を返すこと
end

2007年08月13日

汎用的なフォームタグ生成メソッド

Ruby on Railsの話。

フォームタグ生成メソッドに"_tag"をつけると、ActiveRecordインスタンスに依存しないinputタグを生成するメソッドになる。

<%= text_field_tag(:email, 'メールアドレスを入力してください。', :size => 10, :maxlength => 256) %>
<input id="email" maxlength="256" name="email" size="10" type="text" value="メールアドレスを入力してください。" />

2007年08月23日

カレントディレクトリとそのサブディレクトリから特定の文字を含むテキストファイルを検索するRubyスクリプト

Windowsのファイル検索は、文字コートがシフトJISに限定のようです。

シフトJIS以外の文字コード(euc-jpやutf-8)のファイルも検索したかったので、作成してみました。

カレントディレクトリとそのサブディレクトリから特定の文字を含むテキストファイルを検索します。

サンプルでは、ファイルの拡張子を「.php」に限定しています。
引数の文字列で検索します。

#!/usr/bin/ruby -Ks
require 'kconv'

keyword = ARGV.shift
Dir.glob("**/*.php") {|f|
  file = File.read(f).tosjis
  puts f if file.include?(keyword)
}

もっと早い方法がありますか?

2007年10月13日

Rubyist Magazine 出張版 正しいRubyコードの書き方講座

Rubyist Magazine 出張版 正しいRubyコードの書き方講座―RubyistのRubyistによる、Rubyistとそうでない人のための

本書の対象は、Rubyを始めたけれどRubyらしいコードが書けないRuby初級者。
Rubyで実際にコードを書いていない入門者には難しい。
中級者には、自分のスタイルの確認になるかもしれません。

他の言語、たとえばJavaから移行してきた人は、RubyでもJavaっぽくコードを書いてしまいがち。
でも、そうするとRubyの良さが生きてきません。
言語の思想・特徴を理解することで、効率よくコードが書けるようになります。

2008年02月04日

VineLinux4.2にRubyGemsのパッケージが用意されてた

VineLinux4.2にRubyGemsのパッケージが用意されてた。
いつのまに。

ということで、

apt-get install rubygems

で、RubyGemsを必要なパッケージもまとめてインストールできました。

$ gem -v
0.9.2

ちょっと古いですね。

Ruby on Rails 2.0にバージョンアップする手順

Ruby on Rails 2.0にバージョンアップする手順。

railsをインストールします。

gem install rails

config/environment.rbを開きます。

RAILS_GEM_VERSIONに新しいRailsのバージョンを指定します。

RAILS_GEM_VERSION = '2.0.2'

設定ファイルを更新してみます。

$ rake rails:update:configs
...
Rails requires RubyGems >= 0.9.4 (you have 0.9.2). Please `gem update --system` and try again.

RubyGemsのバージョンが古いようなので、更新します。

gem update --system

再び挑戦。設定ファイルを更新します。

$ rake rails:update:configs

問題ないようなので、まとめて更新します。

$ rake rails:update

config/environment.rb の config.action_controller.session に設定を行います。

手間を省くため、

rails test

で新しいプロジェクトを作成します。

作成したプロジェクトのconfig/environment.rb から config.action_controller.session の設定をコピーします。

config.action_controller.session = {
  (省略)
}

以上で、一応、動作するはず。

2008年02月07日

RailsによるアジャイルWebアプリケーション開発 第2版

同書第1版からのマイナーバージョンアップかと思ったら大間違い。
第1版以降のノウハウを詰め込んだメジャーバージョンアップでした。

まず、大きく異なっていたのは、本番サーバーでの稼働方法。
第1版で推奨されていたFastCGIは、第2版では推奨されなくなりました。

RailsによってFastCGIに世間の目が集まるようになったあとも、本番レベルの品質の高いFastCGI環境はほとんど現れませんでした。

第2版では、FastCGIにかわる新しい方法を説明しています。

# 苦労してFastCGI環境で稼働させたのに…

第1版はRails 0.13をベースに書かれていました。
第2版では、Rails 1.2をベースに書かれています。
さらにRails 2.0についての記述もあります。

ActiveSupportの章が新しく追加されていますし、RESTについても多くのページが割かれています。
Capistranoの説明もあります。

2006年の後半にRailsに関する本がたくさん出版されました。
その時期の本はRails 1.1をベースに書かれています。
Rails 1.2以前の知識でストップしている人は、第2版からたくさんのことを学ぶことができると思います。

Ruby on Railsを使うなら、ぜひとも読んでおきたい本です。

2008年02月14日

has_and_belongs_to_manyでDuplicate entryエラー

has_and_belongs_to_manyを使うときは、結合テーブルにidをつけません。

idフィールドがあると、Duplicate entryというエラーになります。

idをつけないようにするには、Migrationでcreate_tableに id => false をつけます。

class CreateClubsStudents < ActiveRecord::Migration
  def self.up
    create_table(:clubs_students, :id => false)do |t|

    end
  end

2008年03月05日

Ruby on Railsのacts_as_listプラグインを使用するときはposition列を :null => false にしてはならない

Ruby on Railsのacts_as_listプラグインを使用するときはposition列を :null => false にしてはならない。

acts_as_list: Don't use ":null => false" for the position column

migrationで、position列にnot null制約を付与すると、

t.integer :position, :null => false

レコードを削除したときに

SQLite3::SQLException: SQL logic error or missing database:

というエラーになる。

Ruby on Railsの選択リスト(プルダウンメニュー)の選択要素の書き方

Ruby on Railsの選択リスト(プルダウンメニュー)の選択要素の書き方。

基本は、selectヘルパーメソッドを使います。
2番目の引数が選択要素の配列になります。

<%= f.select(:user_id, User.find(:all).map{|u| [u.name, u.id]}) %>

collection_selectヘルパーメソッドは、モデルの配列から選択要素を設定できます。
2番目の引数にモデルの配列、3番目と4番目の引数はid値とname値の属性を指定します。

<%= f.collection_select(:user_id, User.find(:all), :id, :name) %>

でも、コードの長さはほとんど変わらないようです。

Ruby on Railsのルーティングを確認する方法。

Ruby on Railsのルーティングを確認する方法。(Rails 2.0からの機能?)

rake routes

でルーティングの情報が表示されます。

困ったときに便利。

2008年03月06日

Ruby on Rails 2.0でRuby-GetText-Packageを使う方法の簡単なまとめ

以前に日本語化に使用していたActiveHeartは、現在では推奨されないようです。
Ruby-GetText-Packageを使いましょう。という方向みたい。

Ruby-GetText-Packageの簡単な使い方。

まずは、インストール

gem install gettext

[Ruby on Rails]2.0でRuby-GetTextを使うを参考に、
config/environment.rb
app/controllers/application_controller.rb
lib/tasks/gettext.rake
を編集します。
これだけで、validationのエラーメッセージが日本語になります。

poファイルを作成します。

rake updatepo

poディレクトリにpotファイルができるので、それをpo/ja/ディレクトリにコピーして、拡張子をpoに変更します。

rails_root
└po
  ├app.pot
  └ja/
    └app.po

poファイルを開いて編集します。
poファイルには、

msgid "クラス名|フィールド名"
msgstr ""

という記述があるので、対応するフィールド名の日本語をmsgstrに設定します。

設定ができれば、poファイルを作成します。

rake makemo

rails_root/locale/ディレクトリにファイルが作成されます。

後は、サーバーを再起動すると完了です。

2008年03月13日

NetBeans 6.1ベータ版を使ってみました。

NetBeans 6.1用の日本語 zip を公開」を参考にして、NetBeans 6.1ベータ版を日本語化して使ってみました。

NetBeans 6.1はまだベータ版ですが、今のところ問題なく使用できています。

むしろ、NetBeans 6.0.1よりも快適な気がします。

WindowsにおけるRuby on Railsの開発環境は、

です。
なかなか快適な開発環境が見つかりません。

LinuxならEmacs + rails.elで解決なのですが。

NetBeans 6.0.1でSubversionを使っていましたが、コミットできていないファイルがあったことに、今、気がつきました。
どうやら、コミットしていないファイルはNetBeans 6.0.1を介さなかったファイルのようです。

2008年03月14日

VineLinux4.2にRuby-GetText-Packageが用意されていた

VineLinux4.2にRuby-GetText-Packageが用意されていました。

apt-get install ruby-gettext-package

でインストールできます。 バージョンは0.8.0。ちょっと古いみたいです。

`gem_original_require': no such file to load -- ./boot

Ruby on Rails(2.0.2)でRuby-GetText-Package(1.10.0)を使い、「rake updatepo」をすると、

`gem_original_require': no such file to load -- ./boot

と表示されました。

試していくうちに、「config/environment.rb」に

#class ActiveRecord::Base

という行があると、このエラーメッセージが表示されることがわかりました。

コメントにしているので、プログラムの動作には関係ないのですが。

Ruby-GetText-Packageで The error occurred while evaluating nil.[]

Ruby-GetText-Packageを導入したら、

/!\ FAILSAFE /!\  Fri Mar 14 19:36:24 +0900 2008
  Status: 500 Internal Server Error
  You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
    /usr/lib/ruby/1.8/cgi.rb:1165:in `[]'

というエラーが出るようになりました。

gettextでinternal server error - cracchoの日記」経由で「Using ruby-gettext with Edge Rails」を見て、解決。

次のコードを「config/environment.rb」の最後にでも追加する。

class CGI
  module QueryExtension
    alias index_without_fix :[]
    def [] (key)
      return nil unless @params[key]
      index_without_fix(key)
    end
  end
end

原因は、Rubyのバグ。
Ruby 1.8.6では修正されているそうです。

2008年03月24日

Ruby on Railsで 553 sorry, that domain isn't in my list of allowed rcpthosts(#5.7.1) のエラーになるとき

Ruby on RailsのActionMailerを使ってメールを送信すると、

553 sorry, that domain isn't in my list of allowed rcpthosts(#5.7.1)

のエラーになってメールが送信できないときの対処法。

ActionMailerの設定は、標準ではSMTPを使用することになっています。

サーバー側の設定で、POP before SMTPなど認証が必要な設定になっている場合は、この認証にひっかかりメールが送信できません。

sendmailが使用できるなら、SMTPでなくsendmailを使用することで回避できます。

RAILS_ROOT/config/environment.rbを編集します。

ActionMailer::Base.delivery_method = :sendmail

2008年04月09日

WindowsにRMagickをインストールする

前準備として、RubyGemsのバージョンを更新しておく。

gem update --system

WindowsにRMagickをインストールする手順。

  1. RubyForge: RMagick: Project InfoからRMagick-1.15.12-ImageMagick-6.3.7-8-Q8.zipをダウンロードする。
  2. ダウンロードしたファイルを展開する。
  3. ImageMagick-6.3.7-8-Q8-windows-dll.exeを実行して、ImageMagickをインストールする。
  4. 「gem install rmagick-1.15.12-x86-mswin32.gem」を実行して、RMagickをインストールする。

今回、実行した環境

  • Ruby 1.8.6
  • RubyGems 1.1.0

2008年05月10日

Ruby on Rails用IDE NetBeans 6.1リリース

NetBeansのバージョン6.1がリリースされました。

リリースされたのは英語版ですが、日本語化zipファイルを入れることにより、日本語化できます。

現在リリースされているRuby on Rails用IDEの中で、最も使える製品だと思います。

Rubyコーディング規約

Rubyコーディング規約について調べてみました。

よくまとまっています。
これをベースに、プロジェクトごとに規約を作成すればよさそう。

if !xのような場合は、 unless xに置き換える。ただし、unlessの場合、 elseは使用しない。

これは、ルールが複雑でわかりにくそう。

Rubyの作者のまつもとゆきひろ氏のMatzにっきより。

青木氏が使っている Ruby のコーディングスタイル。

文字列リテラルついて、Rubyコーディング規約では基本的に「"..."」ですが、RubyCodingStyleでは「'...'」を薦めています。
私個人は、RubyCodingStyleと同じで、「'...'」を使っています。

returnは、Rubyコーディング規約の「メソッドの値を返す場合は、必ずreturnを使用する。また、returnの括弧は省略する。」が、わかりやすいと思います。
RubyCodingStyleでは、returnは「全部省略する」とあります。 これだと返値が期待されているのか(functionなのか、procedureなのか)がわかりにくいのではないでしょうか。

2008年05月11日

Rubyコーディング規約は、実際に使われていないのかもしれない

これから始まるプロジェクトのために、コーディング規約を調べています。

少しだけPrefShimaneCMSを見てみたのですが、Rubyコーディング規約は適用されていませんでした。

たとえば、Rubyコーディング規約によるとクラスメソッドは、

クラスメソッドの定義にはselfを使用する。

とありますが、PrefShimaneCMSでは、

def Advertisement.pref_advertisement
def Advertisement.corp_advertisement
def Advertisement.pref_published_advertisement

と、selfではなく、クラス名を使用して定義されています。

メソッド呼び出しの時の括弧は、Rubyコーディング規約では、

メソッド呼び出しの引数リストには括弧を付ける。ただし、引数がない場合は、括弧を省略する。

とありますが、PrefShimaneCMSでは、括弧があったりなかったりと、全然統一されていません。

コーディング規約というは、実際には使われていないのかもしれません。

コーディング規約ではありませんが、ソースを見ていて気になったこと。

PrefShimaneCMSでは、ActiveHeartを使っているようです。
ActiveHeartは、現在では推奨されていません。
Ruby-GetText-Packageに移行すべきでしょう。

Modelがフィールドにアクセスする時、read_attribute/write_attributeを使わずに、「self.フィールド名」でアクセスしていました。
「self.フィールド名」でアクセスするとmethod_missingが呼び出されるため、効率が悪いように思えます。
method_missingでメソッドが定義されるので、呼び出されることになるのは、一度だけですが。

機種依存文字を見つけるlib/filter.rbや、Chasenを使ってルビを振るlib/ruby_adder.rbは使えるかも。

2008年05月17日

Ruby on Rails用 統合開発環境(IDE) 比較レポート

CodeZineのRuby on Rails用 統合開発環境(IDE) 比較レポートの内容が変です。

NetBeansの短所として、次のように書かれています。

JavaのIDEとしてデファクトスタンダードであるEclipseではないため、見た目、操作感が違い戸惑う可能性がある

Ruby on Rails用の統合開発環境のレポートです。
Javaの統合開発環境のレポートではありません。
Javaの統合開発環境のデファクトスタンダードは、評価に全く関係ありません。
とんでもない言いがかりです。

ちなみに、もう一つの短所として、

IDE起動後の索引作成に時間がかかる

と、あります。
確かに、IDE起動後に索引を作成します。
プロジェクトのサイズによっては、時間もかかるのかもしれません。
ですが、索引作成中でも、操作はできるのです。
IDEが固まるとか、CPUを100%使い切ってしまうとか、そんなことはありません。
何を問題視しているのか、わかりません。

ついでに、機能の比較も、どれだけ意味があるのでしょうか。
機能が多いほど優れた統合開発環境である、とは言えません。
自分の使う機能がしっかりと実装されているのが、優れた統合開発環境です。
使わない機能がたくさんあっても、価値はありません。むしろ、使いにくいだけです。
機能数の比較に、何の意味があるのでしょうか。

それから、Emacsの評価は疑問を感じます。
rails-modeを使っていないのでしょうか。
rails-modeなら「action→view」「view→action」などはできたはずです。

2008年05月18日

Ruby on RailsにRuby-GetTextを入れると、`gem_original_require': no such file to load -- gettext/rails (MissingSourceFile)になった

Ruby on RailsにRuby-GetTextを入れると、エラー

`gem_original_require': no such file to load -- gettext/rails (MissingSourceFile)

になった。

Ruby-GetTextはRubyGemsのgemコマンドでインストールした。
インストールされていることに間違いはない。

いろいろと調べた結果、結局、environment.rbで環境変数「GEM_HOME」を設定したら、動くようになった。

ENV['GEM_HOME'] ||=  '…'

Ruby on RailsとRuby-GetText-Packageを使った日本語メールの送信

Ruby on RailsとRuby-GetText-Packageを使った日本語メールの送信について。

Ruby-GetText-Packageは、ブラウザからのアクセスでコントローラ経由を経由してメールを送信するときは、ちゃんとiso-2022-jpに変換してメールを送ってくれます。

ですが、モデルから直接メールを送信するときは、変換してくれません。

モデルに直接アクセスした場合は、コントローラ内に記述したinit_gettextが呼ばれないため、変換されないのだと思います。

そこで、メールを送信するメソッド内に次のコードを追加することで、直接モデルからメールを送信したときにも、変換されるようになりました。

  require 'gettext/rails'
  ActionController::Base.init_gettext "…"
  GetText.locale = "ja"

2008年05月21日

ISBN10とISBN13を相互に変換するRubyプログラム

ISBN10とISBN13を相互に変換するRubyプログラム

# ISBN10をISBN13に変換する
def isbn10_to_13(isbn10)
  isbn13 = "978#{isbn10[0..8]}"
  #チェックデジット計算用
  check_digit = 0
  isbn13.split(//).each_with_index do |chr, idx|
    #Integer#even?はActiveSupportによる拡張
    check_digit += chr.to_i * (idx.even? ? 1 : 3)
  end
  #総和を10で割ったものを10から引き、10の場合は0にする
  check_digit = (10 - (check_digit % 10)) % 10
  return "#{isbn13}#{check_digit}"
end

# ISBN13をISBN10に変換する
def isbn13_to_10(isbn13)
  isbn10 = isbn13[3..11]
  check_digit = 0
  isbn10.split(//).each_with_index do |chr, idx|
    check_digit += chr.to_i * (10 - idx)
  end
  check_digit = 11 - (check_digit % 11)
  #計算結果が 10 になった場合、10 の代わりに X(アルファベットの大文字)を用いる。
  #また、11 になった場合は、0 となる。
  case check_digit
  when 10: check_digit = "X"
  when 11: check_digit = 0
  end
  return "#{isbn10}#{check_digit}"
end

2008年05月22日

Rubyらしいコードの書き方(入門編)

他人が書いたRubyのコードを見て、Rubyらしくないと思ったところがありました。
このように直すと、Rubyらしいコードになると思います。

String#+ よりも、 "#{String}"を使う。
obj.to_sしなくてもいいし。

String#+の例

year.to_s + "年" + month.to_s + "月" + day.to_s + "日"

"#{String}"の例

"#{year}年#{month}月#{day}日"

sprintf(String, *args)は、String#%と同じ。

sprintf(String, *args)の例

sprintf("%d年%d月%d日", year, month, day)

String#%の例

"%d年%d月%d日" % [year, month, day]

obj = obj + 1 は、obj += 1 と同じ。

nilとの比較は obj == nil より obj.nil? を使う。入力数が短い。

変数が初期化されていない時に初期化する処理で、よくあるのが次の書き方。

if value == nil
  value = 1
end

これは簡潔に書ける。

value ||= 1

Ruby on RailsにあるActiveSuppoertで追加されるメソッド blank? は便利なので、知っておいた方がいい。

if value == nil or value.size == 0

というコードが、

if value.blank?

と書けます。

2008年06月03日

Ruby on Rails 2.1.0公開で、2.0系は切り捨てか

Ruby on Rails 2.1.0が公開されたようだ。
そうすると、Ruby on Rails 2.0.2は大量のバグを抱えたまま開発は終了になるのか。

2.0系が安定したら、1.2系から乗り換えようと考えていたチームも少なくないはず。

安定している1.2系はもう古いし、2.0系はバグが放置されたまま。
2.1系は安定するまで、まだ時間がかかるだろう。
どのバージョンを使うのか、難しい選択に迫られる。

2008年06月07日

Ruby on Railsのlink_toで、https://にリンクする。

Ruby on Railsのlink_toは、通常、コントローラとアクションを指定してリンクを作成します。

link_to('トップページ', :controller => 'top', 
                        :action => 'index')

明示的にhttps://などのプロトコルを指定する場合は、オプションに:protocolをつけます。
このとき、:only_pathの指定も忘れずに。

link_to('トップページ', :controller => 'top', 
                        :action => 'index',
                        :only_path => false,
                        :protocol => 'https://')

2008年06月10日

Ruby on RailsのGeneratorの作り方

Ruby on RailsのGeneratorの作り方。

script/generateでプラグインの雛形を作成する。

ruby script/generate plugin sample

Rails::Generator::NamedBaseを継承したSampleGeneratorクラスを作成する。

Rails::Generator::NamedBaseは、Rails::Generator::Baseを継承し、先頭の引数にクラス名を受け取る。

vendor/plugins/sample/generators/sample/sample_generator.rb

class SampleGenerator < Rails::Generator::NamedBase

次に、generatorに渡された引数を解釈する処理。

def initialize(runtime_args, runtime_options = {})
  super #ここでクラス名が解釈される
  #ここからは必要な引数やオプションを解釈する処理を自分で書く
end

Rails::Generator::NamedBase#initializeで引数が解釈され、 先頭の引数であるクラス名は、インスタンス変数に格納される。 2番目以降の引数は@argsに格納される。

引数であるクラス名が「HogeHoge::Foo::Bar」と「hoge_hoge/foo/bar」のときの、インスタンス変数の値。

  • @name 先頭の引数であるそのままのクラス名("HogeHoge::Foo::Bar","hoge_hoge/foo/bar")
  • @class_path Railsの命名規則に合わせたモジュール名の配列(["hoge_hoge", "foo"])
  • @file_path ファイルのパス("hoge_hoge/foo/bar")
  • @class_nesting モジュール名("HogeHoge::Foo")
  • @class_nesting_depth モジュール数(2)
  • @class_name_without_nesting 命名規則に従ったクラス名("Bar")
  • @singular_name 命名規則に従ったファイル名("bar")
  • @plural_name @singular_nameの複数形("bars")
  • @table_name モデルのテーブル名(hoge_hoge/foo_bars)
  • @class_name クラス名("HogeHoge::Foo::Bar")
  • @file_name @singular_nameの別名
  • @actions @argsの別名

manifest()メソッドに、やりたい処理を書く。

def manifest
  record do |m|
    #ここにgeneratorでやりたい処理を書く
  end
end

manifestの書き方は、「Generatorプラグインの作り方」の説明が詳しい。

2008年06月12日

Dir.glob()を使うときは、パスを絶対パスに変換してから

WindowsでDir.glob()を使うときは、パスを絶対パスに変換しよう。

Ruby on Railsを使っていて、あるコードで相対パスのままDir.glob(path)としているため、ファイルを見つけてもらえず、はまりました。

path= 'C:\test1\test1_1/../test1_2'

#これはファイルが見つからない
Dir.glob("#{path}/*")

#これはファイルが見つかる
Dir.glob("#{File.expand_path(path)}/*")

2008年06月13日

Ruby 1.8.7とRuby on Rails 2.0.2の組み合わせは、問題が発生する

Ruby 1.8.7とRuby on Rails 2.0.2の組み合わせは、いろいろと問題が発生するようです。

Ruby 1.8.6とRuby on Rails 2.0.2なら、もちろん問題なし。

マイナーバージョンアップなのに、この互換性の低さ。
Rubyの面目躍如でしょうか。

2008年06月26日

正規表現で全角のみ、半角のみのチェックを行う方法

元ネタ:正規表現で全角のみ、半角のみのチェックを行う方法

半角と全角が反対なのでは?
あるいは、unless ではなく if では?

def check(value)
  # 半角のみのチェック
  if value =~ /^[ -~。-゚]*$/
    puts "「#{value}」は半角のみです。"
  end

  # 全角のみのチェック
  if value =~ /^[^ -~。-゚]*$/
    puts "「#{value}」は全角のみです。"
  end
end

%w(abc あいう abcあいう).each do |value|
  check(value)
end

を実行すると、

「abc」は半角のみです。
「あいう」は全角のみです。

になります。

rubyで文字数を数えるには?

元ネタ:rubyで文字数を数えるには?

'abcあいう'.split(//)

は、定番の書き方です。
Rubyレシピブック』にも、掲載されていたと思います。(未確認)

Ruby 1.8.7では、String#charsというメソッドが追加されました。

'abcあいう'.chars.to_a #=> ["a", "b", "c", "あ", "い", "う"]

ちなみに、この機能がRuby on Rails 2.0.2では問題を引き起こします。

2008年06月28日

Rubyの後方互換性の低さは、Rubyの良さである。

先日リリースされたRuby 1.8.7は、Ruby on Rails 2.0.2で問題が発生しました。
(Rails 2.0.2とRuby 1.8.7のString#chars)

さらに脆弱性の問題を修正したRubyで、またもRuby on Railsで問題が発生しています。
(Rubyの脆弱性について)

このような問題が発生することは残念なことだと思います。
ですが、後方互換性の低さはRubyの良さですから、開発チームはあまり気にしないでいいと思います。

後方互換性を重視している言語、たとえばJava言語を見ると、歴史的な理由から推奨されない機能がたくさん残されています。
推奨されない機能をいつまでも残し続けることによって、クラスライブラリは肥大化し、わかりにくくなります。
(非推奨 API のリスト (Java Platform SE 6))

また、Pythonも同様です。
urllibとurllib2という2つのライブラリを見て、設計が美しいと感じる人はいないでしょう。

Rubyは歴史的に、後方互換性によって設計が乱されることを嫌ってきたように思います。
結果として、歴史的な理由に束縛されることなく、優れたAPIを提供できました。

# Rubyコミュニティが見下しているPHPも後方互換性を重視しない言語ですね。

Rubyの最新版は、常にその時点における最良と考えられるものでした。
後方互換性を犠牲にすることによって、優れたプログラミング言語になったのだと思います。

Rubyを使う技術者はRubyの哲学・思想を理解し、適材適所で使えばいいのです。

追記
長期にわたり使い続けるアプリケーションを作成する場合、私はRubyを選択しません。
PythonやC++、Javaを選ぶでしょう。
Rubyを使うのは規模の小さい使い捨てのプログラムが中心です。

関連する記事 「Railsの思想」もどうぞ

2008年07月04日

Ruby on Railsのログファイルに色をつけない方法

Ruby on Railsのログファイルに色をつけない方法。

Ruby on Railsは、標準では、ログファイルに色を付けて出力してくれます。

色付きに対応しているビューアでみれば見やすいのですが、Windowsなどの未対応OSでは逆に見にくくなってしまいます。

ログファイルに色をつけないようにするには、environment.rbを編集し、設定を変更します。

Rails::Initializer.run do |config|
  # ログファイルに色をつけない
  config.active_record.colorize_logging = false

これで、Windowsでログファイルをが見やすくなります。

2008年07月11日

ActiveSupportのEnumerable#group_by

ActiveSupportのEnumerable#group_byについて。

Railsレシピブックの330ページでは、

引数に渡したブロックの戻り値をキー、その戻り値の値となる要素の配列を値とする新たなハッシュを返します。

331ページのコードでは、group_byがHashを返しています。

comments_by_date = UserComment.find(:all).group_by(&:created_at)
#=> { #<Date: 2008/05/03 =>
       [#<UserComment created_at=#<Date: 2008/05/03, text="...">,

Ruby 1.8.6とRuby on Rails2.1.0で実際に試してみたところ、group_byは次のような値を返しました。

#=> [[#<Date: 2008/05/03>, [#<UserComment ...>, #<UserComment ...>]],
     [#<Date: 2008/05/04>, [#<UserComment ...>, #<UserComment ...>]]]

Hashが返ってくるはずなのに、Arrayが返ってきています。

実は、一見するとArrayのように見えますが、ActiveSupport::OrderedHashクラスのインスタンスです。 ActiveSupport::OrderedHashはArrayクラスのサブクラスで、[]メソッドを再定義しています。
そのため、Hashと同じように[]で要素にアクセスすることができます。
ActiveSupport::OrderedHashはArrayなのですが、あたかもHashのように扱うことができるのです。

2008年07月16日

RailsのAPIドキュメントいろいろ

RailsのAPIドキュメントいろいろ

  • Rails-doc

    検索キーワードを途中まで入力すると、候補をリストアップしてくれる機能が便利。
    快適に動作するし、見やすい。

  • RailsBrain.com

    こちらも便利。
    Ruby on Railsのバージョンごとにドキュメントを選択できるのがうれしい。

  • Ruby on Rails API

    候補をリストアップしてくれるけど、期待しているものがない。
    なんでだろう。

  • gem server

    コマンドラインから「gem server」と入力。
    その後「http://localhost:8808」にアクセスする。

  • rakeでAPIドキュメントを作成する
    「gem server」で見ることのできるドキュメントと同じ。

    rake rails:freeze:gems
    rake doc:rails

  • http://api.rubyonrails.org/

    gem_serverで見ることのできるrdocのAPIドキュメントと同じ?

2008年07月21日

Rubyの脆弱性その後

2008年6月、Rubyに任意のコードが実行される脆弱性が見つかり、脆弱性を修正したバージョンが公開されました。
修正されたのはRubyのバージョン1.8.5、1.8.6、1.8.7、1.9.0です。
1.8.4以前の全てのバージョンにも脆弱性は存在しますが、修正バージョンは公開されていません。
任意のコードが実行される脆弱性について

このときに行われた脆弱性の修正に、バグが紛れ込んでいました。
このバグはその後、修正されたようです。
公式サイトには、このことに言及している情報はありません。

なお、バグ修正はRuby 1.8.5には行われず、このまま放置されそうです。
[ruby-list:45247][ruby-list:45248]

ご注意ください。

2008年07月22日

Railsレシピブックのredirect_toは誤植ではないか

Railsレシピブックの57ページにあるredirect_toの記述は誤植ではないでしょうか。

redirect_to :controller => 'users', 
            :action => 'show', params => { :id => 3 }

正しくは、

redirect_to :controller => 'users', 
            :action => 'show', :id => 3

どこかに正誤表はないのでしょうか。

2008年07月23日

Ruby on Railsを使用するのに、どのバージョンのRubyを使えばいいのか

Ruby on Railsを使用するのに、どのバージョンのRubyを使えばいいのでしょうか。

Ruby 1.8.4以前は、「任意のコードが実行される脆弱性」があるため、使用できません。
Ruby 1.8.5は、「任意のコードが実行される脆弱性」の修正時にバグが混入しているため、使用できません。
Ruby 1.8.6とRuby 1.8.7は、「任意のコードが実行される脆弱性」の修正時に混入したバグが修正されており、使用可能です。

使用できるのは、Ruby 1.8.6かRuby 1.8.7のどちらかになります。

次にRuby on Railsとの相性の問題があります。

Ruby on Rails 2.0.2Ruby on Rails 2.1.0
Ruby 1.8.6
Ruby 1.8.7×

Ruby on Rails 2.0.2を使用するならRuby 1.8.6、Ruby on Rails 2.1.0を使用するならRuby 1.8.7がいいでしょう。

どちらを使うか迷ったら、とりあえずはRuby 1.8.6を使えばいいと思います。
近い将来、バージョンアップが必要になるかもしれませんが。

2008年07月25日

SQLiteで「unable to open database file」エラー

SQLiteで「unable to open database file」のエラーがなかなか解決しませんでした。

考えられる原因の一つにアクセス権がありますが、問題ないことは確認済み。

結局、データファイルのフルパスにマルチバイト文字があったことが原因みたいです。
データファイルのファイル名にマルチバイト文字がなくても、パス中のどこかにマルチバイト文字があると、 エラーになるのではないかと思います。

昔のFirebird(バージョン1.x系)でも同じ問題がありました。
Firebird 2.x系では、この問題は解決されており、使いやすくなっています。

Ruby用PDFライブラリでは、Haru Free PDF Libraryがおすすめ

Ruby用PDFライブラリでは、Haru Free PDF Libraryがおすすめ

Ruby用PDFライブラリを調べました。
日本語が正しく使用できるものは「Haru Free PDF Library」だけではないでしょうか。
正直なところ、これほど貧弱だとは思いませんでした。

2008年08月07日

NetBeansのRuby用プラグイン

NetBeansのRuby用プラグイン。
愛用しているものは次の2つ。

Tabsプラグインは、タブ文字をハイライト表示するようにしてくれるプラグイン。
タブ文字をホワイトスペース(半角の空白)に変換する機能もあります。

Trailing Whitespaceプラグインは、行末のホワイトスペース(半角空白、全角空白、タブ文字)をハイライト表示するようにしてくれるプラグイン。
一括して、行末のホワイトスペース(半角空白、全角空白、タブ文字)を削除する機能もあります。

NetBeansのかゆいところに手が届くプラグインです。

NetBeans(Rubyプラグイン)のTODOの書き方

NetBeans(Rubyプラグイン)のTODOの書き方

Rubyスクリプトの中では「#」の後にTODOを書く。

#TODO やること

.erbファイルにTODOを書くときは、HTMLのコメントの中にTODOを書けばよさそう。

<!-- TODO やること -->

2008年08月16日

NetBeansでソースコード行末の不要なスペースやタブを除去するプラグイン

Rubyに限った話ではないが、自動整形機能を備えたエディタでソースコードを編集をすると、行末に無駄なスペースやタブなどがどんどん増えてくる。特にNetBeansやEclipseを利用するとその傾向が顕著である。

ソースコード行末の不要なスペースやタブを除去する

NetBeansには行末の無駄なスペースやタブをハイライト表示してくれるプラグインがあります。

RubyPluginsにあるTrailing Whitespaceプラグインです。

行末の半角スペース、全角スペース、タブ文字をハイライト表示します。

このプラグインには、ファイル中の行末のホワイトスペース(半角空白、全角空白、タブ文字)を一括削除する機能もあります。

行末の無駄なスペースやタブが見えるようになると、どうしても気になりますので、こまめに削除するようになります。

開発チーム全体でこのプラグインを導入すると、行末の無駄なスペースに悩まされることも少なくなるのではないでしょうか。

2008年08月18日

Rubyのクラスメソッドをprivateにする方法

次のソースコードでは、Foo#foo()はprivateなクラスメソッドにはなりません。

class Foo
  private
  def self.foo
    return 'foo'
  end
end

Foo.foo #=> 'foo'

そこで、Rubyでクラスメソッドをprivateにする方法です。

1.private_class_methodを使う方法

private_class_methodはその名の通り、指定したメソッドをprivateなクラスメソッドにします。

class Foo
  def self.foo
    return 'foo'
  end
  def self.bar
    return 'bar'
  end
  private_class_method :foo
end

Foo.bar #=> 'bar'
Foo.foo #=> エラー

2.特異クラスでprivateを使う方法

複数のクラスメソッドをまとめて登録するときは、特異クラスで登録することが多いと思います。

特異クラスでprivateを使うと、privateなクラスメソッドになります。

class Foo
  class << self
    def bar
      return 'bar'
    end

    private
    def foo
      return 'foo'
    end
  end
end

Foo.bar #=> 'bar'
Foo.foo #=> エラー

2008年08月19日

REXML::Element.writeの代わりにREXML::Formattersを使う

Rubyでxmlを編集するために、REXMLで

xml = ''
doc.root.write(xml)

と、REXML::Element.writeを使ったら、

REXML::Element.write is deprecated.  See REXML::Formatters

といわれたので、REXML::Formattersを使ってみました。

xml = ''
formatter = REXML::Formatters::Default.new
formatter.write(doc.root, xml)

REXML::Formatters::Defaultは、XMLをそのまま出力します。

REXML::Formatters::Prettyは、XMLを整形して出力します。

xml = ''
formatter = REXML::Formatters::Pretty.new
formatter.write(doc.root, xml)

REXML::Formatters::Prettyの標準のインデントの空白の数は2です。
コンストラクタに引数を与えることで、空白の数を変更できます。

formatter = REXML::Formatters::Pretty.new(4) #=> インデントの空白の数は4

write()メソッドの引数は、nodeとoutputの2つ。

REXML::Formatters::Default#write(node, output)

nodeのXMLをoutputに出力することになります。
outputはメソッド <<(value) が定義されている必要があります。

2008年08月20日

Rubyのクラスメソッドの別名定義(alias)

特異クラスでaliasを使うと、クラスメソッドの別名を定義することができます。

class Foo
  class << self
    def bar
      return 'bar'
    end
    alias foo bar
  end
end

Foo.bar #=> 'bar'
Foo.foo #=> 'foo'

Rubyの特異クラスのメソッドの振る舞いのまとめ

Rubyの特異クラスのメソッドの振る舞いのまとめ

特異クラスでprivateを使うと、privateなクラスメソッドにすることができます。

class Foo
  class << self
    def bar
      return 'bar'
    end

    private
    def foo
      return 'foo'
    end
  end
end

Foo.bar #=> 'bar'
Foo.foo #=> エラー

特異クラスでaliasを使うと、クラスメソッドの別名を定義することができます。

class Foo
  class << self
    def bar
      return 'bar'
    end
    alias foo bar
  end
end

Foo.bar #=> 'bar'
Foo.foo #=> 'foo'

2008年08月25日

Ruby on Railsで関連テーブルの情報をまとめて読み込む:includeオプションのまとめ

ActiveRecordのfind()では、:includeオプションを指定することで、関連テーブルの情報をまとめて読み込むことができます。

:includeオプションの指定方法をまとめました。

1つの関連先テーブルを読み込む

:include => :foo

複数の関連先テーブルを読み込む

:include => [:foo, :bar]

多段の関連を一度に読み込む

:include => {:foo => :bar}

さらに多段の関連を一度に読み込む

:include => {:foo => {:bar => :baz}}

関連先テーブルから複数の関連テーブルを読み込む

:include => {:foo => [:bar, :baz]}

以上を組み合わせて読み込む

:include => [{:foo => {:bar => :baz}}, :hoge]

2008年08月27日

REXMLのDoS脆弱性

RailsでXMLリクエストのパースに使用されているREXMLに、DoS脆弱性が発見されました。XML entity explosion attackと呼ばれる攻撃手法により、ユーザから与えられたXMLを解析するようなアプリケーションをサービス不能(DoS)状態にすることができます。大部分のRailsアプリケーションはこの攻撃に対して脆弱です。

REXMLのDoS脆弱性

Ruby on Railsを使用しているほとんどのWebアプリケーションで問題が発生します。

対策は公式サイトで配布されているモンキーパッチを適用することです。

submit_to_button

Ruby on Rails入門―優しいRailsの育て方の246ページに「submit_to_button」についての説明があります。

submit_to_button

"link_to_function"に対する"buttton_to_function"のように、"link_to_remote"のボタン版である"submit_to_remote"メソッドも存在します。"button_to_remote"ではありませんので、名前に注意しましょう。これは、単なるボタンだけではなくFORMタグも生成されるため、このような名前になっています。

何を言っているのかわかりませんでした。

どうやら、「submit_to_button」は「submit_to_remote」の間違いのようです。

そう考えると納得できます。

正誤表には記載されていませんね。

2008年08月30日

RailRoadを試してみた

RailRoadを試してみたところ、名前空間に対応していないようだ。
# モデルもコントローラも。

たとえば、次のようなモデルは、

class Blog::Entry < ActiveRecord::Base
end

以下のようなエラーメッセージが出力される。

`load_missing_constant': uninitialized constant Entry (NameError)

ついでにインストール方法と使い方

インストール方法は、RubyGemsを使って、

gem install railroad

あと、Graphvizもインストールしておく。

使い方は、SVGファイルを出力するとき

C:\ruby\bin\railroad.bat -M | "C:\Program Files\Graphviz2.20\bin\dot.exe" -Tsvg > model.svg
C:\ruby\bin\railroad.bat -C | "C:\Program Files\Graphviz2.20\bin\dot.exe" -Tsvg > controller.svg

PNGファイルを出力するとき

C:\ruby\bin\railroad.bat -M | "C:\Program Files\Graphviz2.20\bin\dot.exe" -Tpng > model.png
C:\ruby\bin\railroad.bat -C | "C:\Program Files\Graphviz2.20\bin\dot.exe" -Tpng > controller.png

2008年09月26日

Ruby on Rails 2.1.1でActiveRecordのvalidation機能が変更されている

Ruby on Rails 2.1.1でActiveRecordのvalidation機能が変更されているらしい。

2.1.1では、has_oneの関連で同時に保存した際に、関連テーブルのバリデーションの結果が親のインスタンスに保存されないようです。

続・Rails2.1.1は危ない

Ruby on Railsのアップデートについて行くのは大変です。

2008年10月05日

気がついたら Ruby on Rails 2.0.4 がリリースされていた

気がついたら Ruby on Rails 2.0.4 がリリースされていた。

Ruby on Rails 2.1系が出ているので、放置されているかと思ってた。

Maintenance releaseとあるけど、互換性を失う変更が行われていないか、心配だ。

2009年01月04日

Ruby on Railsでファイルのアップロードの機能テストを行うにはActionController::fixture_file_uploadを使用する

Ruby on Railsでファイルのアップロードの機能テストを行うにはActionController::fixture_file_uploadを使用します。

fixture_file_upload(path, mime_type = nil, binary = false)

Shortcut for ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + path, type):

Module: ActionController::TestProcess

fixture_file_uploadの最初の引数は、アップロードするファイルの場所を指定します。
基準となるディレクトリは「Test::Unit::TestCase.fixture_path(RAILS_ROOT/test/fixtures)」です。
「RAILS_ROOT/test/fixtures/files/spongebob.png」にファイルがある場合は、引数は「/files/spongebob.png」になります。
2番目の引数は、content_typeを指定します。
3番目の引数は、trueにした場合はバイナリモードになります。たぶん。

post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')

Ruby on Railsでファイルのダウンロードの機能テスト

Ruby on Railsでファイルをダウンロードするには、send_data()を使用する。

send_data(csv_data, :type => 'text/csv', :filename => 'filename.csv')

さて、機能テスト(functional test)するには、どうすればいいか。

@response.bodyを参照すると、csv_dataが入っているので、動作のテストができる。

get :download_csv
assert_equal('期待する値', @response.body)

2009年01月08日

Ruby on Railsによるプルダウンメニューの連動

質問をいただきました。

質問お願いします。 2つのプルダウンメニューを連動させたいのですがいい方法はありませんか?

Ruby on Railsによるプルダウンメニューの連動の簡単なサンプルを作りました。
サンプルということで、わかりやすさを重視しています。

Viewの方でプルダウンメニューを操作するJavaScriptの関数を用意しておき、 ControllerからJavaScriptの関数を呼び出すようにしました。
このあたりの処理は、いろいろなやり方があると思います。

index.html.erb

<%= javascript_include_tag(:defaults) %>
<script>
//プルダウンメニューが選択されたときに呼び出される関数
function change_item() {
  <%= remote_function(:url => {:action => 'call_ajax'}, 
                      :with => "'id=' + $('item1').value") %>
}
//プルダウンメニューの選択肢を削除する関数
function clear_item() {
  $('item2').length = 0;
}
//ブルダウンメニューに選択肢を追加する関数
function update_menu(name, value) {
  $('item2').options[$('item2').options.length] = new Option(name, value);
}
</script>
<% form_tag do %>
<%= select_tag('item1',
               options_for_select([['x','1'],['y','2'],['z','3']]), 
               :onchange => 'change_item()') %>
<%= select_tag('item2',options_for_select([])) %>
<% end %>

index_controller.rb

class IndexController < ApplicationController
  def index
  end

  def call_ajax
    # 選択肢のデータを取得
    item_list = {
      '1' => [['x1', '1'], ['x2', '2'], ['x3', '3']],
      '2' => [['y1', '4'], ['y2', '5'], ['y3', '6']],
      '3' => [['z1', '7'], ['z2', '8'], ['z3', '9']]
    }
    items = item_list[params['id']]
    # Javascriptの呼び出し
    render :update do |page|
      #選択肢を削除
      page.call 'clear_item'
      #選択肢を追加
      items.each { |item| page.call 'update_menu', item[0], item[1] }
    end
  end
end

2009年02月02日

SQLite/Rubyのページを更新しました

情報が古く、ファイルがダウンロードできなくなっている、
というメールをいただきましたので、
3年ぶりにSQLite/Rubyのページを更新しました。

お知らせありがとうございました。

他のRuby関連のページもずいぶんと古くなってしまいました。
何とかしなければ。

2009年04月14日

WindowsでRuby1.8.7を使ってRuby on Railsをインストールする

One-Click InstallerにRuby 1.8.7がないので、茨の道を進むことになった。

Ruby 1.8.7のインストール

http://www.garbagecollect.jp/ruby/mswin32/ja/」から1.8.7の最新リリース版をダウンロードする。

ダウンロードが完了したら、ファイルを展開し、適当なフォルダに配置する。
ここではC:\ruby-1.8.7-p72-i386-mswinに配置したとする。

環境変数の「Path」を編集して「C:\ruby-1.8.7-p72-i386-mswin\bin」にパスを通す。

RubyGemsのインストール

RubyGems」のページから「rubygems-x.x.x.zip」をダウンロードする。

ダウンロードが完了したら、ファイルを展開する。

コマンドプロンプトから展開したフォルダに移動して、次のコマンドを入力し、RubyGemsをインストールする。

ruby setup.rb

Ruby on Railsのインストール

Porting Libraries to Win32」から「openssl-xxx-mswin32.zip」をダウンロードする。
ダウンロードが完了したら、ファイルを展開し、binフォルダにある「libeay32.dll」と「ssleay32.dll」をruby.exeと同じフォルダ(C:\ruby-1.8.7-p72-i386-mswin\bin)にコピーする。

Porting Libraries to Win32」から「zlib-xxx-mswin32.zip」をダウンロードする。
ダウンロードが完了したら、ファイルを展開し、binフォルダにある「zlib.dll」をruby.exeと同じフォルダ(C:\ruby-1.8.7-p72-i386-mswin\bin)にコピーする。

これで、RubyGemsが使用できるようになる。

次のコマンドを入力し、Ruby on Railsをインストールする。

gem install rails

「msvcr90.dllが見つからない。」というエラーが発生する場合は、「Microsoft Visual C++ 2008 再頒布可能パッケージ」からダウンロードしてインストールする。

Ruby on Railsに必要なファイルのインストール

iconv-1.8.win32.zipをダウンロードして展開する。
libフォルダにある「iconv.dll」をruby.exeと同じフォルダ(C:\ruby-1.8.7-p72-i386-mswin\bin)にコピーする。

これで、rakeがインストールできるようになる。

次のコマンドを入力し、rakeをインストールする。

gem intall rake

Porting Libraries to Win32」から「http://jarp.does.notwork.org/win32/readline-4.3-2-mswin32.zip」をダウンロードする。
libフォルダにある「readline.dll」をruby.exeと同じフォルダ(C:\ruby-1.8.7-p72-i386-mswin\bin)にコピーする。

これで、「ruby script\console」でエラーが発生しなくなる。

2009年04月20日

Ruby on Rails 2.3でDEPRECATION WARNING: protect_from_forgery only takes :only and :except options now. :digest and :secret have no effect. という警告が出るの対策

Ruby on Rails 2.3でDEPRECATION WARNING: protect_from_forgery only takes :only and :except options now. :digest and :secret have no effect. という警告が出る場合の対策です。

Ruby on Rails 2.3から、protect_from_forgeryへの:digest、:secretオプションは非推奨になりました。つけても何も起こりません。

:digest、:secretオプションがあると警告が出ますから、削除しましょう。

Ruby on Rails 2.3でテストを実行すると、undefined method `use_transactional_fixtures='というエラーになるときの対策

Ruby on Rails 2.3でテストを実行すると

./test/unit/../test_helper.rb:23: undefined method `use_transactional_fixtures=' for Test::Unit::TestCase:Class (NoMethodError)

というエラーになるときの対策です。

Ruby on Rails 2.3では、TestCaseクラスをTest::Unit::TestCaseからActiveSupport::TestCaseに変更する必要があります。

RAILS_ROOT/test/test_helper.rbを修正します。

#class Test::Unit::TestCase #Ruby on Rails 2.2以前
class ActiveSupport::TestCase #Ruby on Rails 2.3以降

RAILS_ROOT/test/unit/にある*.test.rbもすべて修正します。

#class SampleTest < Test::Unit::TestCase #Ruby on Rails 2.2以前
class SampleTest < ActiveSupport::TestCase #Ruby on Rails 2.3以降

Ruby on Rails 2.0で、ActionController::TestCaseのsetupにバグがあったため、Ruby on Rails 1.2と同じようにFunctionalTestにもTest::Unit::TestCaseを使っているケースがあります。
ActionController::TestCaseのsetupにバグは、Ruby on Rails 2.1で修正されているようですから、ActionController::TestCaseを使うようにします。

RAILS_ROOT/test/functional/にある*.test.rbもすべて修正します。

#class SampleTest < Test::Unit::TestCase #Ruby on Rails 2.2以前
class SampleTest < ActionController::TestCase #Ruby on Rails 2.3以降

2009年04月23日

ActiveRecordの設定をDRYにするDrySQLライブラリ

実践 Rails -強力なWebアプリケーションをすばやく構築するテクニック』にDrySQLというライブラリが紹介されていた。

DrySQLを使うと、スキーマ情報を元にテーブルのリレーションシップや検証ルールを自動的に設定してくれる。

たとえば、従来は次のように設定する必要があった。
(DrySQLのサイトの例より)

class Employee < ActiveRecord::Base
  set_table_name "EMP123"
  set_primary_key "EMP_REF_ID"
  belongs_to :department, :foreign_key=>'DEP_NAME'
  has_many :projects, :foreign_key=>'PROJECT_NAME'
  has_one :location
  has_many :whatevers, :through=>projects
  validates_length_of :NAME, :allow_nil=>true, :maximum=>20
  validates_numericality_of :EMP_REF_ID, :allow_nil=>false, only_integer=>true
end

DrySQLを使うと、次のように簡略化できる。
残りの設定は、DrySQLが自動的に設定してくれるのだ。

class Employee < ActiveRecord::Base
  set_table_name "EMP123"
end

これで、テーブルの設計を変更したときに、ソースコードを修正する必要がなくなる。

Ruby on Railsで複数のデータベースを使用する方法

実践 Rails -強力なWebアプリケーションをすばやく構築するテクニック』に紹介されていたRuby on Railsで複数のデータベースを使用する方法。

database.ymlファイルに複数のデータベース環境を指定する。

legary:
  adapter: mysql
  database: my_db
  username: user
  password: pass
  host: legary_host

new:
  adapter: mysql
  database: my_db
  username: user
  password: pass
  host: new_host

ActiveRecordのクラス定義で、接続するデータベースを指定する。

class LegacyClient < ActiveRecord::Base
  establish_connection "legary"
end

class Client < ActiveRecord::Base
  establish_connection "new"
end

以上だ。

2009年04月24日

ActiveRecordでオブジェクトを作成しないSelect

ActiveRecordでオブジェクトを作成しないSelect。
オーバーヘッドが少ないので、オブジェクトが必要ない場合はパフォーマンスが向上する。

select_value は最初の一件を返す。
値は文字列で返される。

>> ActiveRecord::Base.connection.select_value('SELECT zip_code FROM addresses')
=> "1000000"

select_valuesは配列で返す。
値は文字列で返される。

>> ActiveRecord::Base.connection.select_values('SELECT zip_code FROM addresses LIMIT 3')
=> ["1000000", "1020072", "1020082"]

実践 Rails -強力なWebアプリケーションをすばやく構築するテクニック』に紹介されていた方法。
メソッドを一つ追加して、select_valuesを使いやすくする。

class << ActiveRecord::Base
  def select_values(sql)
    connection.select_values(sanitize_sql(sql))
  end
end

sql = %(SELECT id FROM people WHERE last_name = ?)
last_name = %(O'Reilly)
Person.select_values [sql, last_name] #=> ["12", "42"]

2009年04月25日

パフォーマンスのため、RMagickを使わずImageMagickを使う

37signalsのBascampは、イメージのサイズをこの方法で変更している。わざわざRMagickをインストールするのではなく、ImageMagickに渡すのである。

def thumbnail(temp, target)
  system(
    "/usr/local/bin/convert #{escape(temp)} -resize 48x48! #{escape(target)}"
  )
end

実践 Rails -強力なWebアプリケーションをすばやく構築するテクニック

確かに、下手にRMagickを使うよりも、素直にImageMagickに任せた方がいい場合も多そうだ。

2009年05月15日

Ruby on RailsでRSpecを使うときに使用するコマンドのまとめ

Ruby on RailsでRSpecを使うときに使用するコマンドのまとめ

RSpecとrspec-railsのインストール(Ruby on Rails 2.1.0以上の場合)

gem install rspec
gem install rspec-rails

#Ruby on Rails 2.1.0未満の場合はhttp://wiki.github.com/dchelimsky/rspec/railsを参考にしてください。

色づけして表示するならWindowsならwin32consoleが必要

gem install win32console

カバレッジテストに必要

gem install rcov

必要なファイルの作成

ruby script/generate rspec

RSpecのバージョンの確認

ruby script/spec -v

モデルの作成

ruby script/generate rspec_model モデル名 [フィールド名:データ型 …]

# 例
ruby script/generate rspec_model person name:string

コントローラの作成

ruby script/generate rspec_controller コントローラ名 [メソッド名 …]

#例
ruby script/generate rspec_controller person

ひな形の作成

ruby script/generate rspec_scaffold モデル名 [フィールド名:データ型 …]

#例
ruby script/generate rspec_scaffold purchase order_id:integer amount:decimal

任意のspecを実行

ruby script\spec ファイル名

# 例
ruby script\spec spec\models\blog_spec.rb

すべてのspecを実行

rake spec

モデルのspecを実行

rake spec:models

コントローラのspecを実行

rake spec:controllers

ヘルパーのspecを実行

rake spec:helpers

ビューのspecを実行

rake spec:views

データベースにfixtureファイルを読み込む

rake spec:db:fixtures:load

Specdocを出力

rake spec:doc

vendor/plugin 以下にあるspecを実行

rake spec:plugins

vendor/plugin以下にあるspecのSpecdocを出力

rake spec:plugin_doc

すべてのspecを実行し、rcovで測定
($RAILS_ROOT/coverage ディレクトリにファイルが作成される)

rake spec:rcov

RSpec関連のタスクをすべて表示

rake --tasks spec

spec_serverを使用する

ruby script/spec_server &
ruby script/spec --drb spec/models

または、spec/spec.optsを編集して- -drb を追加する

2009年05月18日

RSpecのコントローラのテストで利用できる機能

アクセス

Getでアクセスする

get(action, params={})

#例
get(:index, :username => 'foo', :password => 'bar')

Postでアクセスする

post(action, params={})
#例
post(:index, :username => 'foo', :password => 'bar')

取得

レスポンスを取得する

reponse()

コントローラのインスタンス変数を取得する

assigns[変数名]

#例
user = assigns[:user]

セッションオブジェクトを取得する

session()

#例
session[:user].id.should == 1

flashの内容を取得する

flash()

#例
flash[:error_message].should be_blank

コントローラのテストで使うmatcher

レスポンスのステータスコード

response.should be_success

リダイレクト先

response.should redirect_to(:controller => 'login', :action => 'index')

テンプレート

response.should render_template('login')

2009年07月19日

文字列から正規表現にマッチするすべての文字列を取得する

String#scanを使うと、文字列から正規表現にマッチするすべての文字列を取得することができます。

irb(main):020:0> '1a2b3c4d5e6f7e8h9'.scan(/\d/)
=> ["1", "2", "3", "4", "5", "6", "7", "8", "9"]

NetBeans 6.7でRuby on Railsの開発をして気がついたこと

NetBeans 6.7でRuby on Railsの開発をして気がついたこと

UnitTestのテスト結果のウィンドウが豪華になっていた。
エラーがあると、テスト結果のバーがアニメーション表示する。
# RSpecを使っていたので気がつかなかったが、もしかすると昔からかもしれない。
人の目は敵を素早く見つけるために、動いているもにに視点が移動する。
したがって、アニメーション表示するテスト結果のバーに視点が移動してしまい、
集中してソースコードを見ることができない。
アニメーション表示は見た目はいいが使い勝手は悪い。

愛用していたプラグインが動作しない。
NetBeansのRuby用プラグインで紹介したタブ文字をハイライト表示する「Tabs」プラグインと、行末のホワイトスペース(半角空白、全角空白、タブ文字)をハイライト表示して削除してくれる「Trailing Whitespace」プラグインが動作しない。
このプラグインの機能は多くのエディタに搭載されている機能だ。
NetBeansにも標準で搭載してもらいたい。

2009年07月21日

HashとActiveSupport::OrderedHashのmergeの挙動が異なる

HashとActiveSupport::OrderedHashのmergeの挙動が異なる。
使用しているRuby on Railsのバージョンは2.3.2。

Hash#mergeの場合

>> ha = {1 => 2, 2 => 4}
=> {1=>2, 2=>4}
>> hb = {2 => 1, 3 => 1}
=> {2=>1, 3=>1}
>> hc = ha.merge(hb) {|key, s_val, o_val| s_val + o_val}
=> {1=>2, 2=>5, 3=>1}

ActiveSupport::OrderedHash#mergeの場合

>> h1
=> #<OrderedHash {1=>2, 2=>4}>
>> h2
=> #<OrderedHash {2=>1, 3=>1}>
>> h3 = h1.merge(h2) {|key, s_val, o_val| s_val + o_val}
=> #<OrderedHash {1=>2, 2=>1, 3=>1}>

ActiveSupport::OrderedHashの場合、mergeの引数のブロックが無視されているようだ。
HashのつもりでOrderedHashを使っていたので、問題に気づくのに時間がかかった。
まさかこんな基本的なところに問題があろうとは。

2009年07月22日

Railsの思想

“Railsという現象”とコミュニティの性質」で、Rails開発者がRailsの思想について語っている部分が興味深い。

以前に書いた記事「Rubyの後方互換性の低さは、Rubyの良さである。」と同じようなものだけど。

「枯れたバージョンのRailsというものはない。昨日のバージョンより今日のバージョンのほうがいいし、今日のものより明日のRailsがいい。だから常に“今”のRailsがベスト」

“Railsという現象”とコミュニティの性質

とか、

「Railsでは新機能が未完成のまま出てくることが多い。攻めのプロダクト。完成していなければ、自分が完成させながら使えばいいじゃんという認識がコミュニティ側にある」

“Railsという現象”とコミュニティの性質

など。

こういう思想を理解せずに使うと、後で痛い目に遭うかもしれない。

2009年09月05日

Ruby on Railsを使うならバージョン2.2以上を使おう

Ruby on Railsを使うならバージョン2.2以上を使おう。

バージョン2.1以下は、セキュリティホールが放置されている。
いずれも、バージョン2.3.4と2.2.3で修正されている。

とはいっても、バージョン間での互換性が低いのがRuby on Railsの特徴。
既存のシステムのバージョンアップも楽ではない。

About Ruby

ブログ「山本隆の開発日誌」のカテゴリ「Ruby」に投稿されたすべてのエントリーのアーカイブのページです。過去のものから新しいものへ順番に並んでいます。

前のカテゴリはPythonです。

次のカテゴリはwebgenです。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

Powered by
Movable Type 3.35