Adding method on module class to yield and allow setting of attributes?

I have this Module:

module MyModule
  class << self
    attr_accessor :foo
    alias_method :foo?, :foo
  end
end

I can do:

MyModule.foo # nil
MyModule.foo = true
MyModule.foo # true

I would like to be able to do this:

MyModule.configure do
  foo = false
end

Such that MyModule.foo would then return false:

MyModule.foo # false

I'm a little tired so I'm probably making a mistake:

module MyModule
  class << self
    attr_accessor :foo
    alias_method :foo?, :foo
    # def configure(&block); yield; end # doesn't work
  end
end

# doesn't work either
#class << MyModule
# def configure(&block); yield; end
#end

Thanks in advance for any advice. I'd rather not use any
Active*/Rails-specific method and don't want to use Struct, OpenStruct,
etc. I just want to be able to call the methods created by the
attr_accessor I used to set and get attributes. The reasons I'm doing
this to a module and not a class is it is convenient since it is the
module that my Gem's classes use.

···

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

Hi,

first off, you're not that wrong with your idea. There are two things which need to be changed.

MyModule.configure do
   foo = false
end

The local variable foo is just that - a local variable. You want it to be a class instance variable, so you need to precede it with an @.

module MyModule
   class << self
     attr_accessor :foo
     alias_method :foo?, :foo
     # def configure(&block); yield; end # doesn't work
   end
end

Thanks in advance for any advice. I'd rather not use any
Active*/Rails-specific method and don't want to use Struct, OpenStruct,
etc. I just want to be able to call the methods created by the
attr_accessor I used to set and get attributes. The reasons I'm doing
this to a module and not a class is it is convenient since it is the
module that my Gem's classes use.

You don't need to use an external library. The thing you did wrong in your configure method is that it executes the block in the scope of it's creation, so even if you use the @, it is not a variable in the class/module scope. So what you need to do is call class_eval with the block as a parameter:
   def configure( &block ); class_eval &block; end

This works:

1.9.2p290 :011 > module MyModule
1.9.2p290 :012?> class << self
1.9.2p290 :013?> attr_accessor :foo
1.9.2p290 :014?> alias_method :foo?, :foo
1.9.2p290 :015?> def configure(&blk)
1.9.2p290 :016?> class_eval(&blk)
1.9.2p290 :017?> end
1.9.2p290 :018?> end
1.9.2p290 :019?> end
=> nil
1.9.2p290 :020 > MyModule.configure {self.foo=false}
=> false
1.9.2p290 :021 > MyModule.foo
=> false
1.9.2p290 :022 > MyModule.configure {self.foo=true}
=> true
1.9.2p290 :023 > MyModule.foo
=> true

I don't know if adding the self. is too bad for you. You could also
yield the module itself:

1.9.2p290 :001 > module MyModule
1.9.2p290 :002?> class << self
1.9.2p290 :003?> attr_accessor :foo
1.9.2p290 :004?> alias_method :foo?, :foo
1.9.2p290 :005?> def configure(&blk)
1.9.2p290 :006?> yield self
1.9.2p290 :007?> end
1.9.2p290 :008?> end
1.9.2p290 :009?> end
=> nil
1.9.2p290 :010 > MyModule.foo
=> nil
1.9.2p290 :011 > MyModule.configure {|m| m.foo = false}
=> false
1.9.2p290 :012 > MyModule.foo
=> false

Jesus.

···

On Wed, Sep 26, 2012 at 9:50 PM, Gary Weaver <lists@ruby-forum.com> wrote:

I have this Module:

module MyModule
  class << self
    attr_accessor :foo
    alias_method :foo?, :foo
  end
end

I can do:

MyModule.foo # nil
MyModule.foo = true
MyModule.foo # true

I would like to be able to do this:

MyModule.configure do
  foo = false
end

Such that MyModule.foo would then return false:

MyModule.foo # false

I'm a little tired so I'm probably making a mistake:

module MyModule
  class << self
    attr_accessor :foo
    alias_method :foo?, :foo
    # def configure(&block); yield; end # doesn't work
  end
end

# doesn't work either
#class << MyModule
# def configure(&block); yield; end
#end

Thanks in advance for any advice. I'd rather not use any
Active*/Rails-specific method and don't want to use Struct, OpenStruct,
etc. I just want to be able to call the methods created by the
attr_accessor I used to set and get attributes. The reasons I'm doing
this to a module and not a class is it is convenient since it is the
module that my Gem's classes use.

MyModule.configure do
  foo = false
end

That just creates a block local variable which is destroyed when the
block ends. This block:

MyModule.configure do
  @foo = false
end

...gets its bindings from the surrounding scope at the time the block
executes, and at that time self is 'main' and the 'main' object has no
method foo=() defined for it.

You could do this:

module MyModule
  class <<self
    attr_accessor :foo

    def configure(hash)
      hash.each {|key, val| send(:"#{key}=", val)}
    end
  end

end

p MyModule.foo
MyModule.configure 'foo' => false
p MyModule.foo

--output:--
nil
false

···

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

7stud -- wrote in post #1077677:

MyModule.configure do
  foo = false
end

That just creates a block local variable which is destroyed when the
block ends.

Is there a way to make things in the block execute as if they were being
called on MyModule, i.e.

MyModule.foo = 1
MyModule.bar = 2

could be alternatively:

MyModule.do
  foo = 1
  bar = 2
end

You could do this:

module MyModule
  class <<self
    attr_accessor :foo

    def configure(hash)
      hash.each {|key, val| send(:"#{key}=", val)}
    end
  end

end

p MyModule.foo
MyModule.configure 'foo' => false
p MyModule.foo

--output:--
nil
false

That is helpful, but I have a way to do a hash easily already via
another method, with the idea that the following might be interesting:

MyModule.do
  foo = 1
  def bar
    2 # imagine a more interesting and dynamic result
  end
end

I understand it might require some changes, so syntax might not be
exactly like that, but originally I thought this would be doable, and am
just kind of stuck. Ilia had suggested ActiveSupport::Configurable on
the list but it requires config to be sent in as a parameter and I was
hoping to allow method definition and not have to specify the
config.varname = ...

···

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

Calvin B. wrote in post #1077674:

Hi,

first off, you're not that wrong with your idea. There are two things
which need to be changed.

MyModule.configure do
   foo = false
end

The local variable foo is just that - a local variable. You want it to
be a class instance variable, so you need to precede it with an @.

etc. I just want to be able to call the methods created by the
attr_accessor I used to set and get attributes. The reasons I'm doing
this to a module and not a class is it is convenient since it is the
module that my Gem's classes use.

You don't need to use an external library. The thing you did wrong in
your configure method is that it executes the block in the scope of it's
creation, so even if you use the @, it is not a variable in the
class/module scope. So what you need to do is call class_eval with the
block as a parameter:
   def configure( &block ); class_eval &block; end

Calvin,

Thanks! That is even shorter. It is nice to have both options available,
so when do:

module MyModule
  class << self
    attr_accessor :foo
    alias_method :foo?, :foo
    def configure(&blk)
      class_eval(&blk)
    end
  end
end

can do either:

MyModule.configure do
  @foo = false
  def self.time; Time.now; end
end

or

MyModule.configure do
  self.foo = false
  def self.time; Time.now; end
end

That is so awesome! Thanks for everyone's help!

···

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

I don't know if my previous message got through. If you can handle
some changes in the syntax, using self. before the attributes and the
method names should work if you do a class_eval:

1.9.2p290 :013 > module MyModule
1.9.2p290 :014?> class << self
1.9.2p290 :015?> attr_accessor :foo
1.9.2p290 :016?> alias_method :foo?, :foo
1.9.2p290 :017?> def configure(&blk)
1.9.2p290 :018?> class_eval(&blk)
1.9.2p290 :019?> end
1.9.2p290 :020?> end
1.9.2p290 :021?> end
=> nil
1.9.2p290 :022 > MyModule.configure do
1.9.2p290 :023 > def self.test; puts "test"; end
1.9.2p290 :024?> end
=> nil
1.9.2p290 :025 > MyModule.test
test
=> nil

Jesus.

···

On Wed, Sep 26, 2012 at 10:41 PM, Gary Weaver <lists@ruby-forum.com> wrote:

7stud -- wrote in post #1077677:

MyModule.configure do
  foo = false
end

That just creates a block local variable which is destroyed when the
block ends.

Is there a way to make things in the block execute as if they were being
called on MyModule, i.e.

MyModule.foo = 1
MyModule.bar = 2

could be alternatively:

MyModule.do
  foo = 1
  bar = 2
end

You could do this:

module MyModule
  class <<self
    attr_accessor :foo

    def configure(hash)
      hash.each {|key, val| send(:"#{key}=", val)}
    end
  end

end

p MyModule.foo
MyModule.configure 'foo' => false
p MyModule.foo

--output:--
nil
false

That is helpful, but I have a way to do a hash easily already via
another method, with the idea that the following might be interesting:

MyModule.do
  foo = 1
  def bar
    2 # imagine a more interesting and dynamic result
  end
end

I understand it might require some changes, so syntax might not be
exactly like that, but originally I thought this would be doable, and am
just kind of stuck. Ilia had suggested ActiveSupport::Configurable on
the list but it requires config to be sent in as a parameter and I was
hoping to allow method definition and not have to specify the
config.varname = ...

"Jesús Gabriel y Galán" <jgabrielygalan@gmail.com> wrote in post
#1077681:

You could do this:

That is helpful, but I have a way to do a hash easily already via
exactly like that, but originally I thought this would be doable, and am
just kind of stuck. Ilia had suggested ActiveSupport::Configurable on
the list but it requires config to be sent in as a parameter and I was
hoping to allow method definition and not have to specify the
config.varname = ...

I don't know if my previous message got through. If you can handle
some changes in the syntax, using self. before the attributes and the
method names should work if you do a class_eval:

1.9.2p290 :013 > module MyModule
1.9.2p290 :014?> class << self
1.9.2p290 :015?> attr_accessor :foo
1.9.2p290 :016?> alias_method :foo?, :foo
1.9.2p290 :017?> def configure(&blk)
1.9.2p290 :018?> class_eval(&blk)
1.9.2p290 :019?> end
1.9.2p290 :020?> end
1.9.2p290 :021?> end
=> nil
1.9.2p290 :022 > MyModule.configure do
1.9.2p290 :023 > def self.test; puts "test"; end
1.9.2p290 :024?> end
=> nil
1.9.2p290 :025 > MyModule.test
test
=> nil

Jesus.

Thank you, Jesus! That's exactly what I was looking for!

module MyModule
  class << self
    attr_accessor :foo, :time
    alias_method :foo?, :foo
    def configure(&blk)
      class_eval(&blk)
    end
  end
end

MyModule.foo # nil
MyModule.time # nil

MyModule.configure do
  self.foo = false
  def self.time; Time.now; end
end

MyModule.foo # false
MyModule.time # current date and time

Gary

···

On Wed, Sep 26, 2012 at 10:41 PM, Gary Weaver <lists@ruby-forum.com> > wrote:

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