Advantages of self.attribute vs. #attribute? - ruby

Assuming the following:
def create_new_salt
self.salt = self.object_id.to_s + rand.to_s
end
Why is it better to use the 'self.' rather than an instance variable '#salt'?

Using self.salt = will cause the salt= method to be used, if defined. This is useful for ensuring any error checking / transformations inside the salt= method are used. However, it means there must be an explicit salt= method (possibly created by attr_accessor, for example), and this isn't necessarily a good idea.
Summary: use self.salt = if you have a custom salt= method; use #salt = if you know exactly what you want #salt to be, or if you don't have a custom salt= method.
Final note: I'd probably write it like this (in my opinion it's a little clearer)
def create_new_salt
#salt = "#{object_id}#{rand}"
end
EDIT: thanks to #Chuck's comments on another answer, I've modified this to remove the self.-free code - it was wrong.

Besides Peter's great answer, I usually signal in Ruby that I'm using attr_accessor by using self.attr= and self.attr instead of #attr.
Edit: Without "self.", you're creating a local variable. Yikes.

Further to Peter's answer:
Another interesting point is that self.salt= can be invoked even if the salt= method is private. This is an exception to the normal Ruby rule that a private method can not be invoked with an explicit receiver.

Related

How to automatically log to console when a variable is changed in Ruby?

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.

Ruby nested send

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)

Why use Rails public_method?

I am reading through Avdi Grimm's book 'Objects in Rails' and he uses the method public_method and I dont understand why. Here is the code example:
class Blog
# ...
attr_writer :post_source
# ...
private
def post_source
#post_source ||= Post.public_method(:new)
end
end
Why would you call Post.public_method(:new) and not Post.new? Do these methods do anything different or are they exactly the same? Thanks for the help.
Post.new
is not equivalent to
Post.public_method(:new)
The former is an invocation of method new, which, by default, creates a new Post object. The latter, however, does not call new immediately. It merely prepares it to be called later. I haven't read that particular book, but if you look around in the associated source code, you'll see this line
#post_source.call # maybe some params are passed here
This is where Post#new finally gets called.
Documentation: Object#public_method, Object#method.
Post.public_method(:new) and Post.new are different things. The latter creates an instance of Post. The former creates an instance of Method, which is not the result of applying such method but is an abstraction of the method itself. You can take out the result of it by doing call on it later.
Post.public_method(:new) may be replaced by Post.method(:new), unless there is a private or protected method named new. It is just making sure not to refer to such methods if there are any.

The name of the class that "newed" my class

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.

Ruby syntax question: Rational(a, b) and Rational.new!(a, b)

Today I came across the strange ruby syntax in the Rational class:
Rational(a,b)
(Notice the absence of the .new()portion compared to the normal Ruby syntax). What does this mean, precisely, compared to the normal new syntax? More importantly, how do I implement something like this in my own code, and why would I implement something like this? Specifically for the Rational class, why is this syntax used instead of the normal instantiation? And why is the new method private in the rational class? (And how/why would I do this in my own ruby code?)
Thanks in advance for your answers, especially since I've asked so many questions.
All you have to do is declare a global function with the same name as your class. And that is what rational.rb does:
def Rational(a, b = 1)
if a.kind_of?(Rational) && b == 1
a
else
Rational.reduce(a, b)
end
end
to make the constructor private:
private :initialize
and similarly for the new method:
private_class_method :new
I suppose Rational.new could be kept public and made to do what Rational() does, but having a method that turns its arguments into instances is consistent with Array(), String(), etc. It's a familiar pattern that's easy to implement and understand.
The method Rational() is actually an instance method defined outside of the class Rational. It therefore becomes an instance method of whatever object loads the library 'rational' (normally main:Object) in the same way that 'puts' does, for example.
By convention this method is normally a constructor for the class of the same name.

Resources