Is it possible to access a method without creating an instance? - ruby

I have a class within a module and it has methods:
module D
class Dog
#name = 'pluto'
def setName( n )
#name = n
end
def getName ()
return #name
end
end
end
Can I access getName without creating an instance of Dog like the static method in C++? Something like:
D::Dog.getName ()
instead of:
d = D::Dog.new
d.getName()

I believe you're looking for what is known as a class method in Ruby:
module SomeModule
class SomeClass
#class_variable = "some_string" # An instance variable on a class
def self.some_class_method
#class_variable # Return can be omitted in Ruby
end
# This is how setter methods are usually written in Ruby
def self.some_class_method= new_value
#class_variable = new_value
end
end
end
SomeModule::SomeClass.some_class_method
#=> "some_string"

Related

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}

How to pass a method to instance_eval?

I want to call instance_eval on this class:
class A
attr_reader :att
end
passing this method b:
class B
def b(*args)
att
end
end
but this is happening:
a = A.new
bb = B.new
a.instance_eval(&bb.method(:b)) # NameError: undefined local variable or method `att' for #<B:0x007fb39ad0d568>
When b is a block it works, but b as a method isn't working. How can I make it work?
It's not clear exactly what you goal is. You can easily share methods between classes by defining them in a module and including the module in each class
module ABCommon
def a
'a'
end
end
class A
include ABCommon
end
Anything = Hash
class B < Anything
include ABCommon
def b(*args)
a
end
def run
puts b
end
end
This answer does not use a real method as asked, but I didn't need to return a Proc or change A. This is a DSL, def_b should have a meaningful name to the domain, like configure, and it is more likely to be defined in a module or base class.
class B
class << self
def def_b(&block)
(#b_blocks ||= []) << block
end
def run
return if #b_blocks.nil?
a = A.new
#b_blocks.each { |block| a.instance_eval(&block) }
end
end
def_b do
a
end
end
And it accepts multiple definitions. It could be made accept only a single definition like this:
class B
class << self
def def_b(&block)
raise "b defined twice!" unless #b_block.nil?
#b_block = block
end
def run
A.new.instance_eval(&#b_block) unless #b_block.nil?
end
end
def_b do
a
end
end

How to add an instance variable to a class to back an added method?

I'm new to Ruby and confused about how I can make a method in Class similar to :attr_accessor, in that it adds methods to a user class, but so these added methods have access to a pre-initialized instance variable. It's difficult for me to explain so here is a greatly simplified sample of my efforts:
class Class
def super_accessor_wow(attr_name)
attr_name = attr_name.to_s
new_var_name = "#crazy_var_name"
instance_variable_set(new_var_name, ["hi", "everyone"])
module_eval(%Q/
def super_#{attr_name}()
return ##{attr_name}
end
def super_#{attr_name}=(value)
##{attr_name} = value
end
def greetings
return #{new_var_name}
end
/)
end
end
This is how I'm trying to use the new method on Class to modify my own class:
class Foo
super_accessor_wow(:bar)
end
foo1 = Foo.new()
foo1.super_bar = 1000
puts foo1.super_bar
puts foo1.greetings.inspect
The first puts prints '1000'
The second puts prints 'nil', so my instance_variable_set call in super_accessor_wow seemingly has no effect.
I expected that the second puts would print '['hi', 'everyone']' by the way. All of this code is contained in a single Ruby file.
Your instance_variable_set is called when you call super_accessor_wow during the class definition. No instance of the class exists yet. You create an instance of the class when you call new. You could add your #crazy_var_name initialization to the constructor, or you could define it in the greetings method:
Put the default in a class variable, and initialize the instance variable in the constructor (be aware that this creates a constructor for your class, and if you then create your own constructor, it will override this one):
class Class
def super_accessor_wow(attr_name)
attr_name = attr_name.to_s
new_var_name = "#crazy_var_name"
new_var_name_default = "##{new_var_name}"
module_eval(%Q/
#{new_var_name_default} = ["hi", "everyone"]
def initialize()
#{new_var_name} = #{new_var_name_default}
end
def super_#{attr_name}()
return ##{attr_name}
end
def super_#{attr_name}=(value)
##{attr_name} = value
end
def greetings
return #{new_var_name}
end
/)
end
end
class Foo
super_accessor_wow(:bar)
end
foo1 = Foo.new()
foo1.super_bar = 1000
puts foo1.super_bar
puts foo1.greetings.inspect
puts Foo.class_variable_get('##crazy_var_name').inspect
puts foo1.instance_variable_get('#crazy_var_name').inspect
Outputs:
1000
["hi", "everyone"]
["hi", "everyone"]
["hi", "everyone"]
Define it in the greetings method:
class Class
def super_accessor_wow(attr_name)
attr_name = attr_name.to_s
new_var_name = "#crazy_var_name"
module_eval(%Q/
def super_#{attr_name}()
return ##{attr_name}
end
def super_#{attr_name}=(value)
##{attr_name} = value
end
def greetings
#{new_var_name} = ["hi", "everyone"] unless #{new_var_name}
return #{new_var_name}
end
/)
end
end
class Foo
super_accessor_wow(:bar)
end
foo1 = Foo.new()
foo1.super_bar = 1000
puts foo1.super_bar
puts foo1.greetings.inspect
Outputs
1000
["hi", "everyone"]
As noted in the comment, instance_variable_set takes a symbol, not a string, so we'll fix that up first.
instance_variable_set(new_var_name.to_sym, ["hi", "everyone"])
But the big issue is that instance_variable_set isn't being called by an instance of Foo, it's being called by the Foo class itself. So, an instance variable is being set, but not on what you expected.
Foo.instance_variable_get(:#crazy_var_name).inspect
# ["hi", "everyone"]

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

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