トップ 最新 追記

Masa's blog

検索キーワード:

2009年03月05日 hda: read_intr: status=0x59 { DriveReady SeekComplete DataRequest Error } [長年日記]

_ hda: read_intr: status=0x59 { DriveReady SeekComplete DataRequest Error }

hda: read_intr: status=0x59 { DriveReady SeekComplete DataRequest Error }
hda: read_intr: error=0x40 { UncorrectableError }, LBAsect=12266756, sector=11688416
end_request: I/O error, dev 03:03 (hda), sector 11688416
hda: read_intr: status=0x59 { DriveReady SeekComplete DataRequest Error }
hda: read_intr: error=0x40 { UncorrectableError }, LBAsect=12266764, sector=11688424
end_request: I/O error, dev 03:03 (hda), sector 11688424

lib100にて嫌なメッセージ発見。mke2fs -c で当面しのぐか、HDを買い替えるか思案のしどころ。ともかく、このサーバがこけると我が家がインターネットから隔絶されることと、このブログを始め、さまざまなサービスが停止してしまうことは紛れもない事実。


2009年03月09日 lib100(myh.no-ip.org)ハードディスク(一応)復旧 [長年日記]

_ lib100(myh.no-ip.org)ハードディスク(一応)復旧

先日、I/O errorを出していたlib100のハードディスクを

mke2fs -c -j /dev/hda3

にてフォーマットし直して、バックアップからリストアしなおした。

リストア前に

cat /dev/zero >ほげほげ

をリダイレクト先を変えながら4回実施して(1ファイル2GBまでなので...)、100%になるまで書き込みを実施しI/O errorが発生しない事を確認。いちおう -c オプションによるバッドセクタの割り当ては成功しているよう。

ただし(やはり?)、

 cat /dev/hda >/dev/null

で読み出すと先日と同様のI/O errorが特定セクターで発生しているので、物理的にヘタリかけているのは紛れもない事実のよう。

後は、被害の広がりがゆっくり進行するのを期待するのみ。


2009年03月13日 ruby(1.8.x)儂(わし)的解釈によるメモ [長年日記]

_ ruby(1.8.x)儂(わし)的解釈によるメモ(基本編)

使われている用語等は基本的にテキトーなもの。あまり真に受けないように。あくまで儂(わし)的なrubyプログラミングで困らない程度のTipsの蓄積。

#! /usr/bin/ruby
#

引数

#
p ARGV
puts "1個目の引数 = #{ARGV[0]}"
puts "2個目の引数 = #{ARGV[1]}"
puts "3個目の引数 = #{ARGV[2]}"
puts "引数の個数 = #{ARGV.size}"
["aaa", "bbb"]
1個目の引数 = aaa
2個目の引数 = bbb
3個目の引数 =
引数の個数 = 2

ワーク(文字列, 数値)

#
work = "abcdef"
puts "work = #{work}"
puts "work[2] = #{work[2]}"
puts "work[2].chr = #{work[2].chr}"
puts "work[2..3] = #{work[2..3]}"
puts "work[2,3] = #{work[2,3]}"
work = 12345
puts "work = #{work}"
puts "== work is #{work} =="
puts '== work is #{work} =='
work = abcdef
work[2] = 99
work[2].chr = c
work[2..3] = cd
work[2,3] = cde
work = 12345
== work is 12345 ==
== work is #{work} ==

配列

#
work = []
work = ["aaa", 12345, "ccc", 23456, "eee", 34567]
work2 = ["AAA", 54321, "ccc", 23456, "EEE", 76543]
p work
p work2
puts "work[0] = #{work[0]}"
puts "work[1] = #{work[1]}"
puts "work[2] = #{work[2]}"
puts "work[3] = #{work[3]}"
puts "work[4] = #{work[4]}"
puts "work[5] = #{work[5]}"
puts "work.size = #{work.size}"
puts "work[2..3] = #{work[2..3]}"
puts "work[2,3] = #{work[2,3]}"
puts "work & work2 = #{work & work2}"
puts "work | work2 = #{work | work2}"
puts "work + work2 = #{work + work2}"
puts "work - work2 = #{work - work2}"
["aaa", 12345, "ccc", 23456, "eee", 34567]
["AAA", 54321, "ccc", 23456, "EEE", 76543]
work[0] = aaa
work[1] = 12345
work[2] = ccc
work[3] = 23456
work[4] = eee
work[5] = 34567
work.size = 6
work[2..3] = ccc23456
work[2,3] = ccc23456eee
work & work2 = ccc23456
work | work2 = aaa12345ccc23456eee34567AAA54321EEE76543
work + work2 = aaa12345ccc23456eee34567AAA54321ccc23456EEE76543
work - work2 = aaa12345eee34567

ハッシュ

#
work = {}
work['apple'] = "red"
work['lemon'] = "yellow"
work['melon'] = "green"
p work
puts "work['apple'] = #{work['apple']}"
puts "work['lemon'] = #{work['lemon']}"
puts "work['melon'] = #{work['melon']}"

work2 = {"apple", "red", "lemon", "yellow", "melon", "green"}
p work2
puts "work2['apple'] = #{work2['apple']}"
puts "work2['lemon'] = #{work2['lemon']}"
puts "work2['melon'] = #{work2['melon']}"

work3 = {"apple" => "red", "lemon" => "yellow", "melon" => "green"}
p work3
puts "work3['apple'] = #{work3['apple']}"
puts "work3['lemon'] = #{work3['lemon']}"
puts "work3['melon'] = #{work3['melon']}"
{"apple"=>"red", "melon"=>"green", "lemon"=>"yellow"}
work['apple'] = red
work['lemon'] = yellow
work['melon'] = green
{"apple"=>"red", "melon"=>"green", "lemon"=>"yellow"}
work2['apple'] = red
work2['lemon'] = yellow
work2['melon'] = green
{"apple"=>"red", "melon"=>"green", "lemon"=>"yellow"}
work3['apple'] = red
work3['lemon'] = yellow
work3['melon'] = green

if文

#
work = {"apple" => "red", "lemon" => "yellow", "melon" => "green"}
#if (work['lemon'] == "red")
#if (work['lemon'] != "red")
#if (work['lemon'] > "red")
#if (work['lemon'] <"red")
#if (work['lemon'] >= "red")
#if (work['lemon'] <= "red")
if (work['lemon'] == "red")
       puts "lemon is red"
elsif (work['lemon'] == "yellow")
       puts "lemon is yellow"
else
       puts "lemon is not red and yellow"
end
lemon is yellow

if文(パターンマッチング)

#
work = {"apple" => "red", "lemon" => "yellow", "melon" => "green"}
if (/^y.*w$/ =~ work['lemon'])
       puts "lemon is y.*w"
else
       puts "lemon is not y.*w"
end
lemon is y.*w

case文

#
work = {"apple" => "red", "lemon" => "yellow", "melon" => "green"}
case work['lemon']
when "red"
       puts "lemon is red"
when "yellow"
       puts "lemon is yellow"
else
       puts "lemon is not red and yellow"
end
lemon is yellow

while文

#
work = ["melon", "lemon", "apple"]
i = 0
while (i < 3)
       if (work[i] == "apple")
#              next
#              redo
               break
       end
       puts work[i]
       i += 1
end
melon
lemon

for文

#
work = ["melon", "lemon", "apple"]
#for i in work
for i in work.sort
       puts i
end
apple
lemon
melon

for文(2)

#
work = ["melon", "lemon", "apple"]
for i in 0..2
       puts work[i]
end
melon
lemon
apple

each文

#
work = ["melon", "lemon", "apple"]
work.each do |i|
       puts i
end
melon
lemon
apple

for文(ハッシュの添字でループ)

#
work = {"melon", "green", "lemon", "yellow", "apple", "red"}
#for i in work.keys
for i in work.keys.sort
       puts work[i]
end
red
yellow
green

each文(ハッシュ)

#
work = {"melon" => "green", "lemon" => "yellow", "apple" => "red"}
work.each do |key, val|
       puts key + "=" + val
end
apple=red
lemon=yellow
melon=green

tr文(文字置換)

#
work = "ABCDEFG"
puts "work = #{work}"
puts "work.tr('C-E', 'c-e') = #{work.tr('C-E', 'c-e')}"
work = ABCDEFG
work.tr('C-E', 'c-e') = ABcdeFG

gsub文(文字列置換)

#
work = "ABCDEFG"
puts "work = #{work}"
puts "work.gsub(/C.*E/, 'cde') = #{work.gsub(/C.*E/, 'cde')}"
work = ABCDEFG
work.gsub(/C.*E/, 'cde') = ABcdeFG

gsub文(文字列置換)(2)

#
work = "ABCDEFG"
puts "work = #{work}"
puts "work.gsub(/(C.*E)/){'==' +  $1 + '=='} = #{work.gsub(/(C.*E)/){'==' +  $1 + '=='}}"
work = ABCDEFG
work.gsub(/(C.*E)/){'==' +  $1 + '=='} = AB==CDE==FG

gsub文(文字列置換)(3)

#
work = "%41%42%43+%44%45%46"
puts "work(URL encoded) = #{work}"
work = work.tr("+", " ")
work = work.gsub(/%([0-9a-zA-Z][0-9a-zA-Z])/){$1.hex.chr}
puts "work(URL decoded) = #{work}"
work(URL encoded) = %41%42%43+%44%45%46
work(URL decoded) = ABC DEF

gsub文に関して追記 2009.04.07

(a, b) = "aaa=".split("=")
b.gsub("bbb", "")

上記を実行すると、

./test.rb:?: private method `gsub' called for nil:NilClass (NoMethodError)

のようなエラーが発生する。bが空文字列なのがいけないらしい。これを回避するには、

(a, b) = "aaa=".split("=")
b.to_s.gsub("bbb", "")

の様に明示的にbを文字列クラスに変換してやるとよい。

split文(文字列分割)

#
work = "apple-=-lemon-=-melon-=-orange"
puts "work = #{work}"
work2 = work.split('-=-')
puts "work2 = work.split('-=-')"
puts "work2[0] = #{work2[0]}"
puts "work2[1] = #{work2[1]}"
puts "work2[2] = #{work2[2]}"
puts "work2[3] = #{work2[3]}"
work = apple-=-lemon-=-melon-=-orange
work2 = work.split('-=-')
work2[0] = apple
work2[1] = lemon
work2[2] = melon
work2[3] = orange

split文(文字列分割:分割制限有り)

#
work = "apple-=-lemon-=-melon-=-orange"
puts "work = #{work}"
(var1, var2, var3)  = work.split("-=-", 3)
puts "(var1, var2, var3)  = work.split('-=-', 3)"
puts "var1 = #{var1}"
puts "var2 = #{var2}"
puts "var3 = #{var3}"
work = apple-=-lemon-=-melon-=-orange
(var1, var2, var3)  = work.split('-=-', 3)
var1 = apple
var2 = lemon
var3 = melon-=-orange

length, index, rindex文(文字列長、位置取得)

#
work = "apple apple apple"
puts "work = #{work}"
puts "work.length = #{work.length}"
puts "work.index('apple') = #{work.index('apple')}"
puts "work.rindex('apple') = #{work.rindex('apple')}"
work = apple apple apple
work.length = 17
work.index('apple') = 0
work.rindex('apple') = 12

to_?文(整数化、実数化、文字列化)

#
works = "123"
puts "works = #{works}(is string)"
puts "works.to_i * 10 = #{works.to_i * 10}"
puts "works.to_f * 10 = #{works.to_f * 10}"
puts "works.to_f.to_s + works.to_f.to_s = #{works.to_f.to_s + works.to_f.to_s}"
works = 123(is string)
works.to_i * 10 = 1230
works.to_f * 10 = 1230.0
works.to_f.to_s + works.to_f.to_s = 123.0123.0

ファイル(書き込み:puts)

#
f = open("junk.txt", "w")
f.puts("line1")
f.puts("line2\n")
f.puts("line3\n\n")
f.close

ファイル(書き込み:write)

#
f = open("junk2.txt", "w")
f.write("line1")
f.write("line2\n")
f.write("line3\n\n")
f.close

ファイル(行読み込み:gets from puts)

#
f = open("junk.txt", "r")
while (r = f.gets)
       p r
       p r.chomp
       r.chomp!
       p r
end
f.close
"line1\n"
"line1"
"line1"
"line2\n"
"line2"
"line2"
"line3\n"
"line3"
"line3"
"\n"
""
""

ファイル(行読み込み:gets from write)

#

f = open("junk2.txt", "r")
while (r = f.gets)
       p r
       p r.chomp
       r.chomp!
       p r
end
f.close
"line1line2\n"
"line1line2"
"line1line2"
"line3\n"
"line3"
"line3"
"\n"
""
""

ファイル(ランダム読み:read)

#
f = open("junk.txt", "r")
#f.seek(6)
f.pos = 6
r = f.read(12)
p r
f.close
"line2\nline3\n"

ファイル(書き込み:putc)

#
work = "line1\nline2\nline3\n"
f = open("junk.txt", "w")
for i in 0..(work.length-1)
       f.putc(work[i])
end
f.close

ファイル(行読み込み:getc from putc)

#
f = open("junk.txt", "r")
while (ch = f.getc)
       print " #{ch.chr}"
end
f.close
 l i n e 1
 l i n e 2
 l i n e 3

【追記】標準入力、標準出力、エラー出力

標準入力、標準出力、エラー出力に対しては、それぞれSTDIN、STDOUT、STDERRが自動的にオープンされているので、いきなり

STDOUT.puts("ほげほげ")

とか、

r = STDIN.gets

みたいに書ける。

ファイル(外部コマンド実行)

#
f = open("|ls -al|head -5", "r")
while (r = f.gets)
       puts "==" + r.chomp + "=="
end
f.close
==合計 40==
==drwxr-xr-x 2 m-ito m-ito  4096 2009-03-13 19:45 .==
==drwxr-xr-x 3 m-ito m-ito  4096 2009-03-13 18:24 ..==
==-rw-r--r-- 1 m-ito m-ito  2896 2009-03-13 19:45 junk==
==-rw-r--r-- 1 m-ito m-ito    18 2009-03-13 19:45 junk.txt==

外部コマンド実行

#
for i in `ls -al|head -5`
       puts "==" + i.chomp + "=="
end
==合計 40==
==drwxr-xr-x 2 m-ito m-ito  4096 2009-03-13 19:45 .==
==drwxr-xr-x 3 m-ito m-ito  4096 2009-03-13 18:24 ..==
==-rw-r--r-- 1 m-ito m-ito  2896 2009-03-13 19:45 junk==
==-rw-r--r-- 1 m-ito m-ito    18 2009-03-13 19:45 junk.txt==

追記(2011/07/04) : ruby-1.9.xだと(Stringクラスからeachメソッドが削除されたので?)上記の書き方はできない。以下のようにする。

#
`ls -al|head -5`.each_line do |i|
       puts "==" + i.chomp + "=="
end

popen文(外部コマンド実行)

#! /usr/bin/ruby
io = IO.popen("cat -n", "r+")
io.puts("line1")
io.puts("line2")
io.puts("line3")
##io.flush      # flushをするのが確実。
io.close_write  # ただし、起動プログラムの作りによるとclose_writeする必要がある。
r = io.gets
puts r
r = io.gets
puts r
r = io.gets
puts r
io.close
     1  line1
     2  line2
     3  line3

system文(外部コマンド実行)

#
system("ls -al|head -5")
合計 40
drwxr-xr-x 2 m-ito m-ito  4096 2009-03-13 19:45 .
drwxr-xr-x 3 m-ito m-ito  4096 2009-03-13 18:24 ..
-rw-r--r-- 1 m-ito m-ito  3657 2009-03-13 19:45 junk
-rw-r--r-- 1 m-ito m-ito    18 2009-03-13 19:45 junk.txt

print文各種

#
work = "ABCDEFG"
p work
puts work
print work + "\n"
printf("%s %d %f\n", work, 123, 123.456)
"ABCDEFG"
ABCDEFG
ABCDEFG
ABCDEFG 123 123.456000

例外処理

全てのエラーをトラップする場合
begin
    エラーを検出したい処理
rescue => e
    エラー検出時のトラップ処理
    put e
else
    エラー未検出時の処理
ensure
    エラーの検出のいかんにかかわらず行う処理
end
特定のエラーをトラップする場合
begin
    エラーを検出したい処理
rescue Errno::EEXIST => e
    エラー検出時のトラップ処理
    put e
else
    エラー未検出時の処理
ensure
    エラーの検出のいかんにかかわらず行う処理
end

「=>e」は省略可能。

ファイル操作

#
File.umask(022)
File.chmod(0777, "junk.txt")
File.rename("junk.txt", "junk3.txt")
File.unlink("junk2.txt")
File.unlink("junk3.txt")

mkdir, rmdir(ディレクトリ操作)

#
retry_max = 3
retry_cnt = 0
begin
       Dir.mkdir("lock.dir", 0755)
rescue Errno::EEXIST
       puts "lock NG"
       retry_cnt += 1
       if (retry_cnt < retry_max)
               retry
       else
               puts "lock give up!"
       end
else
       puts "lock OK"
ensure
       Dir.rmdir("lock.dir")
end
lock OK

関数定義

#
def myfunc(v1="defval1", v2="defval2")
       local_work = "initialized in myfunc"

       puts "$Global_work = #{$Global_work}"
       puts v1
       puts v2
       return "ok"
end

$Global_work = "initialized in main"
local_work = "initialized in main"

puts myfunc()
puts myfunc("poipoi", "hogehoge")

puts "local_work = #{local_work}"
$Global_work = initialized in main
defval1
defval2
ok
$Global_work = initialized in main
poipoi
hogehoge
ok
local_work = initialized in main

クラス定義

#
class Enzan
       attr_accessor :x, :y
       attr_reader :x_readonly, :y_readonly
       attr_writer :x_writeonly, :y_writeonly

       def initialize(i=100, j=200)
               @x = i
               @y = j
       end

       def tasu
               @@global_in_class_work = @x + @y
               return @x + @y
       end

       def hiku
               @@global_in_class_work = @x - @y
               return @x - @y
       end

       def compute(i)
               case i
               when "+"
                       @@global_in_class_work = @x + @y
                       return @x + @y
               when "-"
                       @@global_in_class_work = @x - @y
                       return @x - @y
               else
                       return 0
               end
       end

       def global_in_class_work
               return @@global_in_class_work
       end
end

e = Enzan.new
puts e.tasu
puts e.hiku
puts e.compute("+")

f = Enzan.new(10, 20)
puts f.tasu
puts f.hiku
puts f.compute("+")

f.x = 1000.0
f.y = 2000.0
puts f.tasu
puts f.hiku
puts f.compute("-")

puts e.global_in_class_work
300
-100
300
30
-10
30
3000.0
-1000.0
-1000.0
-1000.0

クラス定義(継承有り)

#
class Oya
       def msg1(arg = "Oya.msg1")
               puts arg + " from Oya.msg1"
       end
       def msg2(arg = "Oya.msg2")
               puts arg + " from Oya.msg2"
       end
end

class Kodomo < Oya
       def msg2(arg = "Kodomo.msg2")
               super(arg)
               puts arg + " from Kodomo.msg2"
       end
       def msg3(arg = "Kodomo.msg3")
               puts arg + " from Kodomo.msg3"
       end
end

msg = Kodomo.new
msg.msg1("message 1")
msg.msg2("message 2")
msg.msg3("message 3")
message 1 from Oya.msg1
message 2 from Oya.msg2
message 2 from Kodomo.msg2
message 3 from Kodomo.msg3

環境変数取得

#
puts ENV['PATH']
/home/m-ito/bin:/home/m-ito/etc:/home/m-ito/prov/bin:/usr/local/bin:/usr/local/etc:/usr/local/sbin:/usr/bin:/usr/etc:/bin:/etc:/usr/sbin:/sbin:/usr/X11R6/bin:/opt/gnome/bin:/opt/kde/bin:/usr/local/jvim2.0/bin:/usr/local/canna/bin:/usr/local/lib/w3m:/usr/local/ssl/bin:/usr/local/mrtg/bin:/usr/local/Adobe/Reader8/bin

日時時刻取得

#
day = Time.now
puts day
puts day.year
puts day.month
puts day.day
puts day.hour
puts day.min
puts day.sec
puts day.wday
Fri Mar 13 19:45:44 +0900 2009
2009
3
13
19
45
44
5

kill文(シグナル送信)

#
begin
Process.kill("TERM", 1234)
rescue Errno::ESRCH
       puts "no such process"
else
       puts "signal send ok"
ensure
end

my_pid = $$
begin
Process.kill(0, my_pid)
rescue Errno::ESRCH
       puts "pid(#{my_pid}) is dead"
else
       puts "pid(#{my_pid}) is alive"
ensure
end
no such process
pid(3544) is alive

nkf文(文字コード変換)

#
require "nkf"
puts NKF.nkf("-S -X -e", "\x8a\xbf\x8e\x9a\x82\xc5\x82\xb7")
漢字です

timeout文

#
require "timeout"
begin
       timeout(3){
               sleep 10
       }
rescue Timeout::Error
       puts "timeout happened!"
else
       puts "10sec sleepd"
ensure
end
timeout happened!

yield(イテレータ作成)

#
def hoge(arg1, arg2)
       puts "arg1=#{arg1}, arg2=#{arg2}"
       yield(arg1, arg2, arg1 * arg2)
end

hoge(12,13) do |v1, v2, v3|
       puts "v1=#{v1}, v2=#{v2}, v3=#{v3}"
end
arg1=12, arg2=13
v1=12, v2=13, v3=156

_ ruby(1.8.x)儂(わし)的解釈によるメモ(クライアント編)

#! /usr/bin/ruby
require "socket"

s = TCPSocket.new("localhost", 12345)
s.sync = true
s.puts "#{ARGV[0]}"
print s.gets
s.puts "Q"

_ ruby(1.8.x)儂(わし)的解釈によるメモ(サーバ編)

#! /usr/bin/ruby
require 'socket'

def daemon
#
# バックグラウンドに移行する
# プロセスグループリーダ(、セッションリーダ)でなくなる -> 次のsetsidが成功する条件
# 制御端末はまだ持っている
       if (pid = fork)
               exit 0
       end
#
# 新しいセッションのリーダとなり制御端末を持たなくなる
# 新しいプロセスグループのリーダとなる
       Process.setsid
#
# セッションリーダ、プロセスグループリーダでなくなる
# よって、再び制御端末を持つ事ができなくなる
       if (pid = fork)
               exit 0
       end
#
       Dir.chdir("/")
       File.umask(0000)
       STDIN.close
       STDOUT.close
       STDERR.close

       Signal.trap("SIGTERM"){
               exit 0
       }
       Signal.trap("SIGHUP"){
               exit 0
       }
       Signal.trap("SIGINT"){
               exit 0
       }
end

daemon

port = 12345

gs = TCPServer.open(port)
while true
  ns = gs.accept
#  p ns.peeraddr
  Thread.start(ns) do |s|
    s.sync = true
    while l = s.gets
      break if /^q/i =~ l
      l.chomp!
      s.puts "#{l.swapcase}"
    end
    s.close
  end
end

_ ruby(1.8.x)儂(わし)的解釈によるメモ(基本編の追加編)

times文

3.times do |i|
	puts i
end
0
1
2

step文

1.step(10, 3) do |i|
	puts i
end
1
4
7
10

loop文

i = 0
loop do
	puts i
	i += 1
	if (i > 3)
		break
	end
end
0
1
2
3

module文

module Mymodule
	def msg(i)
		puts "#{i} is hogehoge"
	end
	def msg2(i)
		puts "#{i} is poipoi"
	end
	module_function :msg, :msg2
end

class Myclass
	include Mymodule
	def initialize(i)
		@msg = i
	end
	def hoge
		msg(@msg)
	end
	def poi
		msg2(@msg)
	end
end

mc = Myclass.new("hogepoi")
mc.hoge
mc.poi
hogepoi is hogehoge
hogepoi is poipoi

演算式(余り:%, べき乗:**)

puts 12 % 5
puts 2 ** 8
2
256

ビット演算式

a = 0
printf("%d\n", ~a)

a = 0x77
b = 0xee
printf("%x\n", a & b)

a = 0x77
b = 0xee
printf("%x\n", a | b)

a = 0x77
b = 0xee
printf("%x\n", a ^ b)

a = 0x77
printf("%x\n", a << 1)

a = 0xee
printf("%x\n", a >> 1)
-1
66
ff
99
ee
77

条件文(結合)

a = 10
b = 20
if (a < 15 && b > 15)
	puts "true"
else
	puts "false"
end

if (a < 15 || b < 15)
	puts "true"
else
	puts "false"
end
true
true

配列操作

a = ["w", "x", "y", "z"]
a[1, 2] = ["a", "b"]
p a
["w", "a", "b", "z"]
a = ["w", "x", "y", "z"]
a[1, 0] = ["a", "b"]
p a
["w", "a", "b", "x", "y", "z"]
a = ["w", "x", "y", "z"]
a.push("a")
p a
["w", "x", "y", "z", "a"]
p a.pop
p a
"a"
["w", "x", "y", "z"]
p a.shift
p a
"w"
["x", "y", "z"]
a = ["a", "x", "b", "x", "c"]
a.delete("x")
p a
["a", "b", "c"]
a = ["a", "x", "b", "x", "c"]
a.delete_at(2)
p a
["a", "x", "x", "c"]
a = ["a", "x", "b", "x", "c"]
p a.uniq
["a", "x", "b", "c"]
p "aaa" <=> "bbb"
p "bbb" <=> "aaa"
p "aaa" <=> "aaa"

p a.sort do |a, b|
	a <=> b
end
-1
1
0
["a", "b", "c", "x", "x"]

文字列の囲み

s = "aaaaa"
puts %Q{" ' #{s} ' "}
puts %q{" ' #{s} ' "}
" ' aaaaa ' "
" ' #{s} ' "

ヒアドキュメント

s = <<EOD
this is line1
  this is line2
    this is line3
EOD
puts s
this is line1
  this is line2
    this is line3

文字列操作

a = "   ABcdefgabcdeFG   "
puts a.delete("cde")
puts a.strip
puts a.upcase
puts a.downcase
puts a.swapcase
   ABfgabFG
ABcdefgabcdeFG
   ABCDEFGABCDEFG
   abcdefgabcdefg
   abCDEFGABCDEfg

ハッシュ操作

h = {"apple"=>"red", "lemon"=>"yellow", "melon"=>"green"}
p h.has_key?("lemon")
p h.has_value?("yellow")
p h.empty?
true
true
false

ハッシュ初期化

h = {"apple"=>"red", "lemon"=>"yellow", "melon"=>"green"}
i = h
h.clear
p h
p i
{}
{}
h = {"apple"=>"red", "lemon"=>"yellow", "melon"=>"green"}
i = h
h = {}
p h
p i
{}
{"melon"=>"green", "apple"=>"red", "lemon"=>"yellow"}

正規表現

a = "abcde\nfghijk\nlmnop"
p /^f.*k$/ =~ a
p /\Af.*k\Z/ =~ a
p /^a.*p$/ =~ a
p /\Aa.*p\Z/m =~ a
6
nil
nil
0

繰り返し(文字列scan)

a = "axxxb...ayyyb...azzzb"
a.scan(/a...b/) do |i|
	puts i
end
axxxb
ayyyb
azzzb

同期出力

STDOUT.puts "aaa"
STDOUT.flush

STDOUT.sync = true
STDOUT.puts "bbb"
aaa
bbb

ディレクトリ操作

puts cd = Dir.pwd
Dir.chdir("/tmp")
puts Dir.pwd
Dir.chdir(cd)
puts Dir.pwd
/home/zaurus/tmp
/tmp
/home/zaurus/tmp

ディレクトリ操作(2)

dir = Dir.open("/")
while (name = dir.read)
	puts name
end
dir.close
.
..
bin
dev
etc
lib
mnt
opt
tmp
var
usr
boot
home
proc
sbin
root

ファイル属性取得

printf("%o\n", File.stat("/etc/fstab").mode)
puts File.stat("/etc/fstab").uid
puts File.stat("/etc/fstab").gid
puts File.stat("/etc/fstab").size
puts File.stat("/etc/fstab").atime
puts File.stat("/etc/fstab").mtime
puts File.stat("/etc/fstab").ctime
100644
0
0
222
Sun Nov 11 23:30:54 +0900 2007
Sun Nov 11 23:30:54 +0900 2007
Sun Nov 11 23:30:54 +0900 2007

ファイル属性変更

f = open("junk.txt", "w")
f.close
puts atime = File.stat("junk.txt").atime
puts mtime = File.stat("junk.txt").mtime
File.utime(atime + 60, mtime + 60, "junk.txt")
puts atime = File.stat("junk.txt").atime
puts mtime = File.stat("junk.txt").mtime
File.unlink("junk.txt")
Sat Mar 14 18:53:35 +0900 2009
Sat Mar 14 18:53:35 +0900 2009
Sat Mar 14 18:54:35 +0900 2009
Sat Mar 14 18:54:35 +0900 2009

ファイル属性変更(2)

#f = open("junk.txt", "w")
#f.close
#File.chown(0, 0, "junk.txt")
#File.unlink("junk.txt")

ファイルテスト

puts File.exist?("/etc/fstab")
puts File.file?("/etc/fstab")
puts File.directory?("/etc/fstab")
puts File.readable?("/etc/fstab")
puts File.writable?("/etc/fstab")
puts File.executable?("/etc/fstab")
puts File.zero?("/etc/fstab")
puts File.size("/etc/fstab")
true
true
false
true
false
false
false
222

ファイル名操作

puts File.basename("/usr/local/doc/ruby.doc", ".doc")
puts File.dirname("/usr/local/doc/ruby.doc")
puts File.expand_path("./junk.txt")
ruby
/usr/local/doc
/home/root/usr/local/home/zaurus/tmp/junk.txt

eval文

#! /usr/bin/ruby

def func_print(str)
        print str
end

eval_str = ARGV[0] + '("hello world\n")'
eval eval_str
$ ./evaltest.rb func_print
hello world

取り扱いに注意しないと、いろんな物を注入(Injection)されちゃう。

_ ruby(1.8.x)儂(わし)的解釈によるメモ(CGI編)

CGIサンプル(ソースは以下を参照)

#! /usr/bin/ruby
#
# 産まれて初めてのrubyによるCGIプログラム...
#
require "cgi"
require "nkf"

cgi = CGI.new("html3")

in1 = NKF.nkf("-X -e", cgi['in1'])

cgi.out({"type" => "text/html",
               "charset" => "euc-jp"}){
       cgi.html("PRETTY" => true){
               cgi.head(){
                       cgi.title(){"rubytest1"}
               }
               cgi.body(){
                       cgi.div({"align" => "center"}){
                               "rubytest1"
                       } +
                       CGI.escapeHTML("Input is #{in1}") +
                       cgi.br() +
                       cgi.form({"action" => "rubytest1.cgi",
                                       "method" => "post"}){
                               cgi.input({"type" => "text",
                                               "name" => "in1",
                                               "id" => "in1"}) +
                               cgi.input({"type" => "submit",
                                               "value" => "ok"})
                       }
               }
       }
}
exit 0

_ ruby(1.8.x)儂(わし)的解釈によるメモ(未検証)

pack, unpack

arry = [0x41, 0x42, 0x43] # A,B,C
buf = arry.pack("C3")
p buf
buf = sock.read(4)
arry = buf.unpack("N") # Network byte order Integer(BIG ENDIAN)
p arry[0]

2009年03月29日 ClamAV 0.95 released [長年日記]

_ ClamAV 0.95 released

UNIX系OS汎用のアンチウイルスソフト ClamAVのエンジンがVersion 0.95にアップ。


2009年03月31日 Ajax儂(わし)的解釈によるメモ [長年日記]

_ Ajax儂(わし)的解釈によるメモ

ウェブアプリを作る時はw3mでも支障無く動くことをモットーとしている。それゆえWeb2.0だとかAjaxというようなキーワードがもてはやされる時代になっても、かたくなに目を背けてやってきた。しかし、他人に「何故お前は(Web2.0||Ajax)を(やらん||できん)のだ」と問われた時に、明解に理由を説明できるだけの知識は持ち合わせていなかった。

ここいらで少しWeb2.0だとかAjaxという世界に寄り道してみようと思う。そう「毒をもって毒を制す」という言葉も在るではないか...。

※ 用語等の利用に関しては自分なりの解釈に元づいたものなので、あまり当てにしないでもらいたい...

とりあえずAsynchronousとJavascript

Ajax = Asynchronous JavaScript + XMLという事らしいのだが、XMLは後回しにして、とりあえずAsynchronousとJavaScriptの部分を探っていきたい。

通常のウェブアプリではユーザからのデータ入力は<form>タグを介して行われ、[submit]ボタンをクリックする事でサーバ側(CGIプログラム)に入力データが渡され、サーバ側で何らかの処理が行われた後に、その結果をhtmlドキュメント形式で表現しブラウザ側に返してやる。サーバ側で処理が行われている間はブラウザ側では一切の作業ができない(旗がゆらめいたり、何かがクルクル回ったりしているだけ)。

JavascriptからXMLHttpRequest()等のオブジェクトを作成して、これを利用すると非同期通信が可能になる。つまりブラウザからサーバへリクエストを送信した後、ブラウザはサーバからのレスポンスを待つこと無くユーザの次の入力要求に答える事ができる。それではサーバのレスポンスは何処へ行ってしまうのか?。

サーバからのレスポンスは「イベント」としてブラウザに通知される。せっかちなユーザの入力を受け付ける事に専念している最中に肩を叩かれる(イベントの到着)ので、その時だけデータを受け取りに行けば良い。

非同期通信にてサーバから返されるデータはhtmlドキュメントである必要はない。もし、たった1つの項目が欲しければ、たった1つの項目だけを返してもらえば良い。

だが待て、ブラウザはhtmlドキュメント(<html>〜</html>)を表示するものではなかったか。全体のリロード無しにどうやって表示を変えるのだ?。

さて、ブラウザ内でhtmlドキュメントはDOM(Document Object Model)という形式で保持されている。タグやら文字列の入れ子構造がそのままツリー構造として存在していると考えればあながち間違いでもなかろう。ツリーの葉っぱの部分は個々のタグや文字列ということか。

JavascriptからはDOMという木の個々の葉っぱ(だけ)を書き換える事ができる。この機能のおかげで、レスポンス到着イベントのタイミングでブラウザの表示の一部分だけを(全体のリロード無しで)更新する事ができる。

以上の事が、Ajax(のXML抜きの)本質部分なのではないかと思う。

Ajax対応Javascript(htmlファイル)サンプル

http://myh.no-ip.org/~m-ito/ajaxtest1.html

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=euc-jp">
<title>ajaxtest1</title>

<script type="text/javascript">
<!--
// ============================================================
//   私製Ajax関連ルーチン(ここから)
//
//     後々、別ファイルに独立させて共通ルーチンとして利用するつもり
//
// ============================================================
//
// 非同期通信オブジェクト作成
//
	function ajax_createXHR(){
		var xhr;
		try{
			xhr = new ActiveXObject("Msxml2.XMLHTTP");
		}catch(e){
			try{
				xhr = new ActiveXObject("Microsoft.XMLHTTP");
			}catch(e){
				try{
					xhr = new XMLHttpRequest();
				}catch(e){
					xhr = null;
				}
			}
		}
		return xhr;
	}

Ajaxの肝となる非同期通信オブジェクトの生成方法はブラウザ依存、バージョン依存が激しい。肝なのに。早速に不審感がつのる。とりあえず3種類の方法を試していって、成功すればそのオブジェクトを返すといった感じ。

//
// idで指定したエレメントを取得する
//
	function ajax_gebi(id){
		return document.getElementById(id);
	}
//
// tagで指定したエレメントの内、idx番目(0オリジン)の物を取得する
//   ただし、idx < 0の場合はgetElementsByTagName(tag)を取得する
//
	function ajax_gebt(tag, idx){
		if (idx < 0){
			return document.getElementsByTagName(tag);
		}else{
			return document.getElementsByTagName(tag).item(idx);
		}
	}
//
// name指定したエレメントの内、idx番目(0オリジン)の物を取得する
//   ただし、idx < 0の場合はgetElementsByName(name)を取得する
//
	function ajax_gebn(name, idx){
		if (idx < 0){
			return document.getElementsByName(name);
		}else{
			return document.getElementsByName(name).item(idx);
		}
	}
//
// idで指定したエレメント配下の子エレメントを全て削除
//
	function ajax_removeChilds(id){
		var max = document.getElementById(id).childNodes.length;
		for (var i = 0; i < max; i++){
			document.getElementById(id).removeChild(document.getElementById(id).childNodes.item(0));
		}
	}
//
// idで指定したエレメント配下の子エレメントをstrで指定したテキストエレメントで置換
//
//   o 単純に文字列表示として利用することを想定している
//   o idで指定するエレメントは<span>であることを想定している
//
	function ajax_setText(id, str){
		ajax_removeChilds(id);
		document.getElementById(id).appendChild(document.createTextNode(str));
	}
//
// idで指定したエレメント配下のテキストエレメントの文字列を取得
//
	function ajax_getText(id){
		var text = "";
		for (var i = 0; i < document.getElementById(id).childNodes.length; i++){
			if (document.getElementById(id).childNodes.item(i).nodeValue){
				text += document.getElementById(id).childNodes.item(i).nodeValue;
			}
		}
		return text;
	}
//
// idで指定したエレメントの値にstrで指定した文字列をセットする
//
//   o idで指定するエレメントは<input>等のvalueを持つものであることを想定している
//
	function ajax_setValue(id, str){
		document.getElementById(id).value = str;
	}
//
// idで指定したエレメントの値を取得する
//
//   o idで指定するエレメントは<input>等のvalueを持つものであることを想定している
//
	function ajax_getValue(id){
		return 	document.getElementById(id).value;
	}

とりあえずJavascriptのプログラミングは記述が長い。まぁオブジェクト、メソッド、プロパティ...等々をドットで繋げて行くスタイルをとるオブジェクト指向プログラミング言語全般に言える事かもしれないが。手が疲れるので適当なラッパールーチンを用意する。

//
// idで指定したエレメントの値を送信できる形式に変換する
//
//   o idで指定するエレメントは<input>等のvalueを持つものであることを想定している
//
	function ajax_encodedValue(id){
		return encodeURIComponent(document.getElementById(id).value);
	}
//
// idで指定したエレメントの値を通常の文字列に戻す
//
//   o idで指定するエレメントは<input>等のvalueを持つものであることを想定している
//
	function ajax_decodedValue(id){
		return decodeURIComponent(document.getElementById(id).value);
	}
//
// strで指定した文字列を送信できる形式に変換する
//
	function ajax_encodedString(str){
		return encodeURIComponent(str);
	}
//
// strで指定した文字列を通常の文字列に戻す
//
	function ajax_decodedString(str){
		return decodeURIComponent(str);
	}

データはエンコードして送信する。エンコード処理にも色々あるがencodeURIComponentdecodeURIComponentの具合いが良さそう。漢字データも`?', `&', `=', `\n'等を含んだデータのやりとりもOK。また、エンコードした際に文字コードはContent-type,charsetの指定とは無関係にUTF-8に変換される。つまり、非同期通信オブジェクトを介した通信ではUTF-8が強制されるらしい。

//
// CGIからのレスポンスをハッシュに取り込む
//
//  o レスポンス形式 : 項目名=値\t項目名=値\t ... \t項目名=値
//
	function ajax_getResponse(xhr){
		var ary = xhr.responseText.split("\t");
		var kv = new Array();
		var res = new Array();
		var i = 0;
		while (i < ary.length){
			kv = ary[i].split("=");
			res[kv[0]] = decodeURIComponent(kv[1]);
			i++;
		}
		return res;
	}

サーバ(CGI)からのレスポンスの形式にはXMLJSONといった形式が使われる事もあるが、とりあえずシンプルな(使い慣れた)テキスト形式(項目名=値\t項目名=値\t ... \t項目名=値)を使う。項目名がハッシュのインデックスとなる配列を返す。

//
// 受信完了状態取得
//
	function ajax_getRecvStatus(xhr){
		if (xhr.readyState == 4){
			if (xhr.status == 200){
				return "OK";
			}else{
				return "NG";
			}
		}else{
			return "NOTREADY";
		}
	}

レスポンス受信イベント(正確には状態変化のイベント)発生の状態を取得する。`OK'(正常の受信完了), `NG'(受信失敗), `NOTREADY'(受信中)の3つのステータスを返す。

//
// リクエスト送信(および受信ハンドラ設定)
//
	function ajax_sendRequest(uri, msghash, async, callbackOK, callbackNG, callbackNR){
		var xhr = ajax_createXHR();
		xhr.onreadystatechange = function(){
			var status = ajax_getRecvStatus(xhr);
			if (status == "OK"){
				if (!(callbackOK == null || callbackOK == "")){
					callbackOK(xhr);
				}
			}else if (status == "NG"){
				if (!(callbackNG == null || callbackNG == "")){
					callbackNG(xhr);
				}
			}else{
				if (!(callbackNR == null || callbackNR == "")){
					callbackNR(xhr);
				}
			}
		}
		var msg = "";
		i = 0;
		for (i in msghash){
			if (i == 0){
				msg = i + '=' + ajax_encodedString(msghash[i]);
			}else{
				msg = msg + '&' + i + '=' + ajax_encodedString(msghash[i]);
			}
			i++;
		}
		xhr.open("POST", uri + "?dummy=" + new Date().getTime(), async);
		xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		xhr.send(msg);
		return xhr;
	}

(ついに)リクエストを送信すると同時に受信時のコールバックを登録する。

  • uriはCGIへのパス
  • msgはCGIパラメータとして標準的な形式(項目名=値&項目名=値& ... &項目名=値)
  • asyncは非同期通信フラグ(true | false)
  • callbackOKはステータス`OK'時の処理関数
  • callbackNGはステータス`NG'時の処理関数
  • callbackNRはステータス`NOTREADY'時の処理関数

uri に "?dummy=" + new Date().getTime()を追加してopenしているのはキャッシュの影響でデータ更新が正常に機能しない場合の対応の為。

//
// テキスト入力エレメント内のキャレット位置を取得する
//
	function ajax_getCaretPos(elm){
		var pos;

		if (elm.selectionStart >= 0){
			elm.selectionEnd = elm.selectionStart;
			pos = elm.selectionStart;
		}else if (document.selection.createRange){
//
// http://d.hatena.ne.jp/language_and_engineering/20090225/p1
//
			elm.focus();	// for Opera
// ボックスの先頭からキャレットまでのrangeを作って,長さを調査
			var range = document.selection.createRange();
			range.moveStart( "character", -elm.value.length );
			pos = range.text.length;
		}

		return pos;
	}

ウェブアプリというとユーザインターフェースが貧弱と思われがち。ブラウザと<form>タグの組合せにまかせっきりになりがちなので。Javascript + DOMを活用するとかなり使い勝手をあげる事ができる。が、やはりブラウザ依存な部分が多い。

//
// [↑][↓][←][→]のイベントにより入力フォーカスを移動させる
//
	function ajax_moveFocus(evt, upId, downId, leftId, rightId){
		var target = evt.target || evt.srcElement;
		if (evt.keyCode == 37){
			if (ajax_getCaretPos(target) == 0){
				document.getElementById(leftId).focus();
				return false;
			}
		}else if (evt.keyCode == 38){
			document.getElementById(upId).focus();
		}else if (evt.keyCode == 39){
			if (ajax_getCaretPos(target) >= target.value.length){
				document.getElementById(rightId).focus();
				return false;
			}
		}else if (evt.keyCode == 40){
			document.getElementById(downId).focus();
		}
		return true;
	}

汎用機(メインフレーム)的なオンライン画面と比べるとウェブアプリって、入力項目の移動がしにくいと感じる。カーソルキーで上下左右に自由に入力項目間の移動ができないとストレスがたまる(ような気がする)。と言いながらも他人にウェブアプリの操作を説明する時には「ウェブアプリとはそういうもんです」と説明してきた自分がいるわけだが、やれば出来るもんだねぇ。ちょっぴりJavascriptが素敵に思えた。

// ============================================================
//   私製Ajax関連ルーチン(ここまで)
// ============================================================
//
//

ここまでのルーチンは別ファイルに保存して、汎用ルーチンとして利用するのが良いかと思う。まぁ、そういう汎用ルーチンは既にいっぱい存在するわけだけれども、どれをとっても自分にはオーバースペックであり、かつ全体を把握しきれないものを使う気にはなれない。

//
// ============================================================
//   ユーザコーディング(ここから)
// ============================================================
// ------------------------------------------------------------
//   非同期リクエストによるテキスト電文通信
// ------------------------------------------------------------
//
//  送信イベントのコールバック
//
//    o 入力データ等から送信メッセージを組み立てる。
//    o ajax_sendRequest()を発行する。
//
	var Xhr_send_msg = "";
	function send_msg(){
		var uri = "cgi-bin/ajaxtest1.cgi";
		var msg = {'req':'req1',
				'tx1':ajax_getValue("tx1"),
				'in1':ajax_getValue("in1"),
				'in2':ajax_getValue("in2")
		}
		var async = true;
		var callbackOK = recv_ok;
		var callbackNG = recv_ng;
		var callbackNR = null;	// 受信が完了してなかった場合のコールバック
		Xhr_send_msg = ajax_sendRequest(uri, msg, async, callbackOK, callbackNG, callbackNR);
	}
//
// OK受信イベントのコールバック
//
//   o 引数にメッセージリクエストオブジェクト(xhr)を指定する。
//   o ajax_getResponse()でメッセージデータを取得する。
//   o メッセージデータを処理する。
//
	function recv_ok(xhr){
		res = ajax_getResponse(xhr);
		ajax_setValue("res_tx1", res["res_tx1"]);	// textareaタグに表示
		ajax_setText("res_in1", res["res_in1"]);	// テキストの表示
		ajax_setText("res_in2", res["res_in2"]);	// テキストの表示
		ajax_gebi("res_in1_font").color = "red";		// タグのパラメータ設定
		ajax_gebt("font", 1).color = "blue";		// タグのパラメータ設定
	}
//
// NG受信イベントのコールバック
//
//   o 引数にメッセージリクエストオブジェクト(xhr)を指定する。
//   o 受信に失敗した場合の処理をする。
//
	function recv_ng(xhr){
		ajax_setText("res1", "Error1");
		ajax_setText("res2", "Error2");
		ajax_setValue("res3", "Error3");
	}

ここまでは非同期通信による送受信のサンプル。

// ------------------------------------------------------------
//   インターバルリクエストによる定期的な表示更新
// ------------------------------------------------------------
//
// インターバル・送信イベントのコールバック
//
	var Xhr_send_psmsg = "";
	var tid = setInterval("send_psmsg()",5000);
	function send_psmsg(){
		var uri = "cgi-bin/ajaxtest1.cgi";
		var msg = {'req':'req2',
				'option':'x'
		}
		var async = true;
		var callbackOK = recv_psok;
		var callbackNG = recv_psng;
		var callbackNR = null;
		Xhr_send_psmsg = ajax_sendRequest(uri, msg, async, callbackOK, callbackNG, callbackNR);
	}
//
// インターバル・OK受信イベントのコールバック
//
	function recv_psok(xhr){
		res = ajax_getResponse(xhr);
		ajax_setText("ps", res["ps"]);
	}
//
// インターバル・NG受信イベントのコールバック
//
	function recv_psng(xhr){
		ajax_setText("ps", "Error5");
	}

ここまでは、インターバルリクエストを利用して、定期的に非同期通信を行うサンプル

// ------------------------------------------------------------
//   キー入力イベントごとのKeyCodeを表示 etc ...
//      Ajaxではないな...javascriptのサンプル
// ------------------------------------------------------------
//
// 入力キーコードを表示
//
	function keyget(evt, displayId){
		ajax_setText(displayId, "KeyCode=" + evt.keyCode);
	}
// ------------------------------------------------------------
//   ラジオボタンの値取得
// ------------------------------------------------------------
	function get_radio(){
		for (var i = 0; i < ajax_gebn("rd1", -1).length; i++){
			if (ajax_gebn("rd1", i).checked){
				ajax_setText("rd", ajax_gebn("rd1", i).value);
				break;
			}
		}
	}
// ------------------------------------------------------------
//   チェックボックスの値取得
// ------------------------------------------------------------
	function get_check(){
		var checked_value = "";
		if (ajax_gebi("ck1").checked){
			checked_value += ajax_gebi("ck1").value;
		}
		if (ajax_gebi("ck2").checked){
			if (checked_value != ""){
				checked_value += "+";
			}
			checked_value += ajax_gebi("ck2").value;
		}
		if (ajax_gebi("ck3").checked){
			if (checked_value != ""){
				checked_value += "+";
			}
			checked_value += ajax_gebi("ck3").value;
		}
		ajax_setText("ck", checked_value);
	}
// ------------------------------------------------------------
//   セレクトボックスの値取得
// ------------------------------------------------------------
	function get_select(){
		ajax_setText("sl", ajax_gebi("sl1").options.item(ajax_gebi("sl1").selectedIndex).value);
	}

各種入力エレメントからの値取得のサンプル。

// ------------------------------------------------------------
//   表示/非表示の制御
// ------------------------------------------------------------
	function toggle_display(){
		if (ajax_gebi("div_display").style.display == "none"){
			ajax_gebi("div_display").style.display = "";
		}else{
			ajax_gebi("div_display").style.display = "none";
		}
	}
// ============================================================
//   ユーザコーディング(ここまで)
// ============================================================
// -->
</script>

スタイルの制御サンプル。

</head>
<body>
<div align="center">
<h1>
ajaxtest1
</h1>
</div>

<hr /> <!-- ------------------------------------------------------------ -->

<h2>
非同期リクエストによるテキスト電文通信
</h2>
+ちょっとだけDOMの操作(なんせ、これがうまれて初めてのAjaxコーディング...)
<div>
tx1:<textarea id="tx1"></textarea>
</div>
<div>
in1:<input type="text" id="in1" name="in1"><br />
in2:<input type="text" id="in2" name="in2" onblur="send_msg()"> ←ここからフォーカスが外れたらメッセージ送信する
</div>
<p></p>
<div>
res_tx1(tx1に対するレスポンス):[<textarea id="res_tx1" name="res_tx1"></textarea>]
</div>
<div>
res_in1(in1に対するレスポンス):[<font id="res_in1_font"><span id="res_in1" name="res_in1"></span></font>]
</div>
<div>
res_in2(in2に対するレスポンス):[<font id="res_in2_font"><span id="res_in2" name="res_in2"></span></font>]
</div>
<hr /> <!-- ------------------------------------------------------------ -->

<h2>
インターバルリクエストによる定期的な表示更新
</h2>
<div>
<pre id="ps">
</pre>
</div>
<div>
<input type="button" onclick="clearInterval(tid);" value="クリックで停止">
</div>
<hr /> <!-- ------------------------------------------------------------ -->

<h2>
ラジオボタンの値取得
</h2>
<div>
<input type="radio" id="rd1-1" name="rd1" value="radio1" checked>選択肢1<br />
<input type="radio" id="rd1-2" name="rd1" value="radio2">選択肢2<br />
<input type="radio" id="rd1-3" name="rd1" value="radio3">選択肢3<br />
<input type="button" value="OK" onclick="get_radio()">
</div>
選択されたのは[<span id="rd"></span>]
<hr /> <!-- ------------------------------------------------------------ -->

<h2>
チェックボックスの値取得
</h2>
<div>
<input type="checkbox" id="ck1" name="ck1" value="check1">選択肢1<br />
<input type="checkbox" id="ck2" name="ck2" value="check2">選択肢2<br />
<input type="checkbox" id="ck3" name="ck3" value="check3">選択肢3<br />
<input type="button" value="OK" onclick="get_check()">
</div>
選択されたのは[<span id="ck"></span>]
<hr /> <!-- ------------------------------------------------------------ -->

<h2>
セレクトボックスの値取得
</h2>
<div>
<select id="sl1">
<option value="none" selected>
<option value="select1">選択肢1
<option value="select2">選択肢2
<option value="select3">選択肢3
</select>
<input type="button" value="OK" onclick="get_select()">
 </div>
選択されたのは[<span id="sl"></span>]
<hr /> <!-- ------------------------------------------------------------ -->

<h2>
表示/非表示の制御
</h2>
<div id="div_display">
出て来たり〜消えたり〜
</div>
<input type="button" value="表示/非表示" onclick="toggle_display()">
<hr /> <!-- ------------------------------------------------------------ -->

<h2>
キー入力イベントごとのKeyCodeを表示
</h2>
<div>
<input type="text" onkeydown="keyget(event, 'kd1');">onKeyDown : <span id="kd1"></span>
</div>
<div>
<input type="text" onkeyup="keyget(event, 'ku1');">onKeyUp : <span id="ku1"></span>
</div>
<div>
<input type="text" onkeypress="keyget(event, 'kp1');">onKeyPress : <span id="kp1"></span>
</div>

<h2>
[↑][↓][←][→]のイベントにより入力フォーカスを移動させる
</h2>
<div>
<input type="text" id="in11" onkeydown="keyget(event, 'kd11');return ajax_moveFocus(event, 'in32', 'in21', 'in12', 'in12');">onKeyDown : <span id="kd11"></span>
<input type="text" id="in12" onkeydown="keyget(event, 'kd12');return ajax_moveFocus(event, 'in32', 'in22', 'in11', 'in11');">onKeyDown : <span id="kd12"></span>
</div>
<div>
<input type="text" id="in21" onkeydown="keyget(event, 'kd21');return ajax_moveFocus(event, 'in11', 'in31', 'in22', 'in22');">onKeyDown : <span id="kd21"></span>
<input type="text" id="in22" onkeydown="keyget(event, 'kd22');return ajax_moveFocus(event, 'in12', 'in32', 'in21', 'in21');">onKeyDown : <span id="kd22"></span>
</div>
<div>
<input type="text" id="in31" onkeydown="keyget(event, 'kd31');return ajax_moveFocus(event, 'in21', 'in11', 'in32', 'in32');">onKeyDown : <span id="kd31"></span>
<input type="text" id="in32" onkeydown="keyget(event, 'kd32');return ajax_moveFocus(event, 'in22', 'in12', 'in31', 'in31');">onKeyDown : <span id="kd32"></span>
</div>

</body>
</html>

ajax_moveFocus()はイベントに対してreturn ajax_moveFocus(...)のようにreturnを付けて指定するのがミソ。こうしないと[←][→]で入力フィールドを移動した時に移動先のフィールドでカーソルが余計に進んでしまう。returnをつけてイベントハンドラを呼び出すとイベントハンドラがfalseを返した場合に、そのイベントの本来の動作をキャンセルできる。

Ajax対応CGIサンプル(ruby)

#! /usr/local/bin/ruby
require "cgi"
require "uri"
require "nkf"
#============================================================
#
# Ajaxサポートクラス(あぁ楽しき哉、車輪の再発明)
#
#============================================================
class Ajax
#------------------------------------------------------------
#
# コンストラクタ
#
# 引数
#
#   nkf_param      : 入力パラメータの文字コード変換指定(for nkf)。省略可。
#   nkf_param_send : 送信パラメータの文字コード変換指定(for nkf)。省略可。
#
# 戻値 : 無し
#
#------------------------------------------------------------
	def initialize(nkf_param = "-X -e", nkf_param_send = "-w")
		@nkf_param = nkf_param
		@nkf_param_send = nkf_param_send
		@cgi = CGI.new("html3")
	end
#------------------------------------------------------------
#
# パラメータ取得
#
# 引数
#
#   name : 項目名称。省略可。
#
# 戻値
#
#   name指定時は、指定した項目の値を返す。
#   name未指定は、全パラメータの[項目名、値]のセットをイテレータで返す。
#
#------------------------------------------------------------
	def get(name = "")
		if (name == "")
			@cgi.each do |key, val|
				yield(key, NKF.nkf(@nkf_param, val))
			end
		else
			return NKF.nkf(@nkf_param, @cgi[name])
		end
	end
#------------------------------------------------------------
#
# レスポンス送信
#
# 引数
#
#   hash	 : 送信データ。ハッシュ形式。
#   boolean_head : HTTPヘッダーを送信(true)/非送信(false)の指定。省略可。
#   head_str     : HTTPヘッダーの内容。省略可。
#
# 戻値 : 無し
#
#------------------------------------------------------------
	def send(hash = {}, boolean_head = true, head_str = "Content-Type: text/html\n\n")
		if (boolean_head)
			print head_str
		end

		i = 0
		hash.each do |key, val|
			print "#{key}="
			print URI.escape(NKF.nkf(@nkf_param_send, val),/[^-_.!~*'()a-zA-Z\d]/n)
			i += 1
			if (i < hash.size)
				print "\t"
			end
		end
	end
end
#============================================================
#
# Main
#
#============================================================
ajax = Ajax.new

if (ajax.get("req") == "req1")
	in1 = ajax.get("in1")
	in2 = ajax.get("in2")
	tx1 = ajax.get("tx1")

	hash = {
		"res_tx1" => "response=(" + tx1 + ")",
		"res_in1" => "response=(" + in1 + ")",
		"res_in2" => "response=(" + in2 + ")"
	}

	ajax.send(hash)
elsif (ajax.get("req") == "req2")
	option = ajax.get("option")

	option.gsub!(/(.)/){'\\' + $1}
	cmdline = "date;ps #{option}"
	result = `#{cmdline}`
	result.gsub!(/$/, "\r")

	hash = {"ps" => result}

	ajax.send(hash)
end

exit 0

rubyにおけるJavascriptのencodeURIComponent()と等価の処理は URI.escape("hogehoge", /[^-_.!~*'()a-zA-Z\d]/n)となる。また、非同期通信オブジェクトに返すメッセージはUTF-8に変換してからエンコードする必要があることに注意。

Javascript + DOMによるhtmlドキュメントの作成サンプル

JavascriptからDOMを操作する事のみでhtmlドキュメントを作制してみる。

http://myh.no-ip.org/~m-ito/ajaxtest2.html

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=euc-jp">
<title>ajaxtest2</title>

<script type="text/javascript">
<!--
// ============================================================
//   ユーザコーディング(ここから)
// ============================================================
	window.onload = function(){
		var max = document.getElementsByTagName("body").item(0).childNodes.length;
		for (var i = 0; i < max; i++){
			document.getElementsByTagName("body").item(0).removeChild(document.getElementsByTagName("body").item(0).childNodes.item(0));
		}
		document.getElementsByTagName("body").item(0).appendChild(document.createElement("div"));
		document.getElementsByTagName("div").item(0).align = "center";
		document.getElementsByTagName("div").item(0).appendChild(document.createElement("h1"));
		document.getElementsByTagName("h1").item(0).appendChild(document.createTextNode("javascript & DOMでウェブページを作る"));
		document.getElementsByTagName("body").item(0).appendChild(document.createElement("h2"));
		document.getElementsByTagName("h2").item(0).appendChild(document.createTextNode("はじめに"));
		document.getElementsByTagName("body").item(0).appendChild(document.createElement("p"));
		document.getElementsByTagName("p").item(0).appendChild(document.createTextNode("javascriptでDOM(Document Object Model)を操作すると動的にページを作ることができます。とはいえ結構面倒くさい作業になります。まぁ普通はこんなことはしないと思われます(^^;。"));
		document.getElementsByTagName("body").item(0).appendChild(document.createElement("h2"));
		document.getElementsByTagName("h2").item(1).appendChild(document.createTextNode("document.getElementsByTagName"));
		document.getElementsByTagName("body").item(0).appendChild(document.createElement("p"));
		document.getElementsByTagName("p").item(1).appendChild(document.createTextNode("document.getElementsByTagName(\"タグ名称\").item(番号)を使ってタグのエレメントを特定します。document.getElementsByTagName(\"タグ名称\").item(番号).appendChild(エレメント)を使ってタグに子どものエレメントを追加していきます。基本的にはこの作業を繰り返してドキュメント構造を組み立てていきます。"));
		document.getElementsByTagName("body").item(0).appendChild(document.createElement("h2"));
		document.getElementsByTagName("h2").item(2).appendChild(document.createTextNode("document.createElement"));
		document.getElementsByTagName("body").item(0).appendChild(document.createElement("p"));
		document.getElementsByTagName("p").item(2).appendChild(document.createTextNode("document.createElement(\"タグ名称\")を使ってタグ(エレメント)を作成します。作成されたエレメントはappendChildの引数に指定でき、ドキュメントの構成要素となります。"));
		document.getElementsByTagName("body").item(0).appendChild(document.createElement("h2"));
		document.getElementsByTagName("h2").item(3).appendChild(document.createTextNode("document.createTextNode"));
		document.getElementsByTagName("body").item(0).appendChild(document.createElement("p"));
		document.getElementsByTagName("p").item(3).appendChild(document.createTextNode("document.createTextNode(\"文字列\")を使ってテキスト(エレメント)を作成します。作成されたエレメントはappendChildの引数に指定でき、これもまたドキュメントの構成要素となります。また、表示された文字列は自動的にエスケープ処理されるのでXSS対策もなされた状態になります。この辺りがinnerHTMLを使ってお手軽に表示した場合との違いです。"));
		document.getElementsByTagName("body").item(0).appendChild(document.createElement("h2"));
		document.getElementsByTagName("h2").item(4).appendChild(document.createTextNode("まとめ?"));
		document.getElementsByTagName("body").item(0).appendChild(document.createElement("p"));
		document.getElementsByTagName("p").item(4).appendChild(document.createTextNode("基本的にhtmlドキュメントは「タグ」と「テキスト」の織りなすものなので、上記の機能を組み合わせて利用するだけで作成することができます(!?)。"));
	}
// ============================================================
//   ユーザコーディング(ここまで)
// ============================================================
// -->
</script>

</head>
<body>
<div>
aaa
</div>
<div>
bbb
</div>
<div>
ccc
</div>
</body>
</html>

現時点での結論

Javascriptの言語仕様そのものは意外にも比較的受け入れ易いものだった。しかしJavascriptでのビジネスロジックまでの構築は抵抗がある。理由としては、

  • ブラウザ間での仕様の相違が大きい
  • 同一ブラウザ間であっても異なるバージョン間での仕様の相違が大きい
  • エラーメッセージ等の問題点を通知する仕組みが弱い(文法エラーはダンマリとなる事が多い)

のような事があげられる。とにかくデバッグしずらい。

とは言えAjax + Javascript + DOM (+ XML?)を使うと、格段にユーザインターフェースの利便性が向上するのは間違いないし、非常に魅力的ではある。個人的にはそういう用途に絞って利用してみようかと思う。