Railscasts (no1 ~ no5) : 레일즈 스크린캐스트


아래내용은 railscasts.com 사이트에 올라온 동영상의 내용입니다. 레일즈 관련 팁들을 screencast 형태로 제공하고 있습니다. 직접 동영상을 보시면 더욱 좋습니다.

No1. Caching with instance variable

컨트롤러에서 모델의 find 메소드를 사용하는 경우 매번 DB 에 쿼리를 날리게 됩니다.

class ApplicationController < ActionController::Base

def current_user

User.find(session[:user_id]) # 이렇게 하면 매번 쿼리를 날리게 됨.

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

end

end

볼드체로된 부분 처럼 항상 DB 쿼리를 하지 않도록, 인스턴스 변수를 활용해서 캐싱하는 형태로 사용하면 항상 DB자원을 사용하는 것을 줄여줄 수 있습니다.

No2. Dynamic find by methods

find 메소드의 형태를 좀더 간소하게 사용할 수 있는 find_by_methods 의 사용에 대해서 알아보도록 합니다. 간단하므로 아래 예제를 보도록 하죠.

find 메소드의 컨디션에 해당하는 컬럼을 메소드 명으로 줄 수 있습니다. 레일즈가 다이내믹하게 처리한다는 얘기죠.

class TasksController < ApplicationController

def incomplete

@tasks = Task.find(:all, :conditions => [‘complete =?’, false])

@tasks = Task.find_all_by_complete(false)

end

def last_incomplete

@task = Task.find(:first, :conditions => [‘complete = ?’, false], :order => ‘created_at DESC’)

@task = Task.find_by_complete(false, :order => ‘created_at DESC’)

end

end

No3. Find through association

모델간의 관계를 이용해서 데이타를 찾아도록 하는 방법에 대해서 알아보도록 합시다.

모델의 관계는 다음과 같습니다.

class Project < ActiveRecord::Base

has_many :tasks

end

class Task < ActiveRecord::Base

belongs_to :project

end

이경우 Association 을 이용해서 데이타를 가져오면서  No2 에서 제공하는 find_by_methods 까지 활용해서 수정을 하면 아래와 같습니다.

class ProjectsController < ApplicationController

def show

@project = Project.find(params[:id])

@tasks = Task.find(:all, :conditions => [‘project_id = ? AND complete = ?’, @project.id, false])

@tasks = @project.tasks.find_all_by_complete(false)

end

end

깔끔한 코드 ~~

No4. Move find into model

custom find 메소드를 생성해서 find 메소드를 모델로 이동시킬 경우 얻을 수 있는 장점은?

class TaskController < ApplicationController

def index

@tasks = Task.find_all_by_complete(false, :order => ‘created_at DESC’)

@tasks = Task.find_incomplete

end

end

위와 같은 형태에서 find_all_by_complete 를 find_incomplete 라는 클래스 메소드로 활용하게 되면 Task 모델에 다음과 같은 클래스 메소드를 추가하면 됩니다.

def self.find_incomplete

find_all_by_complete(false, :order => ‘created_at DESC’)

end

이런식으로 모델의 클래스 메소드로 생성해 두는 경우 아래와 같이 컨트롤러에서도 활용 가능죠.

class ProjectController < ApplicationController

def show

@project = Project.find(params[:id])

@tasks = @project.tasks.find_all_by_complete(false, :order => ‘created_at DESC’)

@tasks = @project.taskts.find_incomplete

end

end

No5. Using with scope

find 메소드를 커스텀하게 작성해서 모델로 이동시켜서 사용하는 경우, 커스텀 find 메소드로는 파라미터를 어떻게 전달할 수 있을 까요?

class Task < ActiveRecord::Base

belongs_to :project

def self.find_incomplete

find_all_by_complete(false, :order => ‘created_at DESC’)

end

end

이렇게 커스텀 find 메소드인 find_incomplete 를 생성해서 쓰고 있었는데, 이 메소드를 사용하는 컨트롤러에서는 호출시에 현재로서는 파라미터를 전달할 방법이 없습니다.

예를 들어서 아래와 같이 호출하고 싶은 경우 find_incomplete 메소드를 어떻게 수정하면 될까요?

class TaskController < AppliationController

def index

@tasks = Task.find_incomplete :limit => 20  #이렇게 파라미터를 추가해서 호출하고 싶다.

end

end

방법은 Rails 에서 제공하는 함수인 with_scope 를 사용하면 좀더 우아하게 처리할 수 있습니다.

find_incomplete 클래스 메소드를 수정해보도록 합시다.

def self.find_incomplete(options = {})

with_scope :find => options do

find_all_by_complete(false, :order => ‘created_at DESC’)

end

end

이렇게 수정하면 어떠한 형태의 파라미터도 다이내믹하게 처리할 수 있겠네요.

우아해졌습니다 ~~

P.S. 보다보니 벌써 동영상이 #11 까지 올라왔네요.. 나머지도 한번 가서 보세요… 짧으니 한꺼번에 보시면 저같은 초보 분들은 도움이 될만한 것들을 금방 얻을 수 있겠습니다.

[추가내용]
scope access method 관련해서 레일즈 플러그인이 하나 나왔네요.
AutoScope

Examples

Declare your scopes within your ActiveRecord::Base subclasses.

 class Contact < ActiveRecord::Base
auto_scope \
:old => {:find => {:conditions => ["born_on < ?", 30.years.ago]}},
:young => {:find => {:conditions => ["born_on > ?", 1.year.ago]}}
end

class Testimonial < ActiveRecord::Base
auto_scope \
:approved => {
:find => {:conditions => ["approved_at < ?", proc {Time.now}]},
:create => {:approved_at => proc {Time.now}}},
:unapproved => {
:find => {:conditions => "approved_at IS NULL"},
:create => {:approved_at => nil}}
end

이렇게 해주면 아래와 같이 사용하실 수 있습니다.

Testimonial.approved.count
Testimonial.unapproved.create!(params[:testimonial])
@young_contacts = Contact.young
@contacts = Contact.old.find(:all, :conditions => ["name LIKE ?", params[:name]])