I have a class with a private method:
class MyClass
attr_accessor :my_attr
def some_mth?(num)
# I want to use my_attr as a variale #myattr here
#and here i want to check if arr include num
#myattr.include?(num)
end
private
def some_pvt_mth
#myattr = [1,2,3,4]
for example generation array here
end
end
When I call #myattr inside some_mth, my variable #myattr is nil
How to use variable #myatt inside class, in every method is it possible?
How do I do it properly?
You do not need to define attr_accessor in order to use an instance variable within the defined class. It's purpose is to create a 'getter' and a 'setter' method, but those are only needed for other classes to access the data.
This is a class:
class Foo
def initialize
#my_attr = [1,2,3,4]
end
def attr_includes?(x)
#my_attr.include?(x)
end
end
There's no attr accessor, but this will work.
The attr accessor essentially includes this code in your class...
class Foo
def my_attr
#my_attr
end
def my_attr=(x)
#my_attr = x
end
end
But if you don't want that, you can just leave it out, and access the variable via other methods (such as your include example).
You have to define the instance variable value first:
class MyClass
attr_accessor :my_attr
def initialize
#myattr = [1, 2, 3, 4]
end
def some_mth?(num)
#myattr.include?(num)
end
end
Related
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
I think I'm going a bit crazy when trying to understand instance variables in Ruby. My only aim here is to make sure that every object created for a given class has a variable with a predetermined value without writing an initialize method for that class. Something like:
class Test
#my = []
attr_accessor :my
end
t = Test.new
t.my # I want [] but this shows nil
Is it possible to achieve this without touching initialize ? Thanks.
EDIT: To clarify, I'm writing some piece of code which will be executed similar to attr_accessor in the sense that it'll add an instance variable to the class in which it is executed. If I write my own initialize, I will end up clobbering the one written by the user.
What you are doing is defining an instance variable on the class level (Since classes are instances of the Class class, this works just fine).
And no, there is no way around initialize.
Edit: You have a little misconception in your edit. attr_accessor doesn't add an instance variable to the class. What it does, literally, is this (using your example of my):
def my; #my; end
def my=(value); #my = value; end
It doesn't actively create/initialize any instance variable, it just defines two methods. And you could very well write your own class method that does similar things, by using define_method.
Edit 2:
To further illustrate how one would write such a method:
class Module
def array_attr_accessor(name)
define_method(name) do
if instance_variable_defined?("##{name}")
instance_variable_get("##{name}")
else
instance_variable_set("##{name}", [])
end
end
define_method("#{name}=") do |val|
instance_variable_set("##{name}", val)
end
end
end
class Test
array_attr_accessor :my
end
t = Test.new
t.my # => []
t.my = [1,2,3]
t.my # => [1, 2, 3]
# as instance variable without initialize
class Test1
def my; #my ||= [] end
attr_writer :my
end
t = Test1.new
t.my
# as class instance variable
class Test2
#my = []
class << self; attr_accessor :my end
end
Test2.my
I don't think it is, why are you so hesitant to just write a quick initialize method?
In order to ask something like:
MyClass::create().empty?
How would I set up empty within MyClass?
Empty (true/false) depends on whether a class variable #arr is empty or not.
The question mark is actually part of the method name, so you would do this:
class MyClass
def empty?
#arr.empty? # Implicitly returned.
end
end
Exactly the same as I showed in the last post, but with a different method name.
First, create must return something with an empty? method. For example:
class MyClass
def self.create
[]
end
end
If you want to be operating on instances of MyClass as per your last question:
class MyClass
def self.create
MyClass.new
end
def initialize
#arr = []
end
def empty?
#arr.empty?
end
def add x
#arr << x
self
end
end
Here MyClass acts as a simple wrapper around an array, providing an add method.
pry(main)> MyClass.create.empty?
=> true
You might also need to check whether #arr is nil or not. This depends on your class definition of empty.
def empty?
!#arr || #arr.empty?
end
You could use Forwardable to delegate empty? from your class to the array:
require "forwardable"
class MyClass
extend Forwardable
def_delegators :#arr, :empty?
def initialize(arr)
#arr = arr
end
end
my_object = MyClass.new([])
my_object.empty? # => true
How would I create an attr_accessor to array?
for example
class MyClass
attr_accessor :my_attr_accessor
def initialize()
end
def add_new_value(new_array)
#my_attr_accessor += new_array
return #my_attr_accessor
end
end
my_class = MyClass.new
my_class.my_attr_accessor = 1
my_class.my_attr_accessor[1] = 2
my_class.my_attr_accessor.push = 3
my_class.add_new_value(5)
my_class.my_attr_accessor
=> [1, 2, 3, 5]
Just use an instance variable that points to an array and make an accessor from that instance variable.
Inside your class include something like this:
attr_accessor :my_attr_accessor
def initialize
#my_attr_accessor = []
end
Note that usingattr_accessor will allow you to change the value of the variable. If you want to ensure that the array stays, use attr_reader in place of attr_accessor. You will still be able to access and set array elements and perform operations on the array but you won't be able to replace it with a new value and using += for concatenation will not work.
If you are OK with the Array always existing, #david4dev's answer is good. If you only want the array to pop into existence on the first usage, and never want the user to be able to replace it with a new array (via assignment):
class MyClass
def my_attr_accessor
#my_attr_accessor ||= []
end
def add_new_value( value )
my_attr_accessor << value
end
def add_new_values( values_array )
my_attr_accessor.concat values_array
end
end
The user could still call my_class.my_attr_accessor.replace( [] ) to wipe it out.
How can a class method (inside a module) update an instance variable? Consider the code bellow:
module Test
def self.included(klass)
klass.extend ClassMethods
end
module ClassMethods
def update_instance_variable
#temp = "It won't work, bc we are calling this on the class, not on the instance."
puts "How can I update the instance variable from here??"
end
end
end
class MyClass
include Test
attr_accessor :temp
update_instance_variable
end
m = MyClass.new # => How can I update the instance variable from here??
puts m.temp # => nil
You'd have to pass your object instance to the class method as a parameter, and then return the updated object from the method.
That does nto quite make sense.
You use the initialize method to set default values.
class MyClass
attr_accessor :temp
def initialize
#temp = "initial value"
end
end
The initialize method is automatically run for you when you create a new object.
When your class declaration is run, there are no, and cannot be any, instances of the class yet.
If you want to be able to change the default values later you can do something like this:
class MyClass
attr_accessor :temp
##default_temp = "initial value"
def initialize
#temp = ##default_temp
end
def self.update_temp_default value
##default_temp = value
end
end
a = MyClass.new
puts a.temp
MyClass.update_temp_default "hej"
b = MyClass.new
puts b.temp
prints
initial value
hej
If you also want that to change already created instances' variables you need additional magic. Please explain exactly what you wish to accomplish. You are probably doing it wrong :)