Introduction
Before going on to the main topic, let us see a basic definition of class and object in ruby. The main advantage of using classes is that they allow us to write methods and variables that can be reused by many objects. In Ruby, we have “class” as the keyword to create any class with any properties. Classes are simply a clone of objects. Any class can contain methods, constants, etc., and once we create the object from the class, then that object contains all the properties of the class.
Attributes and Object
Attributes are just the properties of system objects. The entity and attributes of an object can describe the status of a real-world object and the action associated with that item. The attributes are classified into three types. They are as follows:
- Simple attributes: primitive data types such as numbers, boolean, strings, arrays, and so on can have literal values. These primitive data types are simple attributes of an object.
- Complex attributes: Complex attributes are characteristics that consist of collections or references to other numerous objects.
- Reference attributes: Reference attributes are characteristics that represent a relationship between objects and consist of a value or set of values.
Checking Whether an Object has Necessary Attributes
We often come across a problem while creating a class or module that delegates the creation of some of its instance variables to a hook function, and want to ensure that the hook method actually created those instance variables.
To ensure the creation of instance variables we use the Object#instance_variables method. This method gives us the list of all the instance variables that are declared within the class. So we can go through the list of variables whether we have defined it or not.
We can call the below-shown Object#must_have_instance_variables method any time in the programme.
class Object
def must_have_instance_variables(*args)
vars = instance_variables.inject({}) { |h,var| h[var] = true; h }
args.each do |var|
unless vars[var]
raise ArgumentError, %{Instance variable "@#{var} not defined"}
end
end
end
end
The following method should be called in initialize or another module setup method. Alternatively, you may take instance variable values as arguments to the setup method as shown below:
module LightEmitting
def LightEmitting_setup
must_have_instance_variables :light_color, :light_intensity
@on = false
end
# Methods that use @light_color and @light_intensity follow...
end
To ensure that subclasses use the setup method appropriately, you can use the following method from a class that provides a virtual setup method:
class Request
def initialize
gather_parameters # This is a virtual method defined by subclasses
must_have_instance_variables :action, :user, :authentication
end
# Methods that use @action, @user, and @authentication follow...
end
Although x is defined and invoked like any other method, it is a "decorator" method in the same way as att_accessor and private are. That's why, even though we called it with several arguments, we didn't use parenthesis in the preceding example. The absence of parentheses indicates that you're calling a decorator method, which changes or inspects a class or object.
Here's another way that can be used from outside the object. It essentially implements a batch form of duck typing: rather than checking an object's instance variables (which are only available within the object), it checks whether the object supports all of the methods you need to call on it. It's handy for determining whether an object is the "shape" you expect:
class Object
def must_support(*args)
args.each do |method|
unless respond_to? method
raise ArgumentError, %{Must support "#{method}"}
end
end
end
end
obj = "a string"
obj.must_support :to_s, :size, "+".to_sym
obj.must_support "+".to_sym, "-".to_sym
# ArgumentError: Must support "-"