미래 지향적인 웹 앱 구축: The Codest의 전문가 팀이 제공하는 인사이트
The Codest가 최첨단 기술로 확장 가능한 대화형 웹 애플리케이션을 제작하고 모든 플랫폼에서 원활한 사용자 경험을 제공하는 데 탁월한 성능을 발휘하는 방법을 알아보세요. Adobe의 전문성이 어떻게 디지털 혁신과 비즈니스를 촉진하는지 알아보세요...
데이터를 필터링하기 위해 레일즈 컨트롤러에서 변하는 인스턴스 변수를 볼 때마다 화가 나시나요? 이 글은 여러분을 위한 글입니다.
이전에 본 적이 있을 것입니다:
# 앱/컨트롤러/api/v1/things_controller.rb
모듈 API
모듈 V1
class ThingsController < BaseController
def index
@things = Thing.all
things = @things.where(size: params[:size]) if params[:size]
things = @things.where('name ILIKE ?', "%#{params[:name_contains]}%") if params[:name_contains]
json을 렌더링합니다: @things
end
end
end
end
왜 나쁜가요? 코드? 컨트롤러를 뚱뚱하게 만들기 때문입니다.
IMHO 우리는 컨트롤러에서 최대한 많은 로직을 추출하고 목적과 관련된 로직을 사용해야 합니다.
유틸리티 또는 서비스. 이 경우 다음과 같은 일반 필터를 구현합니다.
를 사용하여 여러 컨트롤러에서 사용할 수 있습니다.
하지만 잠시만요, 먼저 현재 코드를 분석해 보겠습니다. 나쁘지만 그래도 작동합니다.
초기 범위(Thing.all
)를 통과한 경우 이를 제한하고 있습니다.
관련 매개변수를 추가합니다. 각 필터에 대해 매개변수가 전달되었는지, 전달되었다면 실제로 확인합니다,
필터를 적용합니다. 두 번째는 아이바를 사용할 필요 없이 다음을 사용할 수 있다는 것입니다.
평범한 오래된 로컬 변수입니다.
좋아요, 그럼. 서비스 객체를 사용하여 초기 범위를 변경할 수 없나요?
실행은 다음과 같이 보일 수 있습니다:
# 앱/컨트롤러/api/v1/things_controller.rb
모듈 API
모듈 V1
class ThingsController < BaseController
def index
scope = Thing.all
things = Things::IndexFilter.new.call(scope, params)
렌더 제이슨: things
end
end
end
end
지금은 훨씬 좋아 보이지만 물론 아직 필터를 구현해야 합니다.
호출의 서명은 모든 리소스에 대해 동일하므로 다음을 사용할 수 있습니다.
이 작업에 대한 일반 클래스를 지정합니다.
# app/services/generic/index_filter.rb
모듈 제네릭
클래스 IndexFilter
EMPTY_HASH = {}.freeze
def self.filters
EMPTY_HASH
end
def call(scope, params)
apply_filters!(self.class.filters.keys, scope, params)
end
private
def apply_filters!(filter_keys, scope, params)
filter_keys.inject(scope.dup) do |current_scope, filter_key|
apply_filter!(filter_key, current_scope, params)
end
end
def apply_filter!(filter_key, scope, params)
filter = fetch_filter(filter_key)
apply_filter?(filter, params) 아니면 scope 반환
filter[:apply].call(scope, params)
end
def apply_filter?(filter, params)
filter[:apply?].call(params)
end
def fetch_filter(filter_key)
self.class.filters.fetch(filter_key) { raise ArgumentError, '알 수 없는 필터' }
end
end
end
복잡해 보이시나요? 그렇지 않습니다. 모든 마법은 다음에서 일어납니다. #apply_filters!
.
초기 범위의 복제본을 가져와 각 필터를 적용합니다.
범위를 적용한다는 것은 초기 범위의 복제본을 변경한다는 의미입니다.
그리고 필터가 해시로 구현될 것으로 예상됩니다. self.filters
메서드
의 하위 클래스를 만들 수 있습니다. 해봅시다.
# app/services/things/index_filter.rb
Things 모듈
클래스 인덱스필터 (params) {
params[:size].is_a?(String)
},
apply: ->(scope, params) {
scope.where(size: params[:size])
}
}.freeze,
name_contains_filter: {
apply?: ->(params) {
params[:name_contains].is_a?(문자열)
},
apply: ->(scope, params) {
scope.where('name ILIKE ?', "%#{params[:name_contains]}%")
}
}.freeze
}.freeze
def self.filters
FILTERS
end
end
end
여기까지입니다! 더 많은 코드를 작성했지만 간단한 필터는 동일하게 보입니다.
모든 리소스에 대한 방법. 담당 코드에서 컨트롤러를 정리했습니다.
필터링을 수행하고 이를 위해 다음과 같은 '특수' 클래스를 제공합니다.
명확한 관습.
자세히 읽어보세요: