Class variable using attr_accessor inside class << self - ruby

Can someone explain why self.foo= works outside of the class << self, while foo= does not work within the class << self.
class A
class << self
attr_accessor :foo
foo = 'foo'
end
end
p A.foo # => "nil"
class A
class << self
attr_accessor :foo
end
self.foo = 'foo'
end
p A.foo # => "foo"
This is not the same question as When to use 'self' in Ruby
To be clear, I am not asking when to use self. I am asking why I cannot set the class variable inside a 'class << self' block, but I can outside of it.

your second example isn't a class variable, it's a class instance variable to this class the proof is that if you inherited like this you'll get nil
class A
class << self
attr_accessor :foo
end
self.foo = 'foo'
end
class B < A
end
B.foo # => nil
So if you want to add a class variable in class << self you can use ##
class A
class << self
##foo = :foo
def foo
##foo
end
end
end

Related

Instance variable inside singleton class

I have the following piece of code:
class Fish
# #message = "I can swim"
class << self
#message = "I can jump!"
define_method(:action) { #message }
end
end
Fish.action => nil
As soon as I uncomment the above #message variable, Fish.action returns I can swim. Why in both cases it is ignoring the I can jump message. Why is that? Why is Fish class being binded to the #message defined at the start but not inside the singleton class?
It's because class << self opens the class' singleton class context:
class Foo
p self # Foo
class << self
p self # #<Class:Foo>
define_method(:bar) { p self } # Foo
end
end
Foo.bar
You can verify that by:
Fish.singleton_class.instance_variable_get(:#action) # => "I can jump!"

Why can't a class instance variable be accessed in a singleton-class definition in Ruby?

class MyClass
#my_class_instance_variable = "here"
p #my_class_instance_variable # => "here"
class << self
p #my_class_instance_variable # => nil
end
end
class MyClass
p #my_class_instance_variable # => "here"
end
Why does the second p print nil, when the third p prints "here"? My understanding of a singleton class definition (class << self) is that it has the same scope as a class definition (class MyClass).
(This question has a similar title, but it has a different focus.)
My understanding of a singleton class definition (class << self) is
that it has the same scope as a class definition
Nope. The scope is different. You defined #my_class_instance_variable in the scope of MyClass, but are trying to access it in the scope if MyClass's singleton class.
It is as easy to check as:
class Foo
puts self #=> Foo
class << self
puts self #=> #<Class:Foo>
end
end
With the output of:
Foo
#<Class:Foo>

why there are class variables in ruby?

If creating a class variable is often dangerous and unpredictable why do we need them?
If solution is just to use class instance variable with the class level accessors:
class Foo
#variable = :something
def self.getvariable
#variable
end
def self.setvariable(value)
#variable = value
end
end
Then why do we need class variables???
Class variables have their use on occasion, but I agree that using the eigenclass is frequently more useful:
class Foo
#bar = 'bar'
class << self
attr_accessor :bar
end
end
puts Foo.bar # bar
puts Foo.bar = 'baz' # baz
The above is safe with inheritance, because it sets a variable in the Foo constant, rather than a class variable.
Foo.new.instance_eval { puts ##bar } # error
This has several causes:
It's syntactic sugar. You can always get a class variable (whether you are in class or instance scope) using ##var. This won't work for instance variables of the class.
Class variables persist for the singleton classes of the instances of this class. Example:
class Test
#instance_var = 0
##class_var = 0
def self.instance_var
#instance_var
end
def self.class_var
##class_var
end
end
Test.instance_var #=> 0
Test.class_var #=> 0
Test.new.singleton_class.instance_var #=> nil
Test.new.singleton_class.class_var #=> 0
Here is an example (think ActiveRecord):
class Base
def connect(connection)
##connection = connection
end
def connection
##connection
end
end
class User < Base
end
class SuperUser < User
end
Base.new.connect("A connection")
puts User.new.connection #=> A connection
puts SuperUser.new.connection #=> A connection
The trick here is that class variable is accessible from an instance method and is inherited. Try this:
class Base
def self.connect(connection)
#connection = connection
end
def self.connection
#connection
end
def connection
self.class.connection
end
end
class User < Base
end
Base.connect("A connection")
puts User.new.connection #=> nil
You will get nil as self.connection tries to access it's own class instance variable (from User class), and it is not inherited.
Added: And yes, it can be dangerous if you misuse it:
##a = "A"
class A
def self.a
##a
end
def a
##a
end
end
puts A.a #=> A
puts A.new.a #=> A

How do I extend a Ruby module in class context?

I have the following Ruby script, in which class Foo includes module Baz, module Baz has a included hook to make Bar extended by the including class (Foo). I am just wondering why:
class << klass
extend Bar #this doesn't work. Why?
end
does not work while:
klass.extend(Bar) works.
Here is my code:
#! /usr/bin/env ruby
module Baz
def inst_method
puts "dude"
end
module Bar
def cls_method
puts "Yo"
end
end
class << self
def included klass
# klass.extend(Bar) this works, but why the other approach below doesn't?
class << klass
extend Bar #this doesn't work. Why?
def hello
puts "hello"
end
end
end
end
end
class Foo
include Baz
end
foo = Foo.new
foo.inst_method
Foo.hello
Foo.cls_method
Within the body of class << klass, self refers to the singleton class of klass, not klass itself, whereas in klass.extend(Bar), the receiver is klass itself. The difference comes from there.
class A
end
class << A
p self # => #<Class:A> # This is the singleton class of A, not A itself.
end
p A # => A # This is A itself.
And since you want to apply extend to klass, doing it within the body of class << klass does not work.
What you want is invoke the extend method on the class object (klass) not the singleton class (class << klass).
Therefore the following code doesn't work because you are invoking the extend method on the singleton class:
class << klass
extend Bar # doesn't work because self refers to the the singleton class of klass
def hello
puts "hello"
end
end

Ruby methods similar to attr_reader

I'm trying to make a method similar to attr_reader but I can't seem to get the instance of the class that the method gets called in.
class Module
def modifiable_reader(*symbols)
# Right here is where it returns Klass instead of #<Klass:0x1df25e0 #readable="this">
mod = self
variables = symbols.collect { |sym| ("#" << sym.to_s).to_sym }
attr_reader *symbols
(class << ModifyMethods; self; end).instance_eval do
define_method(*symbols) do
mod.instance_variable_get(*variables)
end
end
end
end
class Object
module ModifyMethods; end
def modify(&block)
ModifyMethods.instance_eval(&block)
end
end
class Klass
modifiable_reader :readable
def initialize
#readable = "this"
end
end
my_klass = Klass.new
my_klass.modify do
puts "Readable: " << readable.to_s
end
I'm not sure what it is you're trying to do.
If it helps, the spell for attr_reader is something like this:
#!/usr/bin/ruby1.8
module Kernel
def my_attr_reader(symbol)
eval <<-EOS
def #{symbol}
##{symbol}
end
EOS
end
end
class Foo
my_attr_reader :foo
def initialize
#foo = 'foo'
end
end
p Foo.new.foo # => "foo"
What I can understand from your code is that you want to have the modify block to respond to the instance methods of Klass, that's as simple as:
class Klass
attr_reader :modifiable
alias_method :modify, :instance_eval
def initialize(m)
#modifiable = m
end
end
Klass.new('john').modify do
puts 'Readable %s' % modifiable
end
About this tidbit of code:
def modifiable_reader(*symbols)
# Right here is where it returns Klass instead of #<Klass:0x1df25e0 #readable="this">
mod = self
...
Probably this can give you a hint of what is going on:
Class.superclass # => Module
Klass.instance_of?(Class) # => true
Klass = Class.new do
def hello
'hello'
end
end
Klass.new.hello # => 'hello'
When you are adding methods to the Module class, you are also adding methods to the Class class, which will add an instance method to instances of Class (in this case your class Klass), at the end this means you are adding class methods on your Klass class

Resources