An instance variable has a name beginning with @, and its scope is confined to whatever object self refers to. Two different objects, even if they belong to the same class, are allowed to have different values for their instance variables. From outside the object, instance variables cannot be altered or even observed (i.e., ruby's instance variables are never public) except by whatever methods are explicitly provided by the programmer.

As with globals, instance variables have the nil value until they are initialized. Instance variables of ruby do not need declaration. This implies a flexible structure of objects. In fact, each instance variable is dynamically appended to an object when it is first referenced.

Example of instance variable in Ruby:

                    
                      class InstanceVariableExample
                        def initialize
                          @name = 'Instance Variable'
                          @message = 'I am an instance variable of this class'
                        end

                        def display_name
                          puts @name
                        end

                        def display_message
                          puts @message
                        end
                      end

                      obj = InstanceVariableExample.new

                      obj.display_name
                      #=> Instance Variable

                      obj.display_message
                      # I am an instance variable of this class
                    
                  

In above example you can see that @name and @message starts with the @ symbol and both are the instance variable of the class mentioned in example. You can clearly see in the example that instance variables can be accessible inside any method of the class.

Ruby is a true object oriented programming language. Classes can be considered as blueprint or container for similar kind of objects. Classes allows us to create objects and methods which are used by objects of that class.

Creating a class is very simple in Ruby. It uses class keyword followed by name of the class. Look at the following example:

                    
                      class ExampleClass
                        #you can add some working code here  
                      end
                    
                  
Defining objects of class:

We can initialize object of a class by invoking new method on class.

                    
                      obj1 = ExampleClass.new
                      obj2 = ExampleClass.new
                    
                  

Here we have created two objects of ExampleClass.

Arrays are ordered, integer-indexed collections of any object. Array indexing starts at 0, as in C or Java. A negative index is assumed to be relative to the end of the array—that is, an index of -1 indicates the last element of the array, -2 is the next to last element in the array, and so on. Look the following example:

                    
                      arr = Array.new
                      arr << 'insert'
                      arr << 'elements'
                      arr << 'into'
                      arr << 'array'

                      puts arr
                      => ["insert", "elements", "into", "array"]
                    
                  

A Hash is a collection of key-value pairs. It is similar to an Array, except that indexing is done via arbitrary keys of any object type, not an integer index. Hashes enumerate their values in the order that the corresponding keys were inserted.

Hashes have a default value that is returned when accessing keys that do not exist in the hash. By default, that value is nil.

                    
                      h = Hash.new
                      h[:name] = 'Lorem Ipsum'
                      h[:email] = 'email@example.com'
                      h[:phone_number] = '1234567890'

                      puts h
                      {:name=>"Lorem Ipsum", :email=>"email@example.com", :phone_number=>"1234567890"}
                    
                  

Cheers!!!

Look at the following snippets:

                    
                      {}.empty?
                      => true
                    
                  

Because the hash object has no key-value pair in it hance it is empty.

                    
                      {}.present?
                      => false
                    
                  

Same reason as above. But the main difference between both methods is that empty? can be used in both Ruby and Rails but present? can be used in Rails only. For verifying this try using present? in two different consoles.

When you try present? with rails console as:

                    
                      {}.present?
                      => false
                    
                  

But when you try present? with irb console, you will get an error::

                    
                      {}.present?
                      => NoMethodError (undefined method `present?' for {}:Hash)
                    
                  

Distructive array methods are the methods which can change the original array once invoking on array. Distructive methods always used using the ! symbol.

Look at the following snippet:

                    
                      a = [5,4,3,2,1]
                      => [5,4,3,2,1]

                      a.sort!
                      => [1,2,3,4,5]

                      puts a
                      => [1,2,3,4,5]
                    
                  

On the other hand Non-distructive methods will only change the output but not the original array.

                    
                      a = [5,4,3,2,1]
                      => [5,4,3,2,1]

                      a.sort
                      => [1,2,3,4,5]

                      puts a
                      => [5,4,3,2,1]
                    
                  

sort method is only example, you can try with other built in array methods too provided by ruby. To check all built in array methods provided by ruby you can do following:

                    
                      arr = Array.new
                      => []
                      arr.methods
                      => [:to_h, :include?, :&, :*, :+, :-, :at, :fetch, :last, :union, :difference, :intersection, :push, :append, :pop, :shift, :unshift, :each_index, :join, :rotate, :rotate!, :sort!, :sort_by!, :collect!, :map!, :select!, :filter!, :keep_if, :values_at, :delete_at, :delete_if, :reject!, :transpose, :fill, :assoc, :rassoc, :uniq!, :compact, :compact!, :flatten, :flatten!, :shuffle!, :shuffle, :sample, :permutation, :combination, :repeated_permutation, :repeated_combination, :product, :bsearch, :sort, :bsearch_index, :deconstruct, :count, :find_index, :select, :filter, :reject, :collect, :map, :first, :all?, :any?, :one?, :none?, :minmax, :|, :reverse_each, :zip, :take, :take_while, :drop, :<=>, :<<, :cycle, :drop_while, :==, :sum, :uniq, :[], :[]=, :insert, :empty?, :eql?, :index, :rindex, :replace, :clear, :max, :min, :inspect, :length, :size, :each, :reverse, :concat, :prepend, :reverse!, :to_ary, :to_a, :to_s, :delete, :pack, :slice, :slice!, :dig, :hash, :each_slice, :each_cons, :each_with_object, :chunk, :slice_before, :slice_after, :slice_when, :chunk_while, :chain, :to_set, :lazy, :find, :entries, :sort_by, :grep, :grep_v, :detect, :find_all, :filter_map, :flat_map, :collect_concat, :inject, :reduce, :partition, :group_by, :tally, :min_by, :max_by, :minmax_by, :member?, :each_with_index, :each_entry, :dup, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :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?, :tap, :class, :display, :singleton_class, :clone, :public_send, :method, :public_method, :singleton_method, :define_singleton_method, :extend, :to_enum, :enum_for, :===, :=~, :!~, :nil?, :respond_to?, :freeze, :object_id, :send, :__send__, :!, :!=, :equal?, :__id__, :instance_eval, :instance_exec]
                    
                  

NOTE: You can only try with the collection methods like sort, flatten etc.

Ruby is a true object oriented programming language which comes with various features. Some of those key features are listed below:

  • Simple syntax
  • Basic OO features (classes, methods, objects, and so on)
  • Special OO features (mixins, singleton methods, renaming, and so on)
  • Operator overloading
  • Exception handling
  • Iterators and closures
  • Garbage collection
  • Dynamic loading (depending on the architecture)
  • High transportability (runs on various Unices, Windows, DOS, macOS, OS/2, Amiga, and so on)

attr_reader works as a getter method in Ruby. When you use this you can only read/get the value of an attribute. But when you will try to update its value this will raise an exception.

Look at the following example:

                    
                      class GetterSetter
                        attr_reader :name

                        def initialize()
                          @name = 'Lorem Ipsum'
                        end
                      end

                      obj = GetterSetter.new

                      # This will print the value of name and will be executed sussessfully
                      puts obj.name
                      => "Lorem Ipsum"

                      # When you execute this line this will raise an exception
                      obj.name = 'Another Name'
                      => NoMethodError (undefined method `name=' for #)
                      Did you mean?  name
                    
                  

hence attr_reader will work as getter method only.

attr_writer works as a setter method in Ruby. When you use this you can only write/change the value of an attribute. But when you will try to read its value this will raise an exception.

Look at the following example:

                    
                      class GetterSetter
                        attr_writer :name

                        def initialize()
                          @name = 'Lorem Ipsum'
                        end
                      end

                      obj = GetterSetter.new

                      # This will change the value of name and will be executed sussessfully
                      obj.name = 'Another Name'
                      => "Another Name"

                      # When you execute this line this will raise an exception
                      puts obj.name
                      => NoMethodError (undefined method `name' for #)
                      Did you mean?  name=
                    
                  

hence attr_writer will work as getter method only.

attr_accessor will work as both getter and setter. When you use this you can read/get the value of attribute and change its value as well.

Look at the following example:

                    
                      class GetterSetter
                        attr_accessor :name

                        def initialize()
                          @name = 'Lorem Ipsum'
                        end
                      end

                      obj = GetterSetter.new

                      # This will print the value of name and will be executed sussessfully
                      puts obj.name
                      => "Lorem Ipsum"

                      # This will change the value of name and will be executed sussessfully
                      obj.name = 'Another Name'
                      => "Another Name"
                    
                  

Now the main confusion is that when we should use which of these three. So the answer is very simple that if you only need to read the value of attributes you need to use attr_reader and when you need to change the value of attribute you must use attr_writer. But when you need both you should use attr_accessor.

Modules are a way of grouping together methods, classes, and constants. Modules give you two major benefits. Modules provide a namespace and prevent name clashes and they also implement the mixin facility.

The main difference between classes and modules is that classes provide more ownership rather than objects. A class in Ruby can be instantiated but a standard module could not. Modules can used inside classes. Look atthe following example:

                    
                      module Greetings
                        def welcome
                          puts 'Welcome to Ruby world.'
                        end

                        def hello
                          puts 'Hello! I am module.'
                        end
                      end

                      class Example
                        include Greetings
                      end

                      obj = Example.new

                      obj.welcome
                      => "Welcome to Ruby world."
                      obj.hello
                      => "Hello! I am module."
                    
                  

There are four ways to invoke a method in Ruby.

  • Using dot operator(.)
  • Using send
  • Using method(method_name).call
  • Using double colon operator(::)

Let's see following example:

                    
                      arr = Array.new
                      => []

                      arr.object_id
                      => 22020

                      arr.send(:object_id)
                      => 22020

                      arr.send('object_id')
                      => 22020

                      arr.method(:object_id).call
                      => 22020

                      arr.method('object_id').call
                      => 22020

                      arr::object_id
                      => 22020
                    
                  

You can invoke any other method just like above on objects of any class.

self is the keyword in Ruby that gives you the access to the current object. self can also be used for defining class level methods.

Look at the example below:

                    
                      class Fish
                        def display_self
                          puts self
                        end
                      end

                      Fish.new.display_self
                      => #

                      class Fish
                        # using self for accessing current object
                        def display_self
                          puts self
                        end

                        # using self for defining class methods
                        def self.display_self
                          puts self
                        end
                      end

                      Fish.new.display_self
                      => #

                      Fish.display_self
                      => Fish
                    
                  

self is also known as default receiver of method calls.

Getter methods are used to retrieve values of instance variables of an object. It is common practice to have the getter method’s name match the instance variable’s name. Below is the example of creating a getter method in Ruby.

                    
                      class Example
                        # initializing the name instance variable
                        def initialize(name)
                          @name = name
                        end

                        # getter method
                        def name
                          @name
                        end
                      end

                      obj = Example.new('Lorem Ipsum')
                      puts obj.name
                      => "Lorem Ipsum"
                    
                  

Setter methods are used to change/write the value of instance variables of an object. Below is the example of creating a setter method in Ruby.

                    
                      class Example
                        # initializing the name instance variable
                        def initialize(name)
                          @name = name
                        end

                        # setter method
                        def name=(value)
                          @name = value
                        end
                      end

                      obj = Example.new('Lorem Ipsum')
                      obj.name = 'Another Name'
                    
                  

Putting it all together

                    
                      class Example
                        # initializing the name instance variable
                        def initialize(name)
                          @name = name
                        end

                        # getter method
                        def name
                          @name
                        end

                        # setter method
                        def name=(value)
                          @name = value
                        end
                      end

                      obj = Example.new('Lorem Ipsum')
                      obj.name = 'Another Name'
                      puts obj.name
                      => "Another Name"
                    
                  

Things to notice

Instead of writing getter and setter method separetly you can also use ruby keywords attr_reader, attr_writer for getting functions of getter and setter methods. attr_reader will work as getter method and attr_writer will work as setter method. There is one more alternative to use more shorter form that is attr_accessor. If you use this this will work as both getter ans setter.

Let's have a look:

                    
                      class Example
                        attr_reader :name
                        
                        def initialize(name)
                          @name = name
                        end
                      end

                      obj = Example.new('Lorem Ipsum')
                      puts obj.name
                      => "Lorem Ipsum"
                    
                  

This will work same as the class mentioned for getter method example.

                    
                      class Example
                        attr_writer :name
                        
                        def initialize(name)
                          @name = name
                        end
                      end

                      obj = Example.new('Lorem Ipsum')
                      obj.name = 'Another Name'
                    
                  

This will work same as the class mentioned for setter method example.

                    
                      class Example
                        attr_accessor :name
                        
                        def initialize(name)
                          @name = name
                        end
                      end

                      obj = Example.new('Lorem Ipsum')
                      obj.name = 'Another Name'
                      puts obj.name
                      => "Another Name"
                    
                  

This will work same as the class mentioned for both getter and setter example.

Compact will remove nil elements from array if there is any. Look at the following example:

                    
                      arr = [1, 2, 3, nil, 4, 5, nil, 6, 7, 8, nil]
                    
                  

This array has three nil elements. Now see the result of calling compact method on this array, you will get following example:

                    
                      arr = [1, 2, 3, nil, 4, 5, nil, 6, 7, 8, nil]
                      arr.compact
                      => [1, 2, 3, 4, 5, 6, 7, 8]
                    
                  

Now the task for you is to create a method which should return the same output as the compact method returns.

                    
                      arr = [1, 2, 3, nil, 4, 5, nil, 6, 7, 8, nil]

                      def custom_compact(arr)
                        temp_arr = []
                        arr.map {|i| temp_arr << i unless i.nil? }
                        temp_arr
                      end

                      custom_compact(arr)
                      => [1, 2, 3, 4, 5, 6, 7, 8]
                    
                  

This is how you can create your own compact method for removing nil elements from any given array. There are so many in built array methods which you might be asked during interview so you must try such logical things.

Cheers!!!

MVC(Model-View-Controller) is a software design pattern and this is conventionally followed by Rails. Let's briefly define each component of MVC.

Model: Model is the layer of the system responsible for representing business data and logic. In Rails models facilitates the creation and use of business objects whose data requires persistent storage to a database.

View: This represents the presentation layer in MVC pattern. Model data is populated on views so that users can see this data. View is also used to make requests for data, to perform certain actions like CRUD by users. In Rails whenever a user perform any action on view or make a request to retrive or submit data, this is first goes to controller via rails router and then appropriate controller gets loaded and then controller serves the requested actions.

Controller: Controller works as the mediater between Model and View, it makes the model data available to the view so it can display that data to the user, and it saves or updates user data to the model. Once a request from the view has been made by users, this is first received by the router and after the router has determined which controller to use for a request, the controller is responsible for making sense of the request, and producing the appropriate output.

In Rails, a controller has three possible ways to create HTTP response:

  • Call render to create a full response to send back to the browser. This is mainly used while retriving information for any single orcollection of objects. This can be also be used for static pages as well.
  • Call redirect_to to send an HTTP redirect status code to the browser. This is mainly used while submitting data for create or update a record or delete a record from database.
  • Call head to create a response consisting solely of HTTP headers to send back to the browser.

With strong parameters, Action Controller parameters are forbidden to be used in Active Model mass assignments until they have been permitted.

The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for passing error messages, etc.

The flash is accessed via the flash method. Like the session, the flash is represented as a hash.

Shallow nesting is useful when you want to avoid deep level of nesting of resources. Shallow routes generate the collection actions scoped under the parent, so as to get a sense of the hierarchy, but to not nest the member actions. In other words, to only build routes with the minimal amount of information to uniquely identify the resource.

For example look at the example below:

                    
                      resources :articles do
                        resources: :comments
                      end
                    
                  

Will generate routes for comments like this:

A Rails Concern is any module that extends ActiveSupport::Concern module. Consider the following example concern which has some class methods implemented:

                    
                    module Scoppable
                      extend ActiveSupport::Concern

                      class_methods do 
                        def created_in_current_month
                          start_date = DateTime.current.beginning_of_month
                          end_date = start_date.end_of_month
                          results(start_date, end_date)
                        end

                        def created_in_last_n_months(n)
                          start_date = (DateTime.current - n.months).beginning_of_month
                          end_date = start_date.end_of_month
                          results(start_date, end_date)
                        end

                        def results(sd, ed)
                          where('created_at BETWEEN ? AND ?', sd, ed)
                        end  
                      end

                    end 
                    
                  

Now you can use this concern into any of your model class. For example I am using this module into a model named Project.

                    
                      class Project < ApplicationRecord
                        include Scoppable
                      end
                    
                  

Now when I run following query:

                    
                      Project.created_in_current_month
                    
                  

this will return all the projects created in current month.

And when I run following query:

                    
                      Project.created_in_last_n_months(10)
                    
                  

this will return all the projects created in last 10 month.

This is just an example and you can implement such concerns as per your needs.