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.

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 █ end

On Wed, Sep 26, 2012 at 9:50 PM, Gary W. [email protected]
wrote:

Such that MyModule.foo would then return false:
end
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.

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.

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 = …

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
is created, 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

On Wed, Sep 26, 2012 at 10:41 PM, Gary W. [email protected]
wrote:

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.

“Jesús Gabriel y Galán” [email protected] wrote in post
#1077681:

On Wed, Sep 26, 2012 at 10:41 PM, Gary W. [email protected]
wrote:

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

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 █ 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!