> Hmm... I think maybe my point is being missed. The goal is to
> initialize a class, not create a simple data struct.
Actually your code is about initializing an instance - not a class.
Basically you just transform a Hash into some other data structure, a
module with constant accessors in your case. At the moment I fail to
see the benefit over something like this:
class Hash
def init(x)
each do |var, val|
x.send("#{var}=", val)
end
end
end
class Foo
def initialize(settings)
settings.init(self)
end
end
I find the approach using a module somewhat convoluted. Also a module
simply does not seem the right vehicle IMHO. For example, what happens
to attr_accessors that you define in that class? Either they override
your anonymous module's methods or the other way round. This does not
seem a good solution to me.
Attrsibutes would have precedence with how things work now. But that
is an interesting point. Not that we have this in Ruby presently, but
a #prepend instead of #extend could override attrs and would even
allow pre-object AOP. But in any case, if one were using this type of
constructor, one wouldn't be using attributes.
> I think the neat thing about this technique is that it could go beyond
> just assigning values, and provide a clean means of dependency
> injection.
> class Module
> def to_module; self; end
> end
> module Container
> def log(msg)
> puts msg
> end
> end
> class Foo
> def initialize( settings )
> extend settings.to_module
> end
> def report_
> log("Ready.")
> end
> end
> f = Foo.new(Container)
> f.report
> produces
> Ready.
Basically what you do is you require an instance that implements a
particular method that you introduce and which has to return a module
and you promise to extend the instance with that module returned. But
this means at the same time that there always *has* to be a module. I
am not sure whether that's a good idea because for one every module
comes at a cost and I think it's not the proper means to carry some
initialization info.
Yea, I'm not really considering the cost at this point. Certainly this
is not the kind of constructor needed for for every class. I'm
thinking more along the lines of high-level integration classes --
integrating the various dependencies of an application, say. Your are
right that this is an exploration in DI/IoC.
Also, Module#to_module and Hash#to_module server
two completely different purposes: the former helps extending an
instance with a predefined module (functionality) and the latter
provides key value pairs via a particular interface.
That's true. But I think that polymorphism can be useful. Production
code uses a functional module, but the class can be tested with a mock-
up via a simple hash.
The more general solution for the "pass argument X that implements
method Y" is of course - blocks. So the pattern that you quoted
earlier is more flexible and general IMHO. I mean
def initialize(&b)
instance_eval(&b)
end
Combining that with Hash#init from above you can solve these tasks with
f = Foo.new { {:foo=>"bar"}.init(self) }
f = Foo.new { extend Container }
However, this is TOO flexible, because it offers no means of control
over what's being passed in. It basically just opens the up the object
to alteration 100% rather then just filling in "slot requirements".
For example we could do:
class Foo
def initialize( di )
di = di.to_module
raise "Unmet Dependencies" unless di.method_defined?(:x)
extend di
end
end
Maybe a wee bit of a more "realistic" example would help (it not a
real one, but it's much closer to such).
class Archiver
def initialize( copyparams, language=nil, fileutils=nil )
extend copyparams.to_module
extend language.to_module if language
extend (fileutils ? fileutils.to_module : FileUtils)
end
# copyparms
def to
raise "where to?"
end
def from
'./*' # default value
end
# language
def greeting
"copying..."
end
# main
def copy
File.cp_r(from, to)
end
end
So how about a dryrun in Spanish:
module SpanishLanguage
def greeting
"copiado..."
end
end
copyparms = {:from => '/home/trans/pics', :to=>'/backup'}
archiver = Archiver.new( copyparms, SpanishLanguage,
FileUtils::DryRun )
archiver.copy
We can alter the interface:
module EnglishLanguage
def greeting
"copying..."
end
def ask_from_where
"from where?"
end
def ask_to_where
"to where?"
end
end
module CopyParams
def to
puts ask_from_where
gets
end
def from
puts ask_to_where
gets
end
end
archiver = Archiver.new( CopyParams, EnglishLanguage )
archiver.copy
Note, I didn't test this code. So forgive any bugs. I'm sure you get
the idea though.
I'm not sure how great an idea all this really is. That's why I'm
asking about it. But is certainly seems very flexible.
T.
···
On Jun 15, 4:35 pm, Robert Klemme <shortcut...@googlemail.com> wrote: