I have a Ruby object #element that I used .inspect on. The result is below
#<Watir::Hidden:0x7b61410 located=false selector={:type=>"hidden", :tag_name=>"input", :id=>"foo"}>
How can I access "foo"
I've tried #element.id (which finds the ruby object id instead), #element[:id] and #element.selector[:id], #element['selector'][:id] etc.
Any help?
You can get attribute values using the attribute_value method:
#element.attribute_value("id")
Generally those are instance variables of an object. If they haven't exposed an attr_accessor, then you're touching private data, something that's generally frowned upon.
You can always get these if they are stored in an instance variable by using something like:
#element.instance_variable_get('#selector')[:id]
I wouldn't make use of this too extensively, it's a bad practice, but sometimes you have to do what you have to do.
Related
Rubocop dislikes the following; it issues Pass a binding, __FILE__ and __LINE__ to eval.:
sort_lambda = eval "->(a) { a.date }"
Yes, I know that eval is a security problem. The issue of security is out of scope for this question.
The Ruby documentation on binding says:
Objects of class Binding encapsulate the execution context at some particular place in the code and retain this context for future use. The variables, methods, value of self, and possibly an iterator block that can be accessed in this context are all retained. Binding objects can be created using Kernel#binding, and are made available to the callback of Kernel#set_trace_func and instances of TracePoint.
These binding objects can be passed as the second argument of the Kernel#eval method, establishing an environment for the evaluation.
The lambda being created does not need to access any variables in any scopes.
A quick and dirty binding to the scope where the eval is invoked from would look like this:
sort_lambda = eval "->(a) { a.date }", self.binding, __FILE__, __LINE__
Ideally, a null binding (a binding without anything defined in it, nothing from self, etc.) should be passed to this eval instead.
How could this be done?
Not exactly, but you can approximate it.
Before I go further, I know you've already said this, but I want to emphasize it for future readers of this question as well. What I'm describing below is NOT a sandbox. This will NOT protect you from malicious users. If you pass user input to eval, it can still do a lot of damage with the binding I show you below. Consult a cybersecurity expert before trying this in production.
Great, with that out of the way, let's move on. You can't really have an empty binding in Ruby. The Binding class is sort of compile-time magic. Although the class proper only exposes a way to get local variables, it also captures any constant names (including class names) that are in scope at the time, as well as the current receiver object self and all methods on self that can be invoked from the point of execution. The problem with an empty binding is that Ruby is a lot like Smalltalk sometimes. Everything exists in one big world of Platonic ideals called "objects", and no Ruby code can truly run in isolation.
In fact, trying to do so is really just putting up obstacles and awkward goalposts. Think you can block me from accessing BasicObject? If I have literally any object a in Ruby, then a.class.ancestors.last is BasicObject. Using this technique, we can get any global class by simply having an instance of that class or a subclass. Once we have classes, we have modules, and once we have modules we have Kernel, and at that point we have most of the Ruby built-in functionality.
Likewise, self always exists. You can't get rid of it. It's a fundamental part of the Ruby object system, and it exists even in situations where you don't think it does (see this question of mine from awhile back, for instance). Every method or block of code in Ruby has a receiver, so the most you can do is try to limit the receiver to be as small an object as possible. One might think you want self to be BasicObject, but amusingly there's not really a way to do that either, since you can only get a binding if Kernel is in scope, and BasicObject doesn't include Kernel. So at minimum, you're getting all of Kernel. You might be able to skimp by somehow and use some subclass of BasicObject that includes Kernel, thereby avoiding other Object methods, but that's likely to cause confusion down the road too.
All of this is to emphasize that a hypothetical null binding would really only make it slightly more complicated to get all of the global names, not impossible. And that's why it doesn't exist.
That being said, if your goal is to eliminate local variables and to try, you can get that easily by creating a binding inside of a module.
module F
module_function def get_binding
binding
end
end
sort_lambda = eval "->(a) { a.date }", F.get_binding
This binding will never have local variables, and the methods and constants it has access to are limited to those available in Kernel or at the global scope. That's about as close to "null" as you're going to get in the complex nexus of interconnected types and names we call Ruby.
While I originally left this as a comment on #Silvio Mayolo's answer, which is very well written, it seems germane to post it as an answer instead.
While most of what is contained within that answer is correct we can get slightly closer to a "Null Binding" through BasicObject inheritance:
class NullBinding < BasicObject
def get_binding
::Kernel
.instance_method(:binding)
.bind(self)
.call
end
end
This binding context has as limited a context as possible in ruby.
Using this context you will be unable to reference constants solely by name:
eval 'Class', NullBinding.new.get_binding
#=> NameError
That being said you can still reference the TOP_LEVEL scope so
eval '::Class', NullBinding.new.get_binding
#=> Class
The methods directly available in this binding context are limited only to the instance methods available to BasicObject. By way of Example:
eval "puts 'name'", NullBinding.new.get_binding
#=> NoMethodError
Again with the caveat that you can access TOP_LEVEL scope so:
eval "::Kernel.puts 'name'", NullBinding.new.get_binding
# name
#=> nil
I have a class I am testing, call it myfoo. It accesses a class called yourbar. Specifically something like this...
yourbar_obj.projects[project_name]
In my spec code I have this
let(:yourbar_obj) { Class.new }
and I want to mock it to respond to the hash attribute access.
So I tried this
expect(yourbar_obj).to receive(projects).and_return(some_obj)
But when I run the code it says
NoMethodError: undefined method `projects' ...
Is it possible to mock a hash access like that? The same type of thing works for regular method calls.
I even tried adding a .with(project_name) just in case. Same error.
thoughts?
Thanks to Max's help. Here is the correct answer...
some_hash_obj[project_name] = some_obj
expect(yourbar_obj).to receive(:projects).and_return(some_hash_obj)
Two key parts. The : before projects, and some_hash_obj must be a hash. I was trying to return the value (which was an obj) at the hash index in one shot, but that ain't how it works. return the hash, and the [] will apply to it.
Is method an object in Ruby? My friend asked me this question.I read about this in website.But still I didn't understand it.Can anyone help me?
There seems to be a confusion here due to ambiguity of the term method. Method in the most ordinary sense is not an object. In the following:
"foo".upcase
the method upcase is applied to an object "foo", but upcase is not an object, as can be seen by the fact that it cannot stand alone:
upcase # => error
(Do not confuse this with when it can be considered that the receiver is omitted).
However, there is a class Method, whose instances correspond to methods, and are objects. They may also be called methods, but that is not the normal usage of the term method.
No, they are not.
Methods themselves are a language structure of Ruby, and they are not objects. But there is a class Method, whose instances represent methods, and can be called using Method#call.
Also, there is another kind of instances - instances of class UnboundMethod, which represent methods that are detached from specific objects. They can't be called directly, but can be used in many different ways.
If you are looking for something like Javascript's functions, then procs and lambdas are what you want.
I thought inspect dumps the contents of an object, but I got far more than I understood the object to contain. I was dumping within a do ... end block. Is there another method similar to inspect that gives only what the object contains, or, how might one inspect with no context?
inspect is supposed to print a human-readable representation of the object. It is often used in trace statements. By default, it will print the object's class, object ID and instance variables associated with their values. So, it allows you to quickly determine the type, identity and attributes of the object.
Consider also the to_s method, which is supposed to convert the object to a string; it will often compose a new string representation using the object's attributes only; it may or may not use all of them.
Both of these methods do not take anything but its receiver into account.
Object#inspect is not a way to dump the contents of an object, it is merely a string representation of the object, meant to be human readable.
Individual classes or objects may overwrite the behavior of this method to return any string they want. The default behavior is to display the class name, object id, and any instance variables (which are converted to string by calling #inspect on each one). This level of recursion, where instance variables are also #inspected may be why you are seeing more "context" than you are expecting.
If you are looking for a way to dump the contents of an object to a string, so that it can be re-created from that string, you should look at Marshal. str = Marshal.dump(obj) attempts to dump the contents of an object to a string, while obj = Marshal.load(str) converts the string back into an object.
Another option, depending on the types of objects, is to serialize them using JSON or YAML.
As stated in the other answers, inspect is meant for debugging by dumping all content of the object (human-readable representation of obj).
I think you might find to_yaml more useful in your case:
object.to_yaml
# #=> --- !ruby/object:ObjectType
# attr1 => value
# attr2 => value
# ... etc
Tip:
I also found adding puts before to_yaml gives a more readable console output:
puts object.to_yaml
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)