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

2014/03/07

Published Date

2014 年 03月中に、Blogger へ投稿する際、mblogger を使って投稿しました。
順調に動いたと思っていたのは間違いでした。

% ./mblogger --update /path/to/投稿した記事に追記などを記入した.txt


を実行すると Blogger から割り振られた公開日時とは異なる公開日時で update してしまうことに気がつきました。
原因は下記で AM PM を含めた文字列を Time.parse すると文字列で指定した日付よりも未来の日付になってしまっていたのでした。
従って、きっちりと AM PM を削除した”文字列”を Time.parse するように変更して Blogger から振られた公開日時を維持しつつ update できるようになりました。

http://github.com/midore/mblogger には、まだ $ git push していない

# mblogger/lib/mblogger-xml.rb

def set_published
  return nil unless @h[:date]
  begin
    d = Time.parse(@h[:date].to_s.gsub(/AM|PM/,''))
    #  d = Time.parse(@h[:date])
  rescue
    return nil
  end
  @xentry.add_element("published").add_text(d.iso8601.to_s)
end


# 2014-03-07
2014年03月中に公開した記事の中には、公開日時が本日(日本時間 2014-03-07)よりも未来の日付になったままの記事が存在していたりしています。

# 2014-03-08
若干修正 & testing

2011/11/12

Time.parse

はまった。

>> Time.parse("2011/11/01 am 10:00:00")
=> Thu Nov 10 01:00:00 +0900 2011
>> Time.parse("2011/11/01 am 00:30:00")
ArgumentError: argument out of range
  from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/time.rb:184:in `local'
  from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/time.rb:184:in `make_time'
  from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/time.rb:243:in `parse'
  from (irb):5
>> Time.parse("2011/11/01 am 01:30:00")
=> Tue Nov 01 01:00:00 +0900 2011
>> 


parse 様にいわせればユーザが am, pm 言うなんておかしいだろってこと?
そ、そうかも。

# 2011-11-04

2011/10/08

Ruby 1.8.7

1.8.7の今後につきまして

(<URL:http://www.ruby-lang.org/en/news/2011/10/06/plans-for-1-8-7/> の翻訳です)
...

今後の予定

2012年6月まで、通常のメンテナンスが続きます。
それ以降、バグフィックスリリースは終了いたします。2013年6月まではセキュリティフィックスは続けますので、それまでは1.8.7をお使いいただけます。
2013年6月より先は、1.8.7はあらゆるサポート対象外になります。


Mac OS X Lion の
/usr/bin/ruby は
ruby 1.8.7 (2010-01-10 patchlevel 249) [universal-darwin11.0]

なので、すぐにでも 1.9 にアップデートして欲しいです。
1日でも早く Lion 標準で 1.9 を使えるようにしてほしいです。
ソフトウェアアップデート経由で 1.9 を提供していただきたい。心から切望。

OS X Lion で似?1.9 をお試ししたい場合

alias 'macruby'=/System/Library/PrivateFrameworks/MacRuby.framework/Versions/A/usr/bin/macruby
alias 'macirb'=/System/Library/PrivateFrameworks/MacRuby.framework/Versions/A/usr/bin/macirb

を ~/.bashrc などなどに追記して

% source ~/.bashrc
% macirb
irb(main):001:0> RUBY_VERSION
=> "1.9.2"
irb(main):002:0>

2011/03/09

hash.merge

Hash の merge

2つのハッシュに同じkey が存在した場合...
merge 元の値は nil じゃない。
merge 対象の値は nil。
これらを merge すると nil ではない merge 元の値は保持されたままになるのだっけ?
なーんて一瞬でも思ったのは大間違いだった。

% irb
>> a = {:a=>'a', :b=>'b'}
=> {:b=>"b", :a=>"a"}
>> b = {:a=>nil, :b=>'c'}
=> {:b=>"c", :a=>nil}
>> a.merge(b)
=> {:a=>nil, :b=>"c"}


nil であろうときっちり上書きされて nil になる。
上書きされたくなければ同じ key を持たない。...というかマージしない...?

2011/02/16

UTF8-MAC and Encoding.list

OSXでのファイル名比較
OSXのファイル名について教えてもらったこと

を拝見しとっても興味深かったので私も試してみました。

% touch るびー
% mkdir が
% ls -F
test.rb       が/         るびー
% cat -n test.rb
  1 # coding: utf-8
  2
  3 Dir.entries('.').each do |d|
  4   next if d =~ /^\.|rb$/
  5   printf("name=%s, size=%d, encoding=%s\n", d, d.size, d.encoding)
  6   d = d.encode('utf-8', Encoding::UTF8_MAC)
  7   printf("name=%s, size=%d, encoding=%s\n", d, d.size, d.encoding)
  8 end

結果

% ruby192old -v test.rb
ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-darwin10.4.0]
name=が, size=2, encoding=UTF-8
test.rb:6:in `block in <main>': uninitialized constant Encoding::UTF8_MAC (NameError)
  from test.rb:3:in `each'
  from test.rb:3:in `<main>'
%
% ruby192 -v test.rb
ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]
name=が, size=2, encoding=UTF-8
name=が, size=1, encoding=UTF-8
name=るびー, size=4, encoding=UTF-8
name=るびー, size=3, encoding=UTF-8

1.9.2p136 で動きました。

1.9.2p0 で動かないことも気になるので Encoding.list をみてみます。

% ruby192old -ve "p Encoding.list"
ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-darwin10.4.0]
[#<Encoding:ASCII-8BIT>, #<Encoding:UTF-8>, #<Encoding:US-ASCII>]

あれ?なんか少ないような気が...。

% ruby192 -ve "p Encoding.list"
ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]
[#<Encoding:ASCII-8BIT>, #<Encoding:UTF-8>, #<Encoding:US-ASCII>, #<Encoding:Big5>, #<Encoding:Big5-HKSCS>, #<Encoding:Big5-UAO>, #<Encoding:CP949>, 

...たくさんあるので以下省略


1.9.2p136 (2010-12-25 revision 30365) の Encoding.list は
1.9.2p0 (2010-08-18 revision 29036)
のそれとは全然違うようです...。

Encoding.list からutf だけをgrep

% cat -n test.rb
  1 # coding: utf-8
  2
  3 a = Encoding.list
  4 p a.map{|x| x.to_s}.grep(/utf/i)

# なぜ map して to_s しているかというと...配列に対して grep かけるにはその配列の要素が String クラスでないとうまくいかない。
# けれどEncoding.list が返した配列、を構成する要素のクラスは Encoding クラスなので String クラスに変える必要があるから。

結果

% ruby192old test.rb
["UTF-8"]

% ruby192 test.rb
["UTF-8", "UTF-16BE", "UTF-16LE", "UTF-32BE", "UTF-32LE", "UTF-7", "UTF8-MAC", "UTF8-DoCoMo", "UTF8-KDDI", "UTF8-SoftBank"]


ruby 1.9.2p136 (2010-12-25 revision 30365) にはいろんな種類の UTF があるけど、
UTF8-MAC は ruby 1.9.2p0 (2010-08-18 revision 29036) にはない?! 。

今度は encode の逆の指定。

  1 # coding: utf-8
  2
  3 str = "が"
  4 p str.size
  5 p str.encode('UTF8-MAC', 'utf-8').size

結果

% ruby192 test.rb
1
2

"が" をutf-8 から UTF8-MAC に変えてみるとsize 1から2に変化しました。

ちょっと rurema で再確認。

% rurema String#encode
...
self を指定したエンコーディングに変換した文字列を作成して返します。引数
を2つ与えた場合、第二引数は変換元のエンコーディングを意味します。さもな
くば self のエンコーディングが使われます
...


最後に iTunes Music Folder の中を検索テスト。
先ずは encode 変換しないでそのまま検索。

% cat -n test.rb
  1 # coding: utf-8
  2 # いかにもそれらしきもの(濁音などを含む)を選んでおく
  3 ary = ["東京スカパラダイスオーケストラ", "Yael Naim", "Yael Naïm"]
  4 dir = '/path/to/itunes/music/'
  5
  6 a = Dir.entries(dir)
  7 a.each{|x|
  8   p x if ary.index(x)
 11 }

結果

% ruby192 test.rb
"Yael Naim"

濁音を含む"東京スカパラダイスオーケストラ" と 特別な文字を含む "Yael Naïm" これら2つの folder は存在しているのにヒットしない。

下記に書き換え。

  6 a = Dir.entries(dir)
  7 a.each{|x|
  8   # p x if ary.index(x)
  9   u = x.encode('utf-8', 'UTF8-MAC')
 10   p x if ary.index(u)
 11 }

結果

% ruby192 -v test.rb
ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]
"Yael Naim"
"Yael Naïm"
"東京スカパラダイスオーケストラ"

期待どおりにヒットしました!

さらに ascii_only な文字を encode しなくてすむようにこんな風にしてみても

  6 a = Dir.entries(dir)
  7 a.each{|x|
  8   (x.ascii_only?) ? u = x : u = x.encode('utf-8', 'UTF8-MAC')
  9   p x if ary.index(u)
 10 }

同じ結果を得ることができましたー。

[関連記事]
<http://midorex.blogspot.com/2010/02/utf8-mac.html>
<http://midorex.blogspot.com/2010/02/utf8-mac-2.html>

2010/08/07

instance_variable

http://midorex.blogspot.com/2010/07/attraccessor.html
http://midorex.blogspot.com/2010/08/singletonclass.html
http://midorex.blogspot.com/2010/08/selfclass.html
これらに関連して...

class Test
  @x = 'x'
  class << self
    attr_accessor :x
  end
end

class Test1
  def initialize
    self.class.singleton_class.send(:attr_accessor, :x)
    self.class.x = 'x1'
  end
end

t, t1 = Test.new, Test1.new
puts "t = Test.new, t1 = Test1.new"
puts "t.class.x=> #{t.class.x}"
puts "t1.class.x=> #{t1.class.x}"
puts "t.class.instance_variables=> #{t.class.instance_variables}"
puts "t1.class.instance_variables=> #{t1.class.instance_variables}"

結果

t = Test.new, t1 = Test1.new
t.class.x=> x
t1.class.x=> x1
t.class.instance_variables=> [:@x]
t1.class.instance_variables=> [:@x]

以上をふまえて

class Test オブジェクトと
それを new した オブジェクトと
class Test の特異クラスになったオブジェクト
この3つのオブジェクトにそれぞれ @x と attr_accessor, :x を定義してみる練習

  4 class Test
  5   @x = 'x'
  6   class << self
  7     @x = 'singleton-xxx'
  8     attr_accessor :x
  9     self.class.send(:attr_reader, :x)
 10   end
 11   def initialize
 12     @x = 'ini-xyz'
 13   end
 14   attr_accessor :x
 15 end
 16
 17 t  = Test.new
 18 puts "t = Test.new"
 19 puts "t.x=> #{t.x}"
 20 puts "t.instance_variables=> #{t.instance_variables}"
 21 puts "--\n"
 22 puts "t.class.x=> #{t.class.x}"
 23 puts "t.class.instance_variables=> #{t.class.instance_variables}"
 24 puts "--\n"
 25 s = t.class.singleton_class
 26 puts "s = t.class.singleton_class"
 27 puts "s=> #{s}"
 28 puts "s.x =>#{s.x}"
 29 puts "s.instance_variables=> #{s.instance_variables}"

実行結果

t = Test.new
t.x=> ini-xyz
t.instance_variables=> [:@x]
--
t.class.x=> x
t.class.instance_variables=> [:@x]
--
s = t.class.singleton_class
s=> #<Class:Test>
s.x =>singleton-xxx
s.instance_variables=> [:@x]


line5 の @x を外から読めるようにしているのは
line8 attr_accessor :x

同様に
line7 の@x は
line9 の self.class.send(:attr_reader, :x)

line12 の@x は
line14 attr_reader :x

おもしろいことに

  4 class Test
  5   @x = 'x'
  6   class << self
  7     @x = 'singleton-xxx'
  8     attr_accessor :x
  9     # self.class.send(:attr_reader, :x)
 10   end
 11   def initialize
 12     @x = 'ini-xyz'
 13     self.class.class.send(:attr_accessor, :x)
 14   end
 15   attr_accessor :x
 16 end

line9 のかわりに line13 にしても同じ結果を得ることができた。

+++ memo +++

あたりまえだけども
class Test
attr_reader :x
def initialize
@x = 'ini-xyz'
end
end
t = Test.new
t.x
=>'ini-xyz'
こうすると class Test のインスタンスが定義したインスタンス変数に代入された値を得られる。

new した class Test のインスタンスではなくて、
class Test そのものや、class Test の特異クラスにも
それぞれインスタンス変数を定義できるしそれらのアクセサを定義できるということを
『プログラミングRuby1.9 言語編』(ISBN: 978-4-274-068096) P.351 から学ぶことができた。

# P.351 あたりをうろうろしたままぜんぜん先に進んでいない。それにしても暑い。

self.class

<http://midorex.blogspot.com/2010/08/singletonclass.html> 前回からの続き

class Test
   def initialize(h)
     h.each{|k,v|
       self.class.send(:attr_accessor, k)
       self.send("#{k}=", v)
     }
  end
end

h={:cat=>'cat'}
t = Test.new(h)
t.cat = 'ddd'
p t

これを実行すると
#<Test:0x00000 @cat="ddd">
となる。でもでも :cat を attr_reader にしたかった場合はこのままだとうまくいかない。んで考えてみたこと。

まずは通常の書き方

class Test
  attr_reader :cat, :dog
  def initialize
    @cat = 'meow'
    @dog = 'bowwow'
  end
end

t = Test.new
p t.instance_variables
p t.class.instance_methods[0..2]
p t.cat
t.cat = "cat"

実行結果

[:@cat, :@dog]
[:cat, :dog, :nil?]
"meow"
./draft.rb:15:in `<main>': undefined method `cat=' for #<Test:0x00000 @cat="meow", @dog="bowwow"> (NoMethodError)

最後の行でcat に代入しようとしても attr_reader だから代入できないことを確認。

この結果と同じ結果を導きだすには?

class Test1
  def initialize(h)
    h.each{|k,v|
      self.class.send(:attr_reader, "#{k}")
      self.send("#{k}=", v)
    }
  end
end

h = {:cat=>'meow', :dog=>'bowwow'}
t = Test1.new(h)
p t.instance_variables
p t.class.instance_methods[0..2]
p t.cat
t.cat = "cat"

実行結果

... `block in initialize': undefined method `cat=' for #<Test1:0x00000> (NoMethodError)

self.send("#{k}=", v) は attr_accessor の時は使えるけど attr_reader の時は使えない。
文字通り reader だから getter であって setter じゃないため instance 変数に"cat"を代入できないためにエラーがかえってきた。
ならば
self.send のかわりに普通に instance_variable_set を使ってみた。

class Test1
  def initialize(h)
    h.each{|k,v|
      instance_variable_set("@#{k}", v)
      self.class.send(:attr_reader, "#{k}")
    }
  end
end

h = {:cat=>'meow', :dog=>'bowwow'}
t = Test1.new(h)
p t.instance_variables
p t.class.instance_methods[0..2]
p t.cat
t.cat = "cat"

実行結果

[:@cat, :@dog]
[:cat, :dog, :nil?]
"meow"
./draft.rb:25:in `<main>': undefined method `cat=' for #<Test:0x00000 @cat="meow", @dog="bowwow"> (NoMethodError)

うまくいった。
instance_variable_set でインスタンス変数に値を代入しておいてその後に、getter を設定した。
self.class.send(:attr_reader, "#{k}")
を削除すると、[通常の書き方]から attr_reader を削除した場合と等しくなる。

# --------------------

次はインスタンス変数を定義しないし attr も必要ない場合。
定義したいのはinstance_method def cat と def dog だけ。

通常の書き方。

class Test
  def initialize
  end
  def cat
    "meow"
  end
  def dog
    "bowwow"
  end
end

t = Test.new
p t.instance_variables
p t.class.instance_methods[0..2]
p t.cat

実行結果

[]
[:cat, :dog, :nil?]
"meow"

上記と同じ結果を導くには?

class Test1
  def initialize(h)
    h.each{|k,v|
      self.class.send(:define_method, k){v}
    }
  end
end

h = {:cat=>'meow', :dog=>'bowwow'}
t = Test1.new(h)
p t.instance_variables
p t.class.instance_methods[0..2]
p t.cat

実行結果

[]
[:cat, :dog, :nil?]
"meow"


まとめ
1, getter setter をinitialize 内で使う
self.class.send(:attr_accessor, k)
self.class.send(:attr_reader, k)
self.class.send(:attr_writer, k)

2, @cat のようなインスタンス変数に値を代入する (k はシンボル)
instance_variable_set("@#{k}", v)
# setter が定義されていることが必要条件
self.send("#{k}=", v)

3, def cat のようなインスタンスメソッドを定義する
self.class.send(:define_method, k){v}

+++ 追記 +++

attr_accessor は通常は クラス内の def initialize の上とか下に書く。
initialize の中には書かない。つか普通にエラーになる。
ということは、class Test のinstance が保有するメソッドじゃなくて
class Test が知っているメソッドだとわかるわけだけども
p Test.methods
とかやったって表示されない。じゃ一体どこにあるんだ?ということで下記を作ってみた。

  4 module My
  5   def self.search_method(x, key)
  6     ary = x.methods
  7     print "=== OBJECT:#{x} ===\n"
  8     ary.each{|m|
  9       next unless /methods/.match(m.to_s)
 10       next if m == :methods
 11       eval "a = x.send(m)
 12         if a.index(key)
 13           print \"[:#{key}] exist in #{m}\n\"
 14         else
 15           print \"# #{m}\n\"
 16         end"
 17     }
 18   end
 19 end
 20
 21 class Test
 22   def initialize
 23     My::search_method(self.class, :attr)
 24     # My::search_method(self, :nil?)
 25   end
 26 end
 27
 28 Test.new


# 実行結果

=== OBJECT:Test ===
# instance_methods
# public_instance_methods
# protected_instance_methods
# private_instance_methods
# singleton_methods
# protected_methods
[:attr] exist in private_methods
# public_methods


クラスの「private_methods」に属していることがわかった。
結局...
self.class.send(:attr_accessor, k)
self.send("#{k}=", v)

こういう方法でインスタンス変数を定義するってことは、親の遺伝子を子供が外側から操作しているように思えてきてよくないような気もしてきた。

2010/08/01

singleton_class

昨日のその後

% ruby192 -v
ruby 1.9.2dev (2010-07-11 revision 28618) [x86_64-darwin10.4.0]

module My
  def self.detail(s)
    p s
    puts "ParentName: #{s.class.name}"
    puts "Object_id: #{s.object_id}"
    puts "Methods 1to5: #{s.methods[0..4]}"
    begin
      puts "Method_define? Hello: #{s.class.method_defined?(:hello)}"
      puts "Class.Ancestors: #{s.ancestors}"
      puts "Superclass: #{s.superclass}"
      puts "Singleton_class #{s.singleton_class}"
      puts "Singleton_class #{s.singleton_class.object_id}"
    rescue
    end
    puts "\n"
  end
end
class Test1
  @@top = self
  puts "# -- main of class Test1 --"
  My::detail(self)
  class << self
    @@top_self = self
    puts "# -- class << self of class Test1 --"
    My::detail(self)
  end
  def initialize
    puts "# -- def initialize of class Test1 --"
    My::detail(self)
    puts "="*10
    puts "# TEST: @@top.singleton_class.equal?(@@top_self)"
    p @@top.singleton_class.equal?(@@top_self)
    puts "#"*5
    puts "self.class:: #{own1 = self.class}"
    puts "self.class.singleton_class::  #{own2 = self.class.singleton_class}"
    puts "# TEST: @@top.equal?(self.class)"
    p @@top.equal?(own1)
    puts "# TEST: @@top_self.equal?(self.class.singleton_class)"
    p @@top_self.equal?(own2)
  end
  def hello
    return self
  end
end
t = Test1.new.hello


実行結果

# -- main of class Test1 --
Test1
ParentName: Class
Object_id: 2151885720
Methods 1to5: [:allocate, :new, :superclass, :freeze, :===]
Method_define? Hello: false
Class.Ancestors: [Test1, Object, Kernel, BasicObject]
Superclass: Object
Singleton_class #<Class:Test1>
Singleton_class 2151885700

# -- class << self of class Test1 --
#<Class:Test1>
ParentName: Class
Object_id: 2151885700
Methods 1to5: [:nesting, :constants, :allocate, :new, :superclass]
Method_define? Hello: false
Class.Ancestors: [Class, Module, Object, Kernel, BasicObject]
Superclass: #<Class:Object>
Singleton_class #<Class:#<Class:Test1>>
Singleton_class 2151884980

# -- def initialize of class Test1 --
#<Test1:0x00000100864970>
ParentName: Test1
Object_id: 2151883960
Methods 1to5: [:hello, :nil?, :===, :=~, :!~]
Method_define? Hello: true

==========
# TEST: @@top.singleton_class.equal?(@@top_self)
true
#####
self.class:: Test1
self.class.singleton_class::  #<Class:Test1>
# TEST: @@top.equal?(self.class)
true
# TEST: @@top_self.equal?(self.class.singleton_class)
true


# TEST: @@top.singleton_class.equal?(@@top_self)
は一体何をやっているのか?

Test1 直下における self が .singleton_class メソッドを呼び出すと
#<Class:Test1>
が得られる。
その #<Class:Test1> は、class << self した self 自身と同等のオブジェクトだろうか?
これを確かめたかった。

結果は ture だった
実際

# -- main of class Test1 --
...略

Singleton_class #<Class:Test1>
Singleton_class 2151885700

# -- class << self of class Test1 --
#<Class:Test1>
ParentName: Class
Object_id: 2151885700

同じobject_id をさしている。

一方
initialize の中から呼び出した
self.class と
self.class.singleton_class
に関しても 同様に true がかえってきた。

# TEST: @@top.equal?(self.class)
true
# TEST: @@top_self.equal?(self.class.singleton_class)
true


# TEST: detail(self.class) in initialize もこの(true)結果を裏付けた。

own1 に代入された self.class はTest1 である。
それは class Test1 直下の self と同等。その証拠に同じobject_id を指している。
own2 に代入された self.class.singleton_class は、#<Class:Test1>である。
それは class << self 内の self と同等。同じobject_id を指している。

あらためて...
Test1.new して出来上がったオブジェクトは、class Test1 自身 のインスタンスだってことを思い知らされた。
class Test1 直下のself と、Test1.new されインスタンスとして登場した self とはそれぞれ異なるオブジェクト。
その証拠に異なる object_id を持つ。

class Test1
class << self
end
end

この時点で2つのオブジェクト(Test1 と #<Class:Test1>)にアクセスできる準備ができており
Test1.new することでインスタンスが生成されて3つめのオブジェクトができあがる、と。

2010/07/31

attr_accessor

『プログラミングRuby1.9 言語編』(ISBN: 978-4-274-068096) を読みながら試してみたこと
# P.351 にクラスレベルのアクセサを定義する方法と解説がありました。

class Test
@var = 99
class << self
attr_accessor :var
end
end

puts "Original value = #{Test.var}"
Test.var = "cat"
puts "New value = #{Test.var}"

# 実行結果

Original value = 99
New value = cat


# 少し書き換えself を表示してみると

class Test
  p self
  @var = 99
  class << self
    p self
    attr_accessor :var
  end
end

# 実行結果

Test
#<Class:Test>
Original value = 99
New value = cat


# P.351 には

... 次のように特異クラス内で attr_accessor を呼び出す必要があります。

# とあるのだからして
# <Class:Test> が特異クラスなのだと理解していいのだろうか?
# #<Class:#<MyClass:xxxxx>> のような形式になっていないのが気になる...
# という疑問は一旦おいといて

# で、この class << self を使わないかぎり attr は定義できないのだろうか?
# class << self と書くのとは異なる方法はないのかな?

class Test1
  @var = 89
  self.class.attr_accessor :var
end

実行結果

# =>
./test.rb:30:in `<class:Test1>': private method `attr_accessor' called for Class:Class (NoMethodError)
  from ./test.rb:28:in `<main>'

# エラーだ。
# attr_accessor は <class:Test1> にとって private method だから外から呼べないよと言われてしまった。
# そんなメソッドは知らない、とは言われてない、てことは?

class Test2
  @var = 123
  self.class.send(:attr_accessor, :var)
end

puts "Original value = #{Test2.var}"
Test2.var = "dog"
puts "New value = #{Test2.var}"

# 実行結果

Original value = 123
New value = dog

# やったー
# では、これをinitialize の中でも使えるかな?

class Test3
  def initialize
    @var = "99-dayo"
    self.class.send(:attr_accessor, :var)
  end
end

t = Test3.new
puts "Original value = #{t.var}"
t.var = "apple"
puts "New value = #{t.var}"

# 実行結果

Original value = 99-dayo
New value = apple


# 使えた!ので ...

# @varなど予め定義されたインスタンス変数が不要な場合で(というか用意したハッシュの値を既定値にしたい)
# 尚かつインスタンス変数を定義する時になんら制限をもたせなくてよい条件下。
# ハッシュをうけとった class Test4 が
# 受け取ったハッシュをもとにインスタンス変数とアクセサを定義するには?

class Test4
  def initialize(h)
    @now ||= Time.now
    h.each{|k,v|
      ks = k.downcase.to_sym
      self.class.send(:attr_accessor, ks)
      self.send("#{ks}=", v)
    }
  end
end

h = {"Now"=>"123","Cat"=>"meow", "Dog"=>"bowwow"}
t = Test4.new(h)
p t, t.cat, t.dog

puts "----\n"
print "Instance_variables: #{t.instance_variables}\n"
print "Singleton: #{t.singleton_methods} \n"
print "Singleton_class: #{t.singleton_class}\n"

# 実行結果

#<Test4:0xxxxxx @now="123", @cat="meow", @dog="bowwow">
"meow"
"bowwow"
----
Instance_variables: [:@now, :@cat, :@dog]
Singleton: []
Singleton_class: #<Class:#<Test4:0xxxxxx>>


# あれーTest4 の中に singleton_class が存在しているのはなぜかしら
# self.class してる時点で自動的に特異クラスができあがっているのだろうか。

# 一瞬 object_id 同じなのか!?とぎょっとしたけど
# print "Object_id: #{t.object_id}, #{t.singleton_class.object_id}\n"
# を実行してみたら同じじゃなかった。

+++ memo +++

これ <http://midorex.blogspot.com/2010/05/selfsend2.html> を書いた頃...
ハッシュを受け取ったクラスがインスタンス変数を定義する際、アクセサによる一定の制限があればいいと考えた。
なぜなら Amazon から受け取る値の[全て]が必要ではないだろうと判断したから。

でも、全ての値をinstanceにしてもいっかと考えれば
アクセサによる制限は必要なくなるので上記のように書き換えてもいいのかもしれない..がよくわかっていない...。

<http://github.com/midore/get_item_amzon>

2010/06/16

refe.rb を読む

これの続き。

たとえば

$ rurema String 

とすると

bitclust の

bitclust/bin/refe.rb String -d db_path

を実行させているってことがわかったので↓
rurema/myrurema/bin/rurema

 92     sh "#{bitclust_path/'bin/refe.rb'}" +
 93           " #{query} -d #{db_path(ver)}", :silent => true


それでふと refe.rb は "String" などの文字列うけとった後どうしているのだろう?と思っておいかけてみた。
(デバッガーみたいなものを使い倒せる人じゃないので p self;exit を付け足すといった原始的な方法)

bitclust/bin/refe.rb

def _main
  refe = BitClust::Searcher.new


BitClust::Searcher をnew して、class Searcher の method parse に "String" という引数を渡しているらしきことがわかったので
class Searcher を探す...

% grep Searcher lib/bitclust
class Searcher は searcher.rb ファイルに納められていることがわかった。

bitclust/lib/bitclust/searcher.rb

 20 module BitClust
 21 
 22   class Searcher


public method parse は80行あたりにあった。

 81     def parse(argv)
 82       @parser.parse! argv


引数としてうけとった argv の中身は
["String", "-d", "/path/to/data/db/1.9.2"]
配列になっていて "/path/to/data/db/1.9.2" は、myrurema でいうところの db_path がわたされていた。

そして条件分岐した後に
refe_mode_check argv
を実行していた。この時 refe_mode_check に渡している argv には ["String"] がおさめられていた。

refe_mode_check をみてみると、(line 118あたり)
@target_type によって条件分岐していて、@target_type が :class だった場合とそうでない場合の処理があった。

でも
% rurema String を実行した場合 @target_type は nil になるので case 中の条件のどれにもあてはまらない。
case がおわった後の箇所から実行されることになる。
(line 135 あたり)

       @view = TerminalView.new(compiler,
...略


class TerminalView を new して @view にそれを代入していることがわかった。
それでこのあとの続きはどこにあるのだろうか?
@view には class TerminalView が代入されているだけで、ほかにメソッドが与えられていない。

これまで辿ってきた道を引き返していくほかなさそうだ... 。
最初までもどってみたら続きがあった。

bitclust/bin/refe.rb

def _main
  refe = BitClust::Searcher.new
  refe.parse ARGV
  refe.exec nil, ARGV

要は
1, _main は ローカル変数 refe に class Searcher を代入する。
2, class Searcher のパブリックメソッドである parse を実行し@view にTerminalView.new を代入する。
3, class Searcher のパブリックメソッドである exec を実行。

ということのようだ。

では早速続きにあたる3番目の箇所 class Searcher の method exec をみてみよう。

 90     def exec(db, argv)
 91       if @listen_url


@listen_url 変数に何か値がはいっていたら spawn_server を、そうでなければ search_pattern を実行する、と。
% rurema String を実行した時 @listen_url は nil なので
line 94 の search_pattern
が実行される。

def search_pattern みてみよう。

200     def search_pattern(db, argv)
201       db ||= new_database()
202       case @target_type || db
203       when :class
...略
214       else


引数として "String" を渡した時は @target_type は nil なので line 214 以降に移動する。移動すると argv のサイズによっての条件分岐が始まる。サイズが 1 の場合は

219         when 1
220           find_class_or_method db, argv[0]

find_class_or_method を実行する...

262     def find_class_or_method(db, pattern)
263       case pattern


pattern の値によって処理を分けている。
pattern には "String" がおさまっている。
db には#<BitClust::MethodDatabase>
 
"String" という文字列受け取った場合は when /\A[A-Z]/ に該当するのでそれに続く begin以降にジャンプする。

270       when /\A[A-Z]/   # Method name or class name, but class name is better.
271         begin
272           find_class db, pattern

次に探すべきメソッドは find_class 。

find_class

241     def find_class(db, c)
242       @view.show_class db.search_classes(c)
243     end


をーここで @view と出会いました。やっと話がつながった感じがします。
つまり

1, _main は ローカル変数 refe に class Searcher を代入する。
2, class Searcher のパブリックメソッドである parse を実行し@view にTerminalView.new を代入する。
3, class Searcher のパブリックメソッドである exec を実行。


この3番目の処理が line 242line 90 にあたると。
2番目の処理で「あらかじめ」Searcher のインスタンス変数である @view に class TerminalView が代入されているからここ(line 242 )で@view.show_class を使うことができるわけね、と。

一応、確認してみると

def find_class(db, c)
  p @view.class, c ; exit
end

% rurema String
BitClust::TerminalView
"String"

ちゃんと TerminalView が代入されている。

ではでは
class TerminalView の methods show_class をみてみよう。

324     def show_class(cs)
325       if cs.size == 1

def show_class は cs をうけとって そのサイズが1だった場合とそうじゃなかった場合とで処理を分岐させている。

% rurema String
の場合、cs は [#<class String>]

% rurema Hash 
の場合、cs は [#<class Hash>]
と変化する。@line が false の場合で、csのサイズが1ならば describe_class を実行する。

なので
% rurema String
した時は

line. 329 describe_class cs.first
を実行するのだとわかった。
では def describe_class はどこだろう? line 411 あたりにあった。

describe_class

411     def describe_class(c)
...略
416       puts "#{c.type} #{c.name}#{c.superclass ? " < #{c.superclass.name}" : ''}"
417       p c.class.name; p c; exit


line. 417 を上のようにしてみると

% rurema String
class String < Object
"BitClust::ClassEntry"
#<class String>

と返ってきたのでローカル変数c には ClassEntry が代入されていてそのクラスは type や name といったメソッドを持っているとわかった。
つづいて

417       unless c.included.empty?
...略
423       unless c.source.strip.empty?

とあって c (すなわち BitClust::ClassEntry)のincluded が empty じゃなかったら空行を1行出力し
c.included.each ...
を実行し
同様に、c.source.strip.empty? がempty じゃなかったら空行を1行出力し
@compiler.compile(c.source.strip)
を実行する。
ようだ。

% rurema String
を実行した場合は何行目の処理が実行されるのか?

line 417 のunless c.included.empty? にキャッチされるとわかった。従ってこの部分

419         c.included.each do |mod|
420           puts "include #{mod.name}"
421         end

を実行していることになる。

変数c には class ClassEntry が代入されているので c.included は、ClassEntryクラスの included メソッド。included メソッドはブロックを受け取ることができるのだなぁ、と。

では class ClassEntry がどうなっているかをみてみよう...。 classentry.rb を開いてみると

bitclust/lib/bitclust/classentry.rb

class ClassEntry < Entry

Entry クラスを継承していた。searcher.rb が使っていた included はどこだろ?継承元の Entry の方なのかな?いや無い。てことは ClassEntry が自分でもっている?ないみたい。

うむむ、どうやら何かにくるまれているようだ...。

hash のキーとしてならあるんだけども...

bitclust/lib/bitclust/classentry.rb

 77       property :included,   '[ClassEntry]'


included のゆくえがわからなくなってしまった。

こういう時は最初に initialize がどうなっているかをみてみよう。

 16   class ClassEntry < Entry
 ... 略 
 36       init_properties


init_properties って何かな?classentry.rb ファイル中にその実態は無いようだ。
継承元のクラスのファイルを開いてみると
% vim entry.rb
init_properties は module_eval によって定義されていた...。

もう一度頭を整理して考えてみよう。呼び出した側は下記のようになっていて
bitclust/lib/bitclust/searcher.rb

419         c.included.each do |mod|
420           puts "include #{mod.name}"
421         end


ClassEntryクラスのパブリックメソッドの中に included という名前のメソッドはない。
classentry.rb ファイル中に included という名前で存在しているのはハッシュのキーのみ。

最終的なところまで近づいてきた感じがしつつもこの先はちょっと難しそうだな。読み解けるかなぁ...
今日はここまで。

2010/06/08

install-rurema

Rubyの新リファレンスマニュアルをコマンドラインから簡単に引くためのツールを書いた

うわっこれいいな。早速 install

http://github.com/yhara/myrurema

README を読むと gem を使ってインストールできるとあったんですが、ちょっと gem 苦手な為 gem を使わないで動せないものかな?と、try してみました。 (OS X 10.6.3)

$ git clone git://github.com/yhara/myrurema  rurema
$ cd rurema/myrurema/bin
$ vim rurema
# shebang を自分の ruby 1.9.2 のパスに修正。#!/usr/bin/env ruby #  changed
$ pwd
/path/to/rurema/myrurema/bin

# とりあえず

$ ./rurema
Usage: rurema [options]
         --init                       initialize rurema
         --update                     update documents and database
...(略)

# README を読みながら
# Init

$ ./rurema --init --ruremadir=/path/to/rurema/data

# これを実行すると ~/home に.subversion ディレクトリができた。

try

$ ./rurema Array#index
You don't have a database for ruby 1.9.2.
109 Make it now? [y/n]
...略

y としたけど ~/home/.rurema を作っていないのだからアップデートできない。

$ ln -s /path/to/rurema/data ~/.rurema

シンボリックはってみた

# Update

$ ./rurema --update --ruremadir=/path/to/rurema/data

# この時勢いよくファンが回りはじめてちょっと不安になった。$ top してみると ruby(OS X に標準インストールされてる方) 一時的に50% 台になったりしつつも基本的には10% 台で動いてた。ずっとみていたら急に90% 近くなってあ”と思ったとたんに終了してほっとした。

# try

$ pwd
$ /path/to/rurema/myrurema/bin
$ ./rurema Array#index
Array#index
--- index(val)           -> Integer | nil 
--- index {|item| ...}   -> Integer | nil 

指定された val と == で等しい最初の要素の位置を返します。
等しい要素がひとつもなかった時には nil を返します。
...(略)


わい
動かすことができたのでエイリアスにしちゃう。

$ vim ~/.bashrc
# alias 'rurema'='/path/to/myrurema/bin/rurema'

$ source ~/.bashrc
$ rurema String#size
String#size
--- length -> Integer
--- size -> Integer

文字列の文字数を返します。バイト数を知りたいときは bytesize メソッドを使ってください。

@see [[m:String#bytesize]]


コマンドラインからるりまをみることができました。うれしいな。便利だなー。

+++ まとめ +++

# Download rurema 
$ git clone git://github.com/yhara/myrurema rurema
$ cd rurema/myrurema/bin

# shebang を自分がインストールした ruby 1.9.2 のパスに修正。#!/usr/bin/env ruby #  changed
$ vim rurema

# 確認 & ヘルプ
$ ./rurema

# database directory
$ mkdir /path/to/rurema/data

# リンク作成
$ ln -s /path/to/rurema/data ~/.rurema

# init and update
$ ./rurema --init --rubyver=1.9.2 --ruremadir=/path/to/rurema/data
$ ./rurema --update --rubyver=1.9.2 --ruremadir=/path/to/rurema/data

# みてみる
$ ./rurema String#size

# alias
# $ vim ~/.bashrc 
# $ source ~/.bashrc


参照
http://ja.wikipedia.org/wiki/Subversion
SnowLeopard には標準でSubversion入っていた。知らなかった。

2010/06/02

install-ruby-1.9.2-preview3

さっそく

% autoconf
% ./configure --prefix=/Volumes/foo/ruby192 --program-suffix=192 --with-x=no
% make
% make test
% make install


% make test の時

PASS all 932 tests
 ./miniruby -I./lib -I.ext/common -I./- -r./ext/purelib.rb  ./tool/runruby.rb --extout=.ext  -- "./bootstraptest/runner.rb" --ruby="ruby1    92"  ./KNOWNBUGS.rb

Driver is ruby 1.9.2dev (2010-05-31 revision 28117) [x86_64-darwin10.3.0]
Target is ruby 1.9.2dev (2010-05-31 revision 28117) [x86_64-darwin10.3.0]


KNOWNBUGS.rb .
PASS all 1 tests

となりましたが KNOWNBUGS とあったので気にしないで install してみました。

github.com/midore/mblogger
をruby 1.9.2 preview3 でもって「この投稿」を投稿してみました。どうかな?
=> 成功しました。
put はどうかな?
=> OK.

github.com/midore/mdiary
こっちも、検索できなくなったかな?と思ったけどできてるみたい。

github.com/midore/2010-amazon-jp-api
は動かない!と思ったらruby 1.9.2 はとはぜんぜーん関係ない問題でした。ショック。ずっと気づいてなかった。

2010/05/20

self.send()[2]

http://midorex.blogspot.com/2010/05/selfsend.html
の続き。

  class Foo
    ary = [
      "Author", "Binding", "Creator",
      "EAN", "ISBN", "Label",
      "Manufacturer", "NumberOfPages",
      "ProductGroup", "ProductTypeName",
      "PublicationDate", "Publisher", "Studio",
      "Title", "MediumImage", "Price", "Edition",
      "Rank", "DetailPageURL", "Artist",
      "Format", "NumddberOfDiscs", "OriginalReleaseDate",
      "PackageQuantity", "ReleaseDate", "UPC", "RunningTime"
    ].each{|x| attr_accessor x.downcase.to_sym}

    def initialize(h)
      return nil unless h
      set_up(h)
      #@created ||= Time.now.to_s
    end

    def detail
      str = ""
      ins_a.each{|i| str << i.to_s.gsub("@",'').upcase + ":\s" + self[i] + "\n" }
      return str
    end

    def set_up(h)
      h.each{|k,v| 
        begin
          self.send("#{k.downcase.to_sym}=", v)
        rescue NoMethodError
          next # ignore new value.
        end
        }
    end

    # def detail needs [] and ins_a
    alias [] instance_variable_get
    alias ins_a instance_variables

    private :set_up, :[], :ins_a

  end
  h = {'Artist'=>'artist-name','Title'=>'music-title'}
  p Foo.new(h)


% ./test-item.rb
#<Foo:0x000000000 @artist="artist-name", @title="music-title">

h = {'Artist'=>'artist-name','Title'=>'music-title'}
h['noindex'] = 'noindex'
p Foo.new(h)
% ./test-item.rb
#<Foo:0x000000000 @artist="artist-name", @title="music-title">

git => github.com/midore/get_item_amzon

2010/05/16

self.send()[1]

gdata-1.1.1 を読んでいたら gdata-1.1.1/lib/gdata/client/base.rb に

41           self.send("#{key}=", value)

とあった。

それで
クラスを初期化する際 hash をもちいてインスタンス変数を一気に設定したい場合について考えてみた。

1) 最初にgdataでも使われている self.send() を使った場合

  3 class My_1
  4   attr_writer :id, :name
  5   attr_reader :name
  6   def initialize(h={})
  7     h.each{|k,v| self.send("#{k}=", v)}
  8   end
  9 end
 10 
 11 h = {:id=>'123', :name=>'abc'}
 12 p My_1.new(h)


実行結果
#<My_1:0xxxxxxxx @id="123", @name="abc">

ハッシュのキーと値がinstance 変数に設定されていることがわかる。
また attr_writer によって :id と :name 以外のkey を受け付けないよう設定されている。
これを確認する為、
h = {:id=>'123', :name=>'abc', :title=>'ddd'}
に変更し実行してみると
...(NoMethodError)

:title は attr_writer に定義されていないので instance 変数として定義できないことが確認できた。

2) 次に self[]= をつかってinstance 変数を設定するクラスの場合

 15 class My_2
 16   attr_writer :id, :name
 17   attr_reader :name
 18   def initialize(h={})
 19     h.each{|k,v| self[:"@#{k.downcase}"] = v}
 20   end
 21    alias []= instance_variable_set
 22    alias [] instance_variable_get
 23    private :[], :[]=
 24 end
 25 p My_2.new(h)


実行結果

#<My_2:0xxxxxxxx @id="123", @name="abc">

My_1 の場合と全く同じ結果を得ることができる。
では、My_1 の場合と同様に h を変更し
h = {:id=>'123', :name=>'abc', :title=>'ddd'}
として実行してみるとどうなるか?

実行結果

#<My_2:0xxxxxxxx @id="123", @name="abc", @title="ddd">


attr_writer とは無関係になんなく :title が定義されてしまった。
15行目のattr_writer :id, :name は class My_2 においては無意味のようだ。

では、あたかも attr_writer を設定したかのようにさせるにはどうすればいいのだろうか?

 14 class My_2
 15   @@ins_keys = [:id, :name]
 16   @@ins_keys.each{|x| attr_reader x}
 17   def initialize(h={})
 18     h.each{|k,v|
 19       key = k.downcase.to_sym 
 20       self[:"@#{key}"] = v if @@ins_keys.index(key)}
 21   end
 22   alias []= instance_variable_set
 23   alias [] instance_variable_get
 24   private :[], :[]=
 25 end
 26 h = {:id=>'123', :name=>'abc', :title=>'ddd'}
 27 p m = My_2.new(h)
 28 p m.name

実行結果

#<My_2:0xxxxxxxx @id="123", @name="abc">
"abc"


@@ins_keys を定義し19行目に if @@ins_keys.has_key?(k) を追加することで
attr_writer に「似た」役目をはたしてもらうことにした。
attr_reader は16行目にまとめた。
配列の要素を全て attr_reader にした。
配列の要素をチョイスして attr_reader したい場合はさらに2行加える必要あり。

@@ins_keys に登録されていない :title は定義されなくなった。
つまり定義したい instance 以外 が定義されるようなことはおこらないという意味で My_1 と同じ結果を得られるようになった。
ただし、My_1 ではerrorが返ってくるが My_2 では error にはならない。
error を返す必要があれば20行目直前に raise unless @@ins_keys.index(key) などすればいいのかも。

+++ メモ +++

0) self[] を initialize で使用しつつどの key を instance 変数として定義するかをちゃんと制御したい時
attr_writer で設定したつもりになっていてもそれは意味をなさないので、別途 式を用意するなどする必要がある。
あるいは最初から My_1 のような方法を採用した方がいいかも。
あるいは通常どおりに def initialize で @id = nil などと定義しておいて instance_variable_defined? を使う。

1) (派手に間違えているかもしれない)書きながら浮かんだイメージ
self.send()
は、一旦廊下に出て attr_writer ドアをノックしてから self 部屋に入るが、
self[]=
は、既にself 部屋の内側に入っていて attr_writer ドアの存在を意識していない。
といったようなイメージ。

2) git にあげてある 2010-amazon-api-jp に、上記の内容を反映する予定は未定。

続き

2010/04/21

2010-amazon-api-jp

SnowLeopard で OpenSSL がバージョンアップしたことに伴い
amazon-jp-api を作り直した。
=>
2010-amazon-jp-api

[参考になったURL]
OpenSSL SHA 256 の使い方はhttp://diaspar.jp/node/239 を参考にしました。

[主な変更点]
1, Mac OS X 10.6.3 で SHA256 を使えるようになったことに伴う変更。
module AmazonAuth => 新規に作ったモジュール。認証用。
2, 設定ファイル => 2つのモジュールにわけた。
3, class AwsItem => インスタンス変数の持ち方の変更。
4, class AmazonAccess => アマゾンにアクセスしデータを獲得したのちに AwsData.new(data).add_data でアイテムまで生成していたが、アイテム生成は他のクラスに移動させて、このクラスはアマゾンにアクセスしデータを獲得するだけのクラスに変更した。
5, class AwsDataWriter => 新規に作ったクラス。マーシャルデータを変更するためのクラス。
6, class AwsDataReader => 新規に作ったクラス。マーシャルデータを読み込んで表示させるためのクラス。
7, module AmazonAuth => 新規に作ったモジュール。アマゾン認証を行うためのもの 。

[変更されていない点]
1, ローカルで使うもの。
2, Ruby 1.9.1 だけで動き Ruby 1.8 系はサポートしていない。
3, マーシャルデータを使用している。
4, 指定したEAN の本やCDのデータをアマゾンから受け取ることができるがアマゾンサーバに対して検索はできない。
5, README に書いたオプション引数はそのまま。

[少しだけ気になっている点]
1, もともとのものは force_encodig を使わないように 変更している。今回もこの状態でうまくいったので受け取ったXMLデータが valid? かどうかだけチェックするにとどめ encode 変換は行っていない。このことによってなにか弊害があるのかないのかは謎。
2, timeout を使っている。使わないバージョンも作ってみたがあっさり挫折。
3, exec を使っている。exec を使わないバージョンを考えてみたがわからなくなって中断。
4, send を使っている。最近使い方を覚えたため、使ってみたかったから使っているにすぎない。あえて使う必要はないのかもしれない。

[このブログ内の関連する投稿]
=> http://midorex.blogspot.com/2009/06/amazon-web-with-ruby-191.html
=> http://midorex.blogspot.com/2009/08/amazon-product-advertising-api.html
=> http://midorex.blogspot.com/2009/12/forceencoding.html
=> http://midorex.blogspot.com/2008/12/amazon-aws-with-ruby191.html

[Amazon ラベルをもつ全ての投稿]
Amazon

2010/04/03

はじめての send

ISBN: 978-4048678841 | リファクタリング:Rubyエディション (大型本)

この本、私は対象読者ではなかった。飛ばし読みしていたらあっという間に p.165 までたどり着いてしまった。

そこで目にしたもの
p.165

サンプル
引数をまとめたHash を受け付けて、それらの引数をインスタンス変数に代入する SearchCriteria クラスがあるものとする。

とあり
p.166

Class.send :include, CustomInitilizers


send って何?といろいろ試してみた。

  1 #!/usr/local/bin/ruby191
  2 # coding: utf-8
  3 
  4 module Items
  5   module Setup
  6     def base(h)
  7       h.each{|k,v| self["@#{k}"] = v}
  8     end
  9     alias []= instance_variable_set
 10     alias [] instance_variable_get
 11   end
 12   class Music
 13     def initialize(h)
 14       self.base(h)
 15     end
 16   end
 17   class Book
 18     def initialize(h)
 19       base(h)
 20     end
 21   end
 22   Object.send :include, Setup
 23 end
 24 
 25 h = {:isbn=>'978xxx', :title=>'book-title', :price=>'1000'}
 26 p b = Items::Book.new(h)
 27 h = {:isbn=>'077xxx', :title=>'music-title', :price=>'2000'}
 28 p b = Items::Music.new(h)
 29 p b['@title']

=>
#<Items::Book:0xxxx @isbn="978xxx", @title="book-title", @price="1000">
#<Items::Music:0xxx @isbn="077xxx", @title="music-title", @price="2000">
"music-title"


最近知った あのaliasを使ってみた。

そして
「プログラミング言語 Ruby」p.285
を読み直した
Book.public_send :include, Setup
にしてもOKだった。

さらに
「Ruby Way」( Second edition)
にて send を探してみたら p.350 にsort_by のカスタムバージョン例の中で send を使う例があった。

self.sort{|x,y| x.send(sym) <=> y.send(sym)


この場合、x はArrayの要素でその要素のひとつひとつには Preson.new() Object が入っていて、
で、Presonの@name は attre_reader で読み込めるようになっている。ここがミソ。従ってソート対象として:name を指定することができる、と。
attre_writer :name だったらこれは成功しないだろう。
p.350 をジーとみているとうっかり send(@name)なのだと勘違いして理解しそうになるが、
send は「シンボル」を受けつけるのだから、send(@name) ではダメ。

+++ まとめ +++
send or public_send (Ruby 1.9) はシンボルを受けつける

module XXX
  module Setup ... end
  class Book ... end
  class Music ... end
  Object.send :include, Setup
end

Object.send とした場合は module が内包する[全て]のクラスのinclude メソッドに Setup module をおくりつけたことになる、と。
Object.send を Class.send としてみたら
`initialize': undefined method `base'
のエラーになった。

module XXX
  module Setup ... end
  class Book ... end
  class Music ... end
  Book.send :include, Setup
end

とした場合は module XXX が内包するBook クラスに[だけ]Setupをおくったことになる
従ってこの場合

module XXX
  module Setup ... end
  class Book
    include Setup
  end
end

と全くおなじ、と。

2010/03/08

instance_variable

http://ujihisa.blogspot.com/2009/12/left-hand-values-in-ruby.htmlを読んで試してみたこと。

  1 #!/usr/local/bin/ruby191
  2 # coding: utf-8
  3 
  4 class Diary
  5   def initialize
  6     @title, @date = nil, nil
  7   end
  8   alias [] instance_variable_get
  9   #alias []= instance_variable_set
 10   def []=(i, v)
 11     instance_variable_set(i, v) if i_defined?(i)
 12   end
 13   def i_defined?(i)
 14     instance_variable_defined?(i)
 15   end
 16 end
 17 
 18 d = Diary.new
 19 d['@title'] = "NewTitle"
 20 d['@date'] = Time.local(2010,02,01)
 21 d['@ddd'] = 'ddd'
 22 p d['@title']
 23 p d
 24 # =>
 25 # "NewTitle"
 26 #<Diary:0xxxx @title="NewTitle", @date=2010-02-01 00:00:00 +0900>


initialize をとりさり i_defined? もとりさってみる

 29 class Diary
 30   alias [] instance_variable_get # instance_variable_defined?
 31   alias []= instance_variable_set
 32 end
 33 
 34 d = Diary.new
 35 d['@title'] = "NewTitle"
 36 d['@date'] = Time.local(2010,02,01)
 37 p d

% ./try.rb
#<Diary:0xxxx @title="NewTitle", @date=2010-02-01 00:00:00 +0900>

勉強になった。

self.instance_variable_set
にしなくちゃいけないと思っていた。
instance_variable_set
だけで動くなんて知らなかった!

alias [] instance_variable_get
これでinstance の値をとれるなんて!

instance_variable_get
のところを
alias [] instance_variable_defined?
に変えてみたら値をとるかわりに定義されているか否かを true or false で返してくれた!びっくり。

alias [] instance_variable_get
alias []= instance_variable_set
alias [] instance_variable_defined?

のようなことをしてみたくなったが
[] methods
は1度使ってしまったら2個目は使えない。の overwrite されちゃう。あたりまえかも。
1回目の [] と
2回目の [] を
区別させる方法はない? ぽい。

2010/02/26

UTF8-MAC [2]

UTF8-MAC みなおし。

要は \\ があればいいんでしょ?とお気楽な発想を展開してみました。
が、以下は非常によろしくないことをしている可能性あり。
あくまでも実験なので試さない方がいいです。おい

macustr.rb

  1 #!/usr/local/bin/ruby19
  2 # coding: utf-8
  3 
  4 exit unless Encoding.default_external.name == 'UTF-8'
  5 
  6 require 'nkf'
  7 def macustr(line)
  8   str = String.new # p str.encoding
  9   line.chars.entries.each{|x| str << "\\" + x}
 10   #line.codepoints.entries.each{|x| str << "\\" + [x].pack("U*")}
 11   mustr = NKF::nkf('--ic=UTF-8 --oc=UTF8-MAC', str)
 12   return mustr
 13 end
 14 
 15 ARGF.each{|line|
 16   exit unless line.encoding.name == "UTF-8"
 17   next unless line.valid_encoding?
 18   mustr = macustr(line.strip)
 19   next unless  mustr.valid_encoding?
 20   sh = system("mkdir #{mustr}")
 21   print "Maked Directory: #{line}" if sh
 22 } 
 23 


line 4 : 環境変数LANGが UTF-8 でない場合はおいかえす。
line 16: 読み込んだ文字列が UTF-8 でなければすぐ終了。
line 17: valid でなければ次
line 18: def macustr に一行わたす
line 9 : 冒頭に"\\" を付け加えて一文字ずつ str に追加。最初は line 10 にしたのだが line 9 にしておいた。
line 11: str を UTF8-MAC に変換
line 19: valid でなければ次
line 20: mkdir コマンド
line 21: コマンドが成功したらメッセージ表示

./macustr.rb utf8_text.txt

を実行すると utf8_text.txt を一行ずつ読み込み
同名のDirectory を同じ階層に作成します。
utf8_text.txt は UTF-8 で保存されたテキストファイル。

[2010-02-27]
s/chomp/strip/g

[2010-03-08]
追記
# 失敗例

  1 #!/usr/local/bin/ruby19
  2 # coding: utf-8
  3 # test-encode.rb
  4 
  5 text = 'utf8-text.txt'
  6 # % cat utf8-text.txt 
  7 # and two &three
  8 
  9 File.open(text, "r:utf-8"){|f|
 10   f.each_line{|x|
 11     #  行末\n や行頭\s を削除
 12     line = x.strip
 13     # print out
 14     puts "Line: \"#{line}\"| Encoding: #{line.encoding}| Valid: #{line.valid_encoding?}\n"
 15     puts "# Calling encode\n"
 16     # encode で変換
 17     mustr = line.encode("UTF8-MAC")
 18     # print out
 19     puts "String: \"#{mustr}\"| Encoding: #{mustr.encoding}| Valid: #{mustr.valid_encoding?}\n"
 20     print "# Calling system\n"
 21     # Directory 作成
 22     sh = system("mkdir #{mustr}")
 23     puts "Result: #{sh}\n--\n"
 24   }
 25 }
 26 
 27 =begin
 28 % ./test-encode.rb 
 29 Line: "and two &three"| Encoding: UTF-8| Valid: true
 30 # Calling encode
 31 String: "and two &three"| Encoding: UTF8-MAC| Valid: true
 32 # Calling system
 33 sh: three: command not found
 34 Result: false
 35 
 36 % ls -F
 37 and/            test-encode.rb* two/            utf8-text.txt
 38 =end


17行目

mustr = ine.encode("UTF8-MAC")

mustr = line.force_encoding("UTF8-MAC")

にしても同様の結果だし

require 'nkf'
mustr = NKF::nkf('--ic=UTF-8 --oc=UTF8-MAC', line)

としてみても結果は同じ

検索して次を読んでみたけど よくわかってないというか...。
<http://redmine.ruby-lang.org/issues/show/1411>
<http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/40233?help>

+++ 感想 +++

通常 Mac OS X を使用する上で自ら日本語名のフォルダを作る人はあまりいないだろうし、スペースが混入した文字列をわざわざフォルダの名前にする人もあまりいないはずなので、じゃあ一体どういった場面で上のような必要が生じるかというと
ずばり
iTunes のための iTunes による Folder 生成 | File 生成なのかな? と。
2010/03/08 現在ではこれ以外の場面で UTF8-MAC への変換なんて一般的には必要とされてないことなのかもしれません。

ただ、今後 iPad が登場しまた新しくiのつく名前のアプリケーションがでてきたりすると UTF8-MAC への変換に対する需要は増えるかもしれませんね?電子書籍を買って読むための iBooks.app が OS X に搭載される日は案外近い将来かもしれません。
iPad

それで、例えば iBooks.app が生成するであろう著書名のついたファイルにアクセスする際、もしも著書名に\& や\s が含まれていたり日本語だったりしたらRuby からそれらのファイルにアクセスしようとするのはちょっと困難がありそうです。いまの iTunes ファイル群と同様に。
# Ruby からはそういうファイル群にアクセスしないから大丈夫...といった意見の人もいるかもしれません。
# あるいは上に書いた失敗例を[ちゃんとした]成功例にする方法を私が知らないだけのことなのかもしれません。
# macustr.rb は一見成功してるようにみえるけどもちゃんとはしていないだろうし。

はたまた違う観点からみた場合、セキュリティ的にどうなんだろう?って問題も私にはよくわからないけどきっとあるんだろうと思います。そっちが優先された上で現在こうなっているのであればこのままの方がうれしい人も多いのかもしれません?。

ということで、幾日経過後のまとまりのない&とりとめないのない感想でした。

# 私が間違った認識をしている可能性は多分にあります。何かお気付きの方はコメントお願いします。