Objects and variables.

by msypniewski511 in Ruby for Rails

Objects and variables

Defining object behaviour

obj = Object.new
def obj.talk
  puts "I am an object."
  puts "I can talk"
end

Sending message to objects

object.message

In the context of this construct:

  • object can be, and often is, a variable that stands in for an object. It may also be a literal object construct—for example, a string in quotation marks.
  • The dot ( . ) is the message-sending operator. The message on the right is sent to the object on the left.
  • message is the message that gets sent to object . In practice, message is almost always the name of a method (like talk , the method defined ear- lier). In any case, object always thinks message is the name of a method. If there’s no method by that name, error-handling measures are taken. But the general idea is that every message you send to an object corresponds to a method the object can call.
obj.talk
I am an object.
I can talk

Methods that take arguments
In a method definition, you indicate the required and/or optional arguments by
means of a list of variables in (sometimes optional) parentheses after the method.

def obj.c2f(c)
  c * 9 / 5 + 32
end

---------------------------------------------------------------------------
puts obj.c2f(100)

= 
212

The innate behaviors of an object

Even a newly created object isn’t a blank slate.

p Object.new.methods.sort

[:!,
 :!=,
 :!~,
 :<=>,
 :==,
 :===,
 :__id__,
 :__send__,
 :class,
 :clone,
 :define_singleton_method,
 :display,
 :dup,
 :enum_for,
 :eql?,
 :equal?,
 :extend,
 :freeze,
 :frozen?,
 :hash,
 :inspect,
 :instance_eval,
 :instance_exec,
 :instance_of?,
 :instance_variable_defined?,
 :instance_variable_get,
 :instance_variable_set,
 :instance_variables,
 :is_a?,
 :itself,
 :kind_of?,
 :method,
 :methods,
 :nil?,
 :object_id,
 :pretty_inspect,
 :pretty_print,
 :pretty_print_cycle,
 :pretty_print_inspect,
 :pretty_print_instance_variables,
 :private_methods,
 :protected_methods,
 :public_method,
 :public_methods,
 :public_send,
 :remove_instance_variable,
 :respond_to?,
 :send,
 :singleton_class,
 :singleton_method,
 :singleton_methods,
 :tap,
 :then,
 :to_enum,
 :to_s,
 :yield_self] 

Identifying objects uniquely with the object_id method

obj = Object.new
puts "The id of obj is #{obj.object_id}."

3.2.2 :093 > a = Object.new
 => #<Object:0x00007f3440f72670> 
3.2.2 :094 > b = a
 => #<Object:0x00007f3440f72670> 
3.2.2 :095 > a.id
3.2.2 :096 > a.object_id
 => 1844620 
3.2.2 :097 > b.object_id
 => 1844620 

---------------------------------------------

3.2.2 :098 > string_1 = "Hello"
 => "Hello" 
3.2.2 :099 > string_2 = "Hello"
 => "Hello" 
3.2.2 :100 > string_1.object_id
 => 1906760 
3.2.2 :101 > string_2.object_id
 => 1916160 

Querying an object’s abilities with the respond_to? method

obj = Object.new
if obj.respond_to?("talk")
  obj.talk
else
  puts "Sorry, the object doesn't understand the 'talk' message."
end

Sending messages to objects with the send method

if obj.respond_to?(request)
  obj.send(request)
else
  puts "Object doesn't respond to message"
end

Required, optional, and default-valued arguments

Required and optional arguments

def obj.one_arg(x)
end
obj.one_arg(1,2,3)
results in:
ArgumentError: wrong number of arguments (3 for 1)

It’s possible to write a method that allows a variable number of arguments.

def obj.multi_args(*x)
end

And the mix of them:

def two_or_more(a, b, *c); end

Default values for arguments

def default_args(a,b,c=1)
  puts "Values of variables: ", a,b,c
end

Sample method signatures with required, optional, and default-valued arguments

Argument type(s) Method signature Sample call(s) Variable assignments
Required (Req) def m(a,b,c) m(1,2,3) a = 1, b =2, c = 3
Optional (Opt) def m(*a) m(1,2,3) a = [1,2,3]
Default-valued (Def) def m(a=1) m / m(2) a = 1 / a=2
Req/Opt def m(a,*b) m(1) a = 1, b = []
Req/Def def m(a,b=1) m(2) / m(2,3) a = 2, b = 1 / a = 2, b = 3
Def/Opt def m(a=1, *b) m / m(2) a = 1, b = [] / a = 2, b = []
Req/Def/Opt def m(a, b=2, *c) m(1) / m(1,3) / m(1,3,5,7) a=1, b=2, c=[] / a=1, b=3, c=[] / a = 1, b = 3, c = [5,7]

Local variables and variable assignment

In Ruby, local variables are created by simply assigning a value to them. They start with a lowercase letter or an underscore (_), followed by alphanumeric characters or underscores. Here's an example of local variable assignment:

 # Variable assignment
name = "John"
age = 30

In this example, name and age are local variables. name holds the string value "John", and age holds the integer value 30.

Local variables in Ruby have a lexical scope, meaning they are accessible within the block of code in which they are defined, including any nested blocks. However, they are not accessible outside of their scope. For example:

def my_method
  x = 10
  puts x  # This will print 10
end

my_method
puts x  # This will raise an error because `x` is not defined in this scope

In this example, x is a local variable defined within the scope of the my_method method. It is accessible within that method but not outside of it.

It's important to note that Ruby is dynamically typed, so the type of a variable is determined at runtime based on the value assigned to it. You can reassign a different value to a local variable:

x = 10
puts x  # Output: 10

x = "hello"
puts x  # Output: hello

In this example, x is first assigned the integer value 10, and later it's reassigned the string value "hello". Ruby allows such dynamic variable assignment.

Local variables can come into being in either of two ways:

  • Through assignment: x = object
  • As an entry in the method’s argument list, initialized when the method is called

Here’s how Ruby decides what it’s seeing when it encounters a bareword:
1 If there’s an equal sign ( = ) to the right of the bareword, it’s a local variable
undergoing an assignment.
2 If the bareword is a keyword, it’s a keyword (Ruby has an internal list of
these and recognizes them).
3 Otherwise, the bareword is assumed to be a method call.

Organizing objects with classes

Classes and instances

In Ruby, classes are blueprints for creating objects (instances). Each class defines the behavior and attributes that its instances will have. Here's an example of defining a class and creating instances of that class:

class Person
  def initialize(name, age)
    @name = name
    @age = age
  end

  def greet
    puts "Hello, my name is #{@name} and I'm #{@age} years old."
  end
end

 # Creating instances of the Person class
person1 = Person.new("Alice", 30)
person2 = Person.new("Bob", 25)

 # Calling methods on instances
person1.greet  # Output: Hello, my name is Alice and I'm 30 years old.
person2.greet  # Output: Hello, my name is Bob and I'm 25 years old.

Explanation:

  • We define a class Person using the class keyword.
  • Inside the class, we define an initialize method. This method is a special method in Ruby classes and is called automatically when a new instance of the class is created (Person.new). It initializes the instance variables @name and @age.
  • We define a greet instance method within the class. This method prints a greeting message using the instance variables @name and @age.
  • We create two instances of the Person class (person1 and person2) using the new method. We pass arguments to the initialize method to set the name and age attributes for each instance.
  • We call the greet method on each instance to print the greeting message specific to each person. In Ruby, instance variables (prefixed with @) are used to store data that belongs to each instance of a class. Instance methods can access and manipulate these instance variables to perform actions specific to each instance.

Instance methods

In Ruby, an instance method is a method that belongs to an instance of a class. These methods can access and modify the instance variables of the object on which they are called. Here's an example of defining and using an instance method in Ruby:

class Person
  def initialize(name, age)
    @name = name
    @age = age
  end

   # Instance method
  def greet
    puts "Hello, my name is #{@name} and I'm #{@age} years old."
  end
end

 # Creating an instance of the Person class
person = Person.new("Alice", 30)

 # Calling the instance method
person.greet  # Output: Hello, my name is Alice and I'm 30 years old.

Explanation:

  • We define a class Person with an initialize method, just like in the previous example. This method initializes the instance variables @name and @age.
  • We define an instance method greet within the class. This method prints a greeting message using the instance variables @name and @age.
  • After creating an instance of the Person class (person), we call the greet method on the person object. This invokes the greet method in the context of that particular instance, allowing it to access the instance variables specific to that instance. Instance methods can take arguments just like any other method in Ruby. They can also return values if needed. Inside the instance method definition, you can access instance variables using @variable_name, which gives access to the data stored within the object instance.

Singletone methods

In Ruby, singleton methods are methods that are defined on individual objects rather than on the class itself. They are sometimes referred to as "singleton methods" because they are specific to a single object instance. Here's an example:

# Define a class
class Dog
end

 # Create an instance of the class
dog = Dog.new

 # Define a singleton method on this instance
def dog.bark
  puts "Woof!"
end

 # Call the singleton method
dog.bark  # Output: Woof!

Explanation:

  • We define a class Dog.
  • We create an instance of the Dog class using Dog.new.
  • We define a singleton method bark specifically on this instance dog. This means only this particular instance of Dog will have this method; other instances of Dog won't have it.
  • We call the singleton method bark on the dog instance, and it prints "Woof!".

Singleton methods can also be defined using the define_singleton_method method:

dog.define_singleton_method(:jump) do
  puts "Jumping!"
end

dog.jump  # Output: Jumping!

In this example, define_singleton_method is called on the dog instance to define a singleton method jump. This method will behave exactly like the previous example.

Redefining methods

class C
  def m
    puts "First definition of method m"
  end
  def m
    puts "Second definition of method m"
  end
end

C.new.m  # Output: Second definition of method m

Reopening classes

class C
  # class code here
end

class C
  def x
  end
end
class C
  def y
  end
end

equivalent:

class C
  def x
  end
  def y
  end
end

Instance variables and object state
Information and data associated with a particular object is called the state of
the object. We need to be able to do the following:

  • Set, or reset, the state of an object
  • Read back the state

Instance variables

The instance variable enables individual objects to remember state. Instance variables
work much like other variables: You assign values to them, and you read
those values back; you can add them together, print them out, and so on. However,
instance variables have a few differences.

  • Instance variable names always start with @ (the at sign). This enables you to recognize an instance variable at a glance.
  • Instance variables are only visible to the object to which they belong.
  • An instance variable initialized in one method definition, inside a particular class, is the same as the instance variable of the same name referred to in other method definitions of the same class.
class C
  def inst_var_init(value)
    puts "Setting an instance variable...."
    @ivar = value
  end
  def inst_var_report
    puts "Inspecting the value of the instance variable...."
    puts @ivar
  end
end
c = C.new
c.inst_var_init("Just some string")
c.inst_var_report

In Ruby, instance variables are used to store the state of an object. Each instance of a class has its own set of instance variables, and they are not shared among different instances of the same class. Here's an example to illustrate instance variables and object state:

class Person
  def initialize(name, age)
    @name = name
    @age = age
  end

  def introduce
    puts "Hello, my name is #{@name} and I'm #{@age} years old."
  end

  def birthday
    @age += 1
    puts "Happy birthday! Now I'm #{@age} years old."
  end
end

 # Creating an instance of the Person class
person1 = Person.new("Alice", 30)

 # Calling instance methods to access and modify object state
person1.introduce  # Output: Hello, my name is Alice and I'm 30 years old.
person1.birthday   # Output: Happy birthday! Now I'm 31 years old.

 # Creating another instance of the Person class
person2 = Person.new("Bob", 25)

 # The state of person2 is independent of person1
person2.introduce  # Output: Hello, my name is Bob and I'm 25 years old.

Explanation:

  • We define a Person class with an initialize method that sets the @name and @age instance variables.
  • The introduce method prints a message using the instance variables.
  • The birthday method increments the @age instance variable and prints a birthday message.
  • We create an instance person1 of the Person class with the name "Alice" and age 30. We then call introduce and birthday on this instance.
  • We create another instance person2 of the Person class with the name "Bob" and age 25. We then call introduce on this instance. The state of person2 is independent of person1. Instance variables are prefixed with @ and are accessible throughout the instance's scope, including within instance methods. They are used to store data that is specific to each instance of the class.

Initializing an object with state

In Ruby, you can initialize an object with state by defining an initialize method within the class. This method is automatically called when a new object is created using the new method. Here's an example:

class Person
  def initialize(name, age)
    @name = name
    @age = age
  end

  def introduce
    puts "Hello, my name is #{@name} and I'm #{@age} years old."
  end
end

 # Creating an instance of the Person class with initial state
person = Person.new("Alice", 30)

 # Calling instance methods to access the object state
person.introduce  # Output: Hello, my name is Alice and I'm 30 years old.

Explanation:

  • We define a Person class with an initialize method that sets the @name and @age instance variables.
  • The introduce method prints a message using the instance variables.
  • We create an instance person of the Person class with the name "Alice" and age 30 by calling Person.new("Alice", 30).
  • We call the introduce method on the person object to access its state. When you call Person.new("Alice", 30), Ruby automatically calls the initialize method with the provided arguments "Alice" and 30, setting the initial state of the object. This way, each instance of the Person class can have its own unique state.

Setter methods

In Ruby, setter methods are used to set the values of instance variables of an object. They are typically defined using the convention of appending an equal sign (=) to the method name. Setter methods allow you to control how values are assigned to object attributes and can include validation or additional logic.

Here's a basic example of a setter method in Ruby:

class Person
  def initialize(name)
    @name = name
  end

   # Setter method for the 'name' attribute
  def name=(new_name)
    @name = new_name
  end

   # Getter method for the 'name' attribute
  def name
    @name
  end
end

person = Person.new("John")
puts person.name  # Output: John

person.name = "Jane"  # This calls the setter method

In this example, name= is the setter method for the name attribute of the Person class. It allows you to change the value of the name attribute by assigning a new value to it. The setter method takes one argument (new_name) which is the new value to be assigned to the name attribute.

Setter methods provide a way to encapsulate the logic for assigning values to object attributes, allowing you to control access to those attributes and add validation or additional behavior if needed.

Attributes and the attr_* method family

In Ruby, the attr_* family of methods provide a convenient way to define getter and setter methods for object attributes. These methods help reduce boilerplate code by automatically creating accessor methods for instance variables. There are several variations of the attr_* methods:

  • attr_reader: Creates a getter method for the specified attribute.
  • attr_writer: Creates a setter method for the specified attribute.
  • attr_accessor: Creates both getter and setter methods for the specified attribute. Here's how you can use these methods:
class Person
   # Creates a getter method for the 'name' attribute
  attr_reader :name

   # Creates a setter method for the 'name' attribute
  attr_writer :name

   # Creates both getter and setter methods for the 'age' attribute
  attr_accessor :age

  def initialize(name, age)
    @name = name
    @age = age
  end
end

person = Person.new("John", 30)

 # Using getter method
puts person.name  # Output: John

 # Using setter method
person.name = "Jane"
puts person.name  # Output: Jane

 # Using getter and setter methods created by attr_accessor
puts person.age  # Output: 30
person.age = 35
puts person.age  # Output: 35

In this example, attr_reader :name creates a getter method for the name attribute, attr_writer :name creates a setter method for the name attribute, and attr_accessor :age creates both getter and setter methods for the age attribute.

Using attr_* methods provides a concise and readable way to define attributes and their accessor methods in Ruby classes.

Class methods and the Class class

Constants up close

Inheritance

Modules and program organization

The default object(self) and scope

A Deep Dive into the Ruby Object Model

0 Replies


Leave a replay

To replay you need to login. Don't have an account? Sign up for one.