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
···
On Fri, Jun 27, 2008 at 11:40 AM, Robert Dober <robert.dober@gmail.com> wrote:
On Fri, Jun 27, 2008 at 10:17 AM, Shot (Piotr Szotkowski) <shot@hot.pl> wrote:
Iñaki Baz Castillo:
El Jueves, 26 de Junio de 2008, Shot (Piotr Szotkowski) 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
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.
<snip>
Amen
grep --recursive '@@' lib && echo "Classvariables are the root of all evil ;)"
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.
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 Viernes, 27 de Junio de 2008, Shot (Piotr Szotkowski) escribió:
--
Our chief weapons are who, ps -aux, kill -9, and
a fanatical devotion to fastboot. -- Sun User Group
# recursively adds Logging functionality to all nested classes
extender = lambda do |k|
k.constants.map {|c| k.const_get c }.
select {|c| c.is_a? Class or c.is_a? Module }.
Hmm, I should have used duck-typing in the above line (all the Java
programming I've done recently has corrupted me ;-). So the above line
should be written as:
select {|c| c.respond_to? :constants and c.respond_to? :const_get }.
Now things are becoming quite complex.
Logging is a global task and thusly some form of global handling is perfectly ok IMHO. Using a global variable has the advantage that you do not collide with instance methods (which you do if you place #debug etc. in all classes either via inheritance, meta programming or other means). IMHO the only bad aspect about the global is that you cannot ensure automatic initialization. An alternative would be to add a single method to Kernel that lazily initializes a Logger instance.
My 0.02 EUR.
Kind regards
robert
···
On 23.06.2008 03:29, Suraj Kurapati wrote:
Iñaki Baz Castillo wrote:
Humm, I don't like adding "include Logging" to every classes I use since
sometimes I use objects, sometimes classes, so I'd also need to "extend" some classes not just "include".
You could use meta programming to include Logging in all nested classes and modules of your top-level module:
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
41. 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
···
On Wed, Jun 25, 2008 at 3:36 PM, Andrea Fazzi <andrea.fazzi@alca.le.it> wrote:
Robert Dober ha scritto:
On Wed, Jun 25, 2008 at 11:22 AM, Robert Klemme >> <shortcutter@googlemail.com> wrote:
2008/6/24 Iñaki Baz Castillo <ibc@aliax.net>:
El Martes, 24 de Junio de 2008, Andrea Fazzi escribió:
require 'logger'
class Container
def logger @logger ||= Logger.new STDOUT
end
def foo
Foo.new(logger)
end
end
class Foo
def initialize(logger) @logger = logger
end
def bar @logger.info('Foo#bar invoked.')
end
end
c = Container.new
c.foo.bar
That's a cool solution
I do not think so. Reasons: it clutters every instance with a
reference which can have a significant impact on memory if there are a
lot objects. Then, you have to change a class's #initialize signature
for all classes that do want to do logging and also manually code the
assignment inside the class. Accessing a logger from a global context
(whichever way you do it) saves memory and is less tedious.
Kind regards
robert
--
use.inject do |as, often| as.you_can - without end
Could not agree more with you. May I add some other reasons.
This approach vioaltes principles we regard rather highly on this list
It is not DRY, you are really repeating yourself and in case of the
metaprogramming solutions you let Ruby repeat itself.
It is just much less simple than necessary.
You spread code dependencies all over the place, as a matter of fact
the expression "dependency injection" says it allready it is almost as
putting a virus (with constructive behavior) into your code, but can
you imagine how much more work refactoring will become?
My order of preference would be
1. $logger or $LOGGER
2. Logger
43. Kernel::log (or Object.log)
Cheers
Hi Robert,
I would known if, in your opinion, DI is not DRY in general or you are
referring to the particular case of logging. Moreover, which are alternative
DRY solutions to DI that guarantees loose coupling between objects?
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.
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.
--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama
2008/6/23, Robert Klemme <shortcutter@googlemail.com>:
Now things are becoming quite complex.
Logging is a global task and thusly some form of global handling is
perfectly ok IMHO. Using a global variable has the advantage that you do
not collide with instance methods (which you do if you place #debug etc. in
all classes either via inheritance, meta programming or other means). IMHO
the only bad aspect about the global is that you cannot ensure automatic
initialization. An alternative would be to add a single method to Kernel
that lazily initializes a Logger instance.
Humm, I don't like adding "include Logging" to every classes I use since
sometimes I use objects, sometimes classes, so I'd also need to "extend"
some classes not just "include".
You could use meta programming to include Logging in all nested classes
and modules of your top-level module:
Now things are becoming quite complex.
Not really. You can refactor that bit of meta programming into its own
module:
Now, you can add this propagation of logging functionality to any
top-level module or class by simply doing:
module MyProjectTopLevel
# ...
include AddLoggingToNested
end
Note that the include musts be done *after* all nested classes have been
defined. That is why I put that include() statement at the bottom of
the module's code.
class Container
def logger @logger ||= Logger.new STDOUT
end
def foo
Foo.new(logger)
end
end
class Foo
def initialize(logger) @logger = logger
end
def bar @logger.info('Foo#bar invoked.')
end
end
c = Container.new
c.foo.bar
That's a cool solution
I do not think so. Reasons: it clutters every instance with a
reference which can have a significant impact on memory if there are a
lot objects. Then, you have to change a class's #initialize signature
for all classes that do want to do logging and also manually code the
assignment inside the class. Accessing a logger from a global context
(whichever way you do it) saves memory and is less tedious.
Kind regards
robert
--
use.inject do |as, often| as.you_can - without end
Could not agree more with you. May I add some other reasons.
This approach vioaltes principles we regard rather highly on this list
It is not DRY, you are really repeating yourself and in case of the
metaprogramming solutions you let Ruby repeat itself.
It is just much less simple than necessary.
You spread code dependencies all over the place, as a matter of fact
the expression "dependency injection" says it allready it is almost as
putting a virus (with constructive behavior) into your code, but can
you imagine how much more work refactoring will become?
My order of preference would be
1. $logger or $LOGGER
2. Logger
43. Kernel::log (or Object.log)
Cheers
Hi Robert,
I would known if, in your opinion, DI is not DRY in general or you are
referring to the particular case of logging. Moreover, which are alternative
DRY solutions to DI that guarantees loose coupling between objects?
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
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 Wed, Jun 25, 2008 at 3:36 PM, Andrea Fazzi <andrea.fazzi@alca.le.it> wrote:
On Wed, Jun 25, 2008 at 11:22 AM, Robert Klemme >>> <shortcutter@googlemail.com> wrote:
2008/6/24 Iñaki Baz Castillo <ibc@aliax.net>:
El Martes, 24 de Junio de 2008, Andrea Fazzi escribió:
just really seems too much work to me.
Maybe I shall answer Robert's question at the same time
41. 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?
this is a bad idea. try to use rails' code with other code that uses ruby's logging class and you'll see what i mean. it's better just to keep things simple imho - your meta-programming bit will fail, for example, if someone does
module M
extend AddLoggingToNested
end
i agree with robert that logging is (typically) a global task and thus the interface should reflect that. i'd use something like
module Namespace
module Logger
Instance = ::Logger.new STDERR
%w( debug info warn error fatal ).each do |m|
module_eval <<-code
def #{ m }(*a, &b)
Instance.#{ m }(*a, &b)
end
def self.#{ m }(*a, &b)
Instance.#{ m }(*a, &b)
end
code
end
end
end
which does three things
- box your methods in a namespace
- allow access via: Logger.info ....
- allow access via: include Logger, info ...
perhaps it might make sense to auto-mixin, but tha'ts a layer on top of this.
Now, you can add this propagation of logging functionality to any
top-level module or class by simply doing:
module MyProjectTopLevel
# ...
include AddLoggingToNested
end
Note that the include musts be done *after* all nested classes have been
defined. That is why I put that include() statement at the bottom of
the module's code.
--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama
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 Jun 25, 2008, at 11:04 AM, Andrea Fazzi wrote:
Robert Dober 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..
The problem here is that those code is runned in load time, so if that code
depends on other project libraries or classes you must take care of loading
them first. This is not problem when calling "init" after loading all the
classes or when using "normal" objects (XXXX.new).
···
El Domingo, 29 de Junio de 2008, Shot (Piotr Szotkowski) escribió:
ara.t.howard:
> ruby classes are evaluated top down at load time so, to
> achieve the effect of init you just need to delete some code
this is a bad idea. try to use rails' code with other code that uses
ruby's logging class and you'll see what i mean.
Perhaps they modify ruby's logging class directly? I am proposing
something different: a selective injection of logging functionality into
a library's top-level module.
it's better just to keep things simple imho
I agree, but keep what simple? Implementation or interface?
Iñaki seems to want the interface to be simple, i.e. an automatic
logging facility that is (1) private to his library and (2) does not
require any explicit enabling (include Logging) or addressing (a LOG
constant).
your meta-programming bit will fail, for example, if someone does
module M
extend AddLoggingToNested
end
That is easily solved by aliasing included() to extended():
module AddLoggingToNested
# ... code from previous email ...
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.
Ok, you're using setter injection instead of constructor injection.
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.
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?