ラベル Ruby の投稿を表示しています。 すべての投稿を表示
ラベル Ruby の投稿を表示しています。 すべての投稿を表示

2010年4月5日月曜日

いまさらだけどRubyをWindows(32bit)にインストールするときのためのメモ

RubyをWindowsでcygwinに依存せずに快適に使うために必要なファイルの場所のメモ。

@ITの「WindowsにRuby1.9.1をインストールする」によると

Ruby mswin32はftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/から持ってこれる
GNU Readlineなどはhttp://jarp.does.notwork.org/win32/から持ってこれる
OpenSSLはhttp://www.deanlee.cn/programming/openssl-for-windows/から持ってこれる
Iconvはhttp://www.meadowy.org/meadow/dists/snapshot/old/から持ってこれる

Ruby1.8.6やRuby1.8.7でもこれらのDLLを使える。

ソースパッケージのほうが微妙に新しくなってるから
できれば(Rubyも含めて)自分でコンパイルしたい。
Cでext書くこともあるし、Cなgemをインストールすることもあるし...
でも、VC6が動くマシンは持ってないからなぁ...

2010年1月27日水曜日

jrubycでプリコンパイルしてみた

JRubyではRubyスクリプトをプリコンパイルしてclassファイルにすることができる。

% type sample.rb
puts "hello world

% mkdir bin
% jruby -S jrubyc -t bin sample.rb
Compiling sample.rb to class sample

実行する時にはJRubyのライブラリをクラスパスに指定する。
JRuby 1.4.0では、jruby.jarを指定すれば十分なようだ。

% java -cp ./bin;C:/jruby-1.4.0/lib/jruby.jar sample
hello world

jarファイルにjarファイルを埋め込むことができるから、(Fat JarとかOne-Jarとか)
これで「rubyなんてインストールしてないよ」っていう人でも
Javaがインストールされていれば作成したプログラムを使ってもらえる。

ちなみに、jrubyはコンパイル済みのクラスファイルを実行することもできる。

% jruby sample.class

便利そうだけど、JRubyがインストールされている環境でjrubycでコンパイルしたclassファイルを実行できて嬉しいことがあるのかどうかちょっと疑問。

exerbみたいにrequireをトレースして必要なファイルを全てコンパイルしてJarに固めるような機能があったら便利そう。

2009年12月4日金曜日

Rubyでメールアドレスかどうか調べたくなってみた

結論→今日はメールアドレスかどうかをちゃんと調べる必要がないので適当にごまかしておく。

「やっとくか〜?」と思ったけれど、やっぱり面倒だった。あはは...

厳密には正しくないけど今はまぁこれで十分な感じの正規表現
valid_chars = '[0-9A-Za-z_!#\$%&*+\-\/=\?^_{|}\~\.]+'
valid_pattern = Regexp.new("^(#{valid_chars})@(#{valid_chars})$", nil, 'n')

ところで元ネタは2002年のRubyなので、Regexp#newのパラメータが現在(Ruby 1.8とか1.9)とは異なるようだ。
元ネタではRegexpの引数が2つになっていて、第2引数は 'n' だ。
この 'n' は文字コード "none" の指定じゃないかと思われる。
Regexpのリファレンスマニュアル見ると第2引数がFixnum以外で真ならIGNORECASEになるって。
当時はオプションの指定ができなかったのだろうか?それとも単なる転記ミス?

2009年11月19日木曜日

RubyでGIFを読んでみた

gif.rb
class GIF
attr_accessor :version, :width, :height, :bgcolor_index, :aspect_ratio, :sort_flag
attr_accessor :color_table, :blocks

def self.load(istream)
self.new.load(istream)
end

def self.load_file(filename)
self.new.load_file(filename)
end

def initialize
@blocks = []
end

def [](index)
@blocks[index]
end

def []=(index, block)
@blocks[index] = block
end

def <<(block)
@blocks << block
end

def clear
@blocks = []
@version = @width = @height = @bgcolor_index = @aspect_ratio = @sort_flag =
@color_table = nil
end

def load(istream)
clear
reader = Reader.new(self, istream)
reader.run
end

def load_file(filename)
File.open(filename, 'rb'){|f| load(f) }
end

class GraphicControlExtension
attr_accessor :disposal_method, :user_input_flag, :delay_time, :transparent_index
# case disposal_method
# when 0; # no action is required
# when 1; # do not dispose
# when 2; # restore to background color
# when 3; # restore to previous
# end
end

class CommentExtension
attr_accessor :data
end

class PlainTextExtension
attr_accessor :x, :y, :width, :height, :cell_width, :cell_height
attr_accessor :fgcolor_index, :bgcolor_index
attr_accessor :text
end

class ApplicationExtension
attr_accessor :app_id, :auth_code, :data
end

class ImageBlock
attr_accessor :x, :y, :width, :height, :interlace_flag, :sort_flag, :color_table
attr_accessor :code_size, :data

def decode
LZW.decode(@code_size, @data)
end

def encode(indexes)
largest = indexes.sort[-1]
@code_size = ("%b" % largest).size
@code_size = 2 if 2 > @code_size
@data = LZW.encode(@code_size, indexes)
end

def encode_with_code_size(indexes, code_size)
@data = LZW.encode(@code_size = code_size, indexes)
end
end
end

######################################################################
class GIF::Reader
def initialize(gif, istream)
@gif = gif
@istream = istream
end

def run
read_header
read_blocks
@gif
end

def read_header
sig,
@gif.version, @gif.width, @gif.height,
bits,
@gif.bgcolor_index, @gif.aspect_ratio = read_fmt('a3a3vvCCC')

gctf = 0 != (0b1000_0000 & bits) # global color table flag
cr = (0b0111 & (bits >> 4)) + 1 # color resolution
@gif.sort_flag = 0 != (0b1000 & bits)
gct_size = 2 ** ((0b0111 & bits) + 1) # size of global color table
@gif.color_table = (0...gct_size).map{ read_fmt('CCC') } if gctf
end

def read_blocks
loop do
case block_type = read_fmt('C')[0]
when 0x3B # trailer
break
when 0x21 # Extension
@gif << read_extension_block
when 0x2C # Image
@gif << read_image_block
else
raise "Not Yet Impl : BlockType is 0x#{"%02x" % block_type}"
end
end
end

def read_extension_block
label = read_fmt('C')[0]
case label
when 0xF9 # Graphic Control
read_graphic_control
when 0xFE # Comment Label
read_comment_label
when 0x01 # Plain Text
read_plain_text
when 0xFF # Application Extension
read_application_extension
else
raise 'Not Yet Impl'
end
end

def read_graphic_control
b = GIF::GraphicControlExtension.new
block_size, bits, b.delay_time, b.transparent_index, zero = read_fmt('CCvCC')
b.disposal_method = 0b111 & (bits >> 2)
b.user_input_flag = 0 != 0b10 & bits
transparent_color_flag = 0 != 0b1 & bits
b.transparent_index = nil unless transparent_color_flag
b
end

def read_comment_label
b = GIF::CommentExtension.new
b.data = read_block_data
b
end

def read_plain_text
b = GIF::PlainTextExtension.new
block_size, b.x, b.y, b.width, b.height,
b.cell_width, b.cell_height,
b.fgcolor_index, b.bgcolor_index = read_fmt('CvvvvCCCC')
b.text = read_block_data
b
end

def read_application_extension
b = GIF::ApplicationExtension.new
block_size, b.app_id, b.auth_code = read_fmt('Ca8a3')
b.data = read_block_data
b
end

def read_block_data
bytes = ''
loop do
size = read_fmt('C')[0]
return bytes if size == 0
bytes << read(size)
end
end

def read_image_block
b = GIF::ImageBlock.new
b.x, b.y, b.width, b.height, bits = read_fmt('vvvvC')
lctf = (0 != 0x80 & bits) # local color table flag
b.interlace_flag = (0 != 0x40 & bits)
b.sort_flag = (0 != 0x20 & bits)
lct_size = 2 ** ((0x07 & bits) + 1)
b.color_table = (0...lct_size).map{ read_fmt('CCC') } if lctf
b.code_size = read_fmt('C')[0]
b.data = read_block_data
b
end

def read(size)
bytes = @istream.read(size)
raise 'End of file' if bytes.nil?
raise 'Not enough' if bytes.size != size
bytes
end

def read_fmt(fmt)
read(pack_size(fmt)).unpack(fmt)
end

def pack_size(fmt)
total_size = 0
item_size = 0
fmt.scan(/[a-zA-Z]|[0-9]+/).each do |v|
case v
when 'C'; item_size = 1
when 'a'; item_size = 1
when 'v'; item_size = 2
when /[0-9]+/
item_size *= v.to_i - 1
else
raise 'Not Yet'
end
total_size += item_size
end
total_size
end
end

######################################################################
module GIF::LZW
MAX_NBITS = 12

module_function
def encode(nbits, istring)
GIF::LZW::Encoder.encode(nbits, ArrayReader.new(istring), StringIO.new).string
end

def decode(nbits, istring)
GIF::LZW::Decoder.decode(nbits, StringIO.new(istring), StringIO.new).string
end

def bit_mask(nbits)
(1 << nbits) - 1
end
end

######################################################################
class GIF::LZW::Dictionary
attr_reader :clear_code, :end_code
def initialize(nbits)
dict_size = 1 << nbits
@dict = (0...dict_size).map{|k| [nil, k] }
@clear_code = dict_size
@dict << nil # Clear Code
@end_code = dict_size + 1
@dict << nil # End Code
@code = {}
end

def reset
@dict.slice!(@end_code + 1, @dict.size)
@code = {}
end

def size
@dict.size
end

def add(w, k)
new_code = @code[(w << 8) + k] = @dict.size
@dict << [w, k]
return new_code
end

def code(w, k)
return k unless w
return @code[(w << 8) + k]
end

def string(code)
w, k = @dict[code]
return nil unless k
return string(w) << k if w
return [k]
end
end

######################################################################
# src[0] : <abcde> : 11100
# src[1] : <fghij> : 11000
# src[2] : <klmno> : 10000
#
# dst[0] : <hijabcde> : 00011100
# dst[1] : <.klmnofg> : 01000011
class GIF::LZW::BitWriter
attr_reader :stream

def initialize(stream)
@stream = stream
@value = @nbits = 0
end

def write(value, nbits)
@value |= (GIF::LZW.bit_mask(nbits) & value) << @nbits
@nbits += nbits
while 8 <= @nbits
@stream.write([0xFF & @value].pack('C'))
@value >>= 8
@nbits -= 8
end
end

def flush
if 0 < @nbits
@stream.write([0xFF & @value].pack('C'))
@value = @nbits = 0
end
@stream.flush
end
end

######################################################################
class GIF::LZW::BitReader
attr_reader :stream

def initialize(stream)
@stream = stream
@value = @nbits = 0
end

def read(nbits)
while @nbits < nbits
byte = @stream.read(1)
return nil unless byte
byte = byte.unpack('C')[0]
@value |= byte << @nbits
@nbits += 8
end
v = GIF::LZW.bit_mask(nbits) & @value
@value >>= nbits
@nbits -= nbits
return v
end
end

######################################################################
class GIF::LZW::ArrayReader
def initialize(array)
@array = array
@pos = 0
end

def getc
if @pos < @array.size
pos = @pos
@pos += 1
return @array[pos]
end
end
end

######################################################################
class GIF::LZW::Encoder
# @return ostream
def self.encode(nbits, istream, ostream)
encoder = self.new(nbits, istream, ostream)
encoder.run
end

def initialize(nbits, istream, ostream)
@nbits = nbits
@istream = istream
@bit_writer = GIF::LZW::BitWriter.new(ostream)
@code_size = nbits + 1
@dict = GIF::LZW::Dictionary.new(@nbits)
@w = nil
write_code(@dict.clear_code)
end

def run
while k = @istream.getc
write(k)
end
finish
end

def write(k)
if c = @dict.code(@w, k)
@w = c
return
end

c = @dict.add(@w, k)
write_code(@w)
@w = k

return if (1 << @code_size) != c

if @code_size < GIF::LZW::MAX_NBITS
@code_size += 1
return
end

write_code(@dict.clear_code)
@code_size = @nbits + 1
@dict.reset
end

def finish
write_code(@w) if @w
write_code(@dict.end_code)
@bit_writer.flush
end

def write_code(code)
@bit_writer.write(code, @code_size)
end
end

######################################################################
class GIF::LZW::Decoder
# @return ostream
def self.decode(nbits, istream, ostream)
decoder = self.new(nbits, istream, ostream)
decoder.run
end

def initialize(nbits, istream, ostream)
@nbits = nbits
@bit_reader = GIF::LZW::BitReader.new(istream)
@ostream = ostream
@code_size = nbits + 1
@dict = GIF::LZW::Dictionary.new(@nbits)
end

def run
decode_1st && while decode_2nd; end
@ostream.flush
end

def decode_1st
@w = read_code
@w = read_code while @dict.clear_code == @w
return nil if @w.nil? || @dict.end_code == @w
@ws = @dict.string(@w)
@ostream.write(@ws.pack('C*'))
true
end

def decode_2nd
@code_size += 1 if @dict.size == (1 << @code_size) && @code_size < GIF::LZW::MAX_NBITS
k = read_code

return if k.nil? || @dict.end_code == k

if @dict.clear_code == k
@code_size = @nbits + 1
@dict.reset
return decode_1st
end

ks = @dict.string(k)
ks = @ws + @ws.slice(0, 1) unless ks
@ostream.write(ks.pack('C*'))
@dict.add(@w, ks[0])
@w, @ws = k, ks
end

def read_code
@bit_reader.read(@code_size)
end
end

LZWをRubyで実装してみた

lzw.rb
#
# LZW Algorithm
#
class Dic
def initialize
@dict = (0...256).map{|k| [nil, k]}
@code = {}
end

def add(w, k)
new_code = @code[(w << 8) + k] = @dict.size
@dict << [w, k]
return new_code
end

def code(w, k)
return k unless w
return @code[(w << 8) + k]
end

def string(code)
w, k = @dict[code]
return nil unless k
return string(w) << k if w
return [k]
end
end

def encode(input)
output = []
dic = Dic.new
w = nil
while k = input.shift
c = dic.code(w, k)
if c
w = c
else
output << w
dic.add(w, k)
w = k
end
end
output << w if w
return output
end

def decode(input)
output = []
dic = Dic.new
w = input.shift
return output unless w
ws = dic.string(w)
ws.each{|v| output << v}
while k = input.shift
ks = dic.string(k)
ks = ws + ws.slice(0, 1) unless ks
ks.each{|v| output << v}
dic.add(w, ks[0])
w, ws = k, ks
end
return output
end

encode([0,1,0,1,0,1,0,1]) # => [0, 1, 256, 258, 1]
decode([0,1,256,258,1]) # => [0, 1, 0, 1, 0, 1, 0, 1]

2009年9月21日月曜日

Treetop - RubyのPEG

これは面白いかも

Treetop is a language for describing languages. Combining the elegance of Ruby with cutting-edge parsing expression grammars, it helps you analyze syntax with revolutionarily ease.

Treetopは言語を記述するための言語だ。Rubyの簡潔さと最先端のParsing Expression Grammarを融合させたもので、構文解析を革新的に容易にする。

http://treetop.rubyforge.org/syntactic_recognition.html

2009年9月16日水曜日

Rubyでwithしてみた

VBで評価の高いWithをRubyで。
とっても簡単。
あらためてRubyすごいなぁ。

sample.rb
def with(instance, &block)
instance.instance_eval(&block)
end

class Hello
def say
puts 'hello world'
end
end

with(Hello.new) do
say
say
say
end

2009年9月2日水曜日

Zellerの公式

まぁ普通はZellerの公式使ってすっきり書くのでしょうねぇ・・・

weekday.rb
#!/usr/bin/env ruby

# 曜日を求める
# y ... 西暦年
# m ... 月
# d ... 日
def weekday(y, m, d)
if 2 >= m
y -= 1
m += 12
end
(y + (y / 4) - (y / 100) + (y / 400) + (13 * m + 8) / 5 + d) % 7
end

require 'date'

def check
d = Date.new(2000, 1, 1)
50000.times do
unless d.wday == weekday(d.year, d.month, d.day)
puts "#{d} ng"
break
end
d = d.next
end
end

check

2009年9月1日火曜日

曜日を計算する関数を作ってみた

カレンダーライブラリが使えない環境で任意の日付の曜日を求める必要があったので、いろいろごちゃごちゃやっている間に何となくできた関数。

weekday.rb
#!/usr/bin/env ruby

# 曜日を求める
# y ... 西暦年
# m ... 月
# d ... 日
def weekday(y, m, d)
ms = [ 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 ]
x = y
if 2 >= m
y -= 1
end
(x + (y / 4) - (y / 100) + (y / 400) + ms[m-1] + (d-1)) % 7
end

require 'date'

def check
d = Date.new(2000, 1, 1)
50000.times do
unless d.wday == weekday(d.year, d.month, d.day)
puts "#{d} ng"
break
end
d = d.next
end
end

check

2009年8月19日水曜日

SELinuxのせいでreadline.soをロード失敗してみた

CentOS 5.2 で ruby-1.8.7-p174 をビルド&インストールした時のこと。
irb -rirb/completionしたら下記のエラー。

/usr/local/lib/ruby/1.8/i686-linux/readline.so: /usr/local/lib/ruby/1.8/i686-linux/readline.so: cannot restore segment prot after reloc: Permission denied - /usr/local/lib/ruby/1.8/i686-linux/readline.so (LoadError)

これはSELinuxの設定が原因。

ls -lZ /usr/local/lib/ruby/1.8/i686-linux/*.so

してみたところ、

-rwxr-xr-x 1 user_u:object_r:lib_t root root 116642 8月 19 17:14 readline.so

とのこと。

chcon system_u:object_r:textrel_shlib_t zlib.so

このコマンドを実行して設定を正しくする。

-rwxr-xr-x 1 system_u:object_r:textrel_shlib_t root root 116642 8月 19 17:14 readline.so

2009年8月7日金曜日

壁のばし方で迷路を作ってみた

maze.rb
#!/usr/bin/env ruby
# -*- encoding: utf-8 -*-

require 'rubygems'
require 'win32console'
require 'term/ansicolor'

include Win32::Console::ANSI
include Term::ANSIColor

class Maze
PATHWAY = 1
WALL = 2

attr_accessor :width, :height

def initialize(width, height)
@width = width * 2 + 1
@height = height * 2 + 1
@map = [ PATHWAY ] * @width * @height
@console = Win32::Console.new(STD_OUTPUT_HANDLE)

prologue
dig
epilogue
end

def prologue
@console.Cls
print on_white
dig_edge
end

def epilogue
print reset
@console.Cursor(0, 1)
print ' '
@console.Cursor(@width - 1, @height - 2)
print ' '
@console.Cursor(0, @height + 1)
end

def dig
loop do
w0, w1 = select_next
break unless w0
set_wall(*w0)
set_wall(*w1)
loop do
w0, w1 = candidates(*w1)
break unless w0
set_wall(*w0)
set_wall(*w1)
sleep 0
end
end
end

def select_next
r = walls
loop do
i = rand(r.size)
x, y = r[i]
r.delete_at(i)
z = candidates(x, y)
return z if z
break if r.empty?
end
end

def candidates(x, y)
c = []
f0(c, x + 1, y, x + 2, y)
f0(c, x - 1, y, x - 2, y)
f0(c, x, y + 1, x, y + 2)
f0(c, x, y - 1, x, y - 2)
c[rand(c.size)] unless c.empty?
end

def f0(z, x, y, x1, x2)
if PATHWAY == self[x1, x2]
z << [ [x, y], [x1, x2] ]
end
end

def walls
r = []
0.step(@height - 1, 2) do |y|
0.step(@width - 1, 2) do |x|
r << [x, y] if WALL == self[x, y]
end
end
r
end

def dig_edge
x = @width - 1
@height.times do |y|
set_wall(0, y)
set_wall(x, y)
end

y = @height - 1
@width.times do |x|
set_wall(x, 0)
set_wall(x, y)
end
end

def set_wall(x, y)
@map[x + y * @width] = WALL
@console.Cursor(x, y)
print ' '
end

def [](x, y)
return nil if x < 0 || @width <= x
return nil if y < 0 || @height <= y
@map[x + y * @width]
end
end

m = Maze.new(39, 30)

2009年8月5日水曜日

穴掘り法で迷路を作ってみた

maze.rb
#!/usr/bin/env ruby
# -*- encoding: utf-8 -*-

require 'rubygems'
require 'win32console'
require 'term/ansicolor'

include Win32::Console::ANSI
include Term::ANSIColor

class Maze
OUTSIDE = -1
PATHWAY = 0
WALL = 1

def initialize(width, height)
@console = Win32::Console.new(STD_OUTPUT_HANDLE)
@width = width * 2 + 1
@height = height * 2 + 1
@data = [WALL] * @width * @height
draw_bg
dig
finish
end

def finish
@console.Cursor(0, 1)
print ' '
@console.Cursor(@width + 1, @height)
print ' '
@console.Cursor(0, @height + 1)
end

def dig
x = rand(@width / 2) * 2
y = rand(@height / 2) * 2
loop do
dig_path(x, y)
r = select_next(x, y)
break unless r
set_pathway(*r[0])
x, y = r[1]
end
end

def select_next(x, y)
r = walk(x, y)
until r.empty?
i = rand(r.size)
x, y = r[i]
r.delete_at(i)
s = candidates(x, y)
return s[rand(s.size)] unless s.empty?
end
end

def walk(tx, ty, x = nil, y = nil, fx = nil, fy = nil, r = [])
return if fx == tx && fy == ty
return if PATHWAY != self[tx, ty]
r << [tx, ty] if tx % 2 == 0 && ty % 2 == 0
walk(tx + 1, ty, tx, ty, x, y, r)
walk(tx - 1, ty, tx, ty, x, y, r)
walk(tx, ty + 1, tx, ty, x, y, r)
walk(tx, ty - 1, tx, ty, x, y, r)
r
end

def dig_path(x, y)
set_pathway(x, y)
loop do
s = candidates(x, y)
break if s.empty?
s = s[rand(s.size)]
set_pathway(*s[0])
set_pathway(*s[1])
x, y = s[1]
end
end

def candidates(x, y)
r = []
try_dig(x, y, 1, 0, r)
try_dig(x, y, -1, 0, r)
try_dig(x, y, 0, 1, r)
try_dig(x, y, 0, -1, r)
r
end

def try_dig(x, y, dx, dy, r)
x += dx
y += dy
s = x + dx
t = y + dy
if WALL == self[s, t]
r << [[x, y], [s, t]]
end
end

def [](x, y)
return OUTSIDE if x < 0 || @width <= x
return OUTSIDE if y < 0 || @height <= y
@data[x + y * @width]
end

def set_pathway(x, y)
@data[x + y * @width] = PATHWAY
@console.Cursor(x + 1, y + 1)
print ' '
end

def draw_bg
@console.Cls
(@height + 2).times do
print on_white
print ' ' * (@width + 2)
print reset
puts
end
end
end

#Maze.new(38, 30)
Maze.new(10, 10)

PassengerTempDirを設定したほうが幸せ?

Apache + PassengerをCentOS 5.2で稼働させているのだけれど、たまに、落ちる。
Apacheのerror_logには

The spawn server has exited unexpectedly.

とか

Exception Errno::ENOENT in PhusionPassenger::Rack::ApplicationSpawner (No such file or directory ...

と記録されている。

/tmp/passenger.????

を見てみたら・・・あれ?なんだかキレイ・・・

/tmpなのでファイルを消されちゃっても文句は言えないのかも?

ってなわけで、httpd.conf で PassengerTempDir を勝手にいじられるはずがないところに変更してみた。
この設定でしばらく様子を見てみよう。

せっかくなので、PassengerTempDirは、アプリと同じパーティションのディレクトリを指定。
ファイルアップロードでコピーじゃなくてリネームを使えるかも~?

2009年7月29日水曜日

Flash Lite 1.1 のルートムービークリップの変数を動的生成してみた

swfの動的生成はswfmillやmingという方法があるけれど、ひな形となるswfの変数だけを動的に変えるだけでよい場合にはswfバイナリを直接変更するという方法もあるという例。

生成した変数はルートムービークリップの変数になるようだ。変数名と同じ変数名でダイナミックTextインスタンスをステージに配置すれば、動的設定した文字列を表示することもできる。

ちなみに、検証にはFlash Lite 1.1で作成したswfを使った。

swf.rb
#!/usr/bin/env ruby
# -*- encoding: utf-8 -*-

require 'stringio'

module SWF
def self.read(src)
header = src.read(9)
x = (header[8] >> 3) * 4 + 5
header_size = ((((8 - (x & 7)) & 7) + x) / 8) + 12 + 5
header << src.read(header_size - 9)
body = src.read
[header, body]
end

class Vars
HD = "\x3f\x03\x00\x00\x00\x00"
BE = "\x96"
ZE = "\x00"
ZB = ZE + BE
ED = "\x00\x1d"

def initialize
@vars = []
end

def add(key, value)
@vars << [key, value]
end

def to_bytes
bytes = StringIO.new
bytes << HD
@vars.each do |key, val|
bytes << BE << [key.size + 2].pack('v') << ZE << key
bytes << ZB << [val.size + 2].pack('v') << ZE << val << ED
end
bytes << ZE
bytes.seek 0
bytes = bytes.read
bytes[2, 4] = [bytes.size - 6].pack('V')
bytes
end

def apply(src_io, dst_io)
vars = to_bytes
header, body = SWF.read(src_io)
header[4, 4] = [header.size + body.size + vars.size].pack('V')
dst_io << header
dst_io << vars
dst_io << body
end
end
end

if __FILE__ == $0
require 'yaml'
require 'optparse'
dat, src, dst = nil
OptionParser.new do |opt|
opt.on('-v VARS.yaml'){|v| dat = v }
opt.on('-i INPUT.swf'){|v| src = v }
opt.on('-o OUTPUT.swf'){|v| dst = v }
opt.parse!(ARGV)
unless dat && src && dst
opt.parse!(["-help"])
end
end
vars = SWF::Vars.new
YAML.load_file(dat).each{|key, value| vars.add(key, value) }
File.open(src, 'rb') do |s|
File.open(dst, 'wb') do |d|
vars.apply(s, d)
end
end
end

2009年7月17日金曜日

Rubyの拡張モジュールでリソースを解放する方法

RubyはGCを持っているので、Rubyの世界の中ではリソースの解放をそれほど気にしなくてもよいけれど、Cで記述したRubyの拡張モジュールではそうはいかない。

インスタンス生成で資源を確保し、GCがインスタンスを回収した時に資源を解放するサンプル。

foo.c
#include <ruby.h>
#include <rubysig.h>

static VALUE g_cFoo = Qnil;

struct FooData
{
int x;
int y;
};

static void
Foo_free_data(struct FooData* data)
{
fprintf(stderr, "free 0x%08x\n", data);
#if defined(USE_RUBY_XMALLOC)
ruby_xfree(data);
#else
free(data);
#endif
}

static VALUE
Foo_alloc_data(VALUE self)
{
return Data_Wrap_Struct(self, 0, &Foo_free_data, 0);
}

static struct FooData*
Foo_data(VALUE self)
{
struct FooData* sval = NULL;
Data_Get_Struct(self, struct FooData, sval);
return sval;
}

static VALUE
Foo_initialize(VALUE self)
{
#if defined(USE_RUBY_XMALLOC)
DATA_PTR(self) = ruby_xmalloc(sizeof(struct FooData));
#else
DATA_PTR(self) = malloc(sizeof(struct FooData));
#endif
fprintf(stderr, "alloc 0x%08x\n", DATA_PTR(self));
return self;
}

Init_foo()
{
g_cFoo = rb_define_class("Foo", rb_cObject);

rb_define_alloc_func(g_cFoo, &Foo_alloc_data);
rb_define_method(g_cFoo, "initialize", &Foo_initialize, 0);
}

※注意!このモジュールを使って ruby 1.8.7 p174 {mingw32|linux}で ruby -rfoo -e 'loop{Foo.new}' したら Segmentation faultになった。原因不明>_<

2009年7月16日木曜日

ruby embedded into c++

2003年に書かれたものだから、古いかもしれないけれど、Rubyの拡張モジュールを作成する時の参考になるかもしれない。
http://metaeditor.sourceforge.net/embed/

2009年7月8日水曜日

emacsでruby-modeを使う設定

.emacs
;;;;
;;;; Ruby
;;;;
(add-to-list 'load-path "c:/somewhere/site-lisp")
(autoload 'ruby-mode "ruby-mode"
"Mode for editing ruby source files" t)
(setq auto-mode-alist
(append '(("\\.rb$" . ruby-mode)) auto-mode-alist))
(setq interpreter-mode-alist (append '(("ruby" . ruby-mode))
interpreter-mode-alist))
(autoload 'run-ruby "inf-ruby"
"Run an inferior Ruby process")
(autoload 'inf-ruby-keys "inf-ruby"
"Set local key defs for inf-ruby in ruby-mode")
(add-hook 'ruby-mode-hook
'(lambda ()
(inf-ruby-keys)))

2009年7月7日火曜日

Dokan Ruby 0.1.4 のパッチ

Windows用の仮想ファイルシステムドライバDokanライブラリを使ってみた。
Dokan Rubyを使うとRubyで手軽にファイルシステムを書けてしまうという
素晴らしいライブラリだ。

そのサンプルにmirror.rbというものがある。
任意のフォルダを指定して、ドライブにマウントするサンプルだ。
SUBSTコマンドのようなもの。

しかし、このmirror.rbでマウントしたドライブは、
Explorerでファイルを見ることはできるものの、
コマンドプロンプトで cd FolderName を実行しても
「指定されたパスが見つかりません。」と言われる。

ばぐ?

Dokanライブラリのreadme.ja.txtに答えが書いてあった。

ディレクトリに対するアクセスで有るのにも関わらず,CreateFile が呼ばれることもあります.その場合は,DokanFileInfo->IsDirectory は FALSE がセットされています.ディレクトリの属性を取得する場合などに OpenDirectory ではなく,CreateFileが呼ばれるようです.ディレクトリに対するアクセスなのにも関わらず,CreateFile が呼ばれた場合は,必ず DokanFileInfo->IsDirectory にTRUEをセットしてからreturnしてください.正しくセットされていないと,Dokanライブラリは,そのアクセスがディレクトリに対するアクセスかどうか判断できず,Dokanファイルシステムで Windows に対して正確な情報を返すことが出来なくなります.

mirror.rbはopenでFile.file?(path)の結果を返していて、ディレクトリの場合に対応できていない。
そもそも、dokan_lib.soでFileInfo#directory=が定義されていないし、instans_eval等で無理矢理値を変えたとしても、FileInfoをDokanFileInfoに書き戻すコードは無かった。

dokan_lib.cとmirror.rbを少しだけ書き換えたらうまくいった。めでたしめでたし♪

dokan_lib.c.patch
110a111,118
> static VALUE
> DR_FileInfo_set_directory_p(
> VALUE self,
> VALUE value)
> {
> rb_iv_set(self, "@is_directory", value);
> return self;
> }
151a160
> VALUE is_dir;
154a164,166
>
> is_dir = rb_iv_get(FileInfo, "@is_directory");
> DokanFileInfo->IsDirectory = (Qfalse != is_dir && Qnil != is_dir);
> /* DokanFileInfo->IsDirectory = RTEST(is_dir); */ /* 普通はこう書くのかな? */
173c185,186
< rb_define_method(g_cFileInfo, "directory?", DR_FileInfo_directory_p, 0);
---
> rb_define_method(g_cFileInfo, "directory?", DR_FileInfo_directory_p, 0);
> rb_define_method(g_cFileInfo, "directory=", DR_FileInfo_set_directory_p, 1);
mirror.rb.patch
19c19,24
< File.file?(get_path(path))
---
> s = File.stat(get_path(path)) rescue nil
> if s
> fileinfo.directory = s.directory?
> true
> else
> false
> end

2009年6月29日月曜日

gemの作り方

「自前の gem の作り方」
その記事の中で
と書いてある。
そのうち調べよう。

set_trace_funcは使ったことがない

Ruby で debug する7つの方法という記事。

set_trace_func は使ったことがない。
間接的にはお世話になってるけれど、直接使ったことはない。