Copying parameters to singleton class

Hi list!

I have a metaprogramming question that is driving me mad. I though I
understood how to do it, but obviously I didn't. This is what I want
to do:

I want to create a class method that takes a bunch of options and
returns a singleton class with those options set,

class Opportunities
  def self.using(options)
    # Store options in singleton class variable @options and then
return singleton class
  end
  def self.options
    return @options
  end
end

So that I can use:

foo = Opportunities.using({:one => 1, :two: => 2, :three => 3})
bar = Opportunities.using({:four => 4, :five: => 5, :six => 6})

and then

foo.options => {:one => 1, :two: => 2, :three => 3}
bar.options => {:four => 4, :five: => 5, :six => 6}

Please note that I don't want instances of the Opportunities class, I
want two separate classes that shares the same behavior except for
that they return different values for the Opportunities.options call.

This really should be possible with ruby, right?

/lasso

Right. You can use a closure like this:

class Opportunities
  def self.using(opts)
    opts.freeze
    cl = Class.new self # so we can define instance methods
    class<<cl;self;end.send(:define_method, :options) {opts}
    cl
  end

  # inherited by all
  def xyz
  end
end

irb(main):019:0> foo = Opportunities.using(:one => 1, :two => 2, :three => 3)
=> #<Class:0x10958c78>
irb(main):020:0> bar = Opportunities.using(:four => 4, :five => 5, :six => 6)
=> #<Class:0x1095f7f8>
irb(main):021:0> foo.options
=> {:one=>1, :two=>2, :three=>3}
irb(main):022:0> bar.options
=> {:four=>4, :five=>5, :six=>6}
irb(main):023:0> f = foo.new
=> #<#<Class:0x10958c78>:0x109672dc>
irb(main):024:0> f.class.options
=> {:one=>1, :two=>2, :three=>3}
irb(main):025:0> f.class.ancestors
=> [#<Class:0x10958c78>, Opportunities, Object, Kernel, BasicObject]

Or you can use a member

class Opportunities
  def self.using(opts)
    cl = Class.new self

    class<<cl
      attr_reader :options
    end

    cl.instance_variable_set '@options', opts.freeze

    cl
  end
end

or this way

class Opportunities
  class <<self
    attr_reader :options
  end

  def self.using(opts)
    cl = Class.new self
    cl.instance_variable_set '@options', opts.freeze
    cl
  end
end

or this way

class Opportunities
  class <<self
    attr_reader :options
  end

  def self.using(opts)
    Class.new(self).tap do |cl|
      cl.instance_variable_set '@options', opts.freeze
    end
  end
end

Choose to your liking. :slight_smile:

Cheers

robert

···

On Mon, Apr 11, 2011 at 11:20 AM, Lars Olsson <lasso@lassoweb.se> wrote:

Hi list!

I have a metaprogramming question that is driving me mad. I though I
understood how to do it, but obviously I didn't. This is what I want
to do:

I want to create a class method that takes a bunch of options and
returns a singleton class with those options set,

class Opportunities
def self.using(options)
# Store options in singleton class variable @options and then
return singleton class
end
def self.options
return @options
end
end

So that I can use:

foo = Opportunities.using({:one => 1, :two: => 2, :three => 3})
bar = Opportunities.using({:four => 4, :five: => 5, :six => 6})

and then

foo.options => {:one => 1, :two: => 2, :three => 3}
bar.options => {:four => 4, :five: => 5, :six => 6}

Please note that I don't want instances of the Opportunities class, I
want two separate classes that shares the same behavior except for
that they return different values for the Opportunities.options call.

This really should be possible with ruby, right?

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Hi list!

I have a metaprogramming question that is driving me mad. I though I
understood how to do it, but obviously I didn't. This is what I want
to do:

I want to create a class method that takes a bunch of options and
returns a singleton class with those options set,

class Opportunities
def self.using(options)
# Store options in singleton class variable @options and then
return singleton class
end
def self.options
return @options
end
end

So that I can use:

foo = Opportunities.using({:one => 1, :two: => 2, :three => 3})
bar = Opportunities.using({:four => 4, :five: => 5, :six => 6})

and then

foo.options => {:one => 1, :two: => 2, :three => 3}
bar.options => {:four => 4, :five: => 5, :six => 6}

Please note that I don't want instances of the Opportunities class, I
want two separate classes that shares the same behavior except for
that they return different values for the Opportunities.options call.

If you don't want instances of Opportunities, of which class you want instances?
Where is the common behaviour of those objects defined?

This really should be possible with ruby, right?

I'm not sure if I understand what you want, can you tell us why this
doesn't fit your requirements:

ruby-1.8.7-p334 :005 > class Opportunities
ruby-1.8.7-p334 :006?> attr_reader :options
ruby-1.8.7-p334 :007?> def initialize options
ruby-1.8.7-p334 :008?> @options = options
ruby-1.8.7-p334 :009?> end
ruby-1.8.7-p334 :010?> def self.using options
ruby-1.8.7-p334 :011?> new(options)
ruby-1.8.7-p334 :012?> end
ruby-1.8.7-p334 :013?> end
=> nil
ruby-1.8.7-p334 :014 > foo = Opportunities.using({:a => 3, :b => 4})
=> #<Opportunities:0xb7496418 @options={:b=>4, :a=>3}>
ruby-1.8.7-p334 :015 > foo.options
=> {:b=>4, :a=>3}
ruby-1.8.7-p334 :016 > bar = Opportunities.using({:a => 5, :b => 10})
=> #<Opportunities:0xb748e22c @options={:b=>10, :a=>5}>
ruby-1.8.7-p334 :017 > bar.options
=> {:b=>10, :a=>5}

Jesus.

···

On Mon, Apr 11, 2011 at 11:20 AM, Lars Olsson <lasso@lassoweb.se> wrote:

...but then you could do this:

module ClassFactory
  def ClassFactory.included(includer)
    includer.extend(ClassMethods)
  end

  module ClassMethods

    def using(hash)
      Class.new do
        @options = hash
        class << self
          attr_reader :options
        end
      end
    end

  end
end

class Opportunities
  include ClassFactory
end

Foo = Opportunities.using({one: 1, two: 2, three: 3})
Bar = Opportunities.using({four: 4, five: 5, six: 6})

p Foo.options
p Bar.options

--output:--
{:one=>1, :two=>2, :three=>3}
{:four=>4, :five=>5, :six=>6}

I really wanted to write that like below, but it doesn't work??

module ClassFactory
  def ClassFactory.included(includer)
    includer.extend(ClassMethods)
  end

  module ClassMethods

    def using(hash)
      Class.new do
        @options = hash
      end
    end

    def options
      @options
    end

  end
end

class Opportunities
  include ClassFactory
end

Foo = Opportunities.using({one: 1, two: 2, three: 3})
Bar = Opportunities.using({four: 4, five: 5, six: 6})

p Foo.options
p Bar.options

--output:--
prog.rb:28:in `<main>': undefined method `options' for Foo:Class
(NoMethodError)

···

--
Posted via http://www.ruby-forum.com/.

and:

    class <<singleton
      attr_reader :options
    end

···

--
Posted via http://www.ruby-forum.com/.

Too many variables named 'options'!

···

--
Posted via http://www.ruby-forum.com/.

Try something like this:

   def using(new_options)
      Class.new(self) do
        old_options = superclass.options
        @options = old_options.merge(new_options).select do |key|
          old_options.has_key?(key)
        end.freeze
      end
    end

···

--
Posted via http://www.ruby-forum.com/.

Actually, I don't want any instances at all. I would have preferred to
use instances myself, but since my task involves calling a framework
method that takes a class and not an "ordinary" instance I needed a
hack to be override the options method. Robert's solution does exactly
what I need.

Thank you for your speedy answers!

/lasso

···

On 11 Apr, 11:36, Jesús Gabriel y Galán <jgabrielyga...@gmail.com> wrote:

On Mon, Apr 11, 2011 at 11:20 AM, Lars Olsson <la...@lassoweb.se> wrote:
> Hi list!

> I have a metaprogramming question that is driving me mad. I though I
> understood how to do it, but obviously I didn't. This is what I want
> to do:

> I want to create a class method that takes a bunch of options and
> returns a singleton class with those options set,

> class Opportunities
> def self.using(options)
> # Store options in singleton class variable @options and then
> return singleton class
> end
> def self.options
> return @options
> end
> end

> So that I can use:

> foo = Opportunities.using({:one => 1, :two: => 2, :three => 3})
> bar = Opportunities.using({:four => 4, :five: => 5, :six => 6})

> and then

> foo.options => {:one => 1, :two: => 2, :three => 3}
> bar.options => {:four => 4, :five: => 5, :six => 6}

> Please note that I don't want instances of the Opportunities class, I
> want two separate classes that shares the same behavior except for
> that they return different values for the Opportunities.options call.

If you don't want instances of Opportunities, of which class you want instances?
Where is the common behaviour of those objects defined?

> This really should be possible with ruby, right?

I'm not sure if I understand what you want, can you tell us why this
doesn't fit your requirements:

ruby-1.8.7-p334 :005 > class Opportunities
ruby-1.8.7-p334 :006?> attr_reader :options
ruby-1.8.7-p334 :007?> def initialize options
ruby-1.8.7-p334 :008?> @options = options
ruby-1.8.7-p334 :009?> end
ruby-1.8.7-p334 :010?> def self.using options
ruby-1.8.7-p334 :011?> new(options)
ruby-1.8.7-p334 :012?> end
ruby-1.8.7-p334 :013?> end
=> nil
ruby-1.8.7-p334 :014 > foo = Opportunities.using({:a => 3, :b => 4})
=> #<Opportunities:0xb7496418 @options={:b=>4, :a=>3}>
ruby-1.8.7-p334 :015 > foo.options
=> {:b=>4, :a=>3}
ruby-1.8.7-p334 :016 > bar = Opportunities.using({:a => 5, :b => 10})
=> #<Opportunities:0xb748e22c @options={:b=>10, :a=>5}>
ruby-1.8.7-p334 :017 > bar.options
=> {:b=>10, :a=>5}

Jesus.

Robert K. wrote in post #992088:

Choose to your liking. :slight_smile:

Hey Robert K,

I read your factory pattern thread the other day, and I wonder if that
pattern would apply here? After all, producing several anonymous
instances of Class is akin to producing instances of various classes.
Something like this:

class ClassFactory
  def self.using(hash)
    Class.new do
     @options = hash

       class << self
         attr_reader :options
       end

     end
  end
end

foo = Opportunities.using({one: 1, two: 2, three: 3})
bar = Opportunities.using({four: 4, five: 5, six: 6})

p foo.options
p bar.options

--output:--
{:one=>1, :two=>2, :three=>3}
{:four=>4, :five=>5, :six=>6}

Hey Lars Olsson,

Check out the shorter syntax for hashes when your keys are symbols.

foo = Opportunities.using({one: 1, two: 2, three: 3})
bar = Opportunities.using({four: 4, five: 5, six: 6})

p foo.options
p bar.options

···

--
Posted via http://www.ruby-forum.com/\.

...but then you could do this:

module ClassFactory
def ClassFactory.included(includer)
includer.extend(ClassMethods)
end

module ClassMethods

def using(hash)
Class.new do
@options = hash
class << self
attr_reader :options
end
end
end

end
end

class Opportunities
include ClassFactory
end

I think we can do this simpler if you want to reuse that options
creation functionality:

module ClassFactory
  attr_reader :options

  def using(opts)
    Class.new(self).tap do |cl|
      cl.instance_variable_set '@options', opts.freeze
    end
  end
end

class Opportunities
  extend ClassFactory
end

irb(main):020:0> foo = Opportunities.using(foo: 1, bar: 2)
=> #<Class:0x10952bc0>
irb(main):021:0> foo.options
=> {:foo=>1, :bar=>2}
irb(main):022:0> foo.ancestors
=> [#<Class:0x10952bc0>, Opportunities, Object, Kernel, BasicObject]

I really wanted to write that like below, but it doesn't work??

module ClassFactory
def ClassFactory.included(includer)
includer.extend(ClassMethods)
end

module ClassMethods

def using(hash)
Class.new do
@options = hash
end
end

def options
@options
end

end
end

class Opportunities
include ClassFactory
end

Foo = Opportunities.using({one: 1, two: 2, three: 3})
Bar = Opportunities.using({four: 4, five: 5, six: 6})

p Foo.options
p Bar.options

--output:--
prog.rb:28:in `<main>': undefined method `options' for Foo:Class
(NoMethodError)

Your new class does not inherit module ClassMethods, that's why.

Kind regards

robert

···

On Tue, Apr 12, 2011 at 4:48 AM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Robert K. wrote in post #992088:

or this way

class Opportunities
  class <<self
    attr_reader :options
  end

  def self.using(opts)
    Class.new(self).tap do |cl|
      cl.instance_variable_set '@options', opts.freeze
    end
  end
end

The using() method in your last solution can be simplified:

  def self.using(opts)
    Class.new(self) do
      @options = opts.freeze
    end
  end

And this bit in my last post:

    singleton.singleton_class.class_eval do #<---SOLUTION***
      def options
        @options
      end
    end

is equivalent to:

    singleton.instance_eval do
      def options
        @options
      end
    end

···

--
Posted via http://www.ruby-forum.com/\.

I'm happy to see that is was possible to do this in so many ways :slight_smile:
For what it is worth, here's what I finally settled for:

class Opportunities

  @options = {:opt1 => :default, :opt2 => :default, :opt3
=> :default}.freeze

  def self.options
    @options
  end

  def self.using(options)
    Class.new(self) do
      @options = superclass.options.merge(options).select do |key|
        superclass.options.has_key?(key)
      end.freeze
    end
  end

end

Thank you all for showing how crazy simple this stuff is to do in
ruby!

/lasso

···

On 13 Apr, 02:29, 7stud -- <bbxx789_0...@yahoo.com> wrote:

and:

class &lt;&lt;singleton
  attr\_reader :options
end

--
Posted viahttp://www.ruby-forum.com/.

7stud -- wrote in post #992632:

Try something like this:

   def using(new_options)
      Class.new(self) do
        old_options = superclass.options
        common_keys = old_options.keys & new_options.keys

        common_keys.each do |key|
          old_options[key] = new_options[key]
        end

        @options = old_options
        @options.freeze
      end
    end

Whoops. That doesn't work:

prog.rb:18:in `=': can't modify frozen hash (RuntimeError)

Hmmm...why can you merge() but not directly assign to individual keys
when a hash is frozen?

Ok, merge() with some better variable names:

   def using(new_options)
      Class.new(self) do
        old_options = superclass.options

        @options = old_options.merge(new_options).select do |key|
           old_options.has_key?(key)
        end.freeze
      end
    end

···

--
Posted via http://www.ruby-forum.com/\.

7stud -- wrote in post #992201:

Robert K. wrote in post #992088:

Choose to your liking. :slight_smile:

Hey Robert K,

I read your factory pattern thread the other day, and I wonder if that
pattern would apply here? After all, producing several anonymous
instances of Class is akin to producing instances of various classes.

In hindsight, I guess that wouldn't work if Opportunities is an existing
class that needs to be reopened to provide the needed functionality.

···

--
Posted via http://www.ruby-forum.com/\.

Robert K. wrote in post #992088:

Choose to your liking. :slight_smile:

Hey Robert K,

I read your factory pattern thread the other day, and I wonder if that
pattern would apply here? After all, producing several anonymous
instances of Class is akin to producing instances of various classes.
Something like this:

class ClassFactory
def self.using(hash)
Class.new do
@options = hash

  class &lt;&lt; self
    attr\_reader :options
  end

end

end
end

Well, basically you just exchanged Opportunities with ClassFactory
compared to one of my examples. Actually method #using *is* a factory
method, no matter what class or instance it sits in.

foo = Opportunities.using({one: 1, two: 2, three: 3})
bar = Opportunities.using({four: 4, five: 5, six: 6})

I don't think this can work - at least there's something missing. Did
you mean to make Opportunities inherit ClassFactory? But IMHO there
is no point in splitting this up. Please note also that I made new
classes inherit Opportunities in order for inheritance to work.

p foo.options
p bar.options

--output:--
{:one=>1, :two=>2, :three=>3}
{:four=>4, :five=>5, :six=>6}

Hey Lars Olsson,

Check out the shorter syntax for hashes when your keys are symbols.

foo = Opportunities.using({one: 1, two: 2, three: 3})
bar = Opportunities.using({four: 4, five: 5, six: 6})

If we start shortening it, we should do

foo = Opportunities.using(one: 1, two: 2, three: 3)

i.e. get rid of the superfluous pair of curly braces. :slight_smile:

Kind regards

robert

···

On Tue, Apr 12, 2011 at 4:05 AM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Hello again!

I've now incorporated your solution into my code and it works very
well. I still have a small question though. What's the reason for
freezing the options? I can see no obvious explanation for it except
that one might not want to change them by accident. Or is there
another reason for this?

/lasso

···

On 12 Apr, 09:00, Robert Klemme <shortcut...@googlemail.com> wrote:

On Tue, Apr 12, 2011 at 4:48 AM, 7stud -- <bbxx789_0...@yahoo.com> wrote:
> ...but then you could do this:

> module ClassFactory
> def ClassFactory.included(includer)
> includer.extend(ClassMethods)
> end

> module ClassMethods

> def using(hash)
> Class.new do
> @options = hash
> class << self
> attr_reader :options
> end
> end
> end

> end
> end

> class Opportunities
> include ClassFactory
> end

I think we can do this simpler if you want to reuse that options
creation functionality:

module ClassFactory
attr_reader :options

def using(opts)
Class.new(self).tap do |cl|
cl.instance_variable_set '@options', opts.freeze
end
end
end

class Opportunities
extend ClassFactory
end

irb(main):020:0> foo = Opportunities.using(foo: 1, bar: 2)
=> #<Class:0x10952bc0>
irb(main):021:0> foo.options
=> {:foo=>1, :bar=>2}
irb(main):022:0> foo.ancestors
=> [#<Class:0x10952bc0>, Opportunities, Object, Kernel, BasicObject]

> I really wanted to write that like below, but it doesn't work??

> module ClassFactory
> def ClassFactory.included(includer)
> includer.extend(ClassMethods)
> end

> module ClassMethods

> def using(hash)
> Class.new do
> @options = hash
> end
> end

> def options
> @options
> end

> end
> end

> class Opportunities
> include ClassFactory
> end

> Foo = Opportunities.using({one: 1, two: 2, three: 3})
> Bar = Opportunities.using({four: 4, five: 5, six: 6})

> p Foo.options
> p Bar.options

> --output:--
> prog.rb:28:in `<main>': undefined method `options' for Foo:Class
> (NoMethodError)

Your new class does not inherit module ClassMethods, that's why.

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without endhttp://blog.rubybestpractices.com/

Robert K. wrote in post #992235:

--output:--
prog.rb:28:in `<main>': undefined method `options' for Foo:Class
(NoMethodError)

Your new class does not inherit module ClassMethods, that's why.

I figured out the problem after I posted--including the module
ClassFactory creates class methods in Opportunities--not the anonymous
class that using() returns.

···

--
Posted via http://www.ruby-forum.com/\.

Hi!

Even though I agress that using the same name for both the local
parameter and a method with the same name might be a bad idea, my code
still works without any problem. If I redefine self using to:

(minor change to last version. I split up the calls to merge, select,
and freeze to its own rows.)

  def self.using(options)
    Class.new(self) do
      puts "Superclass options: #{superclass.options.inspect}"
      puts "Options from parameter: #{options}"
      @options = superclass.options.merge(options)
      @options.select! { |key, value| superclass.options.has_key?
(key) }
      @options.freeze
    end
  end

it clearly shows that options (by itself) refers to the local paramter
and not the class name.

/lasso

···

On 14 Apr, 03:09, 7stud -- <bbxx789_0...@yahoo.com> wrote:

7stud -- wrote in post #992632:

> Try something like this:

> def using(new_options)
> Class.new(self) do
> old_options = superclass.options
> common_keys = old_options.keys & new_options.keys

> common_keys.each do |key|
> old_options[key] = new_options[key]
> end

> @options = old_options
> @options.freeze
> end
> end

Whoops. That doesn't work:

prog.rb:18:in `=': can't modify frozen hash (RuntimeError)

Hmmm...why can you merge() but not directly assign to individual keys
when a hash is frozen?

Ok, merge() with some better variable names:

def using(new_options)
Class.new(self) do
old_options = superclass.options

    @options = old\_options\.merge\(new\_options\)\.select do |key|
       old\_options\.has\_key?\(key\)
    end\.freeze
  end
end

--
Posted viahttp://www.ruby-forum.com/.

I've now incorporated your solution into my code and it works very
well.

Fine! (Btw, which one did you pick?)

I still have a small question though. What's the reason for
freezing the options? I can see no obvious explanation for it except
that one might not want to change them by accident. Or is there
another reason for this?

No, that's the only reason. Note also that this is not 100% safe
because elements in the Hash are not frozen so for a more robust
solution you would need to recursively freeze everything contained in
the Hash. I still like it to freeze the Hash to avoid simple errors
and document in code that this is really meant to be constant. (At
least that's what I guessed you wanted them to be.)

Kind regards

robert

···

On Tue, Apr 12, 2011 at 10:30 AM, Lars Olsson <lasso@lassoweb.se> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Lars Olsson wrote in post #992249:

Hello again!

I was fooling around with your original requirement:

I want to create a class method that takes a bunch
of options and returns a singleton class with those
options set,

...and I think I was probably running into the same problems you were:

class Object
  def singleton_class
    class << self
      self
    end
  end

end

class Opportunities
  def self.using(options)
    # Store options in singleton class variable
    # @options and then return singleton class

    obj = self.new
    singleton = obj.singleton_class
    singleton.instance_variable_set(:@options, options)

    singleton.class_eval do #<----PROBLEM HERE
      def options
        @options
      end
    end

    singleton

  end

end

Foo = Opportunities.using(one: 1, two: 2, three: 3)
Bar = Opportunities.using(four: 4, five: 5, six: 6)

p Foo.options
p Bar.options

--output:--
prog.rb:37:in `<main>': undefined method `options' for
#<Class:#<Opportunities:0x88cfef8>> (NoMethodError)

The problem is that options() is an instance method of the singleton
class, but in this line:

Foo.options

Foo is the singelton class, and therefore options() is being called as a
class method of the singleton class. A class method of a singleton
class??!! You can actually create such a thing in ruby:

class Object
  def singleton_class
    class << self
      self
    end
  end
end

class Opportunities
  def self.using(options)
    # Store options in singleton class variable
    # @options and then return singleton class

    obj = self.new
    singleton = obj.singleton_class
    singleton.send(:instance_variable_set, :@options, options)

    singleton.singleton_class.class_eval do #<---SOLUTION***
      def options
        @options
      end
    end

    singleton

  end

end

Foo = Opportunities.using(one: 1, two: 2, three: 3)
Bar = Opportunities.using(four: 4, five: 5, six: 6)

p Foo.options
p Bar.options

--output:--
{:one=>1, :two=>2, :three=>3}
{:four=>4, :five=>5, :six=>6}

···

--
Posted via http://www.ruby-forum.com/\.