Why can't I change the value of self? - ruby

How come I'm allowed to change "self" this way:
self.map! {|x| x*2}
Or this way:
self.replace(self.map {|x| x*2})
But not this way:
self = self.map {|x| x*2}
Why doesn't Ruby allow me to change the object that the "self" variable points to, but does allow me to change the object's attributes?

Not an answer, just a clue.
a=[1,2]
=>[1,2]
a.object_id
=>2938
a.map!{|x| x*2}
=>[2,4]
a.object_id # different data but still the same object
=>2938
a.replace(a.map {|x| x*2})
=>[4,8]
a.object_id # different data but still the same object
=>2938
a = a.map{|x| x*2} # .map will create a new object assign to a
=>[8,16]
a.object_id #different object
=>2940
You can't change your self to another one.

In Ruby, values and variables are references (pointers to objects). Assigning to a variable simply makes it point to a different object; it has no effect on the object the variable used to point to. To change an object, you must call a method (including property getters/setters) on it.
You can think of self as a variable that points to the object the method was called on. If you could assign to it, you could make it point to another object. If you could do that, it would not alter the object the method was called on; instead, you would make it so that any following code in that method that uses self would use that object, not the object the method was called on. This would be super confusing, because self would no longer point to the object the method was called on, which is a basic assumption.

Related

Taking a key from a hash, and interpolate that as a getter on an object

I have a class that creates objects. I've created getter/setter methods for that class.
Class Test
attr_accessor :method1, :method2, :method3, :method4
I have created several instances from that class, and appended them to a list
#object_array = [obj1, obj2, obj3]
I have a dictionary that contains the keys and values. The keys have the same string value that the instance variables in the test class use. (For example 'method1' exists as :method1) I would like to access those keys as the getter methods.
#method_hash = {'method1' => ['data1','data2'], 'method2' => ['data3','data4']}
I expect this to work, but it isn't. What am I doing wrong?
I should mention that the method below is taking place inside a class and that #object_list and #method_list are themselves instance variables of this second class. (This might not be relevant to solve the issue, but wanted to mention just in case..)
block (2 levels) in func1': undefined method `key' for #<Obj:0x007ffbe1061870> (NoMethodError)
def func1
#object_array.each do |o|
#method_hash.each do |key, value|
puts o."#{key}"
end
end
end
I tried using .to_s, .to_sym to get it to work with no luck.
puts o."#{key}" is syntactically wrong. You need to fetch the property on runtime. For this you can:
puts o.instance_variable_get("##{key}") # dynamically inspect instance variable
# above will only work if key is instance variable and not a method of o.
or
puts o.send(key) #dyanmic dispatch
# will work as long o responds to the key like o.method1.
Update: Prefer o.public_send(key) over send to dynamically dispatch only the publicly accessible method/attribute, as it is safer inspection of object without leaking it's private content.
If you are sure that is an instance variable, go with the safer first option.

Best practice for avoiding mutating parameters?

Can someone suggest a good, ruby-idiomatic, way of avoiding this?
class Foo
attr_accessor :bar
end
a = {one: 1}
x = Foo.new; x.bar = a
x.bar[:two] = 2
p a #=> {one: 1, two: 2}
I could simply not allow the users of a class to access its attributes, which solves the problem...in this case. (What about passing parameters to a method?) Anyway, avoiding everything but attr_reader, and using that only on non-mutable attributes, doesn't seem very Ruby-ish.
Or, I can just not write any code which mutates values, which appeals, but is not exactly easy to do in Ruby.
I could systematically dup or clone every parameter my class is given -- except that those methods don't work on Nilclass, Fixnums, Symbols, etc -- and worse, responds_to?(:dup) == true for those types. (Also, neither dup nor clone do a deep copy.)
In the example above I modify the bar attribute in the caller, but the problem remains the same if the code is in the class, or if I use a method on the class instead of attr_accessor : If I want a class which can accept a value and do something with it, and if for some reason I have to do that by mutating that value somewhere -- is there an idiomatic way in ruby to ensure that I don't infect the caller with that mutated value?
In Ruby we are supposed not to care about the type of the incoming data very much, but it looks as if I have to care about it quite a lot in order to tell how to make this value I want to mutate safe. If it's a NullObject or a Fixnum or a Symbol it's fine, otherwise I can dup it ... unless I need to deep copy it.
That can't be right, can it?
Edit: After Some More Thought
Sergio is of course right -- sometimes you want this behaviour. Not because using the side effect in your code is a good idea, but because sometimes the class you are passing a message to needs a live reference to an object that might change afterwards.
The only time this behaviour is going to be problematic is when you are passing an Enumerable. If I pass an Array or a Hash, I really don't want the receiver to modify that. So my takeaway is:
Do what Sergio said and code defensively whenever I pass stuff to a receiver, just in case the person who coded it hasn't been careful.
Implement a blanket rule in my own classes: dup all incoming Enumerables.
It is responsibility of the caller to shield itself from code being called. Let's say, you have some command line options parsing code. You got this hash of parameters and you want to do some validation (or something). Now, the validating code was written by some other guy who likes to do things in-place for "efficiency". So it is likely that your hash will be mutated and you won't be able to use it later.
Solution? Pass a copy.
validate_params! Marshal.load(Marshal.dump(params)) # deep copy
Note that in some cases mutation is desirable. So it must be the caller who controls the effect (allows or prevents it).
I would consider using freeze:
class Foo
attr_reader :bar
def bar=(value)
#bar = value.freeze # You may need to freeze nested values too
end
end
a = { one: 1 }
x = Foo.new
x.bar = a
x.bar[:two] = 2
# raises: can't modify frozen Hash
Or if you prefer to not change Foo, freeze the value when assigning:
class Foo
attr_accessor :bar
end
a = {one: 1}
x = Foo.new
x.bar = a.freeze
x.bar[:two] = 2
# raises: can't modify frozen Hash

What difference has adding the self keyword made?

When I started learning Ruby, I started with Michael Hartl's excellent Rails Tutorial. I just revisited the tutorial and noticed that portions of the example application code were modified. In the authentication chapter two methods were changed:
# SessionsHelper
def sign_in(user)
...
self.current_user = user
end
def sign_out
self.current_user = nil
...
end
Previously, the keyword self was omitted. But the app worked. So, if it ain't broke, why fix it? What value has self added and why is it used?
I understand that self defines class methods as opposed to instance methods. And in models, using self allows us to reference an object's property directly. But I can't tie the dots and see how it's being used in a helper. I've seen it in controllers before and I can't understand why we would want to use it there.
[Edit]
It is likely that the original section of the tutorial was just plain wrong. Omitting the "self" receiver causes the methods to assign a value to the method local variable "current_user" with no other effect.
My original answer below is incorrect and should be ignored (because writer methods foo=(x) cannot be called without a receiver, as can other methods).
[Incorrect Original Answer Below]
The reason for using the "self" receiver is likely for clarity.
If "self" is omitted then, to the untrained eye, it looks like you are simply assigning the "user" variable to a new variable named "current_user" with no other effect. Experienced Rubyists know that if there is a method named "current_user" on the object instance then it will be called instead of just creating and assigning a new variable but this detail can easily be overlooked, especially if the method is inherited or otherwise not declared in the current class definition section.
By using the "self" keyword explicitly you are clearly stating that you are calling a method on this object instance.
class User
attr_accessor :current_user
def sign_in_1
# Assigning local variable here
current_user = "Foo"
end
def sign_in_2
# Calling accessor method
self.current_user = "Bar"
end
end
u = User.new
u.sign_in_1
p u.current_user #=> nil
u.sign_in_2
p u.current_user #=> "Bar"

Ruby - extend built-in variables in Ruby

I would like to extend the functionality of variables in Ruby. The reason is I am working on something similar of a type system or value checker (this sounds a bit crazy but the whole idea is to long to explain, just the reason I would like to extend default variables).
I have read that in Ruby, everything is an object; so variables are objects. And Ruby is supposed to be quite liberal concerning what can be changed via meta-programming.
Is there some kind of 'Class' associated with local variables that I could extend?
I would like to associate a string-variable for each variable that holds a string representation of a type. Further I would like to intercept variable assignments and execute a method each time a new value is assigned to a variable. This is so I can check, if the new value is correct according to the type (stored as string in the Variable).
If local variables in Ruby are defined as object of a class, I can extend that class or modify it via a ruby mixin.
The workaround would be to create a new class for my Variables (and not use the build in local variables of Ruby). This class can have a value attribute, a attribute (stored as string) for the type and a get- and set-method. This way I can solve my problem, but I would like to extend the built in variables in ruby, if that is possible.
current work in progress
class Fixnum
attr_accessor :tp
#tp
def mytype ( type )
#tp = type
end
def typecheck
#call typechecker
puts "checked"
end
end
Test Code:
a = 3
a.mytype("nat")
puts a.tp
a.typecheck
There are still two problems.
First, I think it is not possible to add a new constructor to Fixnum. Second, I would like to intercept the variable access, i.e. "b = a" calls the method 'typecheck' of a. But this would require something similar to Aspect Oriented Programming and I am not sure if this can be solved with Ruby's meta-programming facilitates.
I have read that in Ruby, everything is an object
That depends on your definition of "object" and every-"thing". "Object" can mean "entity that can be manipulated by the program" (which I will call object from now on), or "value that is a member of the object system" (which I will call Object from now on).
In Ruby, everything that can be manipulated by the program (i.e. every object) is also an Object, i.e. an instance of a class. This is unlike Java, for example, where primitives can be manipulated by the program (i.e. are objects in that sense of the word), but aren't Objects. In Ruby, this distinction doesn't exist: every object is an Object and every Object is also an object.
However, there are things in the language, which cannot be manipulated by the program and which aren't instances of a class, i.e. they are neither object s nor Objects. These are, for example, methods, variables, syntax, parameter lists, arguments lists, keywords.
Note: you can use Ruby's reflection API to give you an object that represents a method or a parameter list, but that object is only a proxy, it is not the real thing.
So, when we say "everything is an object", what we really mean is that "every object is an Object", i.e. that everything which can be manipulated by the program is also a member of the object system, or in other words, there are no values outside of the object system (unlike primitives in Java). We do not mean that everything that exists in the language can also be manipulated at runtime by the program.
so variables are objects
No, unfortunately, they are neither object s nor Objects.
This is also clearly stated in the Ruby Language Specification (emphasis added by me):
6.2 Variables
6.2.1 General description
A variable is denoted by a name, and refers to an object, which is called the value of the variable.
A variable itself is not an object.
In the book The Ruby Programming Language by Matz and David Flanagan it says on page 2:
every value is an object
Note, it doesn't say every-thing, only every value.
See also the question Is variable is object in ruby?
There are a couple things you can do. For starters, all (or very nearly all) Ruby classes (including "primitives" such as numbers) support a to_s method which returns a string representation of the object. For numbers, to_s will just return the string representation of that number (e.g., "42" for 42). The string value returned for other classes will vary. The nice thing is that you can override a class's methods via "monkey patching". Here's an extremely contrived example:
class Array
def to_s
return "An array of size #{self.size}."
end
end
a = [1, 2, 3, 4]
puts a.to_s
# => "An array of size 4."
For your other question regarding executing a method every time a variable's value is set, the way to handle this is to always interact with the variable through its accessor methods. This way you can implement custom code inside a property's getter and setter methods (or simply call another method from inside the accessor). Like this:
class MyClass
# Use the default getter for var.
attr_reader :var
def initialize
#var = 1
end
# Custom setter for var.
def var= v
#var = v
puts "var changed to #{v}"
end
end
mc = MyClass.new
mc.var = 9
# => "var chaged to 9"
You could do something like this, but it only works for globals:
$type_checked = {:$a => String, :$b => Array}
$type_checked.keys.each do |var|
trace_var(var) do |obj|
puts "hey, don't assign #{var} to a #{obj.class}" unless $type_checked[var] == obj.class
#or raise an Error
end
end
$a = 1
$a = "a"
$b = 1
#output:
#hey, don't assign $a to a Fixnum
#hey, don't assign $b to a Fixnum
But this clearly goes against the grain of the language.

Something confusing about ruby parameter-passing

I've wrote two ruby files for test
test.rb:
#!/usr/bin/ruby
def foo(bar)
bar['key'] = 'value'
end
def my_print(a)
a.each{|k,v|
puts "#{k} => #{v}"
}
end
test_drive.rb:
#!/usr/bin/ruby
require 'test.rb'
hash_test = Hash.new
foo(hash_test)
my_print(hash_test)
It works as what I expect, the output is
key => value
But when I chanege the test.rb to
#!/usr/bin/ruby
def foo(bar)
pre_defined = {'key' => 'value'}
bar = pre_defined
end
def my_print(a)
a.each{|k,v|
puts "#{k} => #{v}"
}
end
Here I used a pre-defined hash, but now it outputs nothing. The "hash_test" is now an empty hash.
Please illustrate me why indeed does this happen?
Here is the simple answer:
In ruby you pass variables with Objects by reference. When you assign an Object to a variable, the variable does not really contain the Object itself within it. Instead, it contains only a reference to that Object.
It may help for you to start seeing the differences between references and objects in order to understand how sending variables as parameters works: the Object itself does not reside in the variable, the variable is pointing to a reference of the Object in memory, because of this, if one variable points to another object and modifies it, that doesn't mean it modified the Object that it was previously referring to.
The important thing for you to understand is that the bar parameter in the foo method is really only a REFERENCE to the Object in memory, not the Object itself. So if bar once pointed to the Object, but it's now referencing another Object, it will modify what it is referencing, not the previous object it pointed to. Here's the code of the last test.rb commented slightly for you to understand it better:
def foo(bar) # here "bar" points to the same object as "hash_test"
pre_defined = {'key' => 'value'} # here a new object is created and referenced
bar = pre_defined # now "bar" points to a new object and forgets about "hash_test"
end
def my_print(a) # here "a" holds a reference to "hash_test" which is empty
a.each{|k,v| # "a" is empty, so it has nothing to put
puts "#{k} => #{v}"
}
end
I hope this helps. In case you need something a little more detailed:
The reason your last version of test.rb prints an empty hash in test_drive.rb is because the Hash Object that the reference "hash_test" points to is not really being modified at all. Instead the foo method, while initially receiving as a parameter called "bar" a reference of the Hash Object that "hash_test" points to, quickly replaces that reference in "bar" with a brand new reference to a brand new Hash Object that the "pre_defined" variable points to. Now "bar" doesn't point to the same Object as "hash_test" does, and so the Object that "hash_test" points to is never being modified. Thus, the Hash Object that "hash_test" contains is never really populated with anything, and is empty when my_print tries to print it.
That is, because Ruby is pass by value, whereby the reference of an object is passed. It’s a similar behavior of what Java does. That means that you have only a copy of the reference within the foo-method and reassigning it does not change the reference outside of this method.
In more detail:
In your first example you pass the Hash to your foo-method. The reference will be copied, so within the foo-method you have a copy of the reference which points to the same object as 'hash_test', but is not the same one. So calling the Hash methods changes the values of the object outside of the method. But in your second example your are not changing values of the given Hash, you assign a new Hash object to the copy of the method reference, which has no effect to the 'hash_test' reference.

Resources