### Case equality operator (===)
if the right operand has an IS A relationship with the left
operand. As such, the popular name case equality operator is misleading.
This SO answer describes it thus: the best way to describe a === b is "if I have a drawer labeled a, does it make
sense to put b in it?" In other words, does the set a include the member b?
```
(1..5) === 3 => true
(1..5) === 6 => false
Integer === 42
=> true
Integer === 'fourtytwo' # => false
/ell/ === 'Hello'
/ell/ === 'Foobar'
=> true
=> false
```
--------
### 1. Lambdas:
Strict Argument Checking:
Lambdas are strict about the number of arguments you pass to them. If you pass the wrong number of arguments, they throw an ArgumentError.
```
lamb = lambda { |x, y| puts "#{x} and #{y}" }
lamb.call(1, 2) # => "1 and 2"
lamb.call(1) # => ArgumentError: wrong number of arguments (given 1, expected 2)
# Using stabby lambda syntax
add = ->(a, b) { a + b }
puts add.call(2, 3) # Output: 5
# Using lambda keyword
multiply = lambda { |a, b| a * b }
puts multiply.call(2, 3) # Output: 6
```
### 2. Procs:
Flexible Argument Checking:
Procs are more lenient with the number of arguments you pass to them. If you pass fewer arguments than expected, the missing ones are set to nil. If you pass more, the extras are ignored.
```
pr = Proc.new { |x, y| puts "#{x.inspect} and #{y.inspect}" }
pr.call(1, 2) # => "1 and 2"
pr.call(1) # => "1 and nil"
pr.call(1, 2, 3) # => "1 and 2"
# Defining a Proc
square = Proc.new { |x| x * x }
puts square.call(4) # Output: 16
# Proc doesn't check the number of arguments
puts square.call # Output: nil
```
These differences underline the importance of choosing the right tool for the job. If you want strict arity (i.e., strict number of arguments) checking, use a lambda. If you want flexibility with the number of arguments, use a proc.
-----
### 3. destroy vs delete:
#### destroy:
Calls the model's before_destroy and after_destroy callbacks and any associated callbacks.
Objects are instantiated, which can have some overhead.
Will also destroy dependent associations.
```
user = User.find(1)
user.destroy
```
#### delete:
Skips callbacks.
Directly deletes the record in the database without instantiating the object.
Doesn't trigger dependent destruction.
```
user = User.find(1)
user.delete
```
#### Block:
A chunk of code that can be passed to a method.
```
[1,2,3].each { |num| puts num }
```
### Inject Vs Reduce
inject and reduce can be used with shorthand operators for common operations like addition, multiplication, etc. This makes the code more concise.
#### Inject
```
result = [1, 2, 3, 4].inject(:+)
puts result # Output: 10
result = [1, 2, 3, 4].inject(:*)
puts result # Output: 24
result = [1, 2, 3, 4].inject(10, :+)
puts result # Output: 20 (10 + 1 + 2 + 3 + 4)
```
#### Reduce
```
result = [1, 2, 3, 4].reduce(:+)
puts result # Output: 10
result = [1, 2, 3, 4].reduce(:*)
puts result # Output: 24
result = [1, 2, 3, 4].reduce(2, :*)
puts result # Output: 48 (2 * 1 * 2 * 3 * 4)
```
### Optimistic vs Pessimistic Locking:
#### Optimistic Locking:
Assumes conflicts will rarely occur.
When saving, checks if the record has been updated by another transaction.
If it has, raises a conflict (e.g., ActiveRecord::StaleObjectError in Rails).
#### Pessimistic Locking:
Locks the record for the duration of the transaction.
Other transactions are blocked until the lock is released.
Can prevent data inconsistencies but can lead to deadlocks.
### Rails directory structure:
**app/**: This is the heart of any Rails application. It's where you'll find models, views, controllers, and other core components of the application's functionality.
**app/assets/**: Contains subdirectories for JavaScripts, stylesheets, and images. This is where the asset pipeline will look for assets.
**app/controllers/**: This is where your application controllers live. Controllers handle the web requests, manage logic, and hand off to the views to render data.
**app/helpers/**: Helper modules are meant to provide methods that assist with rendering views.
**app/mailers/**: This is where mailer classes live, responsible for sending emails.
**app/models/**: This is where you'll place your ActiveRecord models - classes that represent tables in the database and where you handle related business logic.
**app/views/**: Contains the templates that will be used to render the views. They're organized based on the controller they belong to.
**app/channels/**: Used for Action Cable setup for real-time features.
**app/jobs/**: Place for background jobs, which can be executed asynchronously.
**app/config/**: Contains locale files and initializers.
**bin/**: Contains Rails script files that help with various tasks (e.g., starting a server or console).
**config/**: Contains configuration files:
**config/routes.rb**: Defines the routes for your application.
**config/application.rb**: General configuration settings.
**config/environments/**: Settings specific to development, production, and test environments.
**config/initializers/**: Initialization code for various libraries and gems.
**config/database.yml**: Database configuration.
**db/**: Contains everything related to the database:
**db/migrate/**: Contains all the migration files for altering the database schema.
**db/schema.rb**: Represents the current state of the database schema.
**log/**: Contains application log files.
**public/**: Files in this directory are served directly by the web server. For example, error pages like 404.html are found here.
**test/ or spec/**: Contains tests for your application. Whether it's "test" or "spec" often depends on the testing framework you're using (e.g., Minitest vs RSpec).
**tmp/**: Temporary files (e.g., cache, session, sockets).
**vendor/**: A place for third-party code. In a typical Rails application, you might use gems instead, but sometimes you might want to vendor certain libraries.
**.gitignore**: A file used by Git to exclude certain files and directories from the repository.
**Gemfile and Gemfile.lock**: These files allow you to specify which gems (libraries) your application depends on. The Gemfile.lock is an auto-generated file that ensures all developers on a project are using the exact same gem versions.
**Rakefile**: Provides tasks that can be run from the command line using the rake command. Rails comes with many built-in rake tasks for things like database migrations.
**README.md**: A markdown file where you should describe your project, how to set it up, and other pertinent information.
### PUT VS PATCH
#### PUT:
Idempotent: Making the same PUT request multiple times will have the same effect as making it once. This implies that a PUT request is expected to provide a full representation of the resource being updated.
Definition: PUT is used to update or create a resource at a particular URI. If the resource exists, it's replaced. If it doesn't exist, it's created (if the server supports doing so).
Usage: Given that PUT is idempotent, it typically requires the client to send the entire updated representation of the resource. If you only send a partial update to a PUT endpoint, it might either result in an error or in incomplete data for the resource, depending on the API's design.
```
PUT /users/123
{
"id": 123,
"name": "John Doe",
"email": "johndoe@example.com",
"age": 30
}
```
**Even if only the age field changed, you'd typically send the entire user object to the PUT endpoint.**
#### PATCH:
Not Necessarily Idempotent: While it can be implemented in an idempotent manner, PATCH requests by nature do not guarantee idempotency. This is because PATCH is intended to provide partial updates to a resource.
Definition: PATCH is used to apply partial modifications to a resource.
Usage: Unlike PUT, which requires the entire resource data, PATCH requires only the changes. This makes PATCH more efficient when you only need to update a small part of a resource.
Example: If you're only updating the age of a user:
```
PATCH /users/123
{
"age": 31
}
```
**Here, you only send the age attribute with its new value.**
##### Key Takeaways:
Use PUT when you're replacing a resource entirely or providing a full representation of the resource.
Use PATCH when you're making partial updates to a resource.
### Cron:
A cron is a Unix-based job scheduling program that allows users to run scripts, commands, or software at specified times and dates. The name "cron" comes from the Greek word "chronos", which means "time", and it's used to automate tasks like backups, sending emails, or any repetitive job you can think of.
#### Crontab:
crontab (short for "cron table") is the command used to view, add, remove, or modify cron jobs for a user. The crontab command interfaces with the cron daemon, managing the user's job tables.
```
* * * * * command_to_be_executed
- - - - -
| | | | |
| | | | +----- day of the week (0 - 6) [0 is Sunday, or use names]
| | | +------- month (1 - 12)
| | +--------- day of the month (1 - 31)
| +----------- hour (0 - 23)
+------------- min (0 - 59)
```
The **crontab -l** command is used to list all the cron jobs for the currently logged-in user.
Let's say you want to schedule a backup script, backup.sh, to run every day at 3:30 AM.
```
30 3 * * * /path/to/backup.sh
```
If you wish to run a Python script, data_cleanup.py, every Monday at 5:45 PM:
```
45 17 * * 1 /usr/bin/python3 /path/to/data_cleanup.py
```
### Use Cases:
**Backups**: Automate database or file system backups daily, weekly, or at any desired frequency.
**Reports**: Generate and email weekly sales reports, performance metrics, etc.
**Housekeeping**: Clear out temporary files or cache directories periodically to free up space.
**Notifications**: Send out reminders, e.g., for expiring subscriptions, upcoming appointments, or system metrics.
**Updates**: Automatically update system software or databases.
**Monitoring**: Check the health status of services or websites at regular intervals and alert if they're down
### Modules:
In Ruby, a module is a collection of methods, constants, and class variables. Modules are not classes and can't create instances. They're used for namespacing and mixins (injecting functionality into classes).
```
module Greetable
def greet
"Hello, #{self.name}"
end
end
class User
include Greetable
attr_accessor :name
def initialize(name)
@name = name
end
end
user = User.new("Alice")
puts user.greet # Outputs: Hello, Alice
```
### Concerns:
In Rails, a concern is a specialized module that not only allows you to group related model (or controller) methods but also allows you to add class-level methods and scopes. It's Rails' way to make model or controller code more modular and organized.
```
# app/models/concerns/taggable.rb
module Taggable
extend ActiveSupport::Concern
included do
has_many :tags
end
def tag_list
tags.map(&:name).join(", ")
end
class_methods do
def most_tagged
# Some code to find the most tagged instance
end
end
end
# app/models/post.rb
class Post < ApplicationRecord
include Taggable
end
```
### Use Cases:
**Modules**: When you want to share functionality between classes or prevent name clashes by namespacing.
**Concerns**: When your Rails models or controllers become bulky, and you want to extract and group related functionalities into separate modules. They also allow for a cleaner way to extend Active Record with both instance and class methods.
### Save an object without callbacks:
In Rails, Active Record callbacks allow you to attach code to certain lifecycle events of an object, like before it's saved, after it's created, etc. However, there might be situations where you want to save an object without triggering these callbacks.
Method: update_column and update_columns
update_column and update_columns methods allow you to update attributes of an object directly in the database, skipping validations and callbacks.
```
class User < ApplicationRecord
before_save :set_default_username
def set_default_username
self.username ||= "default_username"
end
end
user = User.find(1)
user.update_column(:username, 'new_username')
# or for multiple columns
# user.update_columns(username: 'new_username', email: 'new_email@example.com')
```
### Rake vs Rack
#### Rake:
It's a build program written in Ruby.
Used for tasks like database setup, cleanup, and general application maintenance tasks.
Rakefile in a Rails project defines tasks which can be executed.
#### Rack:
It's an interface between web servers and Ruby frameworks, including Rails.
Provides a minimal API to connect web servers that support Ruby.
A Rails application is actually a Rack application under-the-hood.
### Use Cases:
#### Rake:
Running database migrations.
Clearing temporary files.
Running custom developer-defined tasks.
#### Rack:
Middleware management.
Connecting Rails to servers like Puma, Unicorn, etc.
Building micro-frameworks or small web applications.
### Application Server vs Web Server
#### Application Server (like Puma, Unicorn):
Runs your Rails application.
Handles Ruby code execution.
Can serve multiple requests simultaneously, depending on the configuration (threaded, multi-process, etc.).
Typically slower at serving static files than a web server.
#### Web Server (like Nginx, Apache):
Can serve static files very efficiently.
Can handle SSL/TLS termination, load balancing, and more.
In a typical Rails deployment, sits in front of the application server, forwarding dynamic requests to it.
##### Use Cases:
###### Application Server:
Running the Ruby on Rails code.
Serving dynamic content.
###### Web Server:
SSL/TLS termination.
Serving static assets like images, stylesheets, and JavaScript files.
Load balancing between multiple application server instances.
Caching content for faster response times.
### has_and_belongs_to_many Table Architecture
The has_and_belongs_to_many association creates a direct many-to-many connection between models without an explicit intermediary model. To implement it, a join table is required, and by convention, the join table's name is the union of the participating model tables in alphabetical order.
Suppose we have a students and courses table, and we want to establish a many-to-many relationship between them:
```
rails generate migration CreateJoinTableStudentsCourses students courses
class CreateJoinTableStudentsCourses < ActiveRecord::Migration[6.0]
def change
create_join_table :students, :courses do |t|
t.index [:student_id, :course_id]
t.index [:course_id, :student_id]
end
end
end
# student.rb
class Student < ApplicationRecord
has_and_belongs_to_many :courses
end
# course.rb
class Course < ApplicationRecord
has_and_belongs_to_many :students
end
```
Many developers prefer to use has_many :through instead of has_and_belongs_to_many because it offers more flexibility by allowing you to work with the relationship model directly, adding validations, callbacks, or extra attributes to the join model.
### **Flatten Method logic**
```
def custom_flatten(array, result = [])
array.each do |element|
if element.is_a?(Array)
custom_flatten(element, result)
else
result << element
end
end
result
end
# Test the custom_flatten method
nested_array = [1, [2, [3, 4]], [5, 6]]
flattened_array = custom_flatten(nested_array)
puts flattened_array.inspect # Output should be [1, 2, 3, 4, 5, 6]
```
### MetaProgramming Examples
This ability to define and alter the structure and behavior of objects and classes during execution is one of Ruby's powerful features.
#### Send() Method
send() is used to pass message to object. send() is an instance method of the Object class.
The first argument in
send() is the message that you're sending to the object - that is, the name of a method.
It could be string or
symbol but symbols are preferred. Then arguments those need to pass in method, those will be the remaining
arguments in send().
```
class Hello
def hello(*args)
puts 'Hello ' + args.join(' ')
end
end
h = Hello.new
h.send :hello, 'gentle', 'readers'
#=> "Hello gentle readers"
# h.send(:hello, 'gentle', 'readers') #=> Here :hello is method and rest are the arguments to method.
```
```
class Account
attr_accessor :name, :email, :notes, :address
def assign_values(values)
values.each_key do |k, v|
# How send method would look a like
# self.name = value[k]
self.send("#{k}=", values[k])
end
end
end
user_info = {
name: 'Matt',
email: 'test@gms.com',
address: '132 random st.',
notes: "annoying customer"
}
account = Account.new
If attributes gets increase then we would messup the code
#--------- Bad way --------------
account.name = user_info[:name]
account.address = user_info[:address]
account.email = user_info[:email]
account.notes = user_info[:notes]
# --------- Meta Programing way --------------
account.assign_values(user_info) # With single line we can assign n number of attributes
puts account.inspect
Note: send() itself is not recommended anymore. Use __send__() which has the power to call private methods, or
(recommended) public_send()
```
#### Dynamic Filters:
Using metaprogramming to dynamically create methods for filtering photos based on attributes.
```
class Album
FILTERS = [:by_date, :by_location, :by_tag]
FILTERS.each do |filter|
define_method("photos_#{filter}") do |value|
# Logic to filter photos by the given criteria
puts "Filtering photos #{filter} with value #{value}"
end
end
end
album = Album.new
album.photos_by_date("2023-08-25")
=> Filtering photos by_date with value 2023-08-25
album.photos_by_location("Paris")
=> Filtering photos by_location with value Paris
```
#### Dynamic Event Hooks:
Creating hooks for certain events in the lifecycle of a photo (e.g., when a photo is viewed, liked, shared).
```
class Photo
EVENTS = [:viewed, :liked, :shared]
EVENTS.each do |event|
define_method("on_#{event}") do
# Logic to handle the event
puts "Photo has been #{event}"
end
end
end
photo = Photo.new
photo.on_viewed
photo.on_shared
```
#### Dynamic Tagging System:
Allow users to add custom tags to photos and then retrieve photos by those tags.
```
class Photo
def add_tag(tag)
# Logic to add a tag to a photo
self.tags << tag
# Dynamically create a method to check for this specific tag
self.class.define_method("with_#{tag}") do
# Logic to fetch photos with this specific tag
puts "Fetching photos with tag: #{tag}"
end
end
end
photo = Photo.new
photo.add_tag("vacation")
Photo.with_vacation # Fetching photos with tag: vacation
```
Imagine you want to categorize your photos into different genres, like 'Portraits', 'Landscapes', 'Wildlife', etc.
With metaprogramming, we can dynamically generate methods to categorize and retrieve photos based on these categories:
1. Define a Photo class:
```
class Photo
attr_accessor :title, :category
@@photos = []
def initialize(title, category = nil)
@title = title
@category = category
@@photos << self
end
def self.all
@@photos
end
def self.categorize(name)
# Dynamically generate method to get photos of this category
define_singleton_method("in_#{name.downcase}") do
@@photos.select { |photo| photo.category == name }
end
end
def self.categories(*names)
names.each { |name| categorize(name) }
end
end
```
Define categories using the categories method:
```
Photo.categories('Portraits', 'Landscapes', 'Wildlife')
```
```
Create and categorize photos:
photo1 = Photo.new("Portrait of John", "Portraits")
photo2 = Photo.new("Sunset at Beach", "Landscapes")
photo3 = Photo.new("Eagle in the Sky", "Wildlife")
photo4 = Photo.new("Mountain Range", "Landscapes")
Use the dynamically generated methods to retrieve photos by category:
puts Photo.in_portraits.map(&:title) # ["Portrait of John"]
puts Photo.in_landscapes.map(&:title) # ["Sunset at Beach", "Mountain Range"]
puts Photo.in_wildlife.map(&:title) # ["Eagle in the Sky"]
```
1. We've created a simple Photo class to represent photos with titles and categories.
2. The categorize method dynamically generates methods to filter photos by category.
3. The categories method is a utility to define multiple categories at once. Each category passed to this method will create a new method to retrieve photos in that category.
4. We then create and categorize photos.
5. Lastly, we use the dynamically generated methods to retrieve photos by their category.
### INNER JOIN
An INNER JOIN returns only the rows that have matching values in both tables.

```
SELECT Students.name, Scores.score
FROM Students
INNER JOIN Scores ON Students.id = Scores.student_id;
```

LEFT OUTER JOIN
A LEFT OUTER JOIN returns all the rows from the left table, and the matching rows from the right table. If there's no match, NULL values are returned for columns from the right table.
```
SELECT Students.name, Scores.score
FROM Students
LEFT OUTER JOIN Scores ON Students.id = Scores.student_id;
```

### Polymorphic associations
```
rails generate model Post title:string
rails generate model Video title:string
rails generate model Comment body:text commentable:references{polymorphic}
```
```
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
create_table :comments do |t|
t.text :body
t.integer :commentable_id
t.string :commentable_type
t.timestamps
end
class Post < ApplicationRecord
has_many :comments, as: :commentable
end
class Video < ApplicationRecord
has_many :comments, as: :commentable
end
# Creating a comment for a post
post = Post.create(title: "Hello World")
comment1 = Comment.create(body: "A comment on a post.", commentable: post)
# Creating a comment for a video
video = Video.create(title: "My Video")
comment2 = Comment.create(body: "A comment on a video.", commentable: video)
# Fetch comments of a post
post_comments = post.comments
# Fetch comments of a video
video_comments = video.comments
# Find the parent model (Post or Video) for a comment
parent_for_comment1 = comment1.commentable # This will return the related Post object
parent_for_comment2 = comment2.commentable # This will return the related Video object
```
#### Benefits of Polymorphic Associations:
DRY code: Without polymorphic associations, you would end up with separate associations and tables for each PostComment, VideoComment, etc. Polymorphic associations eliminate that redundancy.
Flexibility: You can easily add more models that can be commented on without having to change the Comment model or table.
Query Efficiency: It allows you to retrieve the comments for multiple types of commentable objects in one query, making it easier to manage and optimize queries.
### Single Table Inheritance (STI)
It is a feature in Ruby on Rails that allows a single database table to be used to represent multiple classes in an inheritance hierarchy. This is useful when you have models that share common attributes and behavior but also have their own specific attributes and behavior.
##### Rails utilizes a type column in the database table to keep track of the class for a given row.
```
rails generate migration CreateVehicles make:string model:string type:string payload_capacity:integer
# app/models/vehicle.rb
class Vehicle < ApplicationRecord
# common behavior for all vehicles
end
# app/models/car.rb
class Car < Vehicle
# behavior specific to cars
end
# app/models/truck.rb
class Truck < Vehicle
# behavior specific to trucks
end
Car.create(make: 'Toyota', model: 'Camry')
Truck.create(make: 'Ford', model: 'F-150', payload_capacity: 5000)
vehicles = Vehicle.all
# This will give you an array of Vehicle, Car, and Truck instances based on the `type` column
cars = Car.all
# This will only give you the rows where `type` is 'Car'
trucks = Truck.all
# This will only give you the rows where `type` is 'Truck'
class User < ActiveRecord::Base
self.inheritance_column = :entity_type # can be string as well
end
class Admin < User; end
```
By default STI model class name is stored in a column named type. But its name can be changed by overriding
inheritance_column value in a base class.
#### Advantages and Disadvantages
##### Advantages:
1. DRY: Code is not duplicated across multiple tables.
2. Easy Queries: It's easy to fetch all records of a certain type or parent type.
3. Intuitive Modeling: A natural fit for certain kinds of domain models.
##### Disadvantages:
1. Complex Queries: As more subclasses are added, it can become increasingly complex to query for specific attributes.
2. Table Bloat: The single table will contain columns for all attributes of all classes in the hierarchy, which may lead to many NULL values.
3. Type Coupling: Because the type field is string-based, renaming classes can lead to issues unless handled carefully.
Having type column in a Rails model without invoking STI can be achieved by assigning `:_type_disabled` to
inheritance_column:
```
class User < ActiveRecord::Base
self.inheritance_column = :_type_disabled
end
```
### Monkey Patching
Monkey Patching is a way of modifying and extending classes in Ruby. Basically, you can modify already defined classes in Ruby, adding new methods and even modifying previously defined methods.
#### Changing an existing ruby method
```
puts "Hello readers".reverse # => "sredaer olleH"
class String
def reverse
"Hell riders"
end
end
puts "Hello readers".reverse # => "Hell riders"
```
### Mixin
In Ruby, a mixin is a way to share code among multiple classes. This is done through modules, which can contain methods that get mixed into classes using the include or extend keywords. Mixins are a form of composition and offer an alternative to inheritance for code reuse.
Instance Method Mixin
Let's consider a simple example where we have a Document and a Person class, both of which need to output a summary.
```
module Summarizable
def summary
"#{self.class.name} Summary: #{self.to_s}"
end
end
class Document
attr_accessor :text
include Summarizable
def initialize(text)
@text = text
end
def to_s
@text
end
end
class Person
attr_accessor :name, :age
include Summarizable
def initialize(name, age)
@name = name
@age = age
end
def to_s
"#{@name}, #{@age} years old"
end
end
doc = Document.new("This is a sample document.")
puts doc.summary
# Output: "Document Summary: This is a sample document."
person = Person.new("Alice", 30)
puts person.summary
# Output: "Person Summary: Alice, 30 years old"
```
```
module Foo
def foo_method
puts 'foo_method called!'
end
end
module Bar
def bar_method
puts 'bar_method called!'
end
end
class Baz
include Foo
include Bar
def baz_method
puts 'baz_method called!'
end
end
new_baz = Baz.new
new_baz.baz_method #=> 'baz_method called!'
new_baz.bar_method #=> 'bar_method called!'
new_baz.foo_method #=> 'foo_method called!'
```
```
module SomeMixin
def foo
puts "foo!"
end
end
class Bar
extend SomeMixin
def baz
puts "baz!"
end
end
b = Bar.new
b.baz
# => "baz!"
b.foo
# NoMethodError, as the method was NOT added to the instance
Bar.foo
# => "foo!"
# works only on the class itself
```
### Self-Referential Association
Self-referential association is used to associate a model with itself. The most frequent example would be, to manage association between a friend and his follower.
```
rails g model friendship user_id:references friend_id:integer
# db/migrate/xxxxxx_create_friendships.rb
class CreateFriendships < ActiveRecord::Migration[6.0]
def change
create_table :friendships do |t|
t.references :user, null: false, foreign_key: true
t.references :friend, null: false, foreign_key: { to_table: :users }
t.timestamps
end
add_index :friendships, [:user_id, :friend_id], unique: true
end
end
now you can associate models like;
class User < ActiveRecord::Base
has_many :friendships
has_many :friends, :through => :friendships
has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
has_many :inverse_friends, :through => :inverse_friendships, :source => :user
end
and the other model will look like;
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => "User"
end
# Create users
user1 = User.create(name: 'Alice')
user2 = User.create(name: 'Bob')
# Create friendship
user1.friendships.create(friend: user2)
# List friends
user1.friends # => [user2]
user2.inverse_friends # => [user1]
```
#### The has_many :through association
```
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician
belongs_to :patient
end
class Patient < ApplicationRecord
has_many :appointments
has_many :physicians, through: :appointments
end
```
#### The has_one :through association
A has_one :through association sets up a one-to-one connection with another model. This association indicates
that the declaring model can be matched with one instance of another model by proceeding through a third model.
```
class Supplier < ApplicationRecord
has_one :account
has_one :account_history, through: :account
end
class Account < ApplicationRecord
belongs_to :supplier
has_one :account_history
end
class AccountHistory < ApplicationRecord
belongs_to :account
end
```
#### Skipping Validations
decrement!
decrement_counter
increment!
increment_counter
toggle!
touch
update_all
update_attribute
update_column
update_columns
update_counters
#### User.save(validate: false)
### Scopes
Scopes act as predefined filters on ActiveRecord models.
A scope is defined using the scope class method.
```
class Person < ActiveRecord::Base
#attribute :first_name, :string
#attribute :last_name, :string
#attribute :age, :integer
# define a scope to get all people under 17
scope :minors, -> { where(age: 0..17) }
# define a scope to search a person by last name
scope :with_last_name, ->(name) { where(last_name: name) }
end
```
Scopes can be called directly off the model class:
```
minors = Person.minors
```
Scopes can be chained:
`peters_children = Person.minors.with_last_name('Peters')`
User.first
```
SELECT
`users`.* FROM `users`
ORDER BY `users`.`id` ASC LIMIT 1
```
#### Includes
ActiveRecord with includes ensures that all of the specified associations are loaded using the minimum possible
number of queries. So when querying a table for data with an associated table, both tables are loaded into
memory.
```
@authors = Author.includes(:books).where(books: { bestseller: true } )
# this will print results without additional db hitting
@authors.each do |author|
author.books.each do |book|
puts book.title
end
end
```
Author.joins(:books).where(books: { bestseller: true } ) will load only authors with conditions into
memory without loading books. Use joins when additional information about nested associations isn't required.
```
@authors = Author.joins(:books).where(books: { bestseller: true } )
# this will print results without additional queries
@authors.each { |author| puts author.name }
# this will print results with additional db queries
@authors.each do |author|
author.books.each do |book|
puts book.title
end
end
```
#### Joins
User.joins(:posts)
will produce the following SQL query:
`"SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id""`
```
User.joins(:posts).where(posts: { title: "Hello world" })
```
Nested joins:
```
User.joins(posts: :images).where(images: { caption: 'First post' })
"SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" INNER JOIN
"images" ON "images"."post_id" = "images"."id""
```
##### Mind that find_by doesn't throw any exception by default. If the result is an empty set, it returns nil instead of find.
##### delete_all method. to be called directly on a model, to delete all records in that table, or a collection. Beware though, as .delete_all does not instantiate any object hence does not provide any callback (before_* and after_destroy don't get triggered).
#### case insensitive search
```
addresses = Address.arel_table
Address.where(addresses[:address].matches("%street%"))
```
### Docker
Docker is a platform for building, shipping, and running applications in containers. A container is a lightweight, standalone, and executable package of software that includes everything needed to run an application, including code, runtime, libraries, system tools, and settings. Docker makes it easy to package an application and all its dependencies into a single container, which can then be deployed to any environment that supports Docker.
```
# Use an official Ruby runtime as a parent image
FROM ruby:3.0.3
# Set the working directory
WORKDIR /app
# Install dependencies
RUN apt-get update && \
apt-get install -y \
build-essential \
nodejs \
postgresql-client && \
rm -rf /var/lib/apt/lists/*
# Install gems
COPY Gemfile Gemfile.lock ./
RUN gem install bundler && \
bundle install --jobs 4
# Copy the application code
COPY . .
# Expose ports
EXPOSE 3000
# Set the entrypoint command
CMD ["rails", "server", "-b", "0.0.0.0"]
```
```
version: "3"
services:
web:
build: .
command: bundle exec rails s -p 3000 -b '0.0.0.0'
ports:
- "3000:3000"
depends_on:
- db
- redis
db:
image: postgres:12
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: myapp_development
redis:
image: redis:6.0
```
##### docker-compose up --build
```
version: "3"
services:
web:
image: your-dockerhub-username/myapp:latest
environment:
RAILS_ENV: production
DATABASE_URL: "postgres://postgres:password@db/myapp_production"
REDIS_URL: "redis://redis:6379/0"
ports:
- "80:3000"
depends_on:
- db
- redis
restart: always
db:
image: postgres:12
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: myapp_production
volumes:
- db_data:/var/lib/postgresql/data
restart: always
redis:
image: redis:6.0
restart: always
volumes:
db_data:
```
#### Nginx Configuration
```
upstream myapp {
server localhost:3000;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://myapp;
proxy_set_header Host $host
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```
#### Github CI/CD
```
# This is a basic workflow to help you get started with Actions
name: CD
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the production branch
push:
branches: [ uat ]
# pull_request:
# branches: [ main ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
deploy:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: appleboy/ssh-action@master
with:
host: ${{secrets.UAT_SSH_HOST}}
key: ${{secrets.UAT_SSH_KEY}} #private key
username: ${{secrets.UAT_SSH_USERNAME}}
script: |
cd /var/www/html/app
docker-compose stop
git pull
docker-compose up -d
ls -al
```
#### N+1 Query
One of the frequent performance antipatterns in ORMs is the N+1 query problem. It occurs when an application retrieves data from the database and then loops through the results of that data, or when a query is run on each result of the prior query.
```
# app/models/Company.rb
class Company < ApplicationRecord
belongs_to :user
end
# app/models/user.rb
class User < ApplicationRecord
has_many :companies
end
Company.all.each do |Company|
puts "#{Company.title} was written by #{Company.user.username}"
end
The above code works, but it makes far too many independent database queries:
Company Load (0.5ms) SELECT "companies".* FROM "companies"
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 6], ["LIMIT", 1]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 9], ["LIMIT", 1]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 10], ["LIMIT", 1]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 15], ["LIMIT", 1]]
```
That is 7 separate database queries
In contrast to lazy loading, eager loading occurs when a query loads a resource as soon as the code is executed. As part of the query, it also loads related entities.
We need to reduce the number of independent database queries in order to improve the performance of the previous example.
```
Company.includes(:user).each do |Company|
puts "#{Company.title} was written by #{Company.user.username}"
end
Company Load (103.7ms) SELECT "companies".* FROM "companies"
User Load (32.1ms) SELECT "users".* FROM "users" WHERE "users"."id" IN (?, ?, ?, ?, ?) [["id", 1], ["id", 5], ["id", 6], ["id", 9], ["id", 10], ["id", 15]]
```
### RubyGems
RubyGems is a package manager for the Ruby programming language. It provides a standard format for distributing Ruby libraries (known as "gems") and a way to easily install, manage and update these gems in a Ruby application.
A gem is a self-contained package of code, assets, and documentation that can be used in a Ruby application. Gems typically provide additional functionality that is not part of the core Ruby language, such as libraries for connecting to databases, handling HTTP requests, and so on.
RubyGems makes it easy to find, install, and manage gems in a Ruby application. You simply specify the gems that your application depends on in a file called the Gemfile, and then use the bundle install command to download and install all the gems specified in the file. The bundle install command also resolves any dependencies between gems and ensures that the correct version of each gem is installed.
##### Generate the gem skeleton
1. Open your terminal and navigate to the directory where you want to create your gem
2. Run the following command to create the gem skeleton: $ bundle gem my_accounting_app
3. This will create a directory named my_accounting_app with the basic structure of a Ruby gem.
```
module MyAccountingApp
def self.total_amount(amount, tax)
amount + (amount * tax)
end
end
```
In the root directory of your gem, you'll find a file named my_accounting_app.gemspec. This is where you'll specify the metadata for your gem, such as its name, version, author, and description.
```
# my_accounting_app.gemspec
Gem::Specification.new do |spec|
spec.name = "my_accounting_app"
spec.version = "0.1.0"
spec.authors = ["Your Name"]
spec.email = ["your_email@example.com"]
spec.summary = "A gem for accounting applications"
spec.description = "A gem that provides methods for accounting applications"
spec.files = Dir["lib/**/*.rb"]
spec.require_paths = ["lib"]
end
```
##### Build the gem
```
gem build my_accounting_app.gemspec
Create a new Rails application: $ rails new my_rails_app
Add the following line to the Gemfile of your Rails application:
gem "my_accounting_app", path: "/path/to/my_accounting_app"
```
```
class InvoicesController < ApplicationController
def show
amount = params[:amount].to_f
tax = params[:tax].to_f
@total_amount = MyAccountingApp.total_amount(amount, tax)
end
end
Rails.application.routes.draw do
get "/invoices/show", to: "invoices#show"
end
```
Run the following command to publish your gem:
`gem push my_accounting_app-0.1.0.gem`
### Page caching
You can use the ActionPack page_caching gem to cache individual pages
This stores the result of one dynamic
request as a static HTML file, which is served in place of the dynamic request on subsequent requests
```
class UsersController < ActionController::Base
caches_page :index
end
Use expire_page to force expiration of the cache by deleting the stored HTML file:
class UsersController < ActionController::Base
caches_page :index
def index
@users = User.all
end
def create
expire_page :action => :index
end
end
The syntax of expire_page mimics that of url_for and friends.
```
### Fragment caching
Use cache.write to write a value to the cache:
```
Rails.cache.write('city', 'Duckburgh')
Rails.cache.read('city')
=> 'Duckburgh'
```
```
Rails.cache.fetch('user') do
User.where(:is_awesome => true)
end
```
#### Nested Forms
```
<%= nested_form_for @project do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<% # Now comes the part for `Todo` object %>
<%= f.fields_for :todo do |todo_field| %>
<%= todo_field.label :name %>
<%= todo_field.text_field :name %>
<% end %>
<% end %>
```
#### CSV Parsing
```
class Product< ApplicationRecord
def self.import(file)
CSV.foreach(file.path, headers: true) do |row|
Product.create! row.to_hash
end
end
end
```
#### Rails Best Practices
##### Fat Model, Skinny Controller
“Fat Model, Skinny Controller” refers to how the M and C parts of MVC ideally work together. Namely, any non-
response-related logic should go in the model, ideally in a nice, testable method. Meanwhile, the “skinny” controller
is simply a nice interface between the view and model.
```
Let’s look at a simple example. Say you have code like this:
def index
@published_posts = Post.where('published_at <= ?', Time.now)
@unpublished_posts = Post.where('published_at IS NULL OR published_at > ?', Time.now)
end
You can change it to this:
def index
@published_posts = Post.published
@unpublished_posts = Post.unpublished
end
Then, you can move the logic to your post model, where it might look like this:
scope :published, ->(timestamp = Time.now) { where('published_at <= ?', timestamp) }
scope :unpublished, ->(timestamp = Time.now) { where('published_at IS NULL OR published_at > ?',
timestamp) }
```
#### Split routes into multiple files
```
config/routes.rb:
YourAppName::Application.routes.draw do
require_relative 'routes/admin_routes'
require_relative 'routes/sidekiq_routes'
require_relative 'routes/api_routes'
require_relative 'routes/your_app_routes'
end
config/routes/api_routes.rb:
YourAppName::Application.routes.draw do
namespace :api do
# ...
end
end
```
#### Collection Vs Member Routes
A member route will require an ID because it acts on a member of the resource. This means that it's used for actions that are performed on a specific instance of the resource. For example,
##### if you have a Users resource and you want to add a custom action that changes the status of a specific user, you could define a member route like this:
```
resources :users do
member do
get 'change_status'
end
end
/users/1/change_status
```
A collection route doesn't require an ID because it acts on a collection of the resource. This means that it's used for actions that are performed on the resource as a whole and not on individual instances. For example,
##### if you want to add a custom action that displays all users who are online, you could define a collection route like this:
```
resources :users do
collection do
get 'online'
end
end
/users/online
```
10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 = 55
```
def add(n)
if n == 1
return n
end
n + add(n - 1)
end
```
### Database View
A database view is a virtual table based on the result-set of an SQL statement. It does not store the data itself but provides a way to represent the result of complex queries in a simplified manner. You can treat views like regular database tables in your queries.
Example
Let's say you have two tables: orders and products. You want to create a view that shows the total sales for each product.
```
rails generate migration CreateTotalSalesPerProductView
# app/models/total_sales_per_product.rb
class TotalSalesPerProduct < ApplicationRecord
self.table_name = 'total_sales_per_product'
self.primary_key = 'id' # Assuming the view has an 'id' field
end
class CreateTotalSalesPerProductView < ActiveRecord::Migration[6.0]
def up
execute <<-SQL
CREATE VIEW total_sales_per_product AS
SELECT products.id AS product_id, products.name AS product_name, SUM(orders.amount) AS total_sales
FROM products
INNER JOIN orders ON products.id = orders.product_id
GROUP BY products.id;
SQL
end
def down
execute <<-SQL
DROP VIEW IF EXISTS total_sales_per_product;
SQL
end
end
```
### Database Indexing in Rails
Database indexing is a performance optimization technique that speeds up data retrieval operations. An index is a data structure that improves the speed of data retrieval operations on a database table at the cost of additional storage and decreased performance on data modification operations.
```
class AddIndexToUsersEmail < ActiveRecord::Migration[6.0]
def change
add_index :users, :email, unique: true
end
end
```
1. Database Views: Useful for simplifying complex queries and calculations. They act like virtual tables.
2. Database Indexing: Useful for speeding up data retrieval operations. They create a data structure that allows for quicker lookups.
### Class Vs Module
A class is a blueprint for creating objects and encapsulates methods that operate on an object's attributes. Classes support inheritance, allowing you to create a new class that is a subclass of an existing class.
##### Module
A module is a collection of methods and constants that can be mixed into a class using include or extend. Modules cannot be instantiated, and they don't support inheritance. However, they are excellent for reusability and keeping classes clean and organized.
```
# app/models/concerns/authentication.rb
module Authentication
def authenticate(password)
# Logic for authentication
end
end
# app/models/user.rb
class User < ApplicationRecord
include Authentication
def full_name
"#{first_name} #{last_name}"
end
end
user = User.new
user.authenticate("some_password")
```
```
╔═══════════════╦═══════════════════════════╦═════════════════════════════════╗
║ ║ class ║ module ║
╠═══════════════╬═══════════════════════════╬═════════════════════════════════╣
║ instantiation ║ can be instantiated ║ can *not* be instantiated ║
╟───────────────╫───────────────────────────╫─────────────────────────────────╢
║ usage ║ object creation ║ mixin facility. provide ║
║ ║ ║ a namespace. ║
╟───────────────╫───────────────────────────╫─────────────────────────────────╢
║ superclass ║ module ║ object ║
╟───────────────╫───────────────────────────╫─────────────────────────────────╢
║ methods ║ class methods and ║ module methods and ║
║ ║ instance methods ║ instance methods ║
╟───────────────╫───────────────────────────╫─────────────────────────────────╢
║ inheritance ║ inherits behaviour and can║ No inheritance ║
║ ║ be base for inheritance ║ ║
╟───────────────╫───────────────────────────╫─────────────────────────────────╢
║ inclusion ║ cannot be included ║ can be included in classes and ║
║ ║ ║ modules by using the include ║
║ ║ ║ command (includes all ║
║ ║ ║ instance methods as instance ║
║ ║ ║ methods in a class/module) ║
╟───────────────╫───────────────────────────╫─────────────────────────────────╢
║ extension ║ can not extend with ║ module can extend instance by ║
║ ║ extend command ║ using extend command (extends ║
║ ║ (only with inheritance) ║ given instance with singleton ║
║ ║ ║ methods from module) ║
╚═══════════════╩═══════════════════════════╩═════════════════════════════════╝
```
#### String
A string is a mutable object that can be changed after it has been created. Each string you create is a separate object, even if the text content is the same.
```
# Creating a string
name = String.new("John")
# Modifying a string
name << " Doe"
# Comparing two strings with the same content
puts "John" == "John" # Output: true
# Two different string objects with the same content
puts "John".object_id == "John".object_id # Output: false
```
#### Symbol
A symbol is an immutable object, meaning it cannot be changed once it has been created. All symbols with the same content refer to the same object, making them more memory-efficient for certain operations like hash keys.
```
# Creating a symbol
name = :John
# Symbols are immutable
# name << " Doe" # This will raise an error
# Comparing two symbols with the same content
puts :John == :John # Output: true
# Same object ID for the same content
puts :John.object_id == :John.object_id # Output: true
```
### Include
The include method is used to mix in a module's methods as instance methods in the target class. This allows you to share behavior across multiple classes.
```
module Greetable
def greet
"Hello, I am #{self.class.name}"
end
end
class Person
include Greetable
end
p = Person.new
puts p.greet # Output: "Hello, I am Person"
```
### Extend
The extend method is used to add a module's methods as class methods to the target class or object.
```
module ClassMethods
def categories
["Food", "Drink"]
end
end
class Product
extend ClassMethods
end
puts Product.categories # Output: ["Food", "Drink"]
```
### Prepend
The prepend method is used to insert a module's methods into the ancestors chain before the methods of the class itself. This allows you to override methods and call super to get the original functionality.
```
module Greetable
def greet
"Hello, I am #{self.class.name}"
end
end
module OverrideGreet
def greet
"Hi, " + super
end
end
class Person
include Greetable
prepend OverrideGreet
end
p = Person.new
puts p.greet # Output: "Hi, Hello, I am Person"
```
### Require
The require method is used to load a Ruby file and execute all its statements. Once a file has been required, it won't be loaded again if require is called with the same path.
```
# In lib/my_module.rb
module MyModule
def hello
"Hello"
end
end
# In another file
require 'lib/my_module'
class MyClass
include MyModule
end
puts MyClass.new.hello # Output: "Hello"
```
### Load
The load method is similar to require, but it re-executes the file every time it is called, which can be useful for reloading code without restarting the application.
```
# In lib/my_module.rb
module MyModule
def hello
"Hello"
end
end
# In another file
load 'lib/my_module.rb'
class MyClass
include MyModule
end
puts MyClass.new.hello # Output: "Hello"
```
#### Summary
**include**: Adds instance methods from a module to a class.
**extend**: Adds class methods from a module to a class or object.
**prepend**: Inserts a module's methods before the class's own methods, allowing for overrides.
**require**: Loads and executes a Ruby file once.
**load**: Loads and executes a Ruby file every time it's called.
### Scope Variables
#### Local Variable
Local variables are the most straightforward type of variable. They are available only within the method, loop, or block where they are defined.
```
def show
message = "Hello, World!" # Local variable
puts message
end
```
#### Instance Variables
Instance variables are available throughout the current instance of the class. They are prefixed with an @ symbol.
```
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def show
@article = Article.find(params[:id]) # Instance variable
end
end
```
#### Class Variables
Class variables are shared among a class and all its descendants. They are prefixed with @@.
```
class Product
@@count = 0 # Class variable
def initialize
@@count += 1
end
def self.count
@@count
end
end
```
#### Global Variables
Global variables are accessible throughout your entire application. They are prefixed with a $ symbol. However, their use is generally discouraged in favor of other variable types or constants.
```
$global_variable = "I'm available everywhere" # Global variable
```
#### Constants
Constants in Ruby are used to store values that should not be changed once they are assigned. They are written in all uppercase.
```
# app/models/article.rb
class Article < ApplicationRecord
STATUS = ['Draft', 'Published'].freeze # Constant
end
```
#### Summary
1. Local Variables: Limited to the method, loop, or block where they are defined.
2. Instance Variables: Available throughout the current instance of the class.
3. Class Variables: Shared among a class and all its descendants.
4. Global Variables: Accessible throughout the entire application.
5. Constants: Immutable variables, usually used for storing settings or other unchanging values.
### Visiblity Scopes
1. Public
By default, all methods are public except for initialize, which is always private. Public methods can be called by any object that can access the class or instance.
```
class MyClass
def public_method
"I'm public!"
end
end
obj = MyClass.new
puts obj.public_method # Output: "I'm public!"
```
2. Protected
Protected methods can be called only within the same class or its subclasses, and they can also be called by other instances of the same class.
```
class MyClass
protected
def protected_method
"I'm protected!"
end
end
class MyDerivedClass < MyClass
def call_protected_method_on(obj)
puts obj.protected_method
end
end
obj1 = MyDerivedClass.new
obj2 = MyDerivedClass.new
obj1.call_protected_method_on(obj2) # Output: "I'm protected!"
```
3. Private
Private methods can only be called within the context of the current object (self). They cannot be accessed by other instances of the same class.
```
class MyClass
private
def private_method
"I'm private!"
end
public
def call_private_method
puts private_method
end
end
obj = MyClass.new
obj.call_private_method # Output: "I'm private!"
```
### Design Patterns
1. Service Objects
2. View Objects (Presenter)
3. Query Objects
4. Decorators
5. Form Objects
6. Value Objects
7. Policy Objects
8. Builder
9. Interactor
10. Observer
#### Service Objects
A Service Object is PORO – Plain Old Ruby Object, which is meant to encapsulate business logic and complex calculations into manageable classes and methods.
When we need to perform complex calculations or business logic, e.g., if we need to calculate employees’ salaries based on their attendance.
When we need to implement any other API, we need to implement a payment gateway such as Stripe.
When we want to import CSV that contains the bulk of data. 4. When we need to clear garbage/unused/old data from the database efficiently, it won’t affect the existing data.
```
class PaymentsController < ApplicationController
def create
PaymentService.new(params).call
redirect_to payments_path
rescue Stripe: : CardError => e
flash[:error] = e.message
redirect_to new_Payment_path
end
end
```
```
class Payment Service
def initialize(options = {})
options.each pair do Jkey, value
instance_variable_set("@#{key}", value)
end
end
def call
Stripe: : Charge.create(charge_attributes),
end
private
attr_reader : email, :source, :amount, :description
def amount
@amount. to_i * 100
end
def customer
@customer ||= Stripe::Customer.create(customer_attributes),
def customer_attributes
{
email: email,
source: source
}
end
def charge_attributes
{
customer: customer.id,
amount: amount,
description: description
}
end
end
```
#### View Objects (Presenter)
View Objects allow us to encapsulate all view-related logic and keep both models and views neat. View objects are the kind of rails patterns that are easy to test as they are just classes.
To solve the calculation logic problem, we can use rails helper, but if the complexity of the code is high, in that case, we should use the Presenter.
```
<p>
User Full Name: <%= "#{user.first_name} #{user. last_name}" %>
<%= link_to "View More Detail", user, class: "W-75 p-3 text-#{user.active? ? "orange" : "
green"} border-#{user.active? ? "orange" : "green"}" %>
</p>
```
**Solution**
```
app/presenters/user_presenter.rb
class UserPresenter
def initialize(user)
@user = user
end
def full_name
"#{@user.first_name} #{@user. last_name}",
end
def css_color
@user.active?? "orange" : "green",
end
end
<% presenter = UserPresenter.new(user) %>
User Full Name: <%= presenter.full_name %>
<%= link_to "View More Detail", user, class: "W-75 p-3 text-#{presenter.css_color} border-#{presenter.css_color}" %>
```
#### Query Object
Query Object is a design pattern in rails that lets us fetch query logic from Controllers and Models into reusable classes.
```
class PostsController < ApplicationController
def index
@posts = Post.accessible_by(current_ability)
.where(type: :video)
.where('view_count > ?', 100),
end
end
```
Solution
To make the controller skinny, readable, and neat, we can use scopes:
```
class Post < ActiveRecord::Base,
scope : video_type, ->{ where(type: :video) }
scope : popular, -> { where('view_count > ?', 1000) }
scope : popular_video_type, -> { popular.video_type }
end
```
```
class Articles Controller < ApplicationController
def index
@posts = Post.accessible_by(current_ability),
.popular_video_type
end
end
```
```
class VideoQuery
def call(ability)
ability
.where(type: : video)
.where('view_count > ?', 1000)
end
end
class PostsController < ApplicationController
def index
ability = Post.accessible_by(current_ability),
@articles = VideoQuery.new.call(ability)
end
end
```
#### Decorators
The decorator is a design pattern that allows the behavior to be added to an object, dynamically, without disturbing the behavior of other objects of the same class. Decorators can be useful for cleaning up logic/code written inside the view and controller in an RoR application.
Create an app/decorator folder.
Add decorate helper in ApplicationHelper.
```
module ApplicationHelper
def decorate(model_name, decorator_class = nil),
(decorator_class || "#{model_name. class}Decorator".constantize).new(model_name),
end
end
```
Add base_decorator.rb in app/decorators folder.
```
class BaseDecorator < SimpleDelegaton
def decorate (model_name, decorator_class = nil),
ApplicationController.helpers.decorate(model_name, decorator_class),
end
end
```
Add user_decorator.rb in app/decorators folder.
```
class UserDecorator < BaseDecorator
def full name
"#{fname} #{lname}"
end
end
```
Initialize @user_decorator in your user_controller.
```
class users Controller < Application Controller
def show
@user_decorator = helpers. decorate(current_user),
end
end
<%= @user_decorator.full_name %>
<%= @user_decorator.email %>
```
#### Form Objects
The form object is a design pattern that is used to encapsulate the code related to validation and persisting data into a single unit. Let’s have a look at the example of the Form Objects.
Let’s assume that we have a rails post model and a controller (posts_controller) action for creating the new post. Let’s discuss the problem, Here Post Model contains all validation logic, so it’s not reusable for other entities, e.g., Admin. app/controller/posts_controller.rb
```
class PostsController < ApplicationController
def create
@post = Post.new(post_params)
if @post. save
render json: @post
else
render json: @post.error, status: :unprocessable_entity
end
end
private
def post_params
params. require(: post).permit(:title, :description, :content)
end
end
app/model/post.rb
class Post < ActiveRecord: :Base
validates :title, presence: true
validates : content, presence: true
end
```
The better solution is to move the validation logic to a separate singular responsibility class that we might call PostForm:
```
class Post Form
include ActiveModel: :Model
include Virtus.model
attribute :id, Integer
attribute :title, String
attribute :description, String
attribute : content, String
attr_reader : record
def persist
@record = id ? Post. find(id) : Post.new
if valid?
@record. save!
true
else
false
end
end
end
```
```
class PostsController < ApplicationController
def create
@form = Post Form.new(post_params)
if @form. persist
render json: @form. record
else
render json: @form.errors, status: :unpocessably_entity
end
end
private
def post_params
params. require(: post).permit(:title, :description, :content)
end
end
```
#### Value Object
The Value object is a type of Ruby pattern that encourages small, simple objects and allows you to compare these objects as per the given logic or specific attributes. It represents value but not something unique in your system like a user object. Value Objects always return only values
```
class EmailReport
def initialize(emails)
@emails = emails
end
def data
emails_data = [ ]
emails.each do email|
emails_data << {
username: email.match(/([^@]*)/).to_s,
domain: email.split("@"). last
}
end
emails_data
end
private
attr_reader : emails
end
```
Now, let’s create the value object:
```
class Email
def initialize(email)
@email = email
end
def username
email.match(/([^@]*)/).to_s
end
def domain
email.split("a"). last
end
def to_h
{ username: username, domain: domain }
end
private
attr_reader : email
end
```
Calling Email values
```
class EmailReport
def initialize(emails: emails),
@emails = emails
end
def data
emails.map { email| Email.new(email).to_h }
end
private
attr_reader : emails
end
```
#### Policy Object
The policy object is similar to the Service object; the only difference is policy object is responsible for the read operations, and the service object is responsible for write operations.
In rails, we use cancan or pundit gem for the authorization, but these gems are suitable if the application complexity is medium,
If the application complexity is high (in terms of authorization), then, in that case, we use policy object for better efficiency. It returns a boolean value (true or false).
```
class UserService
def initialize(user)
@user = user
end
def name
user_policy.take_email_as_name?? user.email : user.full_name
end
def account_name
user_policy.is_admin? ? "Administrator": "User",
end
private
attr_reader : user
def user_policy
@_user_policy II = UserPolicy.new(user),
end
end
```
Let’s create a policy (app/policies/user_policy.rb):
```
class UserPolicy
def initialize(user)
@user = user
end
def is_admin?
user_role_is_admin?
end
def take_email_as_name?
user_full_name_is_not_present? && is_user_email_present?
end
private
attr_reader : user
def user_full_name_is_not_present?
user.full_name.blank?
end
def is_user_email_present?
user.email.present?
end
def user_role_is_admin?
user.sign_in_count > 0 && user.role == "admin"
end
end
```
#### Observer
In this pattern, other interested objects are notified whenever an event has occurred. The observed object contains its observers’ list and notifies them by sending an update whenever its state changes
When you want to make several views changes manually
When an object’s state is dependent on a specific state
When several views are dependent on a particular object’s state
```
require 'observer'
class Product
include Observable
attr_reader :productName, :availableProducts
def initialize(productName = "", availableProducts = 0)
@productName, @availableProducts = productName, availableProducts
add_observer(Notifier.new)
end
def update_availableProducts(product)
@product = product
changed
notify_observers(self, product)
end
end
```
Now, we will build a class which would be notified with the updates on available products.
```
class Notifier
def update(product, availableProducts)
puts "Yes, #{product.name} is available" if availableProducts > 0
puts "Oops! Sorry, #{product.name} is unavailable!" if availableProducts == 0
end
end
```
```
isProductAvailable = Product.new("Iphone 12")
isProductAvailable.update_availableProducts(5)
# => Yes, Iphone 12 is available
isProductAvailable.update_availableProducts(8)
# => Yes, Iphone 12 is available
isProductAvailable.update_availableProducts(0)
# => Oops! Sorry, Iphone 12 is unavailable
```
---
### Reverse Array
```
a = [1, 2, 3, 4, 5, 6]
left = 0
right = a.length - 1
while left < right
# Swap the elements at the 'left' and 'right' indices
a[left], a[right] = a[right], a[left]
# Move the pointers towards the center
left += 1
right -= 1
end
puts a # Output will be [6, 5, 4, 3, 2, 1]
```
---
### Query Optimisation technique
Query optimization is essential for the performance of any database-driven application, including those built on Ruby on Rails with Active Record. Here are some techniques and best practices for optimizing your queries:
1. **Select Only What You Need**:
- Instead of fetching all columns with `Model.all`, use `select` to fetch only the columns you need.
```ruby
User.select(:id, :name)
```
2. **Use Indexes**:
- Index frequently queried columns to speed up search operations. However, avoid over-indexing as it can slow down write operations.
3. **Eager Loading with `includes` or `eager_load`**:
- Helps to reduce the "N+1 query problem".
```ruby
User.includes(:posts).where("posts.published = ?", true)
```
4. **Use `exists?` for Presence**:
- Instead of `Model.any?` or `Model.count > 0`, use `Model.exists?` to check for a record's presence.
5. **Use Database Constraints**:
- Utilize `UNIQUE`, `FOREIGN KEY`, and other constraints in the database to ensure data integrity without additional application-level checks.
6. **Limit and Offset**:
- Use `limit` and `offset` to paginate results and avoid fetching more records than necessary.
7. **Caching**:
- Use tools like Redis or Memcached to cache frequently accessed queries.
8. **Batch Processing**:
- Use methods like `find_each` or `find_in_batches` for processing large sets of data.
```ruby
User.find_each(batch_size: 100) do |user|
# process each user
end
```
9. **Avoid Ruby Iteration Over Large Data Sets**:
- Instead of loading a large number of records and processing them in Ruby, try to handle as much data processing as possible in the database.
10. **Use SQL Views or Materialized Views**:
- For complex queries that join several tables, consider using database views or materialized views.
11. **Be Mindful of Joins**:
- Limit the number of `joins` in a query, as they can substantially slow down performance. Consider using `subqueries` when applicable.
12. **Optimize Database Configuration**:
- Tools like the `pgtune` for PostgreSQL can help optimize the database configuration.
13. **Review Logs**:
- Regularly check your development logs to spot inefficient queries.
14. **Database Tools and Extensions**:
- Use tools like `EXPLAIN` (in SQL databases) to understand how your query is being executed and optimize accordingly.
15. **Keep Application and Database Updated**:
- New versions often come with optimizations and improvements. Ensure you're using a well-maintained version of both your database and Rails.
16. **Use Counter Cache**:
- Instead of running a `COUNT` query every time you need to know the number of associated records, use Rails' `counter_cache` feature.
17. **Avoid `default_scope`**:
- `default_scope` in Rails can lead to unexpected behaviors and negatively impact performance. Use scopes with caution.
18. **De-normalization**:
- In situations where read performance is crucial, consider de-normalizing the database to reduce the need for complex joins or calculations.
Remember, before making any optimizations, it's essential to identify the bottlenecks. Tools like `rack-mini-profiler`, `Bullet`, and database-specific profilers can be invaluable in this regard.
### SOLID
#### Single Responsibility Principle (SRP)
The Single Responsibility Principle dictates that a class should have only one reason to change, meaning it should have only one job or responsibility.
```ruby
# Good: Single responsibility of sending email
class EmailSender
def send_email(email, content)
# logic to send email
end
end
# Bad: Multiple responsibilities
class UserManager
def create_user(params)
# logic to create user
end
def send_email(email, content)
# logic to send email
end
end
```
#### Open/Closed Principle (OCP)
According to the Open/Closed Principle, software entities (classes, modules, methods, etc.) should be open for extension but closed for modification.
```ruby
# Base class
class ReportGenerator
def generate
# common logic
end
end
# Extended classes
class PDFReport < ReportGenerator; end
class XMLReport < ReportGenerator; end
```
#### Liskov Substitution Principle (LSP)
The Liskov Substitution Principle states that objects of a superclass should be able to be replaced with objects of a subclass without affecting the correctness of the program.
```ruby
class Bird
def fly
'I can fly'
end
end
class Penguin < Bird
def fly
'I cannot fly'
end
end
```
Here, a Penguin is-a Bird, but it violates LSP because it cannot fly, unlike other birds
#### Interface Segregation Principle (ISP)
The Interface Segregation Principle suggests that a class should not be forced to implement interfaces it does not use. In other words, don't add additional responsibilities to an existing class or module.
```ruby
# Good
class OrderProcessor
def process_order; end
end
class PaymentProcessor
def process_payment; end
end
# Bad
class OrderAndPaymentProcessor
def process_order; end
def process_payment; end
end
```
#### Dependency Inversion Principle (DIP)
The Dependency Inversion Principle states that high-level modules should not depend on low-level modules; both should depend on abstractions. Also, abstractions should not depend on details; details should depend on abstractions.
```ruby
# Good
class OrderProcessor
def initialize(payment_gateway)
@payment_gateway = payment_gateway
end
end
# Bad
class OrderProcessor
def initialize
@payment_gateway = CreditCardPaymentGateway.new
end
end
```
---
---
### Transactions
Transactions are a feature of databases that allow you to execute a series of Active Record operations atomically, meaning that either all the operations succeed, or none of them do.
Transactions ensure data integrity and consistency even in situations where multiple operations need to occur in a sequence.
```ruby
User.transaction do
user = User.new(username: 'John')
user.save!
profile = Profile.new(user: user, bio: 'Hello, world!')
profile.save!
end
User.transaction do
user = User.create(username: 'John')
Profile.transaction do
profile = Profile.create(user: user, bio: 'Nested transaction')
raise ActiveRecord::Rollback if some_condition_not_met
end
end
User.transaction do
user.save!
raise ActiveRecord::Rollback if some_condition_not_met
end
```
#### Why Use Transactions?
**Atomicity**: Ensures that a series of operations are atomic.
**Consistency**: Helps maintain database consistency.
**Isolation**: In a multi-user environment, ensures that operations from one transaction are isolated from another until committed.
**Durability**: Once committed, the changes are permanent.
### Eager Loading
One way to improve performance is to cut down on the number of SQL queries. You can do this through eager loading.
`User.find(:all, :include => :friends)`
Here you are firing only two queries :
1) One for all users.
2) One for all friends of users .
### Lazy Loading :
When you have an object associated with many objects like a User has many Friends and you want to display a list as in Orkut you fire as many queries as there are friends, plus one for the object itself.
```ruby
users = User.find(:all)
Then query for each user friend , like :
users.each do |user|
friend = Friend.find_by_user_id(user.id)
end
```
1) One query for all users.
2) N query for N no. of users friends .
### CSRF (Cross-Site Request Forgery)
The CSRF (Cross-Site Request Forgery) mechanism in Ruby on Rails is designed to prevent unauthorized commands from being transmitted from a user that the web application trusts. Here's a brief overview of how Rails handles and validates the CSRF token to ensure this protection:
1. Token Generation:
When you use form_for or other Rails form helpers, a hidden input field named authenticity_token is included in the generated form. This token is generated by the server.
The same token is also stored in the session on the server side.
2. Sending the Token:
For traditional form-based requests, the token is included as a hidden form field and thus sent as part of the form data.
For AJAX requests, the token can be added as an HTTP header called X-CSRF-Token.
3. Validation:
When the form is submitted or an AJAX request is made, Rails will expect the CSRF token to be present either in the form data or in the HTTP header.
The server-side Rails framework retrieves the token from the session and compares it with the token sent with the request.
If the tokens don't match or if the token is missing from the request, Rails will raise an `ActionController::InvalidAuthenticityToken` exception, thereby preventing the request from succeeding.
4. Configuring the CSRF Protection:
In a Rails application, CSRF protection is enabled by default. You'll find the following line in the ApplicationController:
`protect_from_forgery with: :exception`
This means if a CSRF token is invalid or missing, an exception will be raised. However, you can also use with: :null_session if you want to reset the session instead of raising an exception.
**Use Cases**:
If for some reason you need to disable CSRF protection for specific actions or controllers (not typically recommended for web-facing routes due to security concerns), you can use skip_before_action :verify_authenticity_token.
Remember, CSRF attacks rely on the fact that browsers send cookies with every request, even if the request originates from a different site. By requiring a CSRF token, which is not automatically sent by browsers, Rails ensures that the request is made intentionally by the user and from the legitimate site.
### Find the overlapping sets and merge together in ruby on rails
```ruby
def merge_intervals(intervals)
return [] if intervals.empty?
# Sort intervals by start time
intervals.sort_by! { |interval| interval[0] }
merged = [intervals[0]]
intervals[1..-1].each do |current_interval|
last_merged = merged[-1]
# If the current interval overlaps with the last merged interval
if current_interval[0] <= last_merged[1]
# Merge them
last_merged[1] = [last_merged[1], current_interval[1]].max
else
# Otherwise, just add the current interval to the merged list
merged << current_interval
end
end
merged
end
input = [[80,120], [100, 160], [180, 240], [280,320], [300, 360], [340, 400]]
output = merge_intervals(input)
puts output.inspect
# This should output: [[80,160], [180, 240], [280,400]]
```