Ruby on Rails
The Single Engineer Framework
Radoslav Stankov
!
Radoslav Stankov
@rstankov rstankov.com
https://tips.rstankov.com
I began my web development career using
Flash, PHP, and JavaScript at an agency in
Dobrich in 2002.
I was 15 "
... after 22 years #
here some of the technologies, I
have worked with...
Careers in tech are long, and technology trends come and go.
You won't be able to have a meaningful career working with a single
technology your whole career.
- one developer (me) $
- management portal %
- implement a mobile app &
- for iOS ' / Android (
Building mapping )
Mobile app &
Issue tracker *
Employees +
Bulletin board ,
Notifications -
Taxation .
Contacts /
Omni search 0
Reporting 1
Printing 2
Bookkeeping 3
Voting 4
Financials 5
Homebook 6
Debtors 7
Bulk operations ⚙
Messaging 9
Calendar :
Funds ;
ePay / EasyPay <
Bank imports =
Invoicing >
Warranty Issues ?
Technicians @
Individual accounts A
Business accounts )
Audit logs B
Archiving C
Trials D
Demo E
I18n F G H
Marketing site I
& Mobile stack J
% Web stack J
Ruby on Rails and its ecosystem enabled me to develop Angry Building
single-handedly. It felt like having a cheat code.
Ruby on Rails
Ruby on Rails
Ruby on Rails
Ruby on Rails
Ruby
Ruby is an interactive, object-
oriented programming language.
Its motto is:
"Optimized for programmer
happiness"
https://rubygems.org/
Ruby on Rails
Ruby on Rails is a web application framework.
Who uses Ruby? K
... Ruby on Rails old and boring,
designed for monoliths L
class Person
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def first_name
@first_name
end
def first_name=(value)
@first_name = value
end
Classes
@first_name
end
def first_name=(value)
@first_name = value
end
def last_name
@last_name
end
def last_name=(value)
@last_name = value
end
def full_name
"#{first_name} + #{last_name}"
end
end
Classes
Classes
class Person
attr_reader :first_name, :last_name
attr_writer :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def full_name
"#{first_name} + #{last_name}"
end
end
Classes
class Person
attr_accessor :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def full_name
"#{first_name} + #{last_name}"
end
end
Meta programming
class Person
attr_accessor :first_name, :last_name
end
Meta Programming
R Nothing is special
S Everything is an object
T No definitions, just executions
U Cool syntax V
Meta programming
Open classes
class Array
def first
self[0]
end
def second
self[1]
end
end
array = [1, 2, 3, 4]
array.first # 1
array.second # 2
class MyClass
puts "I'll be executed"
unless RUBY_VERSION >= "3.3.3"
puts "I'll be executed after ruby 3.3.3 "
def some_method
# ... code
end
end
end
No definitions, just executions
Meta programming
class Person
attr_accessor :first_name, :last_name
end
Meta programming
class Person
def first_name
@first_name
end
def first_name=(value)
@first_name = value
end
end
Meta programming
class Person
define_method(:first_name) do
@first_name
end
define_method(:first_name=) do |value|
@first_name = value
end
end
Meta programming
class Person
define_method(:first_name) do
instance_variable_get( :@first_name)
end
define_method(:first_name=) do |value|
instance_variable_set( :@first_name, value)
end
end
Meta programming
class Person
define_method(:first_name) do
instance_variable_get :@first_name
end
define_method(:first_name=) do |value|
instance_variable_set :@first_name, value
end
end
Meta programming
class Person
attr_accessor :first_name, :last_name
end
Meta programming
class Person
def self.attr_accessor(attribute)
define_method(attribute) do
instance_variable_get :"@#{attribute}"
end
define_method(:"#{attribute}=") do |value|
instance_variable_set :"@#{attribute}", value
end
end
end
Meta programming
class Person
def self.attr_accessor(*attributes)
attributes.each do |attribute|
define_method(attribute) do
instance_variable_get :"@#{attribute}"
end
define_method(:"#{attribute}=") do |value|
instance_variable_set :"@#{attribute}", value
end
end
end
end
Meta programming
class Person
attr_accessor :first_name, :last_name
end
W Model View Controller (MVC)
X Batteries included
Y Convention over configuration
Z Extendible platform
W Model View Controller (MVC)
W Model View Controller (MVC)
W Model View Controller (MVC)
W Model View Controller (MVC)
W Model View Controller (MVC)
W Model View Controller (MVC)
W Model View Controller (MVC)
W Model View Controller (MVC)
W Model View Controller (MVC)
W Model View Controller (MVC)
ActiveSupport - utilities
ActiveModel - base model & validations
ActiveRecord - object relation mapper (ORM)
ActiveJob - background job processing
ActiveStorage - file uploads
ActionController - Controller from MVC
ActionView - View from MVC
ActionMailer - Sending emails
ActionMailbox - Receiving & processing emails
ActionCable - Web Socket & real time
ActionText - Rich text editing
Propshaft - assets delivery
I18n - internalization
X Batteries included
Y Convention over configuration
https://github.com/maybe-finance/maybe
https://github.com/discourse/discourse
https://github.com/forem/forem
https://github.com/mastodon/mastodon
https://github.com/thoughtbot/upcase
https://github.com/feedbin/feedbin
https://gitlab.com/gitlab-org/gitlab
Open Source Apps
https://blog.rstankov.com/top-8-ruby-on-rails-engines/
Z Extendible platform
Sidekiq
Blazer
Lookbook
Flipper
GraphiQL
... and a lot more ✨
https://rubygems.org/
rails new blog
rails generate scaffold post title:string content:text
db/migrate/[...]_create_posts.rb
class CreatePosts < ActiveRecord::Migration[7.1]
def change
create_table :posts do |t|
t.string :title, null: false
t.text :content, null: false
t.timestamps
end
end
end
rails db:migrate
class Post < ApplicationRecord
validates :title, presence: true
validates :content, presence: true
end
app/models/post.rb
Rails.application.routes.draw do
resources :posts
# Define your application routes per the DSL in
# https://guides.rubyonrails.org/routing.html
# Reveal health status on /up that returns 200
# if the app boots with no exceptions, otherwise 500.
# Can be used by load balancers and uptime monitors to
# verify that the app is live.
get "up" => "rails/health#show", as: :rails_health_check
# Defines the root path route ("/")
# root "posts#index"
end
config/routes.rb
class PostsController < ApplicationController
# GET /posts
def index
@posts = Post.all
end
# GET /posts/1
def show
@post = Post.find(params[:id])
end
# GET /posts/new
def new
@post = Post.new
end
# GET /posts/1/edit
def edit
@post = Post.find(params[:id])
end
# POST /posts
def create
@post = Post.new(post_params)
app/controllers/posts_controller.rb
# POST /posts
def create
@post = Post.new(post_params)
if @post.save
redirect_to post_url( @post), notice: "Post was successfully created. "
else
render :new, status: :unprocessable_entity
end
end
# PATCH/PUT /posts/1
def update
@post = Post.find(params[:id])
if @post.update(post_params)
redirect_to post_url( @post), notice: "Post was successfully updated. "
else
render :edit, status: :unprocessable_entity
end
end
# DELETE /posts/1
def destroy
app/controllers/posts_controller.rb
if @post.update(post_params)
redirect_to post_url( @post), notice: "Post was successfully updated. "
else
render :edit, status: :unprocessable_entity
end
end
# DELETE /posts/1
def destroy
@post = Post.find(params[:id])
@post.destroy!
redirect_to posts_url, notice: "Post was successfully destroyed. "
end
private
def post_params
params.require(:post).permit(:title, :content)
end
end
app/controllers/posts_controller.rb
<p style="color: green"><%= notice %></p>
<h1>Posts</h1>
<div id="posts">
<% @posts.each do |post| %>
<%= render post %>
<p>
<%= link_to "Show this post", post %>
</p>
<% end %>
</div>
<%= link_to "New post", new_post_path %>
app/views/posts/index.html.erb
<%= form_with(model: post) do |form| %>
<% if post.errors.any? %>
<div style="color: red">
<h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved: </h2>
<ul>
<% post.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= form.label :title, style: "display: block" %>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :content, style: "display: block" %>
<%= form.text_area :content %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
app/views/posts/_form.html.erb
rails server
rails action_text:install
bundle
rails server
class Post < ApplicationRecord
validates :title, presence: true
validates :content, presence: true
has_rich_text :content
end
app/models/post.rb
<%= form_with(model: post) do |form| %>
<% if post.errors.any? %>
<div style="color: red">
<h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved: </h2>
<ul>
<% post.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= form.label :title, style: "display: block" %>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :content, style: "display: block" %>
<%= form.rich_text_area :content %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
app/views/posts/_form.html.erb