注意!コード間違ってました。インスタンスを2度生成すると、aliasが2度働いて無限ループになってしまいます。initialize内のコードが2度走らないようにする必要があります。

taken by sun dazed under CC-BY-SA
最近Rubyで遊んでいます。本当はRubyで遊ぶのが目的ではなかったのですが。今までの変遷。
PySpecの新バージョンを作ろう
→PySpecの新バージョンでは新しいアーキテクチャーに挑戦しよう
→軽量メッセージキューを実装しよう。名前は栃木っぽくberryMQ
→せっかくMQなら多言語対応しないと面白くないよね。Ruby版作ろう。
→言語間の通信はXML RPCでもいいけど、JSON RPCかな?Ctrl+Cで止まらないの不便だな
→Rubyはデコレータないのか?似たようなやつに挑戦してみるか?
→MQでPythonのAPIではメソッドの上書き使っているけど、Rubyではどうやるんだろう?←イマココ
ということで、遠回りしまくってます。で、Rubyのメソッドの上書きをやろうと思ったのですが、なかなかうまくいかず、@takaiのアドバイスを元にうまくいきました。せっかくできたのだし、toRubyでスレッドとかミューテックスあたりの勉強をしていることもあり、Javaで「内部実装はすごい」という噂のsynchronizedを実装してみました。使ってみたサンプルコードはこんな感じ。デコレータと組み合わせて、メソッド宣言の前にsynchronized宣言をpublicとかみたいに書くことで、インスタンスのロックが自動で行われるようになります。あと、Javaっぽい、メソッド内でのsynchronizeブロックもできます。
最初、synchronizedの仕様を誤解していたけど、咳さんにアドバイスをもらって、修正しました。@takaiさん、咳さん、ありがとうございました。
class TestClass
include Synchronizable
synchronized
def test_method1(name)
3.times do |i|
puts "test_method1 => #{name}: #{i}"
sleep 1
end
end
def internal_lock(name)
synchronize do
3.times do |i|
puts "internal_lock => #{name}: #{i}"
sleep 1
end
end
end
end
sample = TestClass.new()
thread1 = Thread.new {
sample.test_method1("sub thread 1")
}
thread2 = Thread.new {
sample.internal_lock("sub thread 2")
}
sample.test_method1("main thread")
thread1.join
thread2.join
実際のコードは「続きを読む」からどうぞ。
# -*- coding: utf-8 -*-
# Synchronize decorator for Ruby.
require 'monitor'
module SynchronizeImpl
# Manage decorator information for each class.
module Decorators
@@decorators = Hash.new {|hash, class_name|
hash[class_name] = {:flag=>false, :methods=>[]}
}
# It is called when decorator method is called
def self.activate_flag(klass)
@@decorators[klass][:flag] = true
end
# It is called when new method is defined
def self.regist_method_name(klass, method_name)
decorators = @@decorators[klass]
if decorators[:flag] == true
decorators[:methods].push(method_name)
decorators[:flag] = false
end
end
# Return all decorators of significant class
def self.get(klass)
@@decorators[klass][:methods]
end
end
# This module defines actual decorator methods.
# Target object is extended by this module.
module DecoratorAPI
def method_added(method)
Decorators::regist_method_name(self, method)
end
# Synchronize decorator method
def synchronized
Decorators::activate_flag(self)
end
end
end
# User uses this module
module Synchronizable
def self.included(base)
base.extend(SynchronizeImpl::DecoratorAPI)
end
def initialize
@monitor = Monitor.new
SynchronizeImpl::Decorators::get(self.class).each do |target_method|
original_method = :"original_#{target_method}"
self.class.class_eval do
p target_method
alias_method original_method, target_method
define_method target_method do |*args|
@monitor.synchronize do
__send__ original_method, args
end
end
end
end
end
def synchronize
@monitor.synchronize {
yield
}
end
end
文字列の前にコロンを置くとシンボルになるって知りませんでした。これで変数展開を使って動的にシンボルが作れるんですね。intern使わなくても。Ruby技術者認定試験にパスしていていも、知らないことはたくさんあります。