So what I learned is that when I call a variable on an object e.g.
my_object.variable
A method called variable is returning the value of variable
def variable
#variable
end
In Java, there are access modifiers. How can Ruby make use of access modifiers if getter methods are named after their variables?
First, there is a bit of terminology to clean up in the question before answering it.
One is that you never "call" variables. The Ruby expression
my_object.variable
is a method call. There is no such thing as a variable call. You call methods, not variables. Even if the method is named variable. :)
The second is if you did define the method like this
def variable
#variable
end
either directly or by saying
attr_reader :variable
Then you have a method named variable and a variable named #variable.
Now to answer the question.
Ruby places access modifiers public, protected, and private on methods only, and not on variables. Access controls on variables don't really make sense, because they only be referenced within an object’s methods, and never with a prefix! In other words, you can never write this:
obj.#var
That's just a syntax error. You can write
obj.var
where var is the name of a method. And the access controls apply to that method only.
The fact that you may be making variables and methods with the same name (except for the #) actually doesn't matter. Only methods have access controls.
Hope that helps clear up some understandable confusion!
There are several ways to make a method private in Ruby.
Use private to make all later methods private:
class Foo
def public_method
end
private
def private_method
end
end
Or make a method private after you defined it:
class Foo
def public_method
end
private :public_method # public_method is now private
end
Or - since the method definition returns a symbol too - this also works:
class Foo
private def method_name
end
end
Related
my idea is to create a community wiki for people that come from a java background because reading a lot of explanations, I couldn't comprehend anything until I actually tried a couple of things and the pieces of the puzzle started to find their places. But I first need to make sure I'm getting it right. Coming from such background it was very confusing for me to find out that #variable may mean 2 very different things.
Here is an example:
class Test
#ins = "gah"
def self.ins
puts #ins
end
def initialize()
#ins = "wtf?"
end
def ins2
puts #ins
end
end
As far as I understand, the first #ins is an instance variable of the object representing the class Test. The second #ins is an instance variable in an object of class Test.
Now things start to make some sense to me. Here a couple of examples:
[14] pry(main)> test.ins2
wtf?
We are calling a method of an object and it returns the object's instance variable.
[15] pry(main)> test.ins
NoMethodError: undefined method `ins' for #<Test:0x000000017d9348 #ins="wtf?">
We are trying to call a class method through an object, this method is of the class so we are getting NoMethodError
[16] pry(main)> Test.ins
gah
We are calling a class method so it properly sees the instance variable of the class object.
[17] pry(main)> Test.ins2
NoMethodError: undefined method `ins2' for Test:Class
We are calling an object method through the class which is incorrect so throwing NoMethodError.
All of the above was performed with ruby 2.0. So what am I asking?
Am I getting it right?
Am I getting the ruby terminology correct?
Any real usage of class instance variables that make sense in a properly designed app? Or are these simply the better ##class variables?
it was very confusing for me to find out that #variable may mean 2 very different things.
No, it doesn't. Classes are objects just like any other object. They can have instance variables just like any other object. They can have instance methods just like any other object. In fact, unlike Java, which has three different kinds of "methods" (instance methods, static methods and constructors), in Ruby, there is exactly one kind of method: instance methods.
The beauty of having classes being objects is precisely that #variable always means exactly the same thing.
There is no such thing as a class instance variable: it's just a normal instance variable like any other. The object happens to be an instance of Class, but that doesn't change the nature of the instance variable. An instance variable of an object of class String is not a string instance variable, it's just an instance variable. Likewise, an instance variable of an object of class Class is just an instance variable.
There is no such thing as a class method: it's just a normal singleton method of an object which happens to be an instance of Class. (Acually, there's no such thing as a singleton method either: it's just a normal instance method of the object's singleton class.)
Note: Rubyists may use the term "class method" in casual conversation. But that doesn't mean that class methods actually exist, it only means that "instance method of the class object's singleton class" is a mouthful. The important thing is: because classes are objects, they work exactly like all other objects. They can have instance methods (defined in class Class or inherited from Module, Object, Kernel or BasicObject), they can have "singleton methods" (which really are instance methods of their respective singleton classes), they can have instance variables.
They can also have class variables (##variables) … those are weird. Ignore them :-)
First, to understand instance variables, one need to know this - classes are objects.
All classes are instances of Class(read the doc) which inherits from Object. That's why classes are objects.
Then, every instance variables(ids marked with #, like #ins) are defined in self.
When self is a class, they are instance variables of classes(class instance variables).
When self is a object, they are instance variables of objects(instance variables).
In different code scopes, the self represents different things.
class Test
# class scope, uncomment following line to see the value of self
# p self
#ins = "gah"
def self.ins
# class scope
# p self
puts #ins
end
def initialize()
# object scope
# p self
#ins = "wtf?"
end
def ins2
# object scope
# p self
puts #ins
end
end
Everything looks right to me.
A class variable will be passed down through inheritance while an instance variable on a class will not. (src: Ruby class instance variable vs. class variable)
As far as design goes, I tend to prefer avoiding class variables altogether (I'd rather use a singleton), but if I had to choose one I would probably choose a class variable over a class instance variable to avoid confusion.
As a beginner, I've not quite got my head around self so I'm having trouble understanding how the self.blogs in initialize, and blogs then self.blogs on the next line after in the add_blog method, are all working together in the below code.
Why does blogs in the add_blog method access the same variable as self.blogs in initalize?
And then why is self.blogs used afterwards to sort the blogs array?
Also, would it matter if I used #blogs in initialize, instead of self.blogs?
class User
attr_accessor :username, :blogs
def initialize(username)
self.username = username
self.blogs = []
end
def add_blog(date, text)
added_blog = Blog.new(date, self, text)
blogs << added_blog
self.blogs = blogs.sort_by { |blog| blog.date }.reverse
added_blog
end
end
To answer your question, we have to reveal the true nature of attr_accessor.
class Foo
attr_accessor :bar
end
is completely equivalent to
class Foo
def bar
#bar
end
def bar=(value)
#bar = value
end
end
You can see that attr_accessor :bar defines two instance methods Foo#bar and Foo#bar= that access an instance variable #bar.
Lets then look at your code.
self.blogs = [] in initialize is actually calling the method User#blogs=, and through it sets the instance variable #blogs with an empty array. It can be written as self.blogs=([]) but it's noisy, isn't it? By the way, you can't omit self. here otherwise it just sets a local variable.
blogs << added_blog calls the method User#blog which returns the value of #blogs. It can also be written as self.blogs().push(added_blog), but again it's not rubyish. You can omit self. because there is no local variable named blogs in User#add_blog, so ruby falls back to call the instance method.
self.blogs = blogs.sort_by { |blog| blog.date }.reverse mixes call to User#blogs= and User#blogs.
For most method calls on self, self.method_name is equivalent to just method_name. That's not the case for methods whose name ends with an =, though.
The first thing to note, then, is that self.blogs = etc doesn't call a method named blogs and then somehow 'assign etc to it'; that line calls the method blogs=, and passes etc to it as an argument.
The reason you can't shorten that to just blogs = etc, like you can with other method calls, is because blogs = etc is indistinguishable from creating a new local variable named blogs.
When, on the previous line, you see a bare blogs, that is also a method call, and could just as easily have been written self.blogs. Writing it with an implicit receiver is just shorter. Of course, blogs is also potentially ambiguous as the use of a local variable, but in this case the parser can tell it's not, since there's no local variable named blogs assigned previously in the method (and if there had been, a bare blogs would have the value of that local variable, and self.blogs would be necessary if you had meant the method call).
As for using #blogs = instead of self.blogs =, in this case it would have the same effect, but there is a subtle difference: if you later redefine the blogs= method to have additional effects (say, writing a message to a log), the call to self.blogs = will pick up those changes, whereas the bare direct access will not. In the extreme case, if you redefine blogs= to store the value in a database rather than an instance variable, #blogs = won't even be similar anymore (though obviously that sort of major change in infrastructure will probably have knock-on effects internal to the class regardless).
#variable will directly access the instance variable for that class. Writing self.variable will send to the object a message variable. By default it will return the instance variable but it could do other things depending on how you set up your object. It could be a call to a method, or a subclass, or anything else.
The difference between calling blogs or self.blogs is totally up to syntax. If you use an opinionated syntax checker like rubocop it will tell you that you have a redundant use of self
Suppose I have the following:
module MyModule
module SubModule
Var = 'this is a constant'
var = 'this is not a constant'
def hello_world
return 'hello world!'
end
end
end
In the same file, I can only seem to access MyModule::SubModule::Var, but not any the constant or the method. If I now create a class and include these modules in different ways, I get additional strange behavior:
class MyClass
include MyModule
def initialize()
puts SubModule::Var
end
def self.cool_method
puts SubModule::Var
end
end
In this case, I can again only access Var, but not the other two. SubModule::var and SubModule::hello_world do not work. Finally:
class MyClass
include MyModule::SubModule
def initialize()
puts Var
puts hello_world
end
def self.cool_method
puts Var
puts hello_world
end
end
In this case, I can now access both Var and the method hello_world but not var, and, the weirdest thing, is that hello_world appears to have become an instance method! That is, the call to hello_world in initialize works, but the one in self.cool_method doesn't. This is pretty strange, considering that Var seems to have been included as a class variable, since outside the class, I must access them like so:
MyClass::Var
x = MyClass.new
x.hello_world
So, I have a few major questions.
What is going on behind the scenes with regards to Var vs var? It appears that capitalizing a variable name is more than just a convention after all.
When includeing a module, what kinds of things are passed to the including class, and at what scope?
Is there a way to do the opposite? That is, use include to include an instance variable or a class method?
What is going on behind the scenes with regards to Var vs var? It appears that capitalizing a variable name is more than just a convention after all.
Yes, of course, it's not a convention. Variables which start with an uppercase letter are constants, variables which start with a lowercase letter are local variables. The two are completely different.
When includeing a module, what kinds of things are passed to the including class, and at what scope?
Nothing gets passed anywhere. includeing a mixin simply makes that mixin the superclass of the class you are includeing it into. That's all. Everything else then works exactly as with classes.
Is there a way to do the opposite? That is, use include to include an instance variable or a class method?
I don't understand this question. Instance variables have nothing to do with mixins or classes. They belong to instances, that's why they are called "instance" variables.
There are no such things as "class methods" in Ruby. Ruby only knows one kind of methods: instance methods. When Rubyists talk to each other, they will sometimes use the term "class method" to mean "singleton method of an object that happens to be a class", but they do that knowing full well that class methods don't actually exist, it's just a shorthand in conversation. (And, of course, singleton methods don't exist either, they are just a convenient way of saying "instance method of the singleton class".)
I understand that when we define a class variable in ruby it's preceded with
##variableName but in this code
class Hello
#var2
##var3=0
def foo1
return #var1
end
def set_foo1(par1)
#var1=par1
end
end
I understand that var3 is a class variable and has to be initialized with some value. But what about var2? Is var2 still corresponds to an object?
When i called the program with obj1.var2=100 i get a noMethodError
Also, when i call puts Hello.var3 i get the same noMethodError
Can anyone please explain where i am getting it wrong?
#var2 is a class level instance variable, whereas ##var3 is a class hierarchy variable.
An article about usage and differences between both of them. The important thing to remember: When you declare a class hierachy variable, it is shared between the class and all descending (inheriting) classes. This is rarely what you want.
Classes are objects just like any other object. Objects can have instance variables. Ergo, classes can have instance variables.
There's really nothing special about that. That's very important to understand in Ruby.
Is there any way to make instance variables "private"(C++ or Java definition) in ruby? In other words I want following code to result in an error.
class Base
def initialize()
#x = 10
end
end
class Derived < Base
def x
#x = 20
end
end
d = Derived.new
Like most things in Ruby, instance variables aren't truly "private" and can be accessed by anyone with d.instance_variable_get :#x.
Unlike in Java/C++, though, instance variables in Ruby are always private. They are never part of the public API like methods are, since they can only be accessed with that verbose getter. So if there's any sanity in your API, you don't have to worry about someone abusing your instance variables, since they'll be using the methods instead. (Of course, if someone wants to go wild and access private methods or instance variables, there isn’t a way to stop them.)
The only concern is if someone accidentally overwrites an instance variable when they extend your class. That can be avoided by using unlikely names, perhaps calling it #base_x in your example.
Never use instance variables directly. Only ever use accessors. You can define the reader as public and the writer private by:
class Foo
attr_reader :bar
private
attr_writer :bar
end
However, keep in mind that private and protected do not mean what you think they mean. Public methods can be called against any receiver: named, self, or implicit (x.baz, self.baz, or baz). Protected methods may only be called with a receiver of self or implicitly (self.baz, baz). Private methods may only be called with an implicit receiver (baz).
Long story short, you're approaching the problem from a non-Ruby point of view. Always use accessors instead of instance variables. Use public/protected/private to document your intent, and assume consumers of your API are responsible adults.
It is possible (but inadvisable) to do exactly what you are asking.
There are two different elements of the desired behavior. The first is storing x in a read-only value, and the second is protecting the getter from being altered in subclasses.
Read-only value
It is possible in Ruby to store read-only values at initialization time. To do this, we use the closure behavior of Ruby blocks.
class Foo
def initialize (x)
define_singleton_method(:x) { x }
end
end
The initial value of x is now locked up inside the block we used to define the getter #x and can never be accessed except by calling foo.x, and it can never be altered.
foo = Foo.new(2)
foo.x # => 2
foo.instance_variable_get(:#x) # => nil
Note that it is not stored as the instance variable #x, yet it is still available via the getter we created using define_singleton_method.
Protecting the getter
In Ruby, almost any method of any class can be overwritten at runtime. There is a way to prevent this using the method_added hook.
class Foo
def self.method_added (name)
raise(NameError, "cannot change x getter") if name == :x
end
end
class Bar < Foo
def x
20
end
end
# => NameError: cannot change x getter
This is a very heavy-handed method of protecting the getter.
It requires that we add each protected getter to the method_added hook individually, and even then, you will need to add another level of method_added protection to Foo and its subclasses to prevent a coder from overwriting the method_added method itself.
Better to come to terms with the fact that code replacement at runtime is a fact of life when using Ruby.
Unlike methods having different levels of visibility, Ruby instance variables are always private (from outside of objects). However, inside objects instance variables are always accessible, either from parent, child class, or included modules.
Since there probably is no way to alter how Ruby access #x, I don't think you could have any control over it. Writing #x would just directly pick that instance variable, and since Ruby doesn't provide visibility control over variables, live with it I guess.
As #marcgg says, if you don't want derived classes to touch your instance variables, don't use it at all or find a clever way to hide it from seeing by derived classes.
It isn't possible to do what you want, because instance variables aren't defined by the class, but by the object.
If you use composition rather than inheritance, then you won't have to worry about overwriting instance variables.
If you want protection against accidental modification. I think attr_accessor can be a good fit.
class Data
attr_accessor :id
private :id
end
That will disable writing of id but would be readable. You can however use public attr_reader and private attr_writer syntax as well. Like so:
class Data
attr_reader :id
private
attr_writer :id
end
I know this is old, but I ran into a case where I didn't as much want to prevent access to #x, I did want to exclude it from any methods that use reflection for serialization. Specifically I use YAML::dump often for debug purposes, and in my case #x was of class Class, which YAML::dump refuses to dump.
In this case I had considered several options
Addressing this just for yaml by redefining "to_yaml_properties"
def to_yaml_properties
super-["#x"]
end
but this would have worked just for yaml and if other dumpers (to_xml ?) would not be happy
Addressing for all reflection users by redefining "instance_variables"
def instance_variables
super-["#x"]
end
Also, I found this in one of my searches, but have not tested it as the above seem simpler for my needs
So while these may not be exactly what the OP said he needed, if others find this posting while looking for the variable to be excluded from listing, rather than access - then these options may be of value.