Using extend for initialization settings?

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.

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

···

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

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

:slight_smile:

  robert

···

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?

Trans wrote:

  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?

The behavior is substantially different. With hash initialization, reader and writer accessors are defined in the class. With this technique it seems there's only a reader accessor, and it is only defined per object. What methods the object responds to depend on the initialization values.

In effect this seems to create per-object constants rather than traditional "attributes". Maybe I can express myself better with code:

class Foo
   attr_accessor :a, :b
end
h = Foo.new(:a=>1)
h.a #=> 1
h.a=2 #=> 2
h.b #=> nil
h.c #=> NoMethodError
m = Foo.new(:a=>1) #using to_module
m.a #=> 1
m.a=2 #=> NoMethodError
m.b #=> NoMethodError
m.c #=> NoMethodError
s = OpenStruct.new(:a=>1)
s.a #=> 1
s.a=2 #=> 2
s.b #=> nil
s.c #=> nil

If you can find a use for such behavior then it's fine, but I'm afraid I can't.

Daniel

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.

···

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

class Foo; end

f = Foo.new
f.extend(Container)

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

···

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

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

···

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:

<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

···

On 6/15/07, Gregory Brown <gregory.t.brown@gmail.com> wrote:

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

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

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

Then just pass the constant into the constructor :slight_smile:

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

···

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.

Sorry. I was thingk of #include, not #extensd. So scratch that ...
reverse it. The extension overrides the attributes. So one _can_ do
PER-object AOP with this.

T.

···

On Jun 15, 11:46 pm, Trans <transf...@gmail.com> wrote:

On Jun 15, 4:35 pm, Robert Klemme <shortcut...@googlemail.com> wrote:

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

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

Then just pass the constant into the constructor :slight_smile:

Huh.. that is exactly what I have suggested, right?
I asked why #to_module .

Robert

···

On 6/15/07, Gregory Brown <gregory.t.brown@gmail.com> wrote:

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

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.

···

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 :slight_smile:

Huh.. that is exactly what I have suggested, right?
I asked why #to_module .

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

···

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 :slight_smile:

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

I understand what it's for, I just don't think it's very useful.

···

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

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

Thanks for the clarification, I thaught it was part of the concept and
got confused.
It might indeed be very useful as described above.

Robert

···

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

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

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