why there are class variables in ruby? - 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

Related

Variables in class methods

could you please explain me why the class variable cannot be accessed by attribute_accessors?
As i am trying here to have the list of all methods of all subclasses in one array it works a little different. It created array #method_names in every subclass with specific methods for every class ... so i do need to do a loop through subclasses.
What kind of variable/attribute is #method_names?
Thanks!
module First
class First_class
class << self
def info
puts "First_class method info."
puts #subclasses
puts #method_names
end
def inherited(subclass)
puts "#{subclass} located ..."
subclasses << subclass
end
def subclasses
#subclasses ||= []
end
def method_added(method_name)
puts "Method located #{method_name} ..."
method_names << method_name
end
def method_names
#method_names ||= []
end
end
def initialize
puts "Instance of First_class is created."
end
def first_method
end
end
class Second_class < First_class
def self.info
puts "Second_class method info."
puts #subclasses
puts #method_names
end
def second_method
end
def initialize
puts "Instance of Second_class is created."
end
end
class Third_class < First_class
def third_method
end
def initialize
puts "Instance of Third_class is created."
end
end
end
First::First_class.subclasses.each {
|subclass| puts subclass
subclass.method_names.each {
|methodn| puts methodn
}
}
#################UPDATE#########
Ok, maybe I put the question incorrectly.
Basically what is the difference for ##method_names(class variable) and #method_names (instance variable) if i do not create the instance of object? After inserting more inputs into #method_names it still inserts into the same object_id. So what is benefit of ##method_names?
updated to answer updated question.
Classes in ruby can have class variables. However if you modify the class level variable, ALL instances will be modified. This is not recommended but will illustrate the point. But also see this answer
class Foo
##bar = 'bar'
attr_accessor :bar
def initialize
#bar = 'bar'
end
def class_bar
##bar
end
def change_class_bar string
raise ArgumentError unless string.is_a?(String)
##bar = string
end
end
a = Foo.new
b = Foo.new
# change the class variable ##bar
b.change_class_bar 'wtf?'
# see both instances are changed because objects are passed by referrence
print 'a.class_bar is: '
puts a.class_bar
print 'b.class_bar is: '
puts b.class_bar
# change one instance only
a.bar = 'only a has changed'
print 'a.bar is: '
puts a.bar
print 'b.bar is still: '
puts b.bar
run this and you should get output:
a.class_bar is: wtf?
b.class_bar is: wtf?
a.bar is: only a has changed
b.bar is still: bar
original answer left here
#method_names is an instance variable of an instance of the class from which it was instantiated. However it cannot be accessed for read/write unless those attributes are defined with getter or setter methods defined.
ff = First::First_class.new
Instance of First_class is created.
=> #<First::First_class:0x00007fde5a6867b8>
ff.method_names
NoMethodError: undefined method `method_names' for #<First::First_class:0x00007fde5a6867b8>
Did you mean? methods
Now if you call ff.methods you will see all methods defined through standard Ruby inheritance.
As a side note, class names in Ruby conventionally use PascalCase see PascalCase. Mixed_case is discouraged.

How to create a method that executes a previously given block in Ruby?

I have a class that was built for subclassing.
class A
def initialize(name)
end
def some
# to define in subclass
end
end
# usage
p A.new('foo').some
#=> nil
In my use case, I don't want to create a subclass since I need just one instance. Therefore, I'll change the initialize method to support the following usage.
p A.new('foo') { 'YEAH' }.some
#=> YEAH
How could I support the usage above?
BTW: I found the following solutions for a Ruby 1.8.7 project, but they look awkward to me.
class A
def singleton_class
class << self; self; end
end
def initialize(name, &block)
#name = name
self.singleton_class.send(:define_method, :some) { block.call } if block_given?
end
def some
# to define in subclass
end
end
You can store the block argument in an instance variable and call it later on:
class A
def initialize(name, &block)
#name = name
#block = block
end
def some
#block.call
end
end
A.new('foo') { 'YEAH' }.some
#=> "YEAH"
You can also pass arguments into the block:
class A
# ...
def some
#block.call(#name)
end
end
A.new('foo') { |s| s.upcase }.some
#=> "FOO"
Or instance_exec the block in the context of the receiver:
class A
# ...
def some
instance_exec(&#block)
end
end
Which allows you to bypass encapsulation:
A.new('foo') { #name.upcase }.some
#=> "FOO"

How to get all instances variables in ruby class?

I have a ruby class, and in one of the methods, it calls an external function, and pass in all instance variables, and continue with the return value. Here is the code:
class MyClass
attr_accessor :name1
attr_accessor :name2
...
attr_accessor :namen
def inner_func():
all_vars = ???? # how to collect all my instance variables into a dict/Hash?
res = out_func(all_vars)
do_more_stuff(res)
end
end
The problem is the instance variables might vary in subclasses. I can't refer them as their names. So, is there a way to do this? Or Am I thinking in a wrong way?
You can use instance_variables to collect them in an Array. You will get all initialized instance variables.
class MyClass
attr_accessor :name1
attr_accessor :name2
...
attr_accessor :namen
def inner_func():
all_vars = instance_variables
res = out_func(all_vars)
do_more_stuff(res)
end
end
You could keep track of all accessors as you create them:
class Receiver
def work(arguments)
puts "Working with #{arguments.inspect}"
end
end
class MyClass
def self.attr_accessor(*arguments)
super
#__attribute_names__ ||= []
#__attribute_names__ += arguments
end
def self.attribute_names
#__attribute_names__
end
def self.inherited(base)
parent = self
base.class_eval do
#__attribute_names__ = parent.attribute_names
end
end
def attributes
self.class.attribute_names.each_with_object({}) do |attribute_name, result|
result[attribute_name] = public_send(attribute_name)
end
end
def work
Receiver.new.work(attributes)
end
attr_accessor :foo
attr_accessor :bar
end
class MySubclass < MyClass
attr_accessor :baz
end
Usage
my_class = MyClass.new
my_class.foo = 123
my_class.bar = 234
my_class.work
# Working with {:foo=>123, :bar=>234}
my_subclass = MySubclass.new
my_subclass.foo = 123
my_subclass.bar = 234
my_subclass.baz = 345
my_subclass.work
# Working with {:foo=>123, :bar=>234, :baz=>345}

understand self for attr_accessor class method

class Test
class << self
attr_accessor :some
def set_some
puts self.inspect
some = 'some_data'
end
def get_some
puts self.inspect
some
end
end
end
Test.set_some => Test
puts Test.get_some.inspect => Test nil
Here above I could find self as Test itself but not returning the some_data as output.
But while I modified in following way it returns expected output
class Test
class << self
attr_accessor :some
def set_some
puts self.inspect
self.some = 'some_data'
end
def get_some
puts self.inspect
self.some
end
end
end
Test.set_some => Test
puts Test.get_some.inspect => Test some_data
What is the differences?
EDIT
Now in the first example if I set as get some method as
Test.some = 'new_data'
puts Test.some.inspect #=> new_data
Test.set_some
puts Test.get_some.inspect => new_data
Now it made me much more confused.
some = :foo makes ruby think it should create a new local variable with name some. If you want to call some=(), you have to use an explicit reciever - as in self.some = :foo. I once lost a bet on that... :-/
It's (local) variable in the first example
In the first example some is a local variable.
In the second one, some is a method of self. Why? Because attr_accessor :some is the same as:
def some= (val)
#some = val
end
def some
return #some
end
So, you have created the getter and setter methods for the instance variable #some (it's an instance variable of the object Test, as every class is also an object of class Class).
in the first method
def set_some
puts self.inspect
some = 'some_data'
end
some is a local variable.. its not the same as #some that is a instance variable (in this case a class instance variable) so the value disappears when the method ends.
if you want to call the setter method some or set #some to something then do this
#some = 'some_data'
or
self.some = 'some_data'
in the second method
def get_some
puts self.inspect
self.some
end
your calling the method some. which returns the instace variable #some.. and since at this point #some has no value.. returns nil..
Example 1 with no method override and no local variable
class Foo
def initialize
#foo = 'foo'
end
def print_foo
print #foo
print self.foo
print foo
end
end
#foo, self.foo, and foo will access instance variable #foo within the instance method:
Foo.new.print_foo #=> foofoofoo
Example 2 with method override
class Foo
def initialize
#foo = 'foo'
end
def foo
return 'bar'
end
def print_foo
print #foo
print self.foo
print foo
end
end
#foo will access the instance variable, but self.foo and foo will call the foo override method:
Foo.new.print_foo #=> foobarbar
Example 3 with method override and local variable
class Foo
def initialize
#foo = 'foo'
end
def foo
return 'bar'
end
def print_foo
foo = 'baz'
print #foo
print self.foo
print foo
end
end
#foo accesses instance variable, self.foo accesses override method, and foo accesses local variable:
Foo.new.print_foo #=> foobarbaz

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