Learning Ruby series 2 of 5

Ruby Objects and Methods

We will continue to use irb to learn the concepts of Objects and Methods in ruby.

What is an Object? A more practical way of understanding what an object is is by its behavior. And Method is a way to learn more about an object. Object is the default root of all Ruby objects. It inherits from the BaseObject. Ruby Objects are mixed-in with the Kernel and so built-in functions are accessible globally — from any class or module. If it is still abstract, let us go to our irb and play around.

>> Object # Type Object
=> Object # returns Object
# Now let us understand Object by communicating with the method # ancestors, and we see the Object Hierarchy
>> Object.ancestors #ancestors
=> [Object, Kernel, BasicObject]

A handy way of inspecting methods available on Object

# Calling the methods method on the Object comes back with the list # of behaviors associated with the "Object". We will experiment with # some of the methods in the coming sections
>> Object.methods.sort
=> [:!, :!=, :!~, :<, :<=, :<=>, :==, :===, :=~, :>, :>=, :__id__, :__send__, :alias_method, :allocate, :ancestors, :attr, :attr_accessor, :attr_reader, :attr_writer, :autoload, :autoload?, :class, :class_eval, :class_exec, :class_variable_defined?, :class_variable_get, :class_variable_set, :class_variables, :clone, :const_defined?, :const_get, :const_missing, :const_set, :const_source_location, :constants, :define_method, :define_singleton_method, :deprecate_constant, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :include, :include?, :included_modules, :inspect, :instance_eval, :instance_exec, :instance_method, :instance_methods, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_of?, :method, :method_defined?, :methods, :module_eval, :module_exec, :name, :new, :nil?, :object_id, :prepend, :private_class_method, :private_constant, :private_instance_methods, :private_method_defined?, :private_methods, :protected_instance_methods, :protected_method_defined?, :protected_methods, :public_class_method, :public_constant, :public_instance_method, :public_instance_methods, :public_method, :public_method_defined?, :public_methods, :public_send, :remove_class_variable, :remove_instance_variable, :remove_method, :respond_to?, :send, :singleton_class, :singleton_class?, :singleton_method, :singleton_methods, :superclass, :taint, :tainted?, :tap, :then, :to_enum, :to_s, :trust, :undef_method, :untaint, :untrust, :untrusted?, :yield_self]

Let us continue to explore the concept of Object

>> num = 4 # create a variable num, and assign it a value of 4
=> 4
# Now num should technically be a Ruby Object, which means the #object methods listed above should be available to communicate with # num. Let us explore with some of those available methods
>> num.object_id #As a object, num has a unique id assigned
=> 9
>> num1 = 5
=> 5
>> num1.object_id
=> 11 # unique id for variable object num1
>> num1 <=> num # the <=> comparison operator and its results
=> 1 # evaluated to num1 > num
>> num1 <=> 8
=> -1 # evaluated to num1 < 8
>> num1 <=> 5
=> 0 # evaluated to num1 = 5

A typical construct for Ruby method is shown below:

# let us take a simple add to numbers example:
>> 2 + 3
=> 5
# Let us create a object and provide it a behavior
>> obj = Object.new
=> #<Object:0x0000556f8ba9a150>

Once the object is created, we can associate methods to the object. The object’s behavior is modeled based on the functionality inlcuded with the method.

# The object has been created, not let us define a method to it
# def keyword below is the start of the method construct
# (x,y) are data inputs(args) to the method on the object
?> def obj.add_two_nums(x,y) # def is the start of the construct
?> result = x + y # the body defines method behavior
?> end # end keyword for the method
=> :add_two_nums # the method was successfully created
>> obj.add_two_nums(2,3) # send a message to obj method
=> 5

Once the method has been defined, and we query the methods available on the object, we will see that new method has been added to its built-in list

# see below the new method created is now added to the default list that obj derived from its instantiation
>> obj.method

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

A method can be created without necessarily attaching to an object.

?> def add_two_numbers(x,y)
?> x + y
>> end
=> :add_two_numbers
>> add_two_numbers(2,3)
=> 5

Method arguments are flexible. Let us get back and play around in irb. Let us start with a method having a strict argument definition

# defining arguments below makes both a and b as required
?> def arg_required_example(a,b)
?> puts a, b
?> end
=> :arg_required_example
>> arg_required_example(1,2)
=> nil
>> arg_required_example(1,2,3) # trying to pass a third arg
ArgumentError (wrong number of arguments (given 3, expected 2))
# Real clear feedback on what went wrong!

Having a strict argument requirement has its advantages and disadvantages. Depending on the use case/scenario we may want the ability to have flexibility in defining the method’s argument structure

# Flexible arg definition
?> def arg_flex_example(a,*b,c) # the * before b makes it flexible
?> p a, b, c
?> end
=> :arg_flex_example
>> arg_flex_example(1,2,3,4,5,6) # sending more than 3 args
[2, 3, 4, 5]
=> [1, [2, 3, 4, 5], 6]
>> arg_flex_example(1,2) # sending fewer args
=> [1, [], 2]

Another common variation that we come across while defining a method is one where we want a strict argument definition, but if no value is received, then the method defaults to a pre-defined acceptable value

# Default argument to method if no argument is passed
?> def arg_default_example(x=1) #x=1 defaults the arg value to 1
?> p x
?> end
=> :arg_default_example
>> arg_default_example # invoking method without arg
1 # method defaults to using default arg value
=> 1
>> arg_default_example(2)
=> 2

Summary and Closeout

This was a quick tour of some of the key features of an Object and method types. There is a lot more to explore but with irb handy, and the visibility to the various built-in methods, we can continue experimenting within the irb.

We will now move onto learning about how Class and Module helps with Ruby implementing OOP and code organization. We will look at additional types of methods/functions within a class — like instance, protect and private.