Skip to content

Latest commit

 

History

History
639 lines (484 loc) · 13 KB

File metadata and controls

639 lines (484 loc) · 13 KB

ÖYK 2019 Ruby on Rails - Day 4

Request yani istek olarak bir PDF isteği gelir ise; accept_language PDF olarak ayarlanır(set'lenir.)
İstek html için gelmişse accept_language text/html olur.

Rack

https://rack.github.io/

Ruby'nin http isteklerini anlayabilmesi için araya rack katmanı girer.

Rack provides a minimal interface between webservers that support Ruby and Ruby frameworks.

Herhangi bir dizinde config.ru var ise o bir webserver'dır. Gelen isteklere cevap verebilir.

Uygulama sunucusu alternatifleri

  • puma (application web server) (http://puma.io/)
  • thin ruby webserver
  • ruby unicorn
# config.ru
require './myapp.rb'
run myapp
# myapp.rb
require 'rack'
require "rack/handler/puma"

myapp = Proc.new do |env|
    ['200', {'Content-Type' => 'text/html'}, ['A barebones rack app.']]
end

Rack::Handler::Puma.run myapp
rackup

Development'dayken app server, Production'da ise webserver kullanılır direkt olarak app server kullanılır.

nginx statik veriler, ssl, saldırılardan sorumlu olarak ayarlanır genelde, puma ve alternatif app serverlar ise ruby kodları çalıştırmak için vs.

Nginx aslında bir proxy görevi görmüş olur.

CI

Session, cookie, sunucunun client'ı tanımasını sağlar.

MVC

 _______________
|     || NGINX  | <---------------- Request (client)
|     ||        |
|     || PUMA   |
|     ||        |
|     || APP    | -----> - public/
|     ||        |            - public/assets
|     ||        |        - app/
|     ||        |            - app/controllers
|     ||        |        - config.ru
 ---------------         - routes.rb
    |
    |
    |
    |
    |
    --------------
   |              |
   |  PostgreSQL  |
   |              |
    --------------
  -------                  ------
 | Model |                | View |
  -------                  ------
    \                         /
     \                       /
      \                     /
       \                   /
         ------------------
        | Controller       |
        | Routing          |
        | Front Controller |
         ------------------
- Design Pattern
	- FrontController
		- Routing   <----------------| Dispatch
		- Controller Classes --------|
		- URI

FrontController adında bir design pattern var. MVC'lerin hepsi başlangıçta FrontController uygulayarak başlar. Bu routing ve controller class'larının ilişkili olarak çalışmasını sağlar.

HTTP Request Methods [REST ile doğrudan ilgisi var!]

GET
POST
PUT/PATCH
DELETE

FrontController root path'den sonraki alt path'leri algılar ve nereye yani hangi controller'a istek yapılması gerektiğini anlar, yani dispatch yapar!

Yapılan http request tipine göre de controller'ın hangi method'u yani hangi action'ı çalışacağı anlaşılır.

Controller'a gidilten sonra eğer DB üzerinde bir işlem yapılacak ise Model katmanı araya girer ve DB üzerinde ilgili sorgular yapılır. (SQL)

SQL sorguları iki gruba ayrılır. SQL aslında bir DSL'dir yani Domain Specific Language.

DDL - Data Defination Language DML - Data Manupilation Lanugage

Rails'de SQL sorguları model üzerinde otomatik olarak oluşturulur. Bunu ORM sağlar yani Object Relation Mapping.


Model katmanında yapılacak sorgu işlemleri yapıldıktan sonra controller view katmanınına gider.

View

  • assets
  • layout
  • template
  • render

controllers/users_controller.rb
views/users/index.html.erb
models/user.rb

Active Record

Convention over Configuration.

Kurallara uyulmayabilir ama bu durumda hata oluşabilir.

Keskin bıçaklar. Standartları kendine göre değiştirebilirsin, ama bu durumda bıçak üstünde olduğunu unutma!

    DATABASE
 --------------
|  PostgreSQL  |
|  SQLite      |
|  MySQL       |
|  MongoDB     |
 --------------
DB (host, username, password)                       ORM (Active Record)
  -> Scheama
     -> Table   <----------------------------->  Class
       -> Column  <------------------------>  Attribute
         -> Type
       -----
      | ORM |
       -----
         |
         | Connection
         |
         |
     -----------------
    | PostgreSQL      |
    |                 |
    | Connection Pool |
     -----------------

Connection kurulması için

  • DB Name
  • Username
  • Password
  • Host
  • Adapter (driver)

Model ve Tablo karışılıkları

Model/Class Table/Schema
Article articles
LineItem line_items
Deer deers
Mouse mice
Person people

10000000 == 10_000_000

Primary key
Foreign key

Model'in yazılması

class Product < ApplicationRecord
end

ApplicationRecord tarafından setter ve getter'ları oluşturur. Bu bir kere oluşturulup cache'lenir.

def name #getter method
  @name
end

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

User.class_methods.add(new_method) gibi birşeyler yapılabiliyor.

attr_reader (setter oluşturma)
attr_writer (getter oluşturma)
attr_accessor (setter ve getter oluşturma)


Eğer db'deki farklı bir tabloyu kullanmak, kullanılan tablo adını elle girmek istersek

class Product < ApplicationRecord
  self.table_name = "my_products"
end

şeklinde yapabiliriz.

CRUD (Create, Read, Update, Delete)

Create

user = User.new(name: "Bora", surname: "Tanrikulu")
user.save

Read

# return a collection with all users
users = User.all
# return the first user
user = User.first
# return the first user named Bora
david = User.find_by(name: "Bora")
# find all users named David who are Code Artists and sort by created_at in reverse chronological order
users = User.where(name: 'Bora', surname: 'Tanrikulu').order(created_at: :desc)

Find

Bir object dönerken, Where bir collection dönecektir.

Collection, içersinde birden fazla obje saklayan yapıdır.

Update

user = User.find_by(name: 'Bora')
user.name = 'Asya'
user.save
user = User.find_by(name: 'Bora')
user.update(name: 'Asya')
User.update_all "max_login_attempts = 3, must_change_password = 'true'"

Delete

user = User.find_by(name: 'David')
user.destroy
# find and delete all users named David
User.where(name: 'David').destroy_all

# delete all users
User.destroy_all

Validation

İlgili veriler için kontrol yapabilmemizi sağlar. Bu işlem sunucu tarafı validasyondur. İstemci tarafında da validasyon işlemi yapılabilir (submit butonunu disable yapmak gibi). Fakat asla son kullanıcıya güvenilmemelidir. Sunucu tarafında validasyon yapmak oldukça önemlidir!

class User < ApplicationRecord
  validates :name, presence: true
end

user = User.new
user.save  # => false
user.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
|
|   İstemci Validasyonu (Javascript, HTML ile vs.)
|
|   Sunucu Controller
|
|   Sunucu Validasyonu - Model Katmanı
|
|   Sunucu Validasyonu - Database Katmano
|

Callbacks

Migrations

class CreatePublications < ActiveRecord::Migration[5.0]
  def change
    create_table :publications do |t|
      t.string :title
      t.text :description
      t.references :publication_type
      t.integer :publisher_id
      t.string :publisher_type
      t.boolean :single_issue

      t.timestamps
    end
    add_index :publications, :publication_type_id
  end
end

Migration'ı çalıştırmak:

rails db:migrate

Migration'ı geri almak:

rails db:rollback

Migrations

Rails-6.0.0-rc1 ile yeni bir proje oluşturduk.

Versiyonları tutmak için database'da schema_table oluşturuyor ve versiyonları orada saklayarak db ile migration'ları eş(sync) olup olmadığını kontrol ediyor.

rails db:create
rails generate migration CreateProducts name:string description:text
# db/migrate/create_products.rb
class CreateProducts < ActiveRecord::Migration[6.0]
  def change
    create_table :products do |t|
      t.string :name
      t.text :description
      t.timestamps
    end
  end
end
rails db:migrate
# db/schema.rb
ActiveRecord::Schema.define(version: 2019_07_22_151741) do

  create_table "products", force: :cascade do |t|
    t.string "name"
    t.text "description"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

end
rails generate migration AddPartNumberToProducts part_number:string
# db/migrate/add_part_number_to_products.rb
class AddPartNumberToProducts < ActiveRecord::Migration[6.0]
  def change
    add_column :products, :part_number, :string
  end
end
rails db:migrate

Migration'larda geri gitmek için rollback yaparız. Aşağıdaki gibi

ruby db:rollback

Rollback yapmadan migration'ları edit'lemeyin!

Migration'ların durumuna bakmak için:

ruby db:migrate:status

En son migration'ı aşağıdaki gibi yapıp tekrar migrate çekelim.

class AddPartNumberToProducts < ActiveRecord::Migration[6.0]
  def change
    add_column :products, :part_number, :string
    add_index :products, :part_number
  end
end
rails db:migrate

Peki index nedir ?

Arama sürecini hızlandırmak için index'liyoruz.

index = performans


Bir şey silmek için aşağıdaki gibi yapabiliriz

rails d migration AddDetailsToProducts part_number:string price:decimal
rails db:migrate

Eğer bir migration'da bir çok şey ekleyecek ise ismi direkt olarak açıklayıcı bir şey yazabiliriz. Ardından elimizle migration dosyasını güncelleriz.

rails generate migration AddDetailsToProducts part_number:string:index price:decimal:index
class AddDetailsToProducts < ActiveRecord::Migration[6.0]
  def change
    add_column :products, :part_number, :string
    add_index :products, :part_number
    add_column :products, :price, :decimal
    add_index :products, :price
  end
end
rails db:migrate

Yeni bir tablo oluşturalım.

rails generate migration CreateUsers name:string age:integer
class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :name
      t.integer :age
    end
  end
end

References

rails generate migration AddUserRefToProducts user:references

User'ların product'ları olsun.

    ---------                     ---------
   | Product |  ---------------> | User    |
    ---------                 /   ---------
                             /
    ---------               /
   | Product |  ------ ----/
    ---------

Bu durumda Product altında user_id diye bir column tutarız.

user_id: references

Yani user_id bir foreign_key(ikincil-yabancı anahtar olacaktır.)

Rails'de yazarken user_id değil user yazarız.

class AddUserRefToProducts < ActiveRecord::Migration[6.0]
  def change
    add_reference :products, :user, foreign_key: true
  end
end

Reference kolonları yani foreign_key'ler her zaman index'lenir.

Join Tables

Ara tablolar.

Many-to-many ilişki

    ---------                      ---------
   | Product |  --------------->  | User    |
    ---------                  /   ---------
                              /
    ---------                /
   | Product |  ------------/
    ---------   \
                 \
                  ------------------------
                                          \
                                           ---------
                                          | User    |
                                           ---------

Customer diye bir tablo yaratalım. Bu sefer artık bunun modelini de oluşturmak istiyoruz. Aşağıdaki gibi oluşturalım. Bu sefer hem model hem de migration oluşturacak.

rails generate model Customer first_name:string last_name:string email:string
invoke  active_record
create    db/migrate/create_customers.rb
create    app/models/customer.rb
invoke    test_unit
create      test/models/customer_test.rb
create      test/fixtures/customers.yml

Model generate ederek yapınca, migration oluştururken timestamp'leri de yazıverdi.

class CreateCustomers < ActiveRecord::Migration[6.0]
  def change
    create_table :customers do |t|
      t.string :first_name
      t.string :last_name
      t.string :email

      t.timestamps
    end
  end
end

Şimdi product ile customer arasında aratablo oluşturalım.

rails generate migration CreateJoinTableCustomerProduct customer product
class CreateJoinTableCustomerProduct < ActiveRecord::Migration[6.0]
  def change
    create_join_table :customers, :products do |t|
      t.index [:customer_id, :product_id]
      t.index [:product_id, :customer_id]
    end
  end
end

JoinTable'da yalnızca foreign_key'ler olur. Eğer ek bir şey eklemek istersek(oluşturma tarihi vs. gibi) jointable değil de ayrı bir model olarak yaparız.

rails db:migrate