注意!コード間違ってました。インスタンスを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技術者認定試験にパスしていていも、知らないことはたくさんあります。