I am trying to send an expression to a class and get the result. I don't know in advance what the expression would be and I get it as a string form the user. and I can't run it from in the class
class MyClass
def self.alias1
return 1
end
def self.alias2
return 2
end
# I can't do that:
##result = alias1+alias2
#print #result.to_s
end # end class MyClass
# That is working
inst1 = MyClass.new
result = inst1.send("alias1")
# result = 1
# I want to do that:
inst = MyClass.new
result = inst.send("alias1+alias2")
print result
expected result:
3
now I am getting an error that the method "alias1+alias2" doesn't exist.
From Ruby:
main: undefined method `alias1+alias2' for MyClass:Class (NoMethodError)
Let's clean up your class:
class MyClass
def one
1
end
def two
2
end
end
You can use instance_eval to evaluate arbitrary expression in the context of your MyClass instance:
m = MyClass.new
m.instance_eval("one + two")
#=> 3
But this is dangerous. The user could define additional methods, access the filesystem or run shell commands.
Therefore, you should parse and evaluate the expression yourself.
Related
hey I want that my method logify puts each method with its parameters and return value of my class A. I wrote for example a simple class A with two methods add and sub and the output should look like that:
Output:
Method add(1, 2) called
return value 3
Method sub(1, 2) called
return value -1
I know that I can get each method with self.instance_methods(false) but can someone please help me further?
require_relative "log"
class A
extend Log
def add(a, b)
a + b
end
def sub(a, b)
a - b
end
logify
end
a = A.new
a.add(2,1)
a.sub(2,1)
module Log
def logify
puts self.instance_methods(false)
end
end
You can use Module#prepend and Module#prepended to help with this like so:
module Log
def self.prepended(base)
base.instance_methods(false).each do |m|
define_method(m) do |*args, &block|
puts "Method #{m}(#{args.join(',')}) called"
val = super(*args, &block)
puts "return value #{val}"
val
end
end
end
end
class A
def add(a, b)
a + b
end
def sub(a, b)
a - b
end
end
A.prepend(Log)
What this does is it defines a method in the prepended module with the same name as the original then builds your output and delagets to the original method in the middle (super) to obtain the return value.
Examples
a = A.new
a.add(2,1)
# Method add(2,1) called
# return value 3
#=> 3
a.sub(2,1)
# Method sub(2,1) called
# return value 1
#=> 1
Caveat: this will only show the provided arguments and will not output default arguments in the method signature
The ruby core library includes the class TracePoint, which can be used to trace just about anything - from methods being defined, or invoked, or exceptions being raised, ...
Here is an example usage, which will perform the tracking you desired:
class A
def add(a, b)
a + b
end
def sub(a, b)
a - b
end
end
TracePoint.trace(:call, :return) do |tp|
next unless tp.defined_class == A
case tp.event
when :call
params = tp.parameters.map { |arg| eval(arg[1].to_s, tp.binding) }
puts "Method #{tp.method_id}(#{params.join(', ')}) called"
when :return
puts "return value #{tp.return_value}"
end
end
# The trace has been enabled! Any time one of those events occurs, the block is evaluated.
a = A.new
a.add(2,1)
a.sub(2,1)
Output:
Method add(2, 1) called
return value 3
Method sub(2, 1) called
return value 1
Fetching the params data is, as you can see, a little troublesome. TracePoint has access to the method signature, but you need to make use of the trace's binding to see what values it's actually been called with.
How would you get my 'def showVars' built within the 'second' class to output the 'puts (variables)' that it inherited from the 'First' class?
class First
##varOne = 1
CONSTANT_ONE = 10
end
class Second < First
def showVars
puts ##varOne
puts CONSTANT_ONE
end
end
My failed attempt:
class First
##varOne = 1
CONSTANT_ONE = 10
end
class Second < First
def showVars
puts ##varOne
puts CONSTANT_ONE
end
end
puts Second.showVars # <-- fails
You can't call Second.showVars because it's an instance method. To call it that way, you have to use a class method. You can do that by adding self in the method name.
class First
##varOne = 1
CONSTANT_ONE = 10
end
class Second < First
def self.showVars
puts ##varOne
puts CONSTANT_ONE
end
end
puts Second.showVars
The output now is:
1
10
[Finished in 0.1s]
Class methods are equivalent to static methods in other languages.
Another point I noticed is that you named your method showVars using camelCase. Ruby methods should be named using snake_case.
I want to pass a string in a variable from a method to another class. I have this code:
class A
def method_a
variable = "some string"
B.method_b(variable)
end
end
class B
def self.method_b(parameter)
puts parameter
end
end
This code generates the following error:
Undefined local variable or method `variable`
What am I doing wrong?
What you've defined here is an instance method, one that can only operate on an instance of B:
class B
def self.class_only(v)
puts "Class: #{v}"
end
def instance_only(v)
puts "Instance: #{v}"
end
end
The class_only method does not require an instance:
B.class_only(variable)
The instance_only method must operate on an instance:
b = B.new
b.instance_only(variable)
Now anything the B method is given via arguments is valid, and any local or instance variables on the A side are things you can supply to the call. There's no scope issues here because you're explicitly passing them over.
For example:
class A
def test
variable = SecureRandom.hex(6)
B.class_only(variable)
end
end
A.new.test
In the code below:
::Trace.tracer = ::Trace::ZipkinTracer.new()
what is the relation between Trace and ZipkinTracer?
ZipkinTracer is inside of Trace namespace, like this:
module Trace
class ZipkinTracer
# ...
end
end
The :: before constant name means that you point to the root. For example in the following code:
class Class1
end
module Module1
class Class1
end
def foo
::Class1
end
end
::Class1 ensures that you refer to the "root" Class1. If you had:
def foo
Class1
end
the Module1::Class1 would be referred.
This code does the following thing. First, it instantiates class ZipkinTracer:
new_instance = Trace::ZipkinTracer.new()
Then, it calls #tracer= method of the Trace module:
Trace.tracer=( new_instance )
Ruby syntax allows this to be rewritten as
Trace.tracer = new_instance
In this case, no assignment is happening, but a method ending in = is called. Methods ending in = are allowed in Ruby, used generally for attribute assignment, and they are special in that they always return the assigned value (that is, their argument), regardless of what other return value you might be trying to prescribe:
class Foo
def bar=( value )
puts "Method #bar= called!"
#bar = value
puts "Trying to return Quux!"
return "Quux!"
end
def bar; #bar end
end
foo = Foo.new
foo.bar #=> nil
foo.bar = "Baz!"
#=> Method #bar= called!
#=> Trying to return Quux!
#=> "Baz!" -- attempt to explicitly return "Quux!" failed
foo.bar #=> "Baz!"
In the code below:
::Trace.tracer = ::Trace::ZipkinTracer.new()
what is the relation between Trace and ZipkinTracer?
ZipkinTracer is inside of Trace namespace, like this:
module Trace
class ZipkinTracer
# ...
end
end
The :: before constant name means that you point to the root. For example in the following code:
class Class1
end
module Module1
class Class1
end
def foo
::Class1
end
end
::Class1 ensures that you refer to the "root" Class1. If you had:
def foo
Class1
end
the Module1::Class1 would be referred.
This code does the following thing. First, it instantiates class ZipkinTracer:
new_instance = Trace::ZipkinTracer.new()
Then, it calls #tracer= method of the Trace module:
Trace.tracer=( new_instance )
Ruby syntax allows this to be rewritten as
Trace.tracer = new_instance
In this case, no assignment is happening, but a method ending in = is called. Methods ending in = are allowed in Ruby, used generally for attribute assignment, and they are special in that they always return the assigned value (that is, their argument), regardless of what other return value you might be trying to prescribe:
class Foo
def bar=( value )
puts "Method #bar= called!"
#bar = value
puts "Trying to return Quux!"
return "Quux!"
end
def bar; #bar end
end
foo = Foo.new
foo.bar #=> nil
foo.bar = "Baz!"
#=> Method #bar= called!
#=> Trying to return Quux!
#=> "Baz!" -- attempt to explicitly return "Quux!" failed
foo.bar #=> "Baz!"