I want to treat certain uppercase identifiers as dynamically calculated constants - so when I evaluate a code string such as eval("foo(BAR)") I can look up the value of BAR and supply it to the evaluation.
Don't even ask why I want to do this. :-)
The trouble is that const_missing() (unlike, say, method_missing() ) must be a class method, not an instance method. So if I try to write a function like this:
def self.const_missing(name)
if #data[name]
return #data[name]
end
raise "Missing const #{name.inspect}"
end
I can't see the instance variable #data that tells me what the value of FOO should be.
I could of course use a class variable ##data, but that would not let me a different data for each object. I could set ##data from #data immediately before doing the eval, but that would not be thread-safe nor recursion-friendly.
My fallback is to use thread-local storage to contain a stack of #data, and push and pop #data before and after the eval call. But that feels so... inelegant.
Is there a better way to do this?
Don't even ask why I want to do this. :-)
thx for the suggestion, but why exactly are you doing this?
i'm asking because there probably is a different approach that people can show you if you try to explain the problem you are trying to solve.
another thing, don't ever use eval. just don't! if you are not able to one of the other possible metaprogramming methods like class_eval instance_eval method_missing and friends, you are probably doing it wrong.
let me emphasize, DONT USE eval()!
Related
In Ruby I'd like to have a message printed to console whenever a given variable is changed at any time during execution.
How should I approach this? Should I monkey patch the method for assigning values to variables?
I could only find this related question Hook to be called when a variable changes where an answer is suggesting to redefine #freeze but this approach has limitations. Also it doesn't look right.
Isn't there a better and more consistent solution?
Add your own getter/setter.
Example:
class Person
def name
#name
end
def name=(s)
#name=s
puts 'name has changed!'
end
end
This is not possible.
Neither the set_trace_func nor the TracePoint API support tracing variable assignments, and …
Should I monkey patch the method for assigning values to variables?
… such a method does not exist.
Ruby just doesn't consider variables to part of the object-oriented fabric of the program, I guess. Only objects and methods.
If we call caller method, we get something like:
prog.rb:3:in `a'
prog.rb:6:in `b'
prog.rb:9:in `c'
This is helpful for humans, but if I wanted to analyze the stack programmatically, not really, as two methods called :a may be entirely unrelated.
Is there any way/method to extract information about the receiver of the methods (like its class or object id) as well? For example:
prog.rb:3:in `Klass#a'
prog.rb:6:in `Modoole#b'
prog.rb:9:in `OtherKlass#c'
Formatting is only an example; this info might be an Array or anything.
I'm trying to emulate this with TracePoint, but forming a separate stack is a bad solution. Is there any Ruby way I missed in the docs?
There's an alternative to Kernel#caller named Kernel#caller_locations, that returns an array of Thread::Backtrace::Location objects. According to the manual, these should in theory be able to give you this information through the #label method.
Returns the label of this frame.
Usually consists of method, class, module, etc names with decoration.
After trying this out however, I need to question the term usually in the docs, because it seems to only return the method name still. Unless usually means it works for you, there seems to be no way of accomplishing this as of now.
Edit:
As per comment, one case that satisfies the condition of usually is when the method call is coming from within a Class or Module body:
class A
def trace
puts caller_locations.first.label
end
end
class B
A.new.trace
end
#=> <class:B>
module C
A.new.trace
end
#=> <module:C>
Let's say we have class
class Evaluator
attr_reader :response
def initialize(response)
#response = response
end
def order
#_order ||= Order.find(response.order_id)
end
end
I'd like to ask about #_order which exists only for caching purposes. This convention is to prevent accidental use of an instance variable (when it is not initialized yet, for example). I'd like to force using order instead of #order. What do you think about it? Do you know any better way of achieving this goal?
You cannot make instance variables inaccessible from subclasses, in Ruby. Instance variables are protected (in common programming terms) by default, which means that they are not accessible from the outside of the class, but are accessible from within the class hierarchy.
You can only control the accessibility of methods with the use of private, protected and public.
You can make it painfully obvious that one should not use the member, like calling it #lazy_loaded_order, or even generalize it like this:
def order
#lazy_members[:order] ||= Order.find(response.order_id)
end
This way, it is obvious that anyone trying to use #lazy_members will do it on his own risk...
Say I have an object with a method that accesses an object:
def foo
#foo
end
I know I can use send to access that method:
obj.send("foo") # Returns #foo
Is there a straightforward way to do a recursive send to get a parameter on the #foo object, like:
obj.send("foo.bar") # Returns #foo.bar
You can use instance_eval:
obj.instance_eval("foo.bar")
You can even access the instance variable directly:
obj.instance_eval("#foo.bar")
While OP has already accepted an answer using instance_eval(string), I would strongly urge OP to avoid string forms of eval unless absolutely necessary. Eval invokes the ruby compiler -- it's expensive to compute and dangerous to use as it opens a vector for code injection attacks.
As stated there's no need for send at all:
obj.foo.bar
If indeed the names of foo and bar are coming from some non-static calculation, then
obj.send(foo_method).send(bar_method)
is simple and all one needs for this.
If the methods are coming in the form of a dotted string, one can use split and inject to chain the methods:
'foo.bar'.split('.').inject(obj, :send)
Clarifying in response to comments: String eval is one of the riskiest things one can do from a security perspective. If there's any way the string is constructed from user supplied input without incredibly diligent inspection and validation of that input, you should just consider your system owned.
send(method) where method is obtained from user input has risks too, but there's a more limited attack vector. Your user input can cause you to execute any 0-arghument method dispatchable through the receiver. Good practise here would be to always whitelist the methods before dispatching:
VALID_USER_METHODS = %w{foo bar baz}
def safe_send(method)
raise ArgumentError, "#{method} not allowed" unless VALID_USER_METHODS.include?(method.to_s)
send(method)
end
A bit late to the party, but I had to do something similar that had to combine both 'sending' and accessing data from a hash/array in a single call. Basically this allows you to do something like the following
value = obj.send_nested("data.foo['bar'].id")
and under the hood this will do something akin to
obj.send(data).send(foo)['bar'].send(id)
This also works with symbols in the attribute string
value = obj.send_nested('data.foo[:bar][0].id')
which will do something akin to
obj.send(data).send(foo)[:bar][0].send(id)
In the event that you want to use indifferent access you can add that as a parameter as well. E.g.
value = obj.send_nested('data.foo[:bar][0].id', with_indifferent_access: true)
Since it's a bit more involved, here is the link to the gist that you can use to add that method to the base Ruby Object. (It also includes the tests so that you can see how it works)
In Ruby, is there a way to get the name of the class that creates an instance of MyClass?
I know that I could pass it in as an argument on my initialize method, but I want to know if any data is already there that has to do with the class that created an instance of MyClass in side of MyClass.
So it would be like
class MyClass
def initialize
#who_called_me = who_called_me.name
end
def who_called_me
puts #who_called_me
end
end
Although this is not portable between implementations and versions, this is a crude solution:
who_made_me=caller[3].split(':')[1][4..-2]
What this does is it gets the current stack, skips the strings for initialize, allocate, and new, and then gets the method name out of the string. Again, this is a total hack, and is based around unspecified behavior. I would not suggest using this unless absolutely necessary.
In general, this is evil. I've seen an equivalent in C#, but it produced violently cruel side effects, not to mention ugly-as-heck code.
In Ruby, if you really had to do this, you'd probably start with Kernel.caller. But please don't do that.