Skip to content

Latest commit

 

History

History
827 lines (634 loc) · 20.5 KB

File metadata and controls

827 lines (634 loc) · 20.5 KB

Form Helpers

5.1 ile birlikte form_with geldi. Form_for diye bir şey de var. Form_with default olarak ajax atabilecek şekilde çalışır.

Form helper'ı test edebilmek için rubit örneğimizde posts#new action'nı oluşturalım

posts_controller

def new
end

routes

get 'posts/new'

Ardından view dosyamızı yaratalım.
posts/new.html.erb

<% content_for :action_name,
               render('layouts/shared/current_action',
                      parent_page: 'new')
%>
<%= content_for :page_title, 'Posts details' %>

<h1>Posts#new</h1>
<p>Find me in app/views/posts/new.html.erb</p>

Şimdi form_with helper ile bir form oluşturalım.

<%= form_with do %>
  Form contents
<% end %>

Güvenlik açısından bazı key'ler kullanılır. https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)

url ve method parametreleri ile aşağıdaki gibi yapabiliriz.

<%= form_with(url: "/search", method: "get") do %>
  <%= label_tag(:q, "Search for:") %>
  <%= text_field_tag(:q) %>
  <%= submit_tag("Search") %>
<% end %>

Submit yapıldığında aldığı parametreleri belirlenen hedefe belirlenen method şeklinde yollar.


Form Helpers

<%= text_area_tag(:message, "Hi, nice site", size: "24x6") %>
<%= password_field_tag(:password) %>
<%= hidden_field_tag(:parent_id, "5") %>
<%= search_field(:user, :name) %>
<%= telephone_field(:user, :phone) %>
<%= date_field(:user, :born_on) %>
<%= datetime_local_field(:user, :graduation_day) %>
<%= month_field(:user, :birthday_month) %>
<%= week_field(:user, :birthday_week) %>
<%= url_field(:user, :homepage) %>
<%= email_field(:user, :address) %>
<%= color_field(:user, :favorite_color) %>
<%= time_field(:task, :started_at) %>
<%= number_field(:product, :price, in: 1.0..20.0, step: 0.5) %>
<%= range_field(:product, :discount, in: 1..100) %>

Bu helper'ın hepsi tüm tarayıcılar tarafından desteklenmeyebilir. Kullanırken test edilmesi gerekebilir. Hangi tarayıcılar destekliyor görmek için: https://modernizr.com/


posts_controller

  def new
    @post = Post.new
  end

Yaparak form'da da modeli belirterek o model için yapılması sağlayabiliriz.

Routes

Rails.application.routes.draw do
  resources :posts
end

Form

<%= form_with(model: @post) do %>
  <%= submit_tag("Search") %>
<% end %>

Artık modele göre ne yapmak istediğimiz otomatik anlayacak. Örneğin biz boş bir post oluşturmuştuk, bu yazmak istediğimizi anlayarak create action'a post isteği atacaktır.

create action'da oluşturalım.

def create
  render json:params
end

Default olarak data-remote:true olduğu ajax isteği atar. Bunu tarayıcımızda network'te XHR etiketi altında görebiliriz. Ajax isteği atmaması için local:true olarak setlemeliyiz.

Form

<%= form_with(model: @post, local: true) do %>
  <%= submit_tag("Search") %>
<% end %>

Peki create etmek istediğimizi nasıl anladı ? Çünkü içi boş. Eğer içi dolu olsaydı update etmesi gerektiğini bilecekti.

Form'un girdi değerlerini de model etkiketi içinde çalışacak şekilde yapalım.

Form

<%= form_with model: @post, local: true do |form| %>
  <%= form.text_field :title %>
  <%= form.submit "Search" %>
<% end %>

İstersek model kullanırken url, path vs verebiliriz.


Form_with kullanırken namespace'de kullanabiliriz. Örneğin admin'nin içinde post'a yapmak istiyorsak.

form_with model: [:admin, @post]

Browser'lar aslında sadece get ve post'u tanır. Diğerleri için ekstra bir input alanı eklenerek method oraya yazılır.
REST.


File Upload için ActiveStroge


Simple_form

herşey onun için input olarak yazılıyor, o tipine bakarak hepsini anlıyor.

https://github.com/plataformatec/simple_form

Rails'deki keskin bıçaklar herşeyi yapmanıza olanak sağlıyor.


Nested Forms

accepts_nested_attributes_for

Form içinde form yapabilmemizi sağlar.

Örneğin tek bir form'da hem user hem de profil bilgileri alıyor olalım.

User model'inde

accepts_nested_attributes_for :profile

Artık rails otomatik olarak user formu içinde profil bilgileri gelebileceğini bilecek.

<%= form_with model: @user do |f| %>
  <%= f.text_field :name %>
  Profile:
  <ul>
    <%= f.fields_for :profile do |addresses_form| %>
      <li>
        <%= addresses_form.label :first_name %>
        <%= addresses_form.text_field :first_name %>
 
        <%= addresses_form.label :last_name %>
        <%= addresses_form.text_field :last_name %>
        ...
      </li>
    <% end %>
  </ul>
<% end %>

Artık gelen parametreleri kullanarak direkt yaratabiliriz.

def create
  @user = User.new(user_params)
  # ...
end
 
private
  def user_params
    params.require(:user).permit(:name, profile_attributes: [:first_name, :last_name])
  end

Action Controller

Controller isimleri çoğul.

class ClientsController < ApplicationController
  def new
  end
end
get '/clients/:status', to: 'clients#index'

params[:status]


path'in önüne bir şey koymak için

def default_url_options
  { locale: I18n.locale }
end

Strong Parameters

Controller'da belirli parametreleri kullanmak içindir. Başka gelir ise işleme almaz. Güvenlik için.

Örneğimize devam edip strong params'ı uygulayalım.

class PostsController < ApplicationController
  def index
    @posts = Post.latest_posts
  end

  def show
    @post = Post.find(params[:id])
  end

  def new
    @post = Post.new
  end

  def create
    @post = Post.create(posts_params)
    redirect_to post_path(@post)
  end

  private

    def posts_params
      params.require(:post)
            .permit(:body)
    end
end

https://gist.github.com/peternixey/1978249


Session

Session server'da, cookie user'da olur.

Default olarak session için oluşturulan cookie'ler user'da tutulur.

Cookie'e 4kb'den fazla boyutta veri yazılmaz.

Eğer daha büyük bir şey tutmak için ActiveRecordStore'da tutulur yani db'de tutulur cookie.

Bir değişken set'lemekten farkı yok

Cookie olarak default olarak oluşturulmuş session cookie ile server'deki session'nı karşılaştırır. Temelde session ve cookie birbirine benzese de farklıdır aslında.

class LoginsController < ApplicationController
  # "Create" a login, aka "log the user in"
  def create
    if user = User.authenticate(params[:username], params[:password])
      # Save the user ID in the session so it can be used in
      # subsequent requests
      session[:current_user_id] = user.id
      redirect_to root_url
    end
  end
end

Current user var ise döndür, yok ise db'den bulup yolla, session'a yazılan değeri kullanarak.

class ApplicationController < ActionController::Base
 
  private
 
  # Finds the User with the ID stored in the session with the key
  # :current_user_id This is a common way to handle user login in
  # a Rails application; logging in sets the session value and
  # logging out removes it.
  def current_user
    @_current_user ||= session[:current_user_id] &&
      User.find_by(id: session[:current_user_id])
  end
end

Logout olurken session'lar öldürülür, cookie'ler silinir.

class LoginsController < ApplicationController
  # "Delete" a login, aka "log the user out"
  def destroy
    # Remove the user id from the session
    session.delete(:current_user_id)
    # Clear the memoized current user
    @_current_user = nil
    redirect_to root_url
  end
end

Bu işlemleri otomatik olarak yapmak için devise diye bir gem var.

https://github.com/plataformatec/devise


Flash

flash'ları setlemek için

redirect_to root_url, notice: "You have successfully logged out."
redirect_to root_url, alert: "You're stuck here!"
redirect_to root_url, flash: { referral_code: 1234 }

flash'ları göstermek için

<html>
  <!-- <head/> -->
  <body>
    <% flash.each do |name, msg| -%>
      <%= content_tag :div, msg, class: name %>
    <% end -%>
 
    <!-- more content -->
  </body>
</html>

Cookies

cookies[:test_cookie] = "cookieeeeeee"
class CommentsController < ApplicationController
  def new
    # Auto-fill the commenter's name if it has been stored in a cookie
    @comment = Comment.new(author: cookies[:commenter_name])
  end
 
  def create
    @comment = Comment.new(params[:comment])
    if @comment.save
      flash[:notice] = "Thanks for your comment!"
      if params[:remember_name]
        # Remember the commenter's name.
        cookies[:commenter_name] = @comment.author
      else
        # Delete cookie for the commenter's name cookie, if any.
        cookies.delete(:commenter_name)
      end
      redirect_to @comment.article
    else
      render action: "new"
    end
  end
end

Cookie'yı şifrelenmiş olarakta tutabiliriz.

class CookiesController < ApplicationController
  def set_cookie
    cookies.encrypted[:expiration_date] = Date.tomorrow # => Thu, 20 Mar 2014
    redirect_to action: 'read_cookie'
  end
 
  def read_cookie
    cookies.encrypted[:expiration_date] # => "2014-03-20"
  end
end

Render

controllerde xml, json gibi verileri render'layabiliriz.

Gelen isteğe göre aşağıdaki gibi manüpile edebiliriz.

localhost:3000/posts.json gibi adrese gidildiğinde aşağıdaki gibi döneriz.

class UsersController < ApplicationController
  def index
    @users = User.all
    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render xml: @posts }
      format.json { render json: @posts }
      format.pdf { render plain: @posts }
    end
  end
end

Cookie örnekleri.

class UsersController < ApplicationController
  def index
  end

  def create_cookie
    session[:name] = params[:name]
    redirect_to users_path
  end

  def delete_cookie
    session[:name] = nil
    redirect_to users_path
  end

  private

    def user_params
      params.require(:user)
            .permit(:email)
    end
end
<% if session[:name] %>
  <h1>
    <%= "Hey #{cookies[:name]}" %>
  </h1>
  <%= link_to 'Cookie Sil', users_delete_cookie_path, method: :post %>
<% else %>
  <h1>Hadi adını söyle</h1>
  <%= form_with url: users_create_cookie_path, method: :post do |form| %>
    <%= form.label :name %>
    <%= form.text_field :name %>
    <%= form.submit %>
  <% end %>
<% end %>
post 'users/create_cookie', to: 'users#create_cookie'
post 'users/delete_cookie', to: 'users#delete_cookie'

Sayı artırmak

class UsersController < ApplicationController
  def index
    cookies[:count] = cookies[:count].to_i.next
  end
end

Application Layout

<%= "Sayaç: #{cookies[:count]}" %>

Filters

Filtreler 'before', 'after' ve 'around' olarak çalışabilir.

class ApplicationController < ActionController::Base
  before_action :require_login
 
  private
 
  def require_login
    unless logged_in?
      flash[:error] = "You must be logged in to access this section"
      redirect_to new_login_url # halts request cycle
    end
  end
end

Örneğin sayaç methodu için bu uygulanabilir.

Sayı artırmak

class UsersController < ApplicationController
  before_action :increase_count, only: :index

  def index
  end

  private

    def increase_count
      cookies[:count] = cookies[:count].to_i.next
    end
end

Around kullanımı

class ChangesController < ApplicationController
  around_action :wrap_in_transaction, only: :show
 
  private
 
  def wrap_in_transaction
    ActiveRecord::Base.transaction do
      begin
        yield
      ensure
        raise ActiveRecord::Rollback
      end
    end
  end
end

Request

request.host
request.format
request.method

Response

response.headers["Content-Type"] = "application/pdf"

HTTP Authentications

class AdminsController < ApplicationController
  http_basic_authenticate_with name: "humbaba", password: "5baa61e4"
end

eğer log'a bazı attribute'lerin basılmasını istemiyor isek

config.filter_parameters << :password

Controller'da anlatılan kavramlar

  • Action
  • Cookie
  • Session
  • Params
  • Strong Params
  • Response ve Request objeleri
  • Respond_to
  • Format
  • Before Action vs.
  • Redirect to
  • Flash

Routing

 ----                                 İSTEK
| DB |                               /
 ----                               /
  ||                        --------
  ||                       | Router |
 -------                    --------
| Model |                /
 -------                /
   \\                  /
    \\                /
     \\   ------------               ------
      \\ | Controller | <---------> | View |
          ------------               ------
             REST
URL        /posts/new
 |         /posts/index     
 |------------|-----|
        Controller  Action

|___________________________|
      FrontController
          DesignPattern        

Router'in yaptığı iç controller'ın ilgili action'nına yönlendirmektir.

Path'e, Method'a, Parametrelere bakarak nereye gidilmesi gerektiğine karar verir.


Bir GET isteği yapılmış, bunu yönlendirmeyi sağlar.

get '/patients/:id', to: 'patients#show', as: 'patient'
   |----------------|   |---------------|    |----------|
       PATTERN          CONTROLLER/ACTION    patient_path(id)
                                                  |
                                                  v
                                              /patients/:id   

as'i yazmaz isek otomatik olarak pattern'den yaratılır.

<%= link_to 'Patient Record', patient_path(@patient) %>

Standart, restful dışında bir pattern yazarsanız otomatik olarak helper yaratmaz.


Rails Ujs özelliği ile herşeyi post gibi çalıştırır arka planda yakalar


Restful
index
show
new
create
edit
update
destroy


aaaa_url -> tüm url (http://a.com:3000/hehehe)
aaaa_path -> yalnızca path kısmı (/hehehe)


Eğer RESTFUL yapacaksak, CRUD yapacaksak direkt olarak resources helper'ını kullanabiliriz.

resources :birkelime
birkelime_index GET    /birkelime(.:format)           birkelime#index
                POST   /birkelime(.:format)           birkelime#create
  new_birkelime GET    /birkelime/new(.:format)       birkelime#new
 edit_birkelime GET    /birkelime/:id/edit(.:format)  birkelime#edit
      birkelime GET    /birkelime/:id(.:format)       birkelime#show
                PATCH  /birkelime/:id(.:format)       birkelime#update
                PUT    /birkelime/:id(.:format)       birkelime#update
                DELETE /birkelime/:id(.:format)       birkelime#destroy

only sayesinde istediğimiz action'ların yaratılmasını sağayalabiliriz.

resources :birkelime, only: [:index, :show]
birkelime_index GET    /birkelime(.:format)           birkelime#index
      birkelime GET    /birkelime/:id(.:format)       birkelime#show

HTTP Verb Path Controller#Action Used for
GET /photos photos#index display a list of all photos
GET /photos/new photos#new return an HTML form for creating a new photo
POST /photos photos#create create a new photo
GET /photos/:id photos#show display a specific photo
GET /photos/:id/edit photos#edit return an HTML form for editing a photo
PATCH/PUT /photos/:id photos#update update a specific photo
DELETE /photos/:id photos#destroy delete a specific photo

Çoğul

resources :photos
resources :books
resources :videos

Tekil

resource :geocoder

Namespace

/controllers/admin/articles/

namespace :admin do
  resources :articles, except: :index
end

resources :articles, only: :index

Scope

Bir de scope diye bir şey var. Url'lerin değişmesini istemediğimizde ama modüle (kapsüle) almak istediğimizde uygularız. Linkler aynı kalır ama modüle taşırız.

/controllers/admin/users, path'ler yalın olarak

scope module: 'admin' do
  resources :users
end

Nested Resources

Magazine'lerin her zaman publisher, photo'ların da her zaman magazine'e ihtiyacı olduğu durumlarda nested resources kullanılır.

resources :publishers do
  resources :magazines do
    resources :photos
  end
end

Yukardaki kod aşağıdaki yönlendirmeleri yaratacaktır.

    publisher_magazine_photos GET    /publishers/:publisher_id/magazines/:magazine_id/photos(.:format)           photos#index
                              POST   /publishers/:publisher_id/magazines/:magazine_id/photos(.:format)           photos#create
 new_publisher_magazine_photo GET    /publishers/:publisher_id/magazines/:magazine_id/photos/new(.:format)       photos#new
edit_publisher_magazine_photo GET    /publishers/:publisher_id/magazines/:magazine_id/photos/:id/edit(.:format)  photos#edit
     publisher_magazine_photo GET    /publishers/:publisher_id/magazines/:magazine_id/photos/:id(.:format)       photos#show
                              PATCH  /publishers/:publisher_id/magazines/:magazine_id/photos/:id(.:format)       photos#update
                              PUT    /publishers/:publisher_id/magazines/:magazine_id/photos/:id(.:format)       photos#update
                              DELETE /publishers/:publisher_id/magazines/:magazine_id/photos/:id(.:format)       photos#destroy
          publisher_magazines GET    /publishers/:publisher_id/magazines(.:format)                               magazines#index
                              POST   /publishers/:publisher_id/magazines(.:format)                               magazines#create
       new_publisher_magazine GET    /publishers/:publisher_id/magazines/new(.:format)                           magazines#new
      edit_publisher_magazine GET    /publishers/:publisher_id/magazines/:id/edit(.:format)                      magazines#edit
           publisher_magazine GET    /publishers/:publisher_id/magazines/:id(.:format)                           magazines#show
                              PATCH  /publishers/:publisher_id/magazines/:id(.:format)                           magazines#update
                              PUT    /publishers/:publisher_id/magazines/:id(.:format)                           magazines#update
                              DELETE /publishers/:publisher_id/magazines/:id(.:format)                           magazines#destroy
                   publishers GET    /publishers(.:format)                                                       publishers#index
                              POST   /publishers(.:format)                                                       publishers#create
                new_publisher GET    /publishers/new(.:format)                                                   publishers#new
               edit_publisher GET    /publishers/:id/edit(.:format)                                              publishers#edit
                    publisher GET    /publishers/:id(.:format)                                                   publishers#show
                              PATCH  /publishers/:id(.:format)                                                   publishers#update
                              PUT    /publishers/:id(.:format)                                                   publishers#update
                              DELETE /publishers/:id(.:format)                                                   publishers#destroy

Resources parametreleri yine çalışabilirdir.

resources :publishers, only: [:index, :new, :create] do
  resources :magazines, only: [:index, :show] do
    resources :photos, only: [:index]
  end
end
publisher_magazine_photos GET    /publishers/:publisher_id/magazines/:magazine_id/photos(.:format)   photos#index
      publisher_magazines GET    /publishers/:publisher_id/magazines(.:format)                       magazines#index
       publisher_magazine GET    /publishers/:publisher_id/magazines/:id(.:format)                   magazines#show
               publishers GET    /publishers(.:format)                                               publishers#index
                          POST   /publishers(.:format)                                               publishers#create
            new_publisher GET    /publishers/new(.:format)                                           publishers#new