Scope when reopening method

Here is a dummy class with a method “meth”.

class My

  # str contains some value generated by My
  def meth str
  end

  def call_meth
     meth "abc"
  end

end

------ my application starts here

arr = []

m = My.new
def m.meth(str)
# access some other object in application scope
arr << str
do_something str # call some method in my app
end

I have the method “meth” above which is of interest to user apps.
The app overrides the method and would like to access methods or
variables
in my application scope.

Is there anyway this is possible (other than using global variables) ?
To me it appears that meth() can only access what is in that objects
scope plus globals. thx.

On Thu, Oct 21, 2010 at 9:23 AM, Rahul K. [email protected]
wrote:

end
arr << str
scope plus globals. thx.
If I understand correctly, you want the meth method defined for object
a to be able to access methods and instance variables defined in the
My class. Nothing special needs to be done:

irb(main):001:0> class My
irb(main):002:1> def initialize
irb(main):003:2> @value = 3
irb(main):004:2> end
irb(main):005:1> def do_something
irb(main):006:2> puts “my value is #{@value}”
irb(main):007:2> end
irb(main):008:1> def call_meth
irb(main):009:2> meth “abc”
irb(main):010:2> end
irb(main):011:1> def meth str
irb(main):012:2> str * 2
irb(main):013:2> end
irb(main):014:1> end
=> nil
irb(main):015:0> My.new.call_meth
=> “abcabc”
irb(main):016:0> a = My.new
irb(main):025:0> def a.meth str
irb(main):026:1> do_something
irb(main):027:1> str + @value.to_s
irb(main):028:1> end
=> nil
irb(main):029:0> a.call_meth
my value is 3
=> “abc3”

Maybe I misunderstood the question.

Jesus.

Rahul K. wrote in post #956000:

class My

  # str contains some value generated by My
  def meth str
  end

  def call_meth
     meth "abc"
  end

end

------ my application starts here

arr = []

m = My.new
def m.meth(str)
# access some other object in application scope
arr << str
do_something str # call some method in my app
end

I have the method “meth” above which is of interest to user apps.
The app overrides the method and would like to access methods or
variables
in my application scope.

By “My application scope”, you mean the top-level scope? For example,
you want methods inside instances of My to be able to access local
variables like ‘arr’ ?

The simple answer(*) is that you can’t - each ‘def’ method and ‘class’
definition starts its own local variable scope. That’s why they’re
called “local” variables :slight_smile:

You should pass references to those objects explicitly when needed. For
example:

class My
def initialize(arr)
@arr = arr
end

def meth(str)
@arr << str
do_something str
end
end

Because everything in Ruby is an object reference, you are only copying
the reference, not copying the entire object. So @arr << str modifies
the same array that arr << str did before.

Or you could just pass the object reference only when needed:

def meth(str, arr)
arr << str
do_something str
end

It depends on the cleanest way to encapsulate the logic you’re
implementing.

Is there anyway this is possible (other than using global variables) ?
To me it appears that meth() can only access what is in that objects
scope plus globals. thx.

Not exactly: meth() can only access local variables in that method’s
scope, plus instance variables of the object (@foo), plus global
variables ($bar)

Generally you want to avoid global variables at all costs. At worst, you
can use an instance variable of the class object (since the class itself
is an object)

class Foo
def self.bar=(x)
@bar=x
end
def self.bar
@bar
end
end

Foo.bar = 123
puts Foo.bar

This is like a global variable, but at least it’s qualified by class
‘Foo’ so you know you’re not stamping on any other global variable.

Regards,

Brian.

(*) The long answer is you can break this rule using eval and binding,
e.g. TOPLEVEL_BINDING, but you really don’t want to do that.

Brian C. wrote in post #956015:

The simple answer(*) is that you can’t - each ‘def’ method and ‘class’
definition starts its own local variable scope. That’s why they’re
called “local” variables :slight_smile:

Or you could just pass the object reference only when needed:

def meth(str, arr)
arr << str
do_something str
end

It depends on the cleanest way to encapsulate the logic you’re
implementing.

Thanks. I understand that one way is to pass a reference to an object to
My.

The second option of “when needed” wont work since My will call meth()
at
some point that i don’t know. I would have liked to leave meth() blank
so that callers can do what they want there.

Another approach I had is to pass a block in advance which is stored,
meth calls that block.

However, i had sort of hoped somehow the reopened method could access
scope of the class that was reopening it. You have clearly explained
that it is operating in the space of the class itself. So no magic can
happen here :frowning:

On Thu, Oct 21, 2010 at 10:36 AM, Rahul K. [email protected]
wrote:

Yes, you have misunderstood. “meth” already has access to its own
variables.
There is an application that creates an instance of My.
It also contains an array “arr”. I’d like to refer to that array within
the reopened “meth”.
I would also like to call some methods that are in my app from the
reopened method.

OK. Clear.

def m.meth(str)

access some other object in application scope

arr can be instance variable or local variable in application

arr << str
do_something str # call some method in my app
end

You could create a closure around your global scope. For that, you
need to avoid keywords such as class and def, because those do not
create closures, but create a new scope. You can try something like
this:

irb(main):001:0> arr = [1,2,3]
=> [1, 2, 3]
irb(main):002:0> a = Object.new
=> #Object:0xb7505264
irb(main):003:0> class Object
irb(main):004:1> def metaclass; class << self; self; end
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):008:0> a.metaclass.instance_eval { define_method(:something)
{ “the array #{arr.inspect}”}}
=> #Proc:0xb74d783c@:8(irb)
irb(main):009:0> a.something
=> “the array [1, 2, 3]”

Jesus.

Yes, you have misunderstood. “meth” already has access to its own
variables.
There is an application that creates an instance of My.
It also contains an array “arr”. I’d like to refer to that array within
the reopened “meth”.
I would also like to call some methods that are in my app from the
reopened method.

i create an array that is not inside My class. Its in my

application.
arr = []

this method belongs to my app, not to My class

def do_something str
puts str
end

m = My.new
def m.meth(str)
# access some other object in application scope
# arr can be instance variable or local variable in application
arr << str
do_something str # call some method in my app
end

Hope i am clear. thx.

arr = []
m = My.new

m.define_singleton_method(:meth) do |str|
  arr << str
end

John

John M. wrote in post #956482:

arr = []
m = My.new

m.define_singleton_method(:meth) do |str|
  arr << str
end

Not in 1.8.7 though.

Rahul K. wrote in post #956019:

The second option of “when needed” wont work since My will call meth()
at
some point that i don’t know. I would have liked to leave meth() blank
so that callers can do what they want there.

Yes, so my suggested solution would be to pass arr at the time the
object is instantiated:

arr = []

m = My.new(arr) # <<< NOTE
def m.meth(str)
# access some other object in application scope
@arr << str
do_something str # call some method in my app
end

i.e. you squirrel it away in an instance variable for later use.

However, i had sort of hoped somehow the reopened method could access
scope of the class that was reopening it. You have clearly explained
that it is operating in the space of the class itself. So no magic can
happen here :frowning:

Well, the particular example you showed can be made to work, using
define_method() instead of def. That doesn’t open a new scope, and so
you have a closure, i.e. access to all the local variables in the
enclosing scope.

arr = []

m = My.new
class <<m;self;end.class_eval do
define_method(:meth) do |str|
# access some other object in application scope
arr << str
puts str # call some method in my app
end
end
m.meth “hello”
puts arr.inspect

However I would consider this to be “advanced” ruby, and would advise
against it unless it’s really needed. There may be a simpler solution to
the way you’ve designed your application.