Using a block to configure/initialize a class

Hi! i need a solution for this, it’s pretty simple but i can’t get it to
work…

suppose:

class Options

def self.define_an_option
# …don’t-know-how-implementation :frowning:
end

define_an_option :option1, String
define_an_option :option2, Array
end

Then, i want to do something like this:

opt= Options.new
opt.option1 => “”
opt.option2 => []

opt.option1 do |o|
o= “Hello”
end

opt.option2 do |o|
o << 10
end

options.option1 => “Hello”
options.option2 => [10]

Thats all! Seems pretty simple, but i tried with a lot of variants and I
don’t get it to work. Any ideas?

Thanks!

From: [email protected] [mailto:[email protected]] On Behalf
Of
Emmanuel O.
Sent: Wednesday, September 26, 2007 1:09 AM

end

Before you do it, you should understand your API examples seems
unnatural.

Why not to do

opt.option1 = “Hello”
opt.option2 << 10

?

Than, this example:

opt.option1 do |o|
o = “Hello”
end

is almost impossible to do (possible, but hard), because it should read
like: “pass reference option value into block though ‘o’ variable;
replace
‘o’ with reference to ‘Hello’ string” - option value will left
untouched.

V.

Hi –

On Wed, 26 Sep 2007, Emmanuel O. wrote:

opt.option1 do |o|
o= “Hello”
end

That’s just a re-assignment to the variable ‘o’. It’s not going to
have any impact on opt.option1.

opt.option2 do |o|
o << 10
end

options.option1 => “Hello”
options.option2 => [10]

Thats all! Seems pretty simple, but i tried with a lot of variants and I
don’t get it to work. Any ideas?

The block syntax seems a little odd. Is there any reason not to just
use attr_accessor? If you want it to default to a new instance of some
specific class, you could do something like:

class Options

def self.define_an_option(name, klass)
attr_writer name
iv = “@#{name}”
define_method(name) do
unless instance_variables.include?(iv)
instance_variable_set(iv, klass.new)
end
val = instance_variable_get(iv)
yield val if block_given? # if you really need this
val
end
end

define_an_option :option1, String
define_an_option :option2, Array
end

(and I suspect there are other versions of attr_reader with a default
value out there somewhere).

David

Hi! thanks for you replies. I’m going to analyze them. For the moment i
wanted to answer the question of the unnatural api. The reason i wan’t
to do it that way is because i’m not going to use String or array for
most of the options, but Structs. So i want a way to let the user do
this

Struct.new “Value”, :sub1, :sub2, :sub3 << In fact, the user may not
even know about the Struct::Value class.

class Options
…same as before…
define_option :an_option
end

opt= Options.new

opt.an_option do |o|
o.sub1= …
o.sub2= …
o.sub3= …
end

instead of

an_option.sub1= …
an_option.sub2= …
an_option.sub3= …

On Sep 25, 2007, at 6:09 PM, Emmanuel O. wrote:

end

Thats all! Seems pretty simple, but i tried with a lot of variants
and I
don’t get it to work. Any ideas?

Have you looked at the built-in class Struct? I think it will supply
what you need.

Options = Struct.new(:option1, :option2) opt=Options.new("", []) opt.option1 = "Hello" opt.option2 << 10 < "Hello" opt.option2 # => [10, 42]

Regards, Morton

From: [email protected] [mailto:[email protected]] On Behalf
Of
Emmanuel O.
Sent: Wednesday, September 26, 2007 2:51 AM

class Options
end

instead of

an_option.sub1= …
an_option.sub2= …
an_option.sub3= …

Acceptable explanation. Then, trying to answer:

  1. Defining the method you need “by hands”:

class Option
def an_option
@an_option ||= Value.new
yield @an_option if block_given?
@an_option
end
end

  1. Designing #define_option

class Option
def self.define_option(name, type)
class_eval %Q{
def #{name}
@#{name} ||= #{type.inspect}.new
yield @#{name} if block_given?
@#{name}
end
}
end

define_option :an_option, Array
end

o = Option.new

p o.an_option # => []

o.an_option { |opt|
opt << 1 << 2 << 3
}

p o.an_option # => [1,2,3]

Really, I think it can be done with more grace (without textual eval).

V.