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
Related
Suppose I have a file example.rb like so:
# example.rb
class Example
def foo
5
end
end
that I load with require or require_relative. If I didn't know that example.rb defined Example, is there a list (other than ObjectSpace) that I could inspect to find any objects that had been defined? I've tried checking global_variables but that doesn't seem to work.
Thanks!
Although Ruby offers a lot of reflection methods, it doesn't really give you a top-level view that can identify what, if anything, has changed. It's only if you have a specific target you can dig deeper.
For example:
def tree(root, seen = { })
seen[root] = true
root.constants.map do |name|
root.const_get(name)
end.reject do |object|
seen[object] or !object.is_a?(Module)
end.map do |object|
seen[object] = true
puts object
[ object.to_s, tree(object, seen) ]
end.to_h
end
p tree(Object)
Now if anything changes in that tree structure you have new things. Writing a diff method for this is possible using seen as a trigger.
The problem is that evaluating Ruby code may not necessarily create all the classes that it will or could create. Ruby allows extensive modification to any and all classes, and it's common that at run-time it will create more, or replace and remove others. Only libraries that forcibly declare all of their modules and classes up front will work with this technique, and I'd argue that's a small portion of them.
It depends on what you mean by "the global namespace". Ruby doesn't really have a "global" namespace (except for global variables). It has a sort-of "root" namespace, namely the Object class. (Although note that Object may have a superclass and mixes in Kernel, and stuff can be inherited from there.)
"Global" constants are just constants of Object. "Global functions" are just private instance methods of Object.
So, you can get reasonably close by examining global_variables, Object.constants, and Object.instance_methods before and after the call to require/require_relative.
Note, however, that, depending on your definition of "global namespace" (private) singleton methods of main might also count, so you check for those as well.
Of course, any of the methods the script added could, when called at a later time, themselves add additional things to the global scope. For example, the following script adds nothing to the scope, but calling the method will:
class String
module MyNonGlobalModule
def self.my_non_global_method
Object.const_set(:MY_GLOBAL_CONSTANT, 'Haha, gotcha!')
end
end
end
Strictly speaking, however, you asked about adding "objects" to the global namespace, and neither constants nor methods nor variables are objects, soooooo … the answer is always "none"?
In the book OO Design in Ruby, Sandi Metz says that the main use of modules is to implement duck types with them and include them in every class needed. Why is the Ruby Kernel a module included in Object? As far as I know it isn't used anywhere else. What's the point of using a module?
Ideally,
Methods in spirit (that are applicable to any object), that is, methods that make use of the receiver, should be defined on the Object class, while
Procedures (provided globally), that is, methods that ignore the receiver, should be collected in the Kernel module.
Kernel#puts, for example doesn't do anything with its receiver; it doesn't call private methods on it, it doesn't access any instance variables of it, it only acts on its arguments.
Procedures in Ruby are faked by using Ruby's feature that a receiver that is equal to self can be omitted. They are also often made private to prevent them from being called with an explicit receiver and thus being even more confusing. E.g., "Hello".puts would print a newline and nothing else since puts only cares about its arguments, not its receiver. By making it private, it can only be called as puts "Hello".
In reality, due to the long history of Ruby, that separation hasn't always been strictly followed. It is also additionally complicated by the fact that some Kernel methods are documented in Object and vice versa, and even further by the fact that when you define something which looks like a global procedure, and which by the above reasoning should then end up in Kernel, it actually ends up as a private instance method in Object.
As you already pointed out: Modules provide a way to collect and structure behavior, so does the Kernel module. This module is mixed in early into the class Object so every Ruby class will provide these methods. There is only a BasicObject before in hierarchy, it's child Objects purpose is only to get extended by the Kernel methods. BasicObject has only 7 methods that very very basic like new, __send__ or __id__.
class Object < BasicObject
include Kernel # all those many default methods we appreciate :)
end
I have a class something like below, and I used instance variables (array) to avoid using lots of method parameters.
It works as I expected but is that a good practice?
Actually I wouldn't expect that worked, but I guess class methods are not working as static methods in other languages.
class DummyClass
def self.dummy_method1
#arr = []
# Play with that array
end
def self.dummy_method2
# use #arr for something else
end
end
The reason instance variables work on classes in Ruby is that Ruby classes are instances themselves (instances of class Class). Try it for yourself by inspecting DummyClass.class. There are no "static methods" in the C# sense in Ruby because every method is defined on (or inherited into) some instance and invoked on some instance. Accordingly, they can access whatever instance variables happen to be available on the callee.
Since DummyClass is an instance, it can have its own instance variables just fine. You can even access those instance variables so long as you have a reference to the class (which should be always because class names are constants). At any point, you would be able to call ::DummyClass.instance_variable_get(:#arr) and get the current value of that instance variable.
As for whether it's a good thing to do, it depends on the methods.
If #arr is logically the "state" of the instance/class DummyClass, then store it in instance variable. If #arr is only being used in dummy_method2 as an operational shortcut, then pass it as an argument. To give an example where the instance variable approach is used, consider ActiveRecord in Rails. It allows you to do this:
u = User.new
u.name = "foobar"
u.save
Here, the name that has been assigned to the user is data that is legitimately on the user. If, before the #save call, one were to ask "what is the name of the user at this point", you would answer "foobar". If you dig far enough into the internals (you'll dig very far and into a lot of metaprogramming, you'll find that they use instance variables for exactly this).
The example I've used contains two separate public invocations. To see a case where instance variables are still used despite only one call being made, look at the ActiveRecord implementation of #update_attributes. The method body is simply load(attributes, false) && save. Why does #save not get passed any arguments (like the new name) even though it is going to be in the body of save where something like UPDATE users SET name='foobar' WHERE id=1;? It's because stuff like the name is information that belongs on the instance.
Conversely, we can look at a case where instance variables would make no sense to use. Look at the implementation of #link_to_if, a method that accepts a boolean-ish argument (usually an expression in the source code) alongside arguments that are ordinarily accepted by #link_to such as the URL to link to. When the boolean condition is truthy, it needs to pass the rest of the arguments to #link_to and invoke it. It wouldn't make much sense to assign instance variables here because you would not say that the invoking context here (the renderer) contains that information in the instance. The renderer itself does not have a "URL to link to", and consequently, it should not be buried in an instance variable.
Those are class instance variables and are a perfectly legitimate things in ruby: classes are objects too (instances of Class) and so have instance variables.
One thing to look out for is that each subclass will have its own set of class instance variables (after all these are different objects): If you subclassed DummyClass, class methods on the subclass would not be able to see #arr.
Class variables (##foo) are of course the other way round: the entire class hierarchy shares the same class variables.
In general what is the best practice and pro/cons to creating an instance variable that can be accessed from multiple methods or creating an instance variable that is simply passed as an argument to those methods. Functionally they are equivalent since the methods are still able to do the work using the variable. While I could see a benefit if you were updating the variable and wanted to return the updated value but in my specific case the variable is never updated only read by each method to decide how to operate.
Example code to be clear:
class Test
#foo = "something"
def self.a
if #foo == "something"
puts "do #{#foo}"
end
end
a()
end
vs
class Test
foo = "something"
def self.a(foo)
if foo == "something"
puts "do #{foo}"
end
end
a(foo)
end
I don't pass instance variable around. They are state values for the instance.
Think of them as part of the DNA of that particular object, so they'll always be part of what makes the object be what it is. If I call a method of that object, it will already know how to access its own DNA and will do it internally, not through some parameter being passed in.
If I want to apply something that is foreign to the object, then I'll have to pass it in via the parameters.
As you mentioned, this is a non-functional issue about the code. With that in mind...
It's hard to give a definitive rule about it since it depends entirely on the context. Is the variable set once and forgotten about it, or constantly updated? How many methods share the same variable? How will the code be used?
In my experience, variables that drive behavior of the object but are seldom (if at all) modified are set in the initialize method, or given to the method that will cascade behavior. Libraries and leaf methods tend to have the variable passed in, as it's likely somebody will want to call it in isolation.
I'd suggest you start by passing everything first, and then refactoring if you notice the same variable being passed around all over the class.
If I need a variable that is scoped at the instance level, I use an instance variable, set in the initialize method.
If I need a variable that is scoped at the method level (that is, a value that is passed from one method to another method) I create the variable at the method level.
So the answer to your question is "When should my variable be in scope" and I can't really answer that without seeing all of your code and knowing what you plan to do with it.
If your object behavior should be statically set in the initialization phase, I would use an instance variable.
we can call the Array method in the top level like this
Array(something)
that makes sense to me, it's a method call without explicit receiver, and self, which is main in this case, is inserted at the front of the method call. But isn't it that this is equivalent to :
Kernel.Array(something)
this doesn't make sense to me. Since in the first case, the object main is of class Object, which got Kernel module mixed in, thus have the Array method. But in the second case, we are calling the Array method on the Kernel module object itself, rather than main object, didn't they are NOT the same thing?
sorry for my bad english.
Kernel.Array is what is known as a module function. Other examples of module functions include Math.sin, and Math.hypot and so on.
A module function is a method that is both a class method on the module and also a private instance method. When you invoke Array() at the top-level you are invoking it as a private instance method of the main object. When you invoke it through Kernel.Array() you are invoking it as a class method on Kernel. They are the same method.
To learn more, read up on the module_function method in rubydocs: http://www.ruby-doc.org/core/classes/Module.html#M001642
class Object mixed-in module Kernel, but Kernel is an instance of Object. So Kernel "module" methods - is it's instance methods.
What's confusing you is the difference between class and instance methods.
Class methods don't have an explicit receiver, and thus no self to access other fields with. They just... are.
Generally instance methods are used to query or manipulate the attributes of a given object, whereas the class methods are "helper" or "factory" methods that provide some functionality associated with or especially useful for a certain kind of class, but not dependent on actual live instances (objects) of that class.
Not sure about Ruby, but Java has (for example) a whole class, Math that contains nothing but instance methods like sin(), max(), exp() and so forth: There is no "Math" object, these are just methods that embody mathematical algorithms. Not the best example, because in Ruby those methods are probably embedded right into the numeric classes as instance methods.
The case you mention is a bit confusing because Array's () method and Kernel's Array() method are in fact different methods that do similar things. Both are class methods.
Array() takes a list of arguments and makes and returns an array containing them.
Kernel.Array() takes a single argument of an "array-able" type, such as a sequence, and takes the values returned by this argument and builds an array from those.
UPDATE
The downvote was perhaps justified; I apologize for taking on a subject outside my area of expertise. I think I'll be deleting this answer soon.
# Chuck: I would sincerely hope that a language/library's official documentation would offer some meaningful clues as to how it works. This is what I consulted in answering this question.
The rdoc for Kernel.Array():
Returns arg as an Array. First tries to call arg.to_ary, then arg.to_a. If both fail, creates a single element array containing arg (unless arg is nil).
for Array.():
Returns a new array populated with the given objects.
I don't know about you, but I think if the docs vary that much then either they're talking about separate methods or the documentation is a train wreck.
# freeknight:
But everything in ruby is an object of some kind, even classes and modules. And Kernel.Array is actually a method call on an specific object - the Kernel object.
Yeah, under the covers it's similar in Java too. But the Array() method isn't doing anything with Kernel, any more than Array() is doing anything with the Array class object, so this is really only a semantic quibble. It's an instance method because you could hang it off class IPSocket if you were crazy enough, and it would still work the same way.
They are the same thing:
a = Kernel.Array('aa')
=> ["aa"]
a.class
=> Array
a = Array('aaa')
=> ["aaa"]
a.class
=> Array
Maybe there is an alias?