Get local variables of the previous scope
I am making a Ruby REPL for use inside an application. I made the code:
a = 1 b = 2 currentScope = [] Kernel.local_variables.each do |var| currentScope << [var,Kernel.eval(var.to_s)] end launchREPL(currentScope)
Inside the REPL, I can execute the following code:
@a #=>1
@a+@b #=>3
Ideally, I would not have to write four lines of code before starting the REPL, and instead I would like to run them inside a function launchREPL
. However, this will require access to the previous scope within the function launchREPL
.
Test1
First of all I tried:
launchREPL(Kernel)
When I do the following:
def launchREPL(scope)
F = 0
puts scope.local_variables # => [:F]
end
this method is obviously invalid.
Test2
launchREPL(Kernel.binding)
def launchREPL(scope)
Kernel.binding.local_variables #= Error: private method 'local_variables' called for #<Binding>
end
Is there a way to do what I am trying to do?
Edit: PS Currently, this is the code inside launchREPL:
def launchREPL(scope=nil,winName="Ruby REPL")
# ICM RB file Begin:
puts "\"Starting REPL...\""
__b = binding #Evaluating in a binding, keeps track of local variables
__s = ""
###############################################################################
# SEND INSTANCE VARIABLES TO REPL
###############################################################################
#
#How to prepare scope
# currentScope = []
# Kernel.local_variables.each do |var|
# currentScope << [var,Kernel.eval(var.to_s)]
# end
# launchREPL(currentScope)
if scope != nil
scope.each do |varDef|
__b.instance_variable_set "@#{varDef[0].to_s}" , varDef[1]
__b.eval("@#{varDef[0].to_s} = __b.instance_variable_get(:@#{varDef[0].to_s})")
end
end
# to get instance variables: __b.instance_variable_get(__b.instance_variables[0])
# or better: __b.instance_variable_get(:@pipe1)
#
###############################################################################
bStartup = true
while bStartup || __s != ""
# If startup required skip evaluation step
if !bStartup
#Evaluate command
begin
__ret = __s + "\n>" + __b.eval(__s).to_s
rescue
__ret = __s + "\n> Error: " + $!.to_s
end
puts __ret
else
#REPL is already running
bStartup = false
end
#Read user input & print previous output
__s = WSApplication.input_box(__ret,winName,"")
__s == nil ? __s = "" : nil
end
end
source to share
This doesn't match the comment, so I would post it as an answer.
def launchREPL(scope = nil, winName = "Ruby REPL")
puts '"Starting REPL..."'
scope.eval('local_variables').each do |var|
instance_variable_set "@#{var}", scope.eval(var.to_s)
end if scope
s = ""
loop do
ret = begin
"#{s}\n> #{eval(s)}"
rescue => e
"#{s}\n> Error: #{e.message}"
end
puts ret
# s = WSApplication.input_box(ret, winName, "")
# break if s.empty?
s = "100 * @a" # remove this line and uncomment 2 above
end
end
a = 42
launchREPL(binding)
This is how your function should be written (I'm just making it look like ruby ββcode.) The above works (it doesn't really matter at all at the moment break
, but you can compute it infinitely 4200
).
source to share
While what you are trying to achieve is unclear and there are definitely many ways to get it right, each ruby ββmethod can be called with Object#send
:
def launchREPL(scope)
scope.send :local_variables #β here you go
end
a = 42
launchREPL(binding).include?(:a)
#β true
Sidenote: This is how your "4 lines" are usually written in ruby:
local_variables.map { |var| [var, eval(var.to_s)] }
And this is how they should be written (note Binding#local_variable_get
):
local_variables.map { |var| [var, binding.local_variable_get(var)] }
Summarizing:
def launchREPL(scope)
vars = scope.send(:local_variables).map do |var|
[var, scope.local_variable_get(var)]
end
# some other code
end
a = 42
launchREPL(binding).to_h[:a]
#β 42
source to share