To make a setter and a getter for Dessert class, I made the following code:
class Dessert
def initialize(name,calories)
#name=name
#calories=calories
end
def name
#name
end
def name=(names)
#name=names
end
def calories
#calories
end
def calories=(calory)
#calories=calory
end
end
d=Dessert.new("Salad",1200)
puts d.name("Khoresht")
puts d.calories(1600)
puts d.name()
puts d.calories()
The setter and getter must have the same name, and the compiler recognizes them by their signature. But here, I face the error:
`name': wrong number of arguments (1 for 0) (ArgumentError)
for the method name (the setter).
Why does it happen?
you should change d.name("Khoresht") to d.name=("Khoresht")
d=Dessert.new("Salad",1200)
d.name = "Khoresht" # use setter method, equal to d.name=("Khoresht")
d.calories = 1600
puts d.name
puts d.calories
The setter and getter have different name, in your codes, setter method name contains a =.
pangpang already answered your question. I just want to mention that it is uncommon in Ruby to define getter and setter methods.
Instead a common way is to use attr_accessor to declare getter and setter methods. The following example is equivalent to your code:
class Dessert
attr_accessor :name, :calories
def initialize(name, calories)
#name = name
#calories = calories
end
end
And other option might be to inherit from Struct. The following example has the same behaviour than your code:
class Dessert < Struct.new(:name, :calories)
end
Related
If I create a class like this:
class Player
def initialize(position, name)
#position = position
#name = name
end
end
Isn't that setting the name to an instance variable? if so, why would I need to write a setter like this
class Player
def initialize(position, name)
#position = position
#name = name
end
def name=(name)
#name = name
end
end
Basically when is it necessary to write getters in a class?
Getters and setters job is to provide you a quick implementation of read and write of instance variables that you define in your constructor:
class Player
attr_accessor :name, :position
def initialize(position, name)
#position = position
#name = name
end
end
you can also user attr_reader(for getters) and attr_writer(for setters) specifically for these variables.
Above code: attr_accessor :name, :position gives you: #name, #position, #name=, and #position= methods for instance of Player class.
However, they're not going to give you validation or a customized logic for getters/setters.
For example: you might want to show a player's full name or do not wish your code to accept a 0 or negative position, in such a case you'd have to write getter and setter yourself:
class Player
def initialize(first_name, last_name, position)
#first_name = first_name
#last_name = last_name
#position = position
end
# validation for updating position using setter of position
def position=(new_position)
raise "invalid position: #{new_position}" if new_position <= 0
#position = new_position
end
# customized getter for name method
def name
"#{#first_name} #{#last_name}"
end
end
If you do not require customization as stated above then using attr_* method for these variables makes more sense.
initialize sets attributes during the initialization of a new object.
keeper = Player.new('goalkeeper','Shilton').
What if you want to update an attribute of keeper after initialzation? Well you'll need to use your ordinary setter method:
def name=(name)
#name = name
end
like so:
keeper.name = 'Banks'
If you don't have a setter method defined for Player instances, then you can't do this. Similarly for getter methods. Also be aware you can refactor your code by using attr_accessor like so:
class Player
attr_accessor :name, :position
def initialize(position, name)
#position = position
#name = name
end
end
Getters/setters, also known as "accessors", are accessible outside the class, instance variables are not. If you want things to be able to read or change #name from outside the class, you to define accessors for it.
Additionally accessor methods allow you to perform a certain amount of sanity checking or mutate incoming/outgoing values, and otherwise protect the internal state of your objects.
Trying to solve this problem,
class Person
def initialize(name)
#name=name
end
def greet(other_name)
puts "Hi #{other_name}, my name is #{name}"
end
end
initialize("ak")
greet("aks")
but I am getting the error like:
ArgumentError: wrong number of arguments calling `initialize` (1 for 0)
I don't understand what is asking here, if its just the argument then why the error is like (1 for 0). can someone help me understand this problem.
Look at this code:
class Person
attr_reader :name
def initialize( name )
puts "Initializing Person instance #{object_id}"
#name = name
end
def greet( name )
puts "Hi #{name}, I'm #{name()}"
end
end
When you wrote initialize without explicit receiver:
initialize( "ak" )
It was only a matter of luck that your message was recognized. Look who has responded:
method( :initialize ).owner
#=> BasicObject
BasicObject, the foremother of all Object instances, herself responded to your call, by scolding you about wrong number of arguments, because:
method( :initialize ).arity
#=> 0
Not only this method does not expect any arguments, but also you are not expected to call it at all. In fact, you are not expected to call #initialize on any object by yourself, save for exceptional situations. Class#new method handles calling of Person#initialize method for you:
A = Person.new( 'Abhinay' )
Initializing Person instance -605867998
#=> #<Person:0xb7c66044 #name="Abhinay">
Person.new handled creation of a new instance and automatically called its #initialize method. Also, #initialize method is created private, even if you did not specify it explitcitly. The technical term for such irregular behavior is magic. Person#initialize is magically private:
A.initialize( 'Fred' )
NoMethodError: private method `initialize' called for #<Person:0xb7c66044 #name="Abhinay">
You cannot just reinitialize yourself to 'Fred', you know. All other methods are public unless prescribed otherwise:
A.greet "Arup"
Hi Arup, I'm Abhinay
#=> nil
You need to call the methods on the object (not just call the methods) and initialize is automatically called when creating a new object:
p = Person.new("ak")
p.greet("aks") #=> "Hi aks, my name is ak"
The problem is that to create new object you need to call method new on class, and not initialize on the object.
So code looks like this:
p = Person.new("John")
Please, take a look at code below:
class Person
attr_reader :name
def initialize(name)
#name = name
end
def greet(other_name)
puts "Hi #{other_name}, my name is #{name}"
end
end
person = Person.new("ak")
person.greet("aks")
#=> Hi aks, my name is ak
Class names seem to show up when you check for them with const_defined?() but not when you try to list them with constants(). Since I'm interested in listing any defined class names, I'm just trying to work out what's going on. Example:
class MyClass
def self.examine_constants
puts self.name
puts const_defined? self.name
puts constants.inspect
end
end
MyClass.examine_constants
Under Ruby 2.0.0p195, that code gives the following results:
MyClass
true
[]
Shouldn't the methods agree? What am I missing please?
The Module#constant method you're using return all the constants at the current scope. What you might want to use is the class method Module.constant which returns all the top-level constants. For instance:
class MyClass
def self.examine_constants
puts self.name
puts const_defined? self.name
puts Module.constants.inspect
end
end
MyClass.examine_constants
Response:
MyClass
true
[...lots of other constants, :MyClass]
Having some trouble when it comes to initializing variables within a class (instance variables etc.) and I was wondering if anybody could clarify the proper syntax for me.
Sample code:
Class Pets
attr_accessor :name
def initialize(name)
#name=name
end
def name=(name)
#name = name
#I believe this is where I change #name instance variable
end
#in this space I could create more <methods> for Class.new.<method>
end
My question is do I need to have attr_accessor as well as def initialize and def name=?
In addition, if I have multiple attr_accessors do I need to add them as arguments to def initialize, e.g.:
Class Pets
attr_accessor :name :age :color
def initialize(name, age, color)
#name = name
#age = age
#color = color
#and if this is the case do I need methods for each (name= age= color= etc.)
end
One last thing:
If someone could confirm or deny my thought process on the name= age= and color= type of methods within the classes. Am I correct in thinking method= is necessary to change the instance variable? I am a bit unsure about what the method= is for and why I cannot change the instance variable within initialize.
attr_accessor :symbol do the same as attr_writer :symbol and attr_reader :symbol, i.e. it creates both reader (def symbol; #symbol; end) and writer (def symbol=(value); #symbol = value; end).
Initialize is a method called every time new instance of the class is being created. It is not the same as new method as some classes may have its own custom factory methods. You don't need to define your initialize method, only problem is that then symbol reader would return nil, as the local variable would not been set.
In ruby everything is a method. In case of objects, object.attr = value is just a short for object.attr=(value) where attr= is just another method. (Similarly << operator is defined as a method on Array class, attr_accessor is a method defined on class "Class").
To piggy back on what what said earlier, recall that if you want your attributes to be accessible outside your class (you want to write over the attribute value or you want to read it) you will need to use the attr_accessor (or attr_writer or attr_reader).
If I had a class like ...
class Calendar
attr_reader :event_name, :all_events
def initialize
#event_name = event_name
#all_events = []
end
def create_event(event_name)
puts "#{event_name} has been added to your calendar."
#all_events << event_name
p #all_events
end
def see_all_events
puts "Here are your events --"
#all_events.each {|event| puts "- #{event}"}
end
end
my_calendar=Calendar.new
my_calendar.create_event("interview")
my_calendar.see_all_events
my_calendar.all_events
I can read all my events either with the method see_all_events or by calling all_events on my class Calendar object. If for some reason I did not want a see_all_events method but instead only wanted it to be seen by calling all_events on my object I can only do this because of attr_reader.
Basically the point here is to remember exactly how you want your users to interact with your object attributes. If it needs to be private and only accessed via methods then you should be weary of using attr_accessor or attr_writer or attr_reader (depending on the situation).
I understand (I think) the difference between class variables and instance variables of a class in Ruby.
I'm wondering how one can access the instance variables of a class from OUTSIDE that class.
From within (that is, in a class method instead of an instance method), it can be accessed directly, but from outside, is there a way to do MyClass.class.[#$#]variablename?
I don't have any specific reason for doing this, just learning Ruby and wondering if it is possible.
class MyClass
#my_class_instance_var = "foo"
class << self
attr_accessor :my_class_instance_var
end
end
puts MyClass::my_class_instance_var
The foregoing yields:
>> foo
I believe that Arkku demonstrated how to access class variables (##), not class instance variables (#) from outside the class.
I drew the foregoing from this essay: Seeing Metaclasses Clearly
Ruby has Class, Class Object, and Instance.
A Class variable belongs to a Class. A Class instance variable
belongs to a Class Object
Class variable:
Accessible within the class and its instances.
attr_accessor does not work on class variables.
Class instance variable:
Accessible only through the Class.
attr_accessor works if you define it in the class and not in the class object as below.
class A
#b = 1
class << self
attr_accessor :b
end
end
Defining a getter and setter on the instances for the class instance variable b in:
class A
#b = 1
class << self
attr_accessor :b
end
def b
A.b
end
def b=(value)
A.b=value
end
end
Now the class instance variable b can be accessed via the owner Class and its instances.
As a several days old ruby learner, this is the most I can do.
`irb(main):021:0* class A
irb(main):022:1> #b = 1
irb(main):023:1> class << self
irb(main):024:2> attr_accessor :b
irb(main):025:2> end
irb(main):026:1> def b
irb(main):027:2> A.b
irb(main):028:2> end
irb(main):029:1> def b=(v)
irb(main):030:2> A.b=v
irb(main):031:2> end
irb(main):032:1> end
=> :b=
irb(main):033:0> A.b
=> 1
irb(main):034:0> c = A.new
=> #<A:0x00000003054440>
irb(main):035:0> c.b
=> 1
irb(main):036:0> c.b= 50
=> 50
irb(main):037:0> A.b
=> 50
irb(main):038:0>`
Yes, I'm begining to dislike ruby...iso a better solution.
In ruby you can achieve this in 2 ways
manually defining getter and setters
using attr_* methods
Let me elaborate the above ways for you,
Manually defining getter and setters
class Human
def sex=(gender)
#sex = gender
end
def sex
#sex
end
end
//from outside class
human = Human.new
// getter method call
puts human.sex
// setter method call to explicitly set the instance variable
human.sex = 'female'
puts human.sex
// now this prints female which is set
using attr_* methods
class Human
attr_accessor :sex
end
//from outside
human = Human.new
// getter method call
puts human.sex
// setter method call to explicitly set the instance variable
human.sex = 'female'
puts human.sex
// now this prints female which is set
attr_accessor internally creates setter and getter methods for you, if you want only setter you can just use attr_writer and if you want only getter you can use attr_reader.
I hope, I answered your question