Use a class variable inside a WITH block

I have declared in another file a class like this

class MyClass
class << self
attr_accessor :att1, :att2, :att3, :att4
end
#… (some methods here)
end

And I am using it like this in another file:

MyClass.att1=“xxx”
MyClass.att2=3333
MyClass.att3=“xxx”
MyClass.att4=true

In another languages it is possible to do something like:

with MyClass do
att1=“xxx”
att2=3333
att3=“xxx”
att4=true
end

or like this

with MyClass {
att1=“xxx”
att2=3333
att3=“xxx”
att4=true
}

Is there anything similar in Ruby?

Thanks

Mario R. wrote in post #1169736:

In another languages it is possible to do something like:

with MyClass do
att1=“xxx”
att2=3333
att3=“xxx”
att4=true
end

or like this

with MyClass {
att1=“xxx”
att2=3333
att3=“xxx”
att4=true
}

Is there anything similar in Ruby?

No. You could use #instance_eval but that won’t help in this case
because all these assignments will create local variables in the block.

You could use a Hash:

def MyClass.configure(hash)
hash.each_pair do |k, v|
send("#{k}=", v)
end
end

and then

MyClass.configure(
att1: “xxx”,
att2: 3333
)

Cheers

robert

Thanks for the reply…
and good tip… i will do it that way i think

Thanks again

Yes this is easily accomplished in Ruby:

require ‘ostruct’
=> true

my_class = OpenStruct.new
=> #

my_class.att1 = “xxx”
=> “xxx”

my_class.att2 = 3333
=> 3333

my_class.att3 = “xxx”
=> “xxx”

my_class.att4 = true
=> true

my_class.inspect
=> “#<OpenStruct att1=“xxx”, att2=3333, att3=“xxx”, att4=true>”

I don’t understand what you mean Scott…
What I am looking for is to do something like…
with MyClass do
att1=33
att2=true
att3=12
end

Scott S. wrote in post #1169783:

Yes this is easily accomplished in Ruby

Robert… one thing… maybe not that nice at the end… since to assign
it we need to supply even the {} so it would look like this:
MyClass.configure({
att1: “xxx”,
att2: 3333
})

But for the moment the best solution… :slight_smile:

Robert K. wrote in post #1169749:

Mario R. wrote in post #1169736:

In another languages it is possible to do something like:

Hi Mario,

I showed how in Ruby it is possible to do dynamic method creation and
assignment at runtime.

That seemed to me to be what you were asking. So let’s go back to the
beginning and try to ascertain what you were really asking.

Your code:

class MyClass
class << self
attr_accessor :att1, :att2, :att3, :att4
end
#… (some methods here)
end

What is it that you think this code accomplishes?

Say you create an instance of this class:

an_instance_of_MyClass = MyClass.new

What instance variables and methods do you expect to be available as an
instance of MyClass?

Are you asking if the following is possible in Ruby:

an_instance_of_MyClass = MyClass.new do

… some stuff

end

The answer is yes.

Are you asking if it’s possible to create a DSL using Ruby?

Again, the answer is yes.

The issue is with setter names generated with attr_accessor, ie. their
equal sign `=’ at the end. In such case Ruby can’t distinguish between
setter call and local variable assignment.

You can remove the ambiguity in several ways.

#First, possibly closest to your requirement is use of an explicit
receiver:

def with(klass)
yield klass
end

Or directly written:

World::Europe::Italy.class_exec do |c|

with World::Europe::Italy do |c|
c.food=‘fussili’
c.capital=‘rome’
c.population=2000000
c.visited=true
end

Another way is naming setter in a different way like

class World::Europe::Italy
def self.set_food(val)
@att1 = val
end
end

World::Europe::Italy.class_exec {
set_food ‘fussili’

}

Yet another a bit more hackish with explicit method sending

World::Europe::Italy.class_exec {
send :food, ‘fussili’

}

You may probably invent yet another ways here :wink:

well my case is a little more complicated… i tried to simplify it in
here…

i am already doing all this
class MyClass
class << self
attr_accessor :att1, :att2, :att3, :att4
end
#… (some methods here)
end

and specifying values by default actually… the thing is… I am in
other scripts changing those default values i have in that to another
ones by doing:

MyClass.att1=xxxx
MyClass.att2=yyyy
MyClass.att3=99999
MyClass.att4=11111

actualy I have many attributes to supply sometimes and actually a little
more complicated since those classes are inside other classes so even it
is not nice at all… and loose of time to be repeating writing the same
classes and classes like this:

World::Europe::Italy.food=‘spaguetti’
World::Europe::Italy.capital=‘rome’
World::Europe::Italy.language=‘italian’

World::Europe.revisit=true
World::Europe.xtreme=false

World::Asia::Japan.sushi=true
World::Asia::Japan.language=‘japannese’

World.auto_travel=true

Of course these are not the real classes i have it is just an example of
more or less what i am doing… so what i am asking is if it is possible
to do something like this for the cases with many settings:

World::Europe do {
revisit=true
xtreme=false
}

World::Europe::Italy do {
food=‘fussili’
capital=‘rome’
population=2000000
visited=true
}

or something similar so it is nice to see and not necessary to repeat
again and again the classes

Ok… i see now how should be the send thingy:
World::Europe::Italy.class_exec {
send :food=, ‘fussili’

}

Thanks a lot David

The ‘with’ one looks nice actually and very logical
I tried the last one you propose but… always get this error when using
send :food, ‘fusssili’
wrong number of arguments (1 for 0) (ArgumentError)

I’m using Ruby 2

Mario R. wrote in post #1169793:

Robert… one thing… maybe not that nice at the end… since to assign
it we need to supply even the {} so it would look like this:
MyClass.configure({
att1: “xxx”,
att2: 3333
})

No. That is wrong.

irb(main):001:0> p(this: “is”, a: “hash”)
{:this=>“is”, :a=>“hash”}
=> {:this=>“is”, :a=>“hash”}
irb(main):002:0> p(this: “is”, a: “hash”).class
{:this=>“is”, :a=>“hash”}
=> Hash
irb(main):003:0> p(“this” => “is”, “a” => “hash, too”).class
{“this”=>“is”, “a”=>“hash, too”}
=> Hash

But for the moment the best solution… :slight_smile:

:slight_smile:

Cheers

robert

You are 100% right Robert… I don’t know why it didn’t’ work like that
before, thanks

Hi! The answer of mung(https://www.ruby-forum.com/user/show/mung) is
beautiful.

require ‘ostruct’

It’s useful.

Mario R. wrote in post #1169915:

Ok… i see now how should be the send thingy:
World::Europe::Italy.class_exec {
send :food=, ‘fussili’

}

Oh, sorry for that typo.

Robert K.:

“the point was to initialize an instance with little clutter”

Then what was the point of this code:

class MyClass
class << self
attr_accessor :att1, :att2, :att3, :att4
end
#… (some methods here)
end

Given the above code instances of the class MyClass do not have a method
att1= nor do they have a method att1. The above code defines class
methods.

p MyClass.respond_to?(:att1)
p MyClass.respond_to?(:att1=)

p MyClass.new.respond_to?(:att1)
p MyClass.new.respond_to?(:att1=)

Yang FuSheng wrote in post #1170017:

Hi! The answer of mung(https://www.ruby-forum.com/user/show/mung) is
beautiful.

But it does not solve the problem the OP hat - at least as far as I
understand: the point was to initialize an instance with little clutter
(i.e. not having to repeat the name of the variable which references the
instance).

require ‘ostruct’

It’s useful.

Certainly! But you can do better if you care for shorter code:

irb(main):003:0> OpenStruct.new(
irb(main):004:1* att1: “xxx”,
irb(main):005:1* att2: 3333,
irb(main):006:1* att3: “xxx”,
irb(main):007:1* att4: true
irb(main):008:1> )

Kind regards

robert