I know that self.class.name returns name of the class but how about instance?
For example this code
module Selling
def sell
puts "#{self.class.name} has been sold"
end
end
class Shop
include Selling
def initialize(id)
#id=id
end
end
book=Shop.new(1132)
book.sell
prints Shop and what I need is a book
Objects don't have names. They may or may not be referenced by one or more variables, but there is no way to know what variables reference an object and what the names of those variables are.
Modules are a special case, their name method indeed returns the name of the first constant that they have been assigned to, but that is interpreter magic.
Jörg W Mittag already explained that you can't inspect variable names.
Here's an attempt to solve your problem by using a separate Book instance with a name attribute:
module Selling
def sell(item)
puts "#{item.name} has been sold"
end
end
class Shop
include Selling
end
class Book
attr_accessor :name
def initialize(name)
#name = name
end
end
bookstore = Shop.new
book1 = Book.new('Moby-Dick')
book2 = Book.new('Of Mice and Men')
bookstore.sell(book1)
bookstore.sell(book2)
Output:
Moby-Dick has been sold
Of Mice and Men has been sold
Related
I'm new to ruby and programming in general and I'm having some issues getting methods to work from another class. The method I'm trying to get to work is new_employee and its option 2 if you run the business.rb
business.rb file contains class Business
class Business
attr_accessor :name
def run
self.welcome
end
def welcome
while true
puts "Welcome to Team Green Turf! What do you want to do today?"
puts "1. Add new customer"
puts "2. Add new employee"
puts "3. View current revenue"
choice = gets.chomp.to_i
case choice
when 1
puts "hello!"
when 2
puts new_employee()
when 3
exit
end
end
end
end
team_green_turf = Business.new
team_green_turf.run
---------------------------------
employees.rb file
require_relative 'business'
class Employees
attr_accessor :name
def initialize(name)
#name = name
end
def new_employee(name)
puts "What is the employees name?"
name = gets.chomp
e1 = Employees.new(name)
end
end
There are two main ideas:
You need to ensure that the Ruby interpreter has loaded the file containing the method before you attempt to call it. In your case, you are executing the code at the botton of business.rb before the employees.rb file has been fully loaded, so the Employee class and its methods will not be defined. You could fix this in many different ways, but I suggest moving the last two lines of business.rb into their own file called run.rb and putting require_relative 'business' at the top of run.rb, and putting require_relative 'employees' at the top of business.rb, and removing the require_relative 'business' from the top of employees.rb. (The Employees class does not actually use or depend on the Business class so it doesn't make sense to require it.)
You need to provide the proper prefixes to the method to get it to be called. In your case, you want to call the function from the Business class, you would write Employees.new_employee. You would also need to change new_employee to be a class method by putting self. before its name when you are defining it: def self.new_employee(name)....
I am using super to pass arguments to the parent initialize method, which is not called by default. This is what it looks like. (Notice the use of super on the last two arguments)
module Pet
def initialize name, is_pet
#is_pet = is_pet
if is_pet
#name = name
else
#name = "Unnamed"
end
end
def pet?
return #is_pet
end
def get_name
return #name
end
end
class Dog
include Pet
def initialize tricks, name, is_pet
#tricks = tricks
super name, is_pet
end
def get_tricks
return #tricks
end
end
Here's what I can do with it:
d = Dog.new ["roll", "speak", "play dead"], "Spots", true
d.pet? #=> true
d.get_tricks #=> ["roll", "speak", "play dead"]
d.get_name #=> "Spots"
It works fine, but I'm just wondering if there's a better way to do this.
It is not a good programming practice to hard code a fixed string like "Unnamed" as the value for #name. In such case, you should assign nil, and do whatever modification to it when you print it. Suppose you do this.
Then is_pet can be deduced from whether name is nil or not, so it is redundant to have that as an instance variable. You can simply apply !! to name in order to get is_pet. Therefore, you should get rid of such instance variable.
You have get_ prefixes as getter methods, but in Ruby, it is a better practice to have the same name as the instance variables (without the atmark) as the getter name.
This will give you:
module Pet
attr_reader :name
def initialize name; #name = name end
end
class Dog
include Pet
attr_reader :tricks
def initialize tricks, name
#tricks = tricks
super(name)
end
end
d = Dog.new ["roll", "speak", "play dead"], "Spots"
d.tricks #=> ["roll", "speak", "play dead"]
d.name #=> "Spots"
!!d.name #=> true (= `is_pet`)
Do not write code that calls super to get into an included module. Don't write modules that will expect children to call super. That's not the point of modules.
It's good object oriented style to not ask about what things are. Look up "tell, don't ask" and duck typing in general.
If you want to provide a default initialize method, you probably want inheritance. But there are occasionally valid use cases for overriding initialize in a module. The idiomatic thing to do here is a hook method:
module Pet
def initialize(options = {})
#name = options[:name]
post_initialize(options)
end
def post_initialize(options = {})
# can be overridden in including modules
end
end
class Dog
include Pet
def post_initialize(options = {})
#tricks = options[:tricks]
end
end
dog = Dog.new(name: "Fido", tricks: ["play dead", "roll over"])
A module is for including some shared behavior among many different things. It's good to consider it like an adjective describing what you might do with a class that includes it. Words that end in "-able" (like Enumerable or Comparable), describing a receiving class, or "-or" (Iterator, Interactor), describing a doing class, are good candidates for being modules.
To preface, I am a raw Ruby novice, and fairly new to coding as well, so please forgive my ignorance. I'm trying to figure out a hypothetical exercise involving two classes, and passing variables between an instance of one class to the other. I've been searching for answers, but can't seem to find anything that applies directly to this kind of case.
class Person
def initialize(name, age)
#name = name
#age = age
puts "Hi #{#name}, you are #{#age}."
end
end
class Town
def initialize(town_name)
#town_name = town_name
puts "Welcome to #{#town_name}!"
end
def buy_house(person)
puts "#{#name}, at age #{#age}, you bought a house in #{#town_name}!"
end
end
When I instantiate Person and Town, I can see the instance variables being set:
me = Person.new("Daniel", 38)
townville = Town.new("Townville")
My real question is: how do I pass the variables set for the instance of "me" into any methods defined in Town using something like the line below?
townville.buy_house(me)
You can't use puts "#{#name}..." inside an instance of Town to refer to a Person's #name instance variable. #variable always refers to a variable belonging to the current object. If you want to access Person's instance variables, you need to make accessors for them.
You can do so by explicitly defining methods on Person which operate on the instance variables, or using attr_reader/attr_writer/attr_accessor which do so for you:
class Person
attr_reader :name, :age
# the above line is equivalent to defining two methods:
# def name; #name; end
# def age; #age; end
def initialize(name, age)
#name = name
#age = age
puts "Hi #{#name}, you are #{#age}."
end
end
# class Town ...
def buy_house(person)
puts "#{person.name}, at age #{person.age}, you bought a house in #{#town_name}!"
end
If you want to "inject" all the instance variables of one object into another, that is absolutely possible. Whether this is useful or not is up to you to decide:
class Object
def inject_instance_variables(other)
other.instance_variables.each do |var|
self.instance_variable_set(var, other.instance_variable_get(var))
end
end
end
I saw the following program in Beginning Ruby, and can not figure out the purposes of set_first_name(first_name) and set_last_name(last_name) and why the two methods set_first_name and set_last_name use the same argument name, because I think that name includes first_name and last_name.
class Person
def initialize(name)
set_name(name)
end
def name
#first_name + ' ' + #last_name
end
def set_name(name)
first_name, last_name = name.split(/\s+/)
set_first_name(first_name)
set_last_name(last_name)
end
def set_first_name(name)
#first_name = name
end
def set_last_name(name)
#last_name = name
end
end
p = Person.new("Fred Bloggs")
puts p.name
Ugh. That is a horrible introduction. There's a fair amount of magic that goes into determining if "name" is a local variable or method. And depending on what you do, you can switch it from one to the other.
Add the following method to your class:
def test
puts name
name = "Bob"
puts name
puts self.name
end
Then run it like this:
p = Person.new('Jane Doe')
p.test
You'll get the following output:
Jane Doe
Bob
Jane Doe
What's happening is that at first name isn't found as a local variable so it looks for a method (and will look up through the ancestors/modules of the class as well if there are any). Then we assign a value to name. Which makes my head hurt, but is legal. On the next puts name now refers to that local variable. We can get back to the method by specifying self.name.
Honestly, as the others have noted this class is poorly named (heh, pun intended? :). It's best to avoid this confusion if you can.
David Black has a book called The Well-Grounded Rubyist. Chapter 5 in particular goes over this in great detail. It's a great book in general. No affiliation just a happy customer.
http://www.manning.com/black2/
http://gist.github.com/172341 ( stackoverflow was breaking the formatting )
In the following case method name created by Human is not available to Boy. Is my understanding correct that attr_accessor methods are not
available to subclasses. I need to use superclass to access the method added by attr_accessor.
What you're looking for is cattr_accessor which fixes this specific problem:
http://apidock.com/rails/Class/cattr_accessor
Here's your example, fixed:
class Human
def self.age
#age = 50
end
def self.age=(input)
#age = input
end
cattr_accessor :name
self.name = 'human'
end
class Boy < Human
end
puts Human.age
puts Boy.age
puts Human.name
puts Boy.superclass.name
puts Boy.name # => 'human'
Human and Boy are two different objects. Two objects can never share a single instance variable. They do both have the method, but the method will access the appropriate ivar for the object.
Rails class_attribute method would be better in this case.
Rails Guide