아래내용은 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 관련해서 레일즈 플러그인이 하나 나왔네요.
AutoScopeExamples
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]])