Is it ellegant to use a global variable to store a Logger object?

On Wed, Jun 25, 2008 at 3:36 PM, Andrea F. [email protected]
wrote:

c.foo.bar
(whichever way you do it) saves memory and is less tedious.

you imagine how much more work refactoring will become?

I am by no means qualified to judge DIin general and I have to admit
that my wording might have been indeed to offensive. Scusa per questo.
It is however intriguing to me when dependency injection is a good
solution. I am sure that there is plenty space for its application,
maybe even for logging in a context more specific than the one given
by OP. I believe that the main concern would be the loose coupling of
objects, IIUC you would need a Container method for each class and
each class would need to have an adapted initialize method, well that
just really seems too much work to me.

Maybe I shall answer Robert’s question at the same time :wink:

  1. A logging mixin

module MyLogger
logger = Logger::new $stderr
define_method :log do logger end
end

now all you have to do is to do include MyLogger in all your classes
and it is sufficient to change the MyLogger module once only, that of
course has not yet any advantage over using a global logger variable
or constant.
But you might have much more flexibility by generalizing the approach
above as follows

module MyLogger
logger1 = …
logger2 = …
class << self; self end.module_eval do
define_meyhod :included do |into_module|
logger =
case into_module
when Type1Module
logger1
when Type2Module
logger2
else
Logger::new $stderr
end
into_module.module_eval do
define_method :log do logger end
end*

That would give you pretty easy central control of logger capabilities
on one single point of your project in a more transparent way.
Actually DI makes him carry quite a have load, does it not?

Cheers
Robert

http://ruby-smalltalk.blogspot.com/


Les mêmes questions qu’on se pose
On part vers où et vers qui
Et comme indice pas grand-chose
Des roses et des orties.

Francis Cabrel

2008/6/25 Robert D. [email protected]:

Maybe I shall answer Robert’s question at the same time :wink:

  1. A logging mixin

That’s a classical bug: off by one. :slight_smile:

robert

Robert D. ha scritto:

end

for all classes that do want to do logging and also manually code the
use.inject do |as, often| as.you_can - without end
the expression “dependency injection” says it allready it is almost as

Andrea

I am by no means qualified to judge DIin general and I have to admit
that my wording might have been indeed to offensive. Scusa per questo.

Don’t worry :slight_smile:

It is however intriguing to me when dependency injection is a good
solution. I am sure that there is plenty space for its application,
maybe even for logging in a context more specific than the one given
by OP. I believe that the main concern would be the loose coupling of
objects, IIUC you would need a Container method for each class and

Hum … I don’t understand here. What do you intend for ‘a Container
method for each class’?
In my implementation there is only a Container class that is the central
configuration point of the system.

each class would need to have an adapted initialize method, well that

Yes, and indeed this is very annoying. When the system grows up you have
to do with long constructor signatures:

Foo.new(logger, service, another_service … )

But I wonder if there is a better method to keep things decoupled…

On Jun 25, 2008, at 11:04 AM, Andrea F. wrote:

Robert D. ha scritto:

each class would need to have an adapted initialize method, well that

Yes, and indeed this is very annoying. When the system grows up you
have to do with long constructor signatures:

Foo.new(logger, service, another_service … )

But I wonder if there is a better method to keep things decoupled…

I typically provide a setter on my classes for configuring the log
object. Hardly perfect, but it keeps the constructor signatures short.
I am liking this suggestion to use a global variable though. I think
I’ll experiment.

On a related note, I’m curious how people solve the problem of turning
logging on and off during runtime.

I hate code like:

logger.log(:debug, “some msg”) unless logger.nil?

I usually provide a null class for my loggers which silently swallow
any messages sent to them if the logging function is disabled. It
cleans up the code considerably.

Is there another way to solve the problem of turning logging on/off at
runtime without lots of conditionals?

cr

On Wed, Jun 25, 2008 at 9:00 PM, Chuck R. [email protected]
wrote:

I hate code like:

logger.log(:debug, “some msg”) unless logger.nil?
That is why my debugging or logging routines normally look like this

def logger *args
return unless $LOGGING

end

HTH
Robert

http://ruby-smalltalk.blogspot.com/


Les mêmes questions qu’on se pose
On part vers où et vers qui
Et comme indice pas grand-chose
Des roses et des orties.

Francis Cabrel

A bit offtopic:

If Ruby handled modules more like namespaces in C++ or similar
languages, a case such as ‘log’ would be trivial:

module MyModule
def MyModule.log(a)
puts a
end
class MyClass
def func
log(“event”) # => NoMethodError: Ruby doesn’t search “parent”
modules
end
end
end

Similarly, Ruby doesn’t search for “class” method like other
languages:

class MyClass
def MyClass.log(a)
puts a
end
def func
log(“event”) # => NoMethoderror
end
end

I am interested in rationale for this behaviour in Ruby. The examples
are intuitive, to me at least, and I’m curious what the reason is that
Ruby doesn’t search for method in the class/parent module when an
instance method or doesn’t exist in the class or included Module.

Lars

On Wed, Jun 25, 2008 at 6:55 PM, Robert K.
[email protected] wrote:

2008/6/25 Robert D. [email protected]:

Maybe I shall answer Robert’s question at the same time :wink:

  1. A logging mixin

That’s a classical bug: off by one. :slight_smile:

I fail to understand Robert, look at this IRB session, as you see I
have prepared for your mail :wink:

irb(main):009:0> 41.succ
=> 43
irb(main):010:0> 43.pred
=> 41

irb(main):023:0> 42.class
=> TheAnswerToTheMeaningOfLiveTheUniverseAndEverything
irb(main):024:0> 43.class
=> Fixnum

And what do you say now about the “Mannschaft” :wink: They too were taking
“off by one” goal;)

Robert

2008/6/26 Robert D. [email protected]:

have prepared for your mail :wink:

irb(main):009:0> 41.succ
=> 43
irb(main):010:0> 43.pred
=> 41

irb(main):023:0> 42.class
=> TheAnswerToTheMeaningOfLiveTheUniverseAndEverything
irb(main):024:0> 43.class
=> Fixnum

Amazing! And I thought I knew something about numbers - at least below
100. :slight_smile:

And what do you say now about the “Mannschaft” :wink: They too were taking
“off by one” goal;)

Um, yes. What an awful game.

Cheers

robert

Chuck R. ha scritto:

Foo.new(logger, service, another_service … )

But I wonder if there is a better method to keep things decoupled…

I typically provide a setter on my classes for configuring the log
object. Hardly perfect, but it keeps the constructor signatures short.
I am liking this suggestion to use a global variable though. I think
I’ll experiment.

Ok, you’re using setter injection instead of constructor injection.

cleans up the code considerably.
Of course you can do that because you’re setting a logger instance for
each object with logging capability. But what to do if you are using a
global logger instance and you wish to turn off logging selectively?

Andrea

Iñaki Baz C.:

Hi, I use Logger class in a programm and since I need to log in lot
of different places (into classes, into methods…) I use a global
variable in this way:

$log = Logger.new
$log.debug …

so I can use $log anywhere in the code.
Is it ellegant and “the Ruby way”?

One more elegant (IMHO) opinion, assuming you have a common namespace
(module) for your program, is to create a singleton with the below
approach:

module MyProgram
class Logger
class << self
def debug …
…
end
end
end
end

This way the global namespace is not cluttered, there are no conflicts
with other programs’ Logger classes/objects, while anywhere in your
program (i.e., anywhere in module MyProgram) you can simply call

Logger.debug …

which is the clearest syntax to my eyes.

I have created a Config class with
this approach and it’s a joy to use:

do_something if Config.some_flag?

case Config.setting
when :value_a then do_a
when :value_b then do_b
when :value_c then do_c
end

(I’ve yet to hook Trollop to my Config class so that the command-line
options are nicely parsed and taken into account, but it’s on my todo.)

– Shot

El Jueves, 26 de Junio de 2008, Shot (Piotr S.) escribió:

This way the global namespace is not cluttered, there are no conflicts
with other programs’ Logger classes/objects, while anywhere in your
program (i.e., anywhere in module MyProgram) you can simply call

Logger.debug …

which is the clearest syntax to my eyes.

Ok, this seems really ellegant and I’ve tested that I can
call “Logger.debug…” in any submodule/subclass into the program :slight_smile:

Just a question: Logger must use an instance of a class (real class
Logger),
so to store it I think the best option is using a @@class_variable into
Logger module, something like:

module MyProgram
class MyLogger
@@logger = Logger.new(xxxxx,xxxx)
class << self
def debug(text)
@@logger.debug(txt)
end
end
end
end

Is it ok?
Thanks a lot.

On Fri, Jun 27, 2008 at 10:17 AM, Shot (Piotr S.) [email protected]
wrote:

end

No – by using class << self, you’re operating on the Logger object
(an instance of the class Class), and you can access its instance
variables.

Amen

grep --recursive ‘@@’ lib && echo “Classvariables are the root of all
evil ;)”

Cheers
Robert

El Viernes, 27 de Junio de 2008, Robert D.
escribió:

grep --recursive ‘@@’ lib && echo “Classvariables are the root of all evil
;)”

It’s not the first time I read this, but could I know a good reason for
not
using @@class variables?

Thanks.

El Viernes, 27 de Junio de 2008, Shot (Piotr S.) escribió:

end
No – by using class << self, you’re operating on the Logger object
(an instance of the class Class), and you can access its instance
variables.

Thanks, in fact I didn’t understand the meaning of “class << self” until
now.

or decide whether to use Enumerable#map or the forkoff
gem based on whether Config.processes is one or more. :slight_smile:

This seems really cool. I’ll try to use it.

Thanks a lot.

Iñaki Baz C.:

El Jueves, 26 de Junio de 2008, Shot (Piotr S.) escribió:

module MyProgram
 class Logger
 class << self
  def debug …
   …
  end
 end
 end
end

Ok, this seems really ellegant and I’ve tested that I can call
“Logger.debug…” in any submodule/subclass into the program :slight_smile:

:slight_smile:

Just a question: Logger must use an instance of a class (real
class Logger), so to store it I think the best option is using
a @@class_variable into Logger module

No – by using class << self, you’re operating on the Logger object
(an instance of the class Class), and you can access its instance
variables. That’s the other elegant part about this solution – you can
use anything you’re used to, including, for example, attr_accessors.

I use this approach like in the below code; I can use stuff like

$stderr.puts ‘some debug line’ if Config.debug

1.upto Config.max_pins do |pin|
…
end

or decide whether to use Enumerable#map or the forkoff
gem based on whether Config.processes is one or more. :slight_smile:

shot@asterix:~/work/PhD/bzr/trunk$ cat lib/art-decomp/config.rb
module ArtDecomp class Config

class << self

attr_accessor :debug, :processes, :qu_method, :qv_method, :silicone

def init
@debug = false
@processes = 1
@qu_method = :graph_merger
@qv_method = :graph_merger
@silicone = Set[Arch[4,2], Arch[5,1]]
end

def log string, run, runs, dawn
left = ((Time.now - dawn)/run*(runs - run)).ceil
$stderr << " [#{string} #{runs - run} #{left}s] " if Config.debug
end

def max_pins
@silicone.map{|arch| arch.pins}.max
end

def max_pons
@silicone.map{|arch| arch.pons}.max
end

alias reset init

end

end end

ArtDecomp::Config.init
shot@asterix:~/work/PhD/bzr/trunk$

– Shot

El Viernes, 27 de Junio de 2008, Shot (Piotr S.) escribió:

  @qv_method = :graph_merger
 end

ArtDecomp::Config.init

The only I don’t like about the above code is the need of using .init
method
explicitely. It could be nice if “initialize” would also work
automatically
in some way, is not possible? (of course I understand that we are not
creating an instance here).

Thanks a lot.

On Jun 26, 2008, at 3:53 AM, Andrea F. wrote:

swallow any messages sent to them if the logging function is
disabled. It cleans up the code considerably.

Of course you can do that because you’re setting a logger instance
for each object with logging capability. But what to do if you are
using a global logger instance and you wish to turn off logging
selectively?

Correct. I haven’t had a situation where I needed to do selective
logging. I am open to suggestions for elegantly solving this problem.

cr

On Fri, Jun 27, 2008 at 11:40 AM, Robert D. [email protected]
wrote:

end
a @@class_variable into Logger module
Robert

They are shared by all subclasses, look at this
irb(main):006:0> class A
irb(main):007:1> @@a = self
irb(main):008:1> end
=> A
irb(main):009:0> class B < A
irb(main):010:1> @@a = self
irb(main):011:1> end
=> B
irb(main):013:0> A.send :class_variable_get, :@@a
=> B

this was bad enough for me never wanting have to do with them again,
now look at this

irb(main):014:0> class A
irb(main):015:1> @a = self
irb(main):016:1> end
=> A
irb(main):017:0> class B < A
irb(main):018:1> @a = self
irb(main):019:1> end
=> B
irb(main):020:0> A.instance_variable_get :@a
=> A
irb(main):021:0> B.instance_variable_get :@a
=> B

HTH
Robert


http://ruby-smalltalk.blogspot.com/


AALST (n.) One who changes his name to be further to the front
D.Adams; The Meaning of LIFF

Iñaki Baz C.:

El Viernes, 27 de Junio de 2008, Shot (Piotr S.) escribió:

module ArtDecomp class Config
class << self
 attr_accessor :debug, :processes, :qu_method, :qv_method, :silicone
 def init
  @debug   = false
  @processes = 1
  @qu_method = :graph_merger
  @qv_method = :graph_merger
  @silicone  = Set[Arch[4,2], Arch[5,1]]
 end
…

end
end end
ArtDecomp::Config.init

The only I don’t like about the above code is the need of using .init
method explicitely. It could be nice if “initialize” would also work
automatically in some way, is not possible? (of course I understand
that we are not creating an instance here).

Yeah, that would be the cherry on top. :slight_smile:

I couldn’t come up with any nice solution; note that you can initialize
this class anywhere, so if you have some other place that does any kind
of program setup, you can do it there. Otherwise, doing it right after
the class is defined is the nicest solution in my opinion.

– Shot

El Sábado, 28 de Junio de 2008, ara.t.howard
escribió:

 class << self

attr_accessor :debug, :processes, :qu_method, :qv_method, :silicone
end
end

and the ‘init’ code is called at load time.

Yeah! I just realized of that today doing some tests. It’s really great!