How would you get my 'def showVars' built within the 'second' class to output the 'puts (variables)' that it inherited from the 'First' class?
class First
##varOne = 1
CONSTANT_ONE = 10
end
class Second < First
def showVars
puts ##varOne
puts CONSTANT_ONE
end
end
My failed attempt:
class First
##varOne = 1
CONSTANT_ONE = 10
end
class Second < First
def showVars
puts ##varOne
puts CONSTANT_ONE
end
end
puts Second.showVars # <-- fails
You can't call Second.showVars because it's an instance method. To call it that way, you have to use a class method. You can do that by adding self in the method name.
class First
##varOne = 1
CONSTANT_ONE = 10
end
class Second < First
def self.showVars
puts ##varOne
puts CONSTANT_ONE
end
end
puts Second.showVars
The output now is:
1
10
[Finished in 0.1s]
Class methods are equivalent to static methods in other languages.
Another point I noticed is that you named your method showVars using camelCase. Ruby methods should be named using snake_case.
Related
I am trying to access a class variable from a subclass. I understand that class variables are not inherited which answers the question of why the code does not work, however I don't fully understand how I can work around it.
This is my code:
Class A
...
class << self
def format(a, b)
#format = a
end
def item(a, b)
#item[a] = b
end
end
end
Class B < A
format 4, 7
item 7, 12
...
end
Class C < B
item 7, 18
end
Running the following in a irb session
B.format => 4
C.format => nil
So understanding that class variables aren't inherited, is it possible to make C.format => 4 or will I need to refactor as such:
Class B < A
format 4, 7
item 7, 12
end
Class C < A
format 4, 7
item 7, 18
end
My reason for wanting to avoid the latter is that I have a lot of variables defined in that same way (calling a function to set a class variable) and I don't want to have to duplicate all the code across class B and C because one instance variable differs.
I understand that class variables are not inherited
This is completely false and you have not actually understood what class variables are.
A class variable in Ruby is declared with the ## sigil. And they are definitely inherited:
class A
##x= "Hello World"
end
class B < A
puts ##x # Outputs "Hello World"
end
Class variables are actually shared between a class and its subclasses:
class Animal
##greating = "Hello"
def greet
"#{##greating} I'm a #{self.class.name.downcase}"
end
end
class Cat < Animal
##greating = "Meow"
end
class Dog < Animal
##greating = "Woof"
end
puts Dog.new.greet # Woof I'm a dog
puts Cat.new.greet # Woof I'm a cat
puts Animal.new.greet # Woof I'm a animal
As you can see from the example this can often lead to unexpected and undesireable effects. And class variables are not considered thread safe.
What you are actually setting is referred to as a class instance variable - this is just a instance variable except that its scope is not a instance of A. Instead its scope is the singleton class A which is an instance of the Class class.
Unlike true class variables which have their own sigil class instance variables are not shared between a class and its subclasses as thier scope is the singleton class. Every subclass its own singleton class.
class Animal
#greating = "Hello"
def self.greeting
#greating
end
def greet
"#{self.class.greeting} I'm a #{self.class.name.downcase}"
end
end
class Cat < Animal
#greating = "Meow"
end
class Dog < Animal
#greating = "Woof"
end
puts Dog.new.greet # Woof I'm a dog
puts Cat.new.greet # Meow I'm a cat
puts Animal.new.greet # Hello I'm a animal
Class instance variables are actually far more useful then true class variables. If you want to simulate the inheritance of class variables with class instance variables you can do it with the Class#inherited callback provided by Ruby:
class A
#x = [self.name]
class << self
attr_accessor :x
def inherited(subclass)
puts "#{subclass.name} is inheriting from #{self.name}"
subclass.x = x
subclass.x.push(subclass.name)
end
end
end
class B < A; end # outputs "B is inheriting from A"
class C < B; end # outputs "C is inheriting from B"
puts B.x.inspect # => ['A', 'B']
puts C.x.inspect # => ['A', 'B', 'C']
Firstly, to define a class you need to use the keyword class not Class.
Secondly, there is no reason to pass the method A::format an argument it does not use. I therefore changed it to have just one argument.
Thirdly, after defining A, when A.item is first executed an exception will be raised because #item has not been defined. Specifically, #item (like any other undefined instance variable) will return nil when called and nil has no no method []. I therefore changed the method item to initialize the class instance variable (not class variable) #item to an empty array.
class A
class << self
def format(a)
#format = a
end
def item(a, b)
#item = []
#item[a] = b
end
end
end
class B < A
format 4
item 7, 12
end
class C < B
item 7, 18
def self.format
superclass.instance_variable_get(:#format)
end
end
It is my understanding that you want a method C::format to return the value of B's instance variable #format.
C.format
#=> 4
If one attempts to execute B.format an exception will be raised because B::format requires two arguments. If B.format is meant to return the value of B's class instance variable #format you need to write:
B.instance_variable_get(:#format)
#=> 4
You might add read and write accessors for the the instance variable #format, in which case your code would be simplified somewhat:
class A
class << self
attr_accessor :format
def item(a, b)
#item = []
#item[a] = b
end
end
end
class B < A
#format = 4
item 7, 12
end
class C < B
item 7, 18
def self.format
superclass.format
end
end
C.format
#=> 4
I'm trying to access a class variable from a method outside of the class.
This is my class:
class Book
##bookCount = 0
##allBooks = []
def self.allBooks
##allBooks
end
def self.bookCount
##bookCount
end
attr_accessor :name,:author,:date,:genre,:rating
def initialize(name, author, date, genre, rating)
#name = name
#author = author
#date = date
#genre = genre
#rating = rating
##bookCount += 1
##allBooks << self
end
end
This is the method trying to access the class variable ##bookCount
def seeBookShelf
if ##bookCount == 0
puts "Your bookshelf is empty."
else
puts "You have " + #bookCount + " books in your bookshelf:"
puts allBooks
end
end
When I try to execute the method I get this:
undefined local variable or method `bookCount' for main:Object (NameError)
How can I access bookCount from the outside?
Use class_variable_get to access a class variable outside of a class:
class Foo
##a = 1
end
Foo.class_variable_get(:##a)
=> 1
For most cases, class instance variables are preferred to class variables. The latter are prone to all manner of strange behaviour when used with inheritance.
Consider:
class Book
#book_count = 0
#all_books = []
class << self
attr_reader :book_count
attr_reader :all_books
end
# further code omitted.
end
With this code Book.book_count and Book.all_books get the expected data.
You can use class_eval to evaluate a block of code within the scope of a specific class:
class Book
##bookCount = 1
end
Book.class_eval '##bookCount'
# => 1
And just for fun... you can actually do all kinds of trickery with class_eval such as define a new method in the class without monkey patching:
Book.class_eval { ##bookCount = 5 }
Book.class_eval '##bookCount'
# => 5
Book.class_eval do
def self.hey_look_a_new_method
return "wow"
end
end
Book.hey_look_a_new_method
# => "wow"
You need a getter to access the class variable, try this code.
See http://www.railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/ for an explanation.
You are also better to use string interpolation otherwise you get a Type error, also it is more Rubyesque.
class Book
##bookCount = 0
def self.bookCount
##bookCount
end
end
def seeBookShelf
if Book.bookCount == 0
puts "Your bookshelf is empty."
else
puts "You have #{Book.bookCount} books in your bookshelf:"
end
end
seeBookShelf # Your bookshelf is empty.
You have to specify the Class of the variable :
def seeBookShelf
if Book.bookCount == 0
puts "Your bookshelf is empty."
else
puts "You have " + Book.bookCount + " books in your bookshelf:"
puts Book.allBooks
end
end
What is difference between these two methods in Ruby?
class Mod
def doc(str)
...
end
def Mod::doc(aClass)
...
end
end
Mod::doc()
is a class method, whereas
doc()
is an instance method. Here's an example of how to use both:
class Mod
def doc()
puts 1
end
def Mod::doc()
puts 2
end
end
a = Mod.new
a.doc #=> 1
Mod.doc #=> 2
Here's a question that compares it with
self.doc()
I have ruby code like this:
module Hello
class Hi
def initialize()
puts self.module.name //Should print "Hello"
end
end
end
How can I get the name of the module the class is included in?
Thanks
You can do this with Module::nesting method:
nesting → array
Returns the list of Modules nested at the point of call.
module M
class C
Module.nesting[1] # => M
end
end
If you want to get this value from the instance methods, you can assign it to class variable:
module Hello
class Hi
##parent = Module.nesting[1]
def initialize()
puts ##parent # => Hello
end
end
end
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
ruby: can I have something like Class#inherited that's triggered only after the class definition?
class A
def self.inherited(child)
puts "XXX"
end
end
class B < A
puts "YYY"
end
prints out
XXX
YYY
I'd prefer
YYY
XXX
if I could get it somehow.
You can trace until you find the end of the class definition. I did it in a method which I called after_inherited:
class Class
def after_inherited child = nil, &blk
line_class = nil
set_trace_func(lambda do |event, file, line, id, binding, classname|
unless line_class
# save the line of the inherited class entry
line_class = line if event == 'class'
else
# check the end of inherited class
if line == line_class && event == 'end'
# if so, turn off the trace and call the block
set_trace_func nil
blk.call child
end
end
end)
end
end
# testing...
class A
def self.inherited(child)
after_inherited do
puts "XXX"
end
end
end
class B < A
puts "YYY"
# .... code here can include class << self, etc.
end
Output:
YYY
XXX
This is not possible. Consider this: when should a class definition be considered "done" in Ruby?
Instead of self.inherited, I would personally make a class method called finalize! and put your post-class-creation routine in there.
# A silly, contrived example
class A
def self.author(person)
##authors ||= Array.new
##authors << person
end
def self.finalize!
##authors.map! { |a| Author[a] }
end
end
class B < A
author "Jason Harris"
author "George Duncan"
finalize!
end
You can probably get away with making a wrapper function instead:
class A
def self.define_authors(&blk)
yield
# (...finalize here)
end
...
end
class B < A
define_authors {
author "Jason Harris"
author "George Duncan"
}
end
...Or do consider that there may be ways where that finalizing step may not be needed.
There is no such hook AFAIK.
A workaround could be:
class A
##my_lambda = nil
def self.inherited(child)
##my_lambda = lambda { puts "XXX" }
end
def self.after_inherited
##my_lambda.yield
end
end
class B < A
puts "YYY"
# other definitions
self.superclass.after_inherited
end
It looks a bit messy but its output is:
YYY
XXX
This way you guarantee that your code in B gets executed before the after_interited() of A. So it does what you want but not the way you want to it.