From: Chuck Remes Date: 2008-08-05T09:07:12+09:00 Subject: Re: Is it ellegant to use a global variable to store a Logger object? On Aug 4, 2008, at 6:30 PM, Chuck Remes wrote: > > On Aug 4, 2008, at 5:38 PM, Chuck Remes wrote: >> >> For lurkers, here is some slightly corrected code (the above >> doesn't compile). >> >> require 'logger' >> >> module Namespace >> module Logger >> def Logger.key= *key >> @key = key.first unless key.empty? >> end >> >> def Logger.key >> @key >> end >> >> def Logger.instance >> @instances ||= Hash.new { |h,k| h[key || :default ] >> = ::Logger.new(STDOUT) } >> @instances[@key] >> end >> >> %w( debug info warn error fatal ).each do |method| >> module_eval <<-code >> def Logger.#{ method }(*a, &b) >> instance.#{ method }(*a, &b) >> end >> code >> end >> end >> end > > Hmmm... I puzzled through this all the way home and came to the > conclusion that I don't know why, or if, this works. > > If we are saving information in an instance variable in a class > method, clearly that instance variable can be overwritten by another > writer, yes? These class methods are visible to anyone who can this > module in scope, therefore the @key variable can be pretty volatile. > > This whole setup is predicated on WriterA calling > Namespace::Logger.key = which is later used by > Namespace::Logger.instance as an index into a hash to lookup the > correct ::Logger object in @instances. However, there is no > guarantee that @key will remain for WriterA. If WriterB > comes along and makes the same call, that writer replaces val> with its own value. > > If that didn't happen, then how would @instances store references to > everyone's ::Logger objects? These variables must be shared for that > to work. > > Right? > > If true, I'm back to square one. Each writer would have to pass in > their own unique key so they lookup the right ::Logger object. Now > instead of injecting the ::Logger object into all of my classes, I > have to make sure the @key value is known by all. > > Please correct me where I am wrong. Here's proof it doesn't work as currently written. I'm hoping there's a trick I don't know about ruby that will allow this to work. cr require 'logger' module Namespace module Logger def Logger.key= *key @key = key.first unless key.empty? end def Logger.key @key end def Logger.instance @instances ||= Hash.new { |h,k| h[k || :default ] = ::Logger.new(STDOUT) } @instances[key] end %w( debug info warn error fatal ).each do |method| module_eval <<-code def Logger.#{ method }(*a, &b) instance.#{ method }(*a, &b) end code end end class Bar def initialize(value) Namespace::Logger.key = value end def foo Namespace::Logger.info { "called with Logger.key [#{Namespace::Logger.key}] " } end end end a = Namespace::Bar.new 17 b = Namespace::Bar.new 24 a.foo b.foo -- output -- I, [2008-08-04T19:06:50.597418 #91765] INFO -- : called with Logger.key [24] I, [2008-08-04T19:06:50.598877 #91765] INFO -- : called with Logger.key [24]