[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Using extend for initialization settings?

Trans

6/15/2007 2:45:00 PM

It's not uncommon to see initialize method take a hash or a setting
proc and apply that to accessors. Eg.

def initialize( settings )
settings.each{|k,v| send("{#k}=",v)
end

or

def initialize( &settings )
settings.call(self)
end

Today I come up with another potential approach:

class Hash
def to_module(module_function=false)
m = Module.new
each do |k,v|
m.send(:define_method, k){ v }
m.send(:module_function, k) if module_function
end
return m
end
end

M = { :a => 1 }.to_module(true)
p M.a #=> 1


class Foo
def initialize( settings )
extend settings.to_module
end
end

f = Foo.new(:x => 9)
p f.x #=> 9


Thoughts?

T.


15 Answers

Gregory Brown

6/15/2007 3:17:00 PM

0

On 6/15/07, Trans <transfire@gmail.com> wrote:

> def initialize( settings )
> settings.each{|k,v| send("{#k}=",v)
> end

> def initialize( settings )
> extend settings.to_module
> end

Side by side, I have to say the former is clearer than the latter, and
doesn't require changing core.

Robert Klemme

6/15/2007 5:04:00 PM

0

On 15.06.2007 16:44, Trans wrote:
> It's not uncommon to see initialize method take a hash or a setting
> proc and apply that to accessors. Eg.
>
> def initialize( settings )
> settings.each{|k,v| send("{#k}=",v)
> end
>
> or
>
> def initialize( &settings )
> settings.call(self)
> end
>
> Today I come up with another potential approach:
>
> class Hash
> def to_module(module_function=false)
> m = Module.new
> each do |k,v|
> m.send(:define_method, k){ v }
> m.send(:module_function, k) if module_function
> end
> return m
> end
> end
>
> M = { :a => 1 }.to_module(true)
> p M.a #=> 1
>
>
> class Foo
> def initialize( settings )
> extend settings.to_module
> end
> end
>
> f = Foo.new(:x => 9)
> p f.x #=> 9
>
>
> Thoughts?


irb(main):001:0> require 'ostruct'
=> true
irb(main):002:0> f = OpenStruct.new(:x => 9)
=> #<OpenStruct x=9>
irb(main):003:0> f.x
=> 9

:-)

robert

Trans

6/15/2007 7:13:00 PM

0



On Jun 15, 1:05 pm, Robert Klemme <shortcut...@googlemail.com> wrote:
> On 15.06.2007 16:44, Trans wrote:
>
>
>
> > It's not uncommon to see initialize method take a hash or a setting
> > proc and apply that to accessors. Eg.
>
> > def initialize( settings )
> > settings.each{|k,v| send("{#k}=",v)
> > end
>
> > or
>
> > def initialize( &settings )
> > settings.call(self)
> > end
>
> > Today I come up with another potential approach:
>
> > class Hash
> > def to_module(module_function=false)
> > m = Module.new
> > each do |k,v|
> > m.send(:define_method, k){ v }
> > m.send(:module_function, k) if module_function
> > end
> > return m
> > end
> > end
>
> > M = { :a => 1 }.to_module(true)
> > p M.a #=> 1
>
> > class Foo
> > def initialize( settings )
> > extend settings.to_module
> > end
> > end
>
> > f = Foo.new(:x => 9)
> > p f.x #=> 9
>
> > Thoughts?
>
> irb(main):001:0> require 'ostruct'
> => true
> irb(main):002:0> f = OpenStruct.new(:x => 9)
> => #<OpenStruct x=9>
> irb(main):003:0> f.x
> => 9

Hmm... I think maybe my point is being missed. The goal is to
initialize a class, not create a simple data struct.

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.

T.


Gregory Brown

6/15/2007 7:18:00 PM

0

On 6/15/07, Trans <transfire@gmail.com> wrote:
>
>
> On Jun 15, 1:05 pm, Robert Klemme <shortcut...@googlemail.com> wrote:
> > On 15.06.2007 16:44, Trans wrote:
> >
> >
> >
> > > It's not uncommon to see initialize method take a hash or a setting
> > > proc and apply that to accessors. Eg.
> >
> > > def initialize( settings )
> > > settings.each{|k,v| send("{#k}=",v)
> > > end
> >
> > > or
> >
> > > def initialize( &settings )
> > > settings.call(self)
> > > end
> >
> > > Today I come up with another potential approach:
> >
> > > class Hash
> > > def to_module(module_function=false)
> > > m = Module.new
> > > each do |k,v|
> > > m.send(:define_method, k){ v }
> > > m.send(:module_function, k) if module_function
> > > end
> > > return m
> > > end
> > > end
> >
> > > M = { :a => 1 }.to_module(true)
> > > p M.a #=> 1
> >
> > > class Foo
> > > def initialize( settings )
> > > extend settings.to_module
> > > end
> > > end
> >
> > > f = Foo.new(:x => 9)
> > > p f.x #=> 9
> >
> > > Thoughts?
> >
> > irb(main):001:0> require 'ostruct'
> > => true
> > irb(main):002:0> f = OpenStruct.new(:x => 9)
> > => #<OpenStruct x=9>
> > irb(main):003:0> f.x
> > => 9
>
> Hmm... I think maybe my point is being missed. The goal is to
> initialize a class, not create a simple data struct.
>
> 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

class Foo; end

f = Foo.new
f.extend(Container)


I don't get what this is getting me over plain ruby?

Robert Dober

6/15/2007 8:06:00 PM

0

On 6/15/07, Gregory Brown <gregory.t.brown@gmail.com> wrote:
> On 6/15/07, Trans <transfire@gmail.com> wrote:
<snip>
> >
> > f = Foo.new(Container)
> >
> > f.report
>
> class Foo; end
>
> f = Foo.new
> f.extend(Container)
>
>
> I don't get what this is getting me over plain ruby?

Hmm control maybe, you are not forcing anybody to call
f.extend(Container) in your approach.

Tom's approach would allow you to exercise some control about instantiation
e.g.
def initialize(settings)
check about settings.

I however miss the point of #to_module ? What is that good for....
would you mind to explain please.

Cheers
Robert
>
>


--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

Gregory Brown

6/15/2007 8:12:00 PM

0

On 6/15/07, Robert Dober <robert.dober@gmail.com> wrote:

> Hmm control maybe, you are not forcing anybody to call
> f.extend(Container) in your approach.

Then just pass the constant into the constructor :)

It feels like Trans is working on some DI stuff, but I really don't
see the need for it in most practical ruby...

Robert Klemme

6/15/2007 8:30:00 PM

0

On 15.06.2007 21:13, Trans wrote:
>
> On Jun 15, 1:05 pm, Robert Klemme <shortcut...@googlemail.com> wrote:
>> On 15.06.2007 16:44, Trans wrote:
>>
>>
>>
>>> It's not uncommon to see initialize method take a hash or a setting
>>> proc and apply that to accessors. Eg.
>>> def initialize( settings )
>>> settings.each{|k,v| send("{#k}=",v)
>>> end
>>> or
>>> def initialize( &settings )
>>> settings.call(self)
>>> end
>>> Today I come up with another potential approach:
>>> class Hash
>>> def to_module(module_function=false)
>>> m = Module.new
>>> each do |k,v|
>>> m.send(:define_method, k){ v }
>>> m.send(:module_function, k) if module_function
>>> end
>>> return m
>>> end
>>> end
>>> M = { :a => 1 }.to_module(true)
>>> p M.a #=> 1
>>> class Foo
>>> def initialize( settings )
>>> extend settings.to_module
>>> end
>>> end
>>> f = Foo.new(:x => 9)
>>> p f.x #=> 9
>>> Thoughts?
>> irb(main):001:0> require 'ostruct'
>> => true
>> irb(main):002:0> f = OpenStruct.new(:x => 9)
>> => #<OpenStruct x=9>
>> irb(main):003:0> f.x
>> => 9
>
> 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.

> 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. 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.

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 }

Kind regards

robert

Robert Dober

6/15/2007 8:46:00 PM

0

On 6/15/07, Gregory Brown <gregory.t.brown@gmail.com> wrote:
> On 6/15/07, Robert Dober <robert.dober@gmail.com> wrote:
>
> > Hmm control maybe, you are not forcing anybody to call
> > f.extend(Container) in your approach.
>
> Then just pass the constant into the constructor :)
Huh.. that is exactly what I have suggested, right?
I asked why #to_module .
>
Robert

Gregory Brown

6/16/2007 1:20:00 AM

0

On 6/15/07, Robert Dober <robert.dober@gmail.com> wrote:
> On 6/15/07, Gregory Brown <gregory.t.brown@gmail.com> wrote:
> > On 6/15/07, Robert Dober <robert.dober@gmail.com> wrote:
> >
> > > Hmm control maybe, you are not forcing anybody to call
> > > f.extend(Container) in your approach.
> >
> > Then just pass the constant into the constructor :)
>
> Huh.. that is exactly what I have suggested, right?
> I asked why #to_module .

Right. I was basically saying you don't need to_module because if you
wanted to force your object to use some container module, you could
just pass it in.

The .to_module code is gaining you a few chars at most, and I'm not
sure it's worth it for the lack of clarity it introduces.

Trans

6/16/2007 2:45:00 AM

0



On Jun 15, 9:19 pm, "Gregory Brown" <gregory.t.br...@gmail.com> wrote:
> On 6/15/07, Robert Dober <robert.do...@gmail.com> wrote:
>
> > On 6/15/07, Gregory Brown <gregory.t.br...@gmail.com> wrote:
> > > On 6/15/07, Robert Dober <robert.do...@gmail.com> wrote:
>
> > > > Hmm control maybe, you are not forcing anybody to call
> > > > f.extend(Container) in your approach.
>
> > > Then just pass the constant into the constructor :)
>
> > Huh.. that is exactly what I have suggested, right?
> > I asked why #to_module .
>
> Right. I was basically saying you don't need to_module because if you
> wanted to force your object to use some container module, you could
> just pass it in.
>
> The .to_module code is gaining you a few chars at most, and I'm not
> sure it's worth it for the lack of clarity it introduces.

#to_module is just a convenience so one can pass in a container module
or a hash (or anything else that responds to #to_module). It's
basically the same as when you accept a string but go ahead and
generalize it to accept anything that responds to #to_s. In other
words, it's a better approach than

def initialize(settings)
case settings
when Module
...
when Hash
...

T.