In Ruby, when should you use self. in your classes? [duplicate] - ruby

This question already has answers here:
When to use `self.foo` instead of `foo` in Ruby methods
(3 answers)
Closed 9 years ago.
When do you use self.property_name in Ruby?

Use self when calling a class's mutator. For example, this won't work:
class Foo
attr_writer :bar
def do_something
bar = 2
end
end
The problem is that 'bar = 2' creates a local variable named 'bar', rather than calling the method 'bar=' which was created by attr_writer. However, a little self will fix it:
class Foo
attr_writer :bar
def do_something
self.bar = 2
end
end
self.bar = 2 calls the method bar=, as desired.
You may also use self to call a reader with the same name as a local variable:
class Foo
attr_reader :bar
def do_something
bar = 123
puts self.bar
end
end
But it's usually better to avoid giving a local variable the same name as an accessor.

self references the current object. This lends itself to many uses:
calling a method on the current object
class A
def initialize val
#val = val
end
def method1
1 + self.method2()
end
def method2
#val*2
end
end
Here running A.new(1).method1() will return 3. The use of self is optional here - the following code is equivalent:
class A
def initialize val
#val = val
end
def method1
1 + method2()
end
def method2
#val*2
end
end
self is not redundant for this purpose though - operator overloading makes it neccessary:
class A
def initialize val
#val = val
end
def [] x
#val + x
end
def method1 y
[y] #returns an array!
end
def method2 y
self.[y] #executes the [] method
end
end
This shows how self must be used if you want to call the current object's [] method.
referencing attributes
You can generate the methods to read and write to instance variables using attr_accessor and co.
class A
attr_accessor :val
def initialize val
#val = val
end
def increment!
self.val += 1
end
end
Using self is redundant here because you can just reference the variable directly, eg. #val.
Using the previous class, A.new(1).increment! would return 2.
method chaining
You can return self to provide a form of syntactical sugar known as chaining:
class A
attr_reader :val
def initialize val
#val = val
end
def increment!
#val += 1
self
end
end
Here, because we are returning the current object, methods can be chained:
A.new(1).increment!.increment!.increment!.val #returns 4
creating class methods
You can define class methods using self:
class A
def self.double x
x*2
end
def self.quadruple x
self.double(self.double(x))
end
end
This will enable you to call A.double(2) #= 4 and A.quadruple(2) #=8. Note that in a class method, self references that class because the class is the current object.
how the value of self is determined
The current value of self in a particular method is set to the object that that method was called upon. Normally this uses the '.' notation. When you run some_object.some_method(), self is bound to some_object for the duration of some_method, meaning that some_method can use self in one of the ways mentioned above.

Using self is used will reference the current object accessible within a program. Therefore, self.property is used when accessing a variable through a attr_accessor of some sort. In must cases, it can be used in place of #property from within an object.

Related

Ruby equivalent of lisp-like "apply"?

I would like to create an instance method that takes another instance method of its own class as a parameter, and then applies the passed method on the instance it's working on (known as self):
class MyClass
attr_reader :called_methods
def initialize
#called_methods = []
end
def my_first_method!
#called_methods << :my_first_method
self
end
def my_second_method!
#called_methods << :my_second_method
self
end
def my_strange_method!(secondary)
# Want to apply method whose name is given by secondary, to self
end
end
p MyClass.new.my_second_method!.my_strange_method!(:my_first_method!).called_methods
I suspect the unary & may be key, but all the web pages I can find on that operator involve calling methods on multiple objects, as when iterating over an Enumerable with #each or #map.
Use Object#public_send (or Object#send to apply protected/private method).
def my_strange_method!(secondary)
public_send(secondary)
self
end
p MyClass.new.
my_second_method!.
my_strange_method!(:my_first_method!).
called_methods
#⇒ [:my_second_method, :my_first_method]
There could be more defensive way to apply if and only the method is known:
def my_strange_method!(secondary)
raise ArgumentError.new("Unknown method #{secondary}") \
unless methods.include? secondary.to_s.to_sym
public_send(secondary)
end
p MyClass.new.
my_second_method!.
my_strange_method!(:my_first_method!).
called_methods
#⇒ [:my_second_method, :my_first_method]
p MyClass.new.
my_second_method!.
my_strange_method!(:inexisting_method!).
called_methods
#⇒ ArgumentError: Unknown method inexisting_method!
This is tagged with functional-programming so I'm going to offer a persistent (immutable) design -
class MyClass
attr_reader :called_methods
def initialize(m = [])
#called_methods = m
end
def my_first_method!
MyClass.new(called_methods + [ :first ])
end
def my_second_method!
MyClass.new(called_methods + [ :second ])
end
def my_strange_method!(secondary)
public_send secondary
end
end
MyClass.new.my_second_method!.my_strange_method!(:my_first_method!).called_methods
# [:second, :first]

Get attr_reader, writer, or accessor oustide of the class

I'm currently doing some metaprogramming with ruby, and I'm trying to isolate the methods of class (that class is in another file, that I get by a require). I can get all the methods, thanks to klass.public_instance_methods(false), but I in the sametime, the array given also have all the attributes of the class. How could I isolate them ? In others related questions on SO, they suggest to use klass.instance_variables but when I do that, it only returns an empty array.
I can't seem to wrap my head around that one. I don't understand why there isn't a method specifically for that already...
For example:
I have in a file this class :
class T
attr_reader:a
def initialize(a)
#a = a
end
def meth
#code here
end
end
And, in another file, i have
require_relative 'T.rb'
class meta
def initialize
methods = T.public_instance_methods(false) #=> here methods = [:a,:meth] but I would want only to have [:meth]
#rest of code
end
end
For class defined like this:
class Klass
attr_accessor :variable
def initialize(variable)
#variable = variable
end
def method
end
end
you can find public non-attr instance methods using public_instance_methods and instance_variables methods.
public_instance_methods = Klass.public_instance_methods(false)
# [:method, :variable, :variable=]
instance_variables = Klass.new(nil).instance_variables
# [:#variable]
getters_and_setters = instance_variables
.map(&:to_s)
.map{|v| v[1..-1] }
.flat_map {|v| [v, v + '=']}
.map(&:to_sym)
# [:variable, :variable=]
without_attr = public_instance_methods - getters_and_setters
# [:method]
This is impossible. Ruby's "attributes" are completely normal methods. There is no way to distinguish them from other methods. For example, these two classes are completely indistinguishable:
class Foo
attr_reader :bar
end
class Foo
def bar
#bar
end
end
You can try to be clever and filter them out based on instance variables, but that is dangerous:
class Foo
# can filter this out using #bar
attr_writer :bar
def initialize
#bar = []
end
end
class Foo
def initialize
#bar = []
end
# this looks the same as above, but isn't a normal attribute!
def bar= x
#bar = x.to_a
end
end

Calling Custom Writers Within Instance Methods: Why is self not implicit? [duplicate]

This question already has answers here:
Why do Ruby setters need "self." qualification within the class?
(3 answers)
Closed 6 years ago.
I am familiar with the conventional way of using attr_accessors within the initialize method:
class Dog
attr_accessor :name
def initialize(name)
#name = name
end
end
dog = Dog.new("Denver")
p dog.name
=> "Denver"
I was playing around with custom writers and readers. I was suprised to realize the following:
Within instance methods: you can call upon a reader method without explicitly specifying self because self is implicit.
Within instance methods: you MUST explicitly call self to call upon a writer method. self IS NOT implicit for attr_writers
Example 1 to show that self is implicit when calling attr_reader methods within instance methods:
class Dog
def name
#name
end
def name=(val)
#name = val
end
def initialize(name)
#name = name
end
def call_name
# no need to say: p self.name
p name
end
end
dog = Dog.new("Denver")
dog.call_name
=> "Denver"
Example 2 to show that self IS NOT implicit when calling upon attr_writers within instance methods. In other words: the instance variable is not getting set because I did not prepend the writer method below with self:
class Dog
def name
#name
end
def name=(val)
#name = val
end
def initialize(name_val)
# works as expected with: self.name = name_val
name = name_val
end
def change_name
# works as expected with: self.name = "foo"
name = "foo"
end
end
dog = Dog.new("Denver")
p dog.name
dog.change_name
p dog.name
=> nil
=> nil
Question: why isn't self implicit within instance methods when calling attr_writers? Why do I have to explicitly specify self for attr_writers within instance methods?
Question: why isn't self implicit within instance methods for
attr_writers?
Because defining a local variable takes precedence.
So here:
name = "foo"
you do not write an attribute, but defining a local variable name.

How to create an object in Ruby without using new

It's possible to create a Complex number in Ruby using
c = Complex.new(1,2)
but, it can be shortened to
c = Complex(1,2)
Is it possible to achieve the same functionality without having to define a function outside the class, like in the example below?
class Bits
def initialize(bits)
#bits = bits
end
end
def Bits(list) # I would like to define this function inside the class
Bits.new list
end
b = Bits([0,1])
I think Ruby should allow at least one of the proposed constructors below
class Bits
def initialize(bits)
#bits = bits
end
def self.Bits(list) # version 1
new list
end
def Bits(list) # version 2
new list
end
def Bits.Bits(list) # version 3
new list
end
end
Have this snippet:
def make_light_constructor(klass)
eval("def #{klass}(*args) #{klass}.new(*args) end")
end
Now you can do this:
class Test
make_light_constructor(Test)
def initialize(x,y)
print x + y
end
end
t = Test(5,3)
Yes, I know you're still defining a function outside a class - but it is only one function, and now any class you want can make use of its implementation rather than making one function per class.
c = Complex(1,2)
is actually calling a method on Kernel
Basically you can't - the () operator cannot be overriden in Ruby (Complex class is written in C).
You could achieve something similar using []:
class Bits
def self.[](list)
Bits.new list
end
end
Which would allow something like:
b = Bits[[1,2]]
If you pack your classes into some module you can use 2 methods:
self.included - called when you include Mod
self.extend - called when you extend Mod
I have created very basic method using self.included.
Cons: It is hard to write. You can say it is complex; It may not contain all features.
Pros: It looks exactly like Complex(2,3) (it uses () instead of [] as in https://stackoverflow.com/a/24351316/2597260 answer); You create just initialize, self.included create the rest.
module M1
# some random classes
class A; end
class B
def initialize list
#list = list
end
attr_accessor :list
end
class C
def initialize var1
#var1 = var1
end
attr_accessor :var1
end
Answer = 42
# called on `include module_name`
def self.included mod
# classes are constants (in normal cases)
constants.each do |cons|
class_eval do
# I don't like hard-coded `::M1`
klass = ::M1.const_get cons
if klass.class==Class
define_method cons do |*args, &block|
klass.new *args, &block
end
end
end
end
end
end
include M1
p A()
b = B([1,2,3])
p b.list
c = C 42
p c.var1
puts Answer()
# NoMethodError: undefined method `Answer' for main:Object
# thats good, because Answer is not a class!
Here's another hack that you could (but shouldn't) use, inspired by this blog post:
def method_missing(sym, *args, **kwargs, &blk)
Object.const_get(sym).new(*args, **kwargs, &blk)
end
This simply expects any unknown method name to be the name of a class and calls :new on the class.
With rudimentary error handling:
alias sys_method_missing method_missing
def method_missing(sym, *args, **kwargs, &blk)
cls = Object.const_get(sym) if Object.constants.include? sym
if cls.is_a?(Class) then cls.new(*args, **kwargs, &blk)
else sys_method_missing(sym, *args, **kwargs, &blk) end
end
If an unknown method name is the name of a class, this calls :new on the class. Otherwise, it delegates the call to the original implementation of method_missing().
Usage:
class Foo
end
foo = Foo()
p foo
Result:
#<Foo:0x00007f8fe0877180>

Ruby metaprogramming: adding #variables to existing 'initialize' method (using class_eval) [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Ruby.Metaprogramming. class_eval
I have this little project, the goal is to create a 'attr_accessor_with_history' method, that will keep a record of every single value assigned to variables created by it. Here's the code :
class Class
def attr_accessor_with_history(attr_name)
attr_name = attr_name.to_s # make sure it's a string
attr_reader attr_name # create the attribute's getter
attr_reader attr_name+"_history" # create bar_history getter
a = %Q{
def initialize
##{attr_name}_history = [nil]
end
def #{attr_name}
##{attr_name}
end
def #{attr_name}=(new_value)
##{attr_name}=new_value
##{attr_name}_history.push(new_value)
end }
puts a
class_eval(a)
end
end
Now, when I test the script for one variable. It works fine. But when I try to create two or more variables (like this) ....
class Foo
attr_accessor_with_history :bar
attr_accessor_with_history :lab
end
a = Foo.new
a.bar = 45
a.bar = 5
a.bar = 'taat'
puts a.bar_history
b = Foo.new
b.lab = 4
b.lab = 145
b.lab = 'tatu'
puts b.lab_history
....Ruby gives out a "no-existing 'push' method for (class_eval) bar_history.push(new_value)". I think that 'initialize' method gets overriden on the second call of attr_accessor_with_history, so the record for the first variable gets destroyed.
I have no idea how to get around this. I already tried calling 'super' . Any clue ?
In your setter method just check if the the history instance variable is already initialized:
def #{attr_name}=(new_value)
##{attr_name}=new_value
##{attr_name}_history ||= [nil]
##{attr_name}_history.push(new_value)
end
You'll need another getter for your history variable that sets your default value if it was not set before:
def #{attr_name}_history
##{attr_name}_history ||= [nil]
end
Then you could remove your initialize method, that was btw vulnerable to be overwritten.

Resources