Jumat, 11 September 2015

Active Job di Ruby on Rails

Kali ini kita akan bersama-sama belajar mengenal Active Job di Ruby on Rails. Kita akan mempelajari pengertiannya, manfaatnya dan contoh penggunaannya.

Active Job adalah sebuah framework, digunakan untuk mendlekarasikan sebuah pekerjaan yang nantinya akan dijalankan dala sebuah urutan di backend. Pekerjaan yang dimaksud disini bisa pekerjaan apa saja, mulai dari pembersihan data, pengurutan data, mailing dan pekerjaan lainnya yang bisa dikerjakan secara pararel.

Secara umum manfaat penggunaan Active Job adalah sebagai wrapper background job processing sehingga memudahkan developer untuk menuliskan code secara general. Active job menerjemahkan syntax yang ditulis developer agar bisa dibaca background engine yang digunakan. Terdapat tiga kondisi yang biasanya dibutuhkan sebuah active job:
  1. Sebuah proses yang membutuhkan External API untuk melakukan proses.
  2. Sebuah proses yang memerlukan banyak perhitungan atau proses (contohnya memproses sebuah gambar).
  3. Mengirimkaan email atau posting ke media lain (social media, blog dll).
Namun dalam prakteknya active job belum bisa berjalan sendiri, diperlukan library backend agar bisa menjalankannya. Rails 4 sudah menyediakan beberapa adapter untuk backend tersebut, namun kita masih harus menginstal gem dari backend yang akan kita pakai. List adapter yang bisa kita gunakan di rails bisa klik link ini.

Agar semakin paham, kita akan langsung mencoba menerapkannya dalam sebuah aplikasi sederhana. Kita akan mencoba menghapus beberapa data sekaligus melalui active job dengan adapter Sidekiq.

Buat aplikasi sederhana
Pertama siapkan sebuah aplikasi sederhana dengan dua model yaitu model User dan model Article dimana user has many articles. Kemudian isikan beberapa data di dalamnya. Anda bisa memanfaatkan aplikasi yang sudah pernah kita buat di belajar bareng kita sebelumnya (kalau ketinggalan bisa klik di sini)

Install sidekiq
Setelah aplikasi sederhana sudah berjalan selanjutnya kita install dulu sidekiq di aplikasi kita, caranya dengan memasukan gem sidekiq ke dalam gemlist.

gem 'sidekiq', '3.2.5'

Lalu lakukan bundle install.
Setelah itu kita akan membuat config file untuk sidekiq. Buat sebuah file di path config dengan nama sidekiq.yml

config/sidekiq.yml

:concurrency: 25
:pidfile: ./tmp/pids/sidekiq.pid
:logfile: ./log/sidekiq.log
:queues:
  - default

Untuk mengecek apakah sidekiq sudah terinstall dengan benar, jalankan server ( $ rails s) kemudian buka tab baru dari path tersebut. 
jalankan perintah:

sidekiq -C config/sidekiq.yml

Kalo tidak ada masalah seharusnya kurang lebih tampilannya akan seperti ini:

Jadi sekarang kita seolah-olah menjalankan dua server, satu untuk server rails dan yang satu untuk sidekiq. Biarkan keduanya berjalan namun bila ingin menghentikannya tinggal menekan kombinasi tombol Ctrl + C

Membuat active job
Sekarang kita akan memulai untuk membuat file active job dan melakukan beberapa penyesuaian agar bisa menjalankan active job dengan adapter sidekiq.
Pertama kita buat dulu file active job, caranya dengan mengetikan perintah ini di terminal:
rails generate job articles_cleanup

Secara otomatis akan terbentuk sebuah path baru di dalam app yaitu path jobs dan sudah ada satu file didalamnya dengan nama articles_cleanup_job.rb, kemudian kita isi file tersebut dengan code yang kurang lebih seperti ini:

app/jobs/articles_cleanup_job.rb
class ArticlesCleanupJob < ActiveJob::Base
  queue_as :default

  def perform(user_id)
    user = User.find(user_id)
    count = 0
    if user
      puts "Prepare to Delete article form user #{user.name}"
      articles = user.articles
      if articles
        articles.each do |article|
         
count += 1 if article.destroy
        end   
      end
    puts "Delete article finished, deleted articles = #{count}"
      end   
  end
end

Keterangan:
Agar nanti kita bisa tahu sidekiq kita telah benar-benar mengeksekusi proses delete article, kita tambahkan puts "Prepare to Delete article form user #{user.name}"  di awal proses dan puts "Delete article finished, deleted articles = #{count}" setelah proses selesai

Kemudian kita buat juga sebuah file di path config/initializers dengan nama active_job.rb lalu kita isi dengan code:

config/initiaizers/active_job.rb
ActiveJob::Base.queue_adapter = :sidekiq

Memanfaatkan active job di aplikasi
Pembuatan file active_job dan setup sidekiq sudah selesai, sekarang saatnya mencobanya pada aplikasi.
Saya akan memanfaatkan aplikasi yang sebelumnya dibuat pada sesi sebelumnya (klik di sini bila ingin membaca sesi sebelumnya), tinggal tambahkan sebuah model Article (isinya bebas) yang ber-relasi dengan model User, dimana user has_many articles.

Buat juga sebuah articles_controller dan sebuah method di controller tadi yang nanti berfungsi untuk memanggil active job tadi.


articles_controller.rb
class ArticlesController < ApplicationController
  def delete_articles
      ArticlesCleanupJob.perform_later(params[:id])
      redirect_to root_url
  end
end
 
Jangan lupa untuk mendaftarkan path-nya di routes.rb

routes.rb

resources :articles do
  member do
    get 'delete_articles'
  end

end

Pada view/home/index.html.erb juga kita ubah sedikit untuk memanggil method delete_articles tadi

view/home/index.html.erb
<%if current_user %>
  Conglartulation, you login with facebook
  </br>
  <%= link_to "Delete All articles this user"

      , delete_articles_article_path(current_user.id)%>
<%else%>
  <%= link_to "Login with facebook", "auth/facebook"%>

<%end%>

Sekarang kita akan mencobanya, tapi sebelumnya kita isi dulu tabel article dengan beberapa dummy data, bisa diisi secara random namun yang perlu diingat user_id harus terisi dengan id user yang sudah ada.

Berikut tampilan sesudah login




 Kemudian kita klik "Delete All articles this user". lalu cek di server aplikasi kita

Dari situ akan terlihat sebuah baris log:
[ActiveJob] Enqueued ArticlesCleanupJob (Job ID: 3f3571e7-81d7-48b6-a4a3-55ca7bc6dfaf) to Sidekiq(default) with arguments: "2"


Yang berarti active job kita sudah dijalankan dengan memanfaatkan sidekiq. Bisa kita buktikan dengan melihat server sidekiq

 Yup, penanda awal proses dan setelah proses sudah tertulis, berarti sidekiq telah menjalankan proses penghapusan Articles untuk user tersebut.

Demikianlah pengenalan active_job pada rails 4 secara sederhana ini. Sebenarnya pembelajaran mengenai active joba baik teknik, maupun pemanfaatannya masih sangat luas sekali, dan mungkin akan kita pelajari bersama lain waktu.
Semoga belajar bareng kali ini bermanfaat dan Happy Coding (^.^)


Sumber:
- Active Job Basics 
- How to Integrate Sidekiq With ActiveJob
- Gem Sidekiq

Rabu, 02 September 2015

Login dengan Omniauth pada Rails Part 1 (Facebook)

Sebelumnya kita pernah membahas apa itu User Authentication, bila Anda ketinggalan artikelnya, bisa klik link ini http://www.belajarrubyonrails.com/2015/06/apa-itu-user-authentication.html untuk mendapat pengertian dan penjelasannya. Kali ini kita akan mencoba menerapkannya di dalam sebuah aplikasi Ruby on Rails.

Kita akan mencoba memanfaatkan gem Omniauth dan account facebook. Secara singkat langkah-langkah yang harus kita lakukan adalah:
  1. Buat sebuah aplikasi di facebook developer  dan buat sebuah aplikasi baru Ruby on Rails
  2. Masukan Gem Omniauth dan Gem Omniauth-Facebook
  3. Buat sebuah model User
  4. Buat Sessions Controller
  5. Membuat Omniauth initializer
  6. Menghubungkan login user dengan facebook.
  
Oke, pertama kita harus membuat dahulu sebuah aplikasi di facebook developer. Lalu bila sudah, kita catat dulu app_id dan app_secret-nya. Kemudian kita lanjutkan dengan membuat sebuah aplikasi sederhana Ruby on Rails. Jangan lupa untuk memasukan gem omniauth di dalam Gemfile:

gem 'omniauth'
gem 'omniauth-facebook', '1.4.0'

Jalankan bundle install.

Selanjutnya kita buat dulu halaman root untuk aplikasi baru kita. Seperti biasa kita akan menggunakan controller home dan action index sebagai root aplikasi. Kita buat secara sederhana saja.

home_controller.rb :
class HomeController < ApplicationController
  def index    
  end
end

app/views/home/index.html.erb:
<%if current_user %>
  Congratulation, you login with facebook
<%else%>
  <%= link_to "Login with facebook", "auth/facebook"%>
<%end%>  

Membuat Model User

Sekarang kita akan membuat model User

rails g model User provider uid name oauth_token
oauth_expires_at:datetime
keterangannya adalah:
  • uid : untuk menyimpan id dari facebook
  • name: menyimpan nama user yang diambil dari facebook
  • oauth_token: token yang diberikan facebook
  • oauth_expires_at: untuk mengetahui masa berlaku token.

Jalankan rake db:migrate untuk membuat tabel users di database. Kemudian kita isi model User tadi dengan beberapa logic code agar bisa menampung data dari facebook dan diisikan ke dalam database kita.
model user.rb
class User < ActiveRecord::Base
  def self.from_omniauth(auth)

    where(provider: auth.provider
      , uid: auth.uid).first_or_initialize.tap do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.name = auth.info.name
      user.oauth_token = auth.credentials.token
   user.oauth_expires_at = Time.at(auth.credentials.expires_at)
      user.save!
    end
  end
end

Lalu sekarang kita akan mengisi application_controller.rb dengan logic code, agar bisa menyimpan session current_user.

application_controller.rb :
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  private
  def current_user   

    if session[:user_id]
      @current_user ||= User.find(session[:user_id])
    end

  end
  helper_method :current_user
end

Membuat Session Controller

Session controller dibuat agar nantinya aplikasi kita bisa menerima callback dari auth facebook, yang kemudian memprosesnya agar bisa menjadi login user di aplikasi  kita.

sessions_controller.rb :
class SessionsController < ApplicationController
  def create
    user = User.from_omniauth(env["omniauth.auth"])
    session[:user_id] = user.id
    redirect_to root_url
  end

  def destroy
    session[:user_id] = nil
    redirect_to root_url
  end
end

Owh iya, tadi untuk halaman home, kita belum mendaftarkannya di routes.rb, oleh karena itu kita daftarkan dulu sekaligus kita daftarkan path-path yang dibutuhkan agar aplikasi kita bisa berjalan.

yang perlu kita daftarkan di config/routes.rb :
root 'home#index'
match 'auth/:provider/callback', to: 'sessions#create', via: [:get, :post]
match 'auth/failure', to: redirect('/'), via: [:get, :post]
match 'signout', to: 'sessions#destroy', as: 'signout', via: [:get, :post] 

Membuat Omniauth initializer
Kali ini kita akan memanfaatkan gem yang sudah kita instal tadi dengan membuat initializer-nya.
Buat sebuah file omniauth.rb di dalam path config/initializers

isi dari config/initializers/omniauth.rb adalah:

OmniAuth.config.logger = Rails.logger

Rails.application.config.middleware.use OmniAuth::Builder do

  provider :facebook, 
  'YOUR APP_ID', 
  'YOUR APP_SECRET'
end
 keterangan: 
'YOUR APP_ID'   => Di isi dengan app_id yang kita dapat dari aplikasi facebook tadi.
'YOUR APP_SECRET' => Di isi dengan app_secret_code.
bila masih bingung letaknya, ada di halaman apps, lalu pilih setting :



Kemudian kita akan membuat sebuah file coffescript yang bernama facebook.js.coffescript yang akan membantu kita dalam memunculkan authentikasi pada account facebook. Letakan file facebook.js.coffescript di dalam path app/assets/javascripts .
Isi dari  app/assets/javascripts/facebook.js.coffescript:
jQuery ->
  $('body').prepend('<div id="fb-root"></div>')

  $.ajax
    url: "#{window.location.protocol}//connect.facebook.net/en_US/all.js"
    dataType: 'script'
    cache: true


window.fbAsyncInit = ->
  FB.init(appId: 'YOUR APP_ID', cookie: true)

  $('#sign_in').click (e) ->
    e.preventDefault()
    FB.login (response) ->
      window.location = '/auth/facebook/callback' if response.authResponse

  $('#sign_out').click (e) ->
    FB.getLoginStatus (response) ->
      FB.logout() if response.authResponse
    true

Nah kita kembali harus memasukan app_id dari aplikasi facebook kita tadi ke dalam code coffescript.

Menghubungkan User dengan Login Facebook

Untuk langkah terakhirnya, kita cukup menampilkan Nama user yang login dari facebook di halaman index atau di layout. Kali ini saya akan mencotohkan bila ditulis di dalam layout.

Di file app/views/layouts.application.html.erb tambahka kode:


<% if current_user %>
   Welcome <strong><%= current_user.name %></strong>!
   <%= link_to "Sign out", signout_path, id: "sign_out" %>

<% end %>
sebelum <%= yield %>

Dan selesailah percobaan kita. Bila tidak ada kesalahan seharusnya hasilnya kurang lebih seperti ini:
1. Saat pertama masuk ke halaman home

2. Saat mekalukan authentikasi facebook

3. Selesai, selamat Anda telah sukses login dengan facebook, EUREKAAAA !!!




Yup, dari tutorial sederhana ini kita bisa mengembangkannya menjadi lebih bermacam-macam sesuai kebutuhan.
Demikian belajar bareng kali ini, semoga bisa bermanfaat buat kita semua, Happy Coding (^.^)

Sumber:
- Authentication with Facebook and OmniAuth.

- Rails and OmniAuth
- Rails 4.1.5 omniauth strong parameters

Selasa, 01 September 2015

Concerns di Rails4

Anda mungkin memperhatikan bahwa di dalam Rails 4 terdapat 1 buah path yang ada secara default di dalam path model dan path controller. Path tersebut adalah "Concerns"
Lalu sebenarnya makanan jenis apa sih concerns ini? Dalam artikel ini kita akan mencoba untuk membahasnya.
Fungsi dari concerns secara umum adalah untuk membantu kita agar code yang kita tulis bisa lebih DRY(Don't Repeat Yourself) dan lebih "menguruskan" code di dalam sebuah blok model.

1. Untuk membuat code kita lebih DRY
Kita contohkan kita memiliki 3 model yang saling berhubungan

model Article
class Article < ActiveRecord::Base
  has_many :comments, as: :commentable
  def find_first_comment
    comments.first(created_at DESC
  end
  
  def self.least_commented
    # mengembalikan article yang paling sedikit commentnya
  end
end

model Question
class Question < ActiveRecord::Base
  has_many :comments, as: :commentable
  
  def find_first_comment
    comments.first(created_at DESC)
  end
  
  def self.least_commented
    # mengembalikan article yang paling sedikit commentnya
  end   
end

model Comment
class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

Dapat dilihat ketiga model tersebut saling berhubungan dengan comment sebagai polymorphicnya.
Terjadi pengulangan code pada model Articel dan Question, padahal proses dan hasil yang diinginkan adalah sama. Nah, dengan menggunakan concerns kita bisa merubahnya menjadi lebih DRY. Untuk melakukannya kita cukup membuat sebuah file rb di dalam path app/models/concerns:

Module Commentable
module Commentable
  extend ActiveSupport::Concern

  included do
    has_many :comments, as: :commentable
  end
  
  # mencari komentar pertama
  def find_first_comment
    comments.first(created_at DESC)
  end
  
  module ClassMethods 
    def least_commented
      # mengembalikan article yang paling sedikit commentnya
    end
  end
end

Setelah module tersebut dibuat kita bisa memanggilnya ke dalam model Article dan Question, kurang lebih menjadi seperti ini:

class Article < ActiveRecord::Base
  include Commentable  
end

class Question < ActiveRecord::Base
  include Commentable   
end

Sedangkan untuk model Comment tetap biarkan seperti semula.

Inti penggunaan ini adalah, menggantikan semua code yang ada sebelumnya di model Article dan Question dengan module Commentable, dan module ini bisa diimpelmentasikan ke semua model yang membutuhkan dengan catatan bahwa fungsi dan prosesnya bisa dipakai untuk ke dua model tersebut.

Kalau kita perhatikan terdapat dua cara penulisan method di Commentable.
Pertama:
def <method_name> 
end

Kedua:
module ClassMethods    
  def <method_name>
  end   
end

Untuk code yang pertama digunakan apabila method merupakan instance method di dalam model ( def <methode_name> end). Sedangkan yang kedua dipakai apabila method merupakan class method yang ada di dalam model  ( def self.<methode_name> end).

 2. Lebih "menguruskan" code di dalam sebuah blok model
Terkadang kita akan menjumpai sebuah model dengan banyak sekali method di dalamnya. Bahkan bisa sampai ribuan atau bahkan puluhan ribu baris. Tentu saja hal ini akan sangat menyulitkan bila kita menemukan bug dan harus memeriksa depedency bug tersebut.
Kita harus mensederhanakannya agar lebih enak dipandang dan memudahkan kita nantinya.
Dengan concern kita bisa melakukannya.
Masih menggunakan contoh 3 model tadi:
class Article < ActiveRecord::Base
  has_many :comments
  has_many :questions

  def find_first_comment
    # mencari comment pertama dalam sebuah artikel
  end

  def self.least_commented
    # mencari article dengan comment paling sedikit
  end

  def self.most_questionable
    # returns articel yang paling banyak memiliki question
  end

  def has_question(question_id)
    # returns true jika terdapat question yang diinginkan
  end
end

Kita bisa membuat code di dalam model Article menjadi lebih pendek lagi dengan memanfaatkan concerns. Di dalam path app/models/concerns kita menambahkan dua module:
module quetionable
module Quetionable
  extend ActiveSupport::Concern
  
  included do
    has_many :quetions
  end
  
  def has_quetion(attender_id)
    # returns true jika terdapat question yang diinginkan
  end
  
  module ClassMethods
    def most_quetionable
      # returns articel yang paling banyak memiliki question
    end
  end
end

module commentable
module Commentable
  extend ActiveSupport::Concern
  
  included do
    has_many :comments
  end
  
  def find_first_comment
    # mencari comment pertama dalam sebuah artikel
  end
  
  module ClassMethods
    def least_commented
      # mencari article dengan comment paling sedikit
    end
  end
end

Dengan menggunakan concern diatas kita bisa merubah code di model Article menjadi:
class Article < ActiveRecord::Base
  include Commentable
  include Quetionable
end

Terlihat menjadi sangat pendek bukan?
Penggunannya bisa kita sesuaikan dengan kondisi code kita masing - masing.
Untuk controller penggunannya juga kurang lebih sama dengan model hanya pathnya saja berbeda.

Semoga artikel ini bisa membantu untuk lebih memahami mengenai "concerns" di rails 4. Happy coding(^.^)

Sumber:
Api ruby on rails
How to use concerns in Rails 4 
- Code Concerns in Rails 4 Models
- Controller Concerns in Rails 4