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.

···

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

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.

···

On Thu, Oct 21, 2010 at 9:23 AM, Rahul Kumar <sentinel1879@gmail.com> wrote:

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 &quot;abc&quot;
 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.

Rahul Kumar 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.

···

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

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.

···

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

Brian Candler 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:

···

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

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.

# 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

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@(irb):8>
irb(main):009:0> a.something
=> "the array [1, 2, 3]"

Jesus.

···

On Thu, Oct 21, 2010 at 10:36 AM, Rahul Kumar <sentinel1879@gmail.com> wrote:

Rahul Kumar 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.

···

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

arr = []
    m = My.new

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

John

···

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

John Mair wrote in post #956482:

arr = []
    m = My.new

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

Not in 1.8.7 though.

···

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