Filterameter는 Rails 컨트롤러에 선언적 필터를 제공하여 상용구 코드를 줄이고 가독성을 높입니다. 이 컨트롤러 동작을 몇 번이나 보았거나 작성했습니까?
def index
@films = Films . all
@films = @films . where ( name : params [ :name ] ) if params [ :name ]
@films = @films . joins ( :film_locations ) . merge ( FilmLocations . where ( location_id : params [ :location_id ] ) ) if params [ :location_id ]
@films = @films . directed_by ( params [ :director_id ] ) if params [ :director_id ]
@films = @films . written_by ( params [ :writer_id ] ) if params [ :writer_id ]
@films = @films . acted_by ( params [ :actor_id ] ) if params [ :actor_id ]
end
중복된 코드이며 작성하고 유지 관리하는 데 약간의 어려움이 있습니다. RuboCop이 그것에 대해 말할 내용은 말할 것도 없습니다. 컨트롤러가 허용하는 필터만 선언할 수 있다면 좋지 않을까요?
filter :name , partial : true
filter :location_id , association : :film_locations
filter :director_id , name : :directed_by
filter :writer_id , name : :written_by
filter :actor_id , name : :acted_by
def index
@films = build_query_from_filters
end
Filterameter를 사용하여 필터 매개변수를 선언적으로 만들어 Rails 컨트롤러 개발을 단순화하고 속도를 높이세요.
이 gem은 Rails 6.1 이상이 필요하며 ActiveRecord와 함께 작동합니다.
애플리케이션의 Gemfile에 다음 줄을 추가하세요.
gem 'filterameter'
그런 다음 다음을 실행합니다.
$ bundle install
또는 다음과 같이 직접 설치하십시오.
$ gem install filterameter
필터 DSL을 제공하려면 컨트롤러에 Filterameter::DeclarativeFilters
모듈을 포함하세요. ApplicationController
에 포함되어 모든 컨트롤러에서 기능을 사용할 수 있도록 하거나 사례별로 혼합할 수 있습니다.
filter :color
filter :size , validates : { inclusion : { in : %w[ Small Medium Large ] , allow_multiple_values : true } }
filter :brand_name , association : :brand , name : :name
filter :on_sale , association : :price , validates : [ { numericality : { greater_than : 0 } } ,
{ numericality : { less_than : 100 } } ]
옵션이 없는 필터는 filters
사용하여 한꺼번에 선언할 수 있습니다.
filters :color ,
:size ,
:name
각 필터에 대해 다음 옵션을 지정할 수 있습니다.
매개변수의 이름이 속성 또는 범위의 이름과 다른 경우 name 매개변수를 사용하여 속성 또는 범위의 이름을 지정하십시오. 예를 들어 속성 이름이 current_status
이지만 필터가 단순히 status
로 노출되는 경우 다음을 사용합니다.
filter :status , name : :current_status
이 옵션은 쿼리 매개변수 앞에 모델 이름이 붙을 수 있도록 중첩된 필터에도 유용할 수 있습니다. 예를 보려면 association
옵션을 참조하세요.
특성이나 범위가 중첩된 경우 연결 이름을 지정하여 참조할 수 있습니다. 예를 들어, Manager_id 속성이 직원의 부서 기록에 있는 경우 다음을 사용하십시오.
filter :manager_id , association : :department
특성이나 범위는 둘 이상의 수준에 중첩될 수 있습니다. 연결을 순서대로 지정하는 배열로 필터를 선언합니다. 예를 들어 직원이 부서에 속하고 부서가 사업부에 속하는 경우 다음을 사용하여 사업부 이름을 쿼리합니다.
filter :business_unit_name , name : :name , association : [ :department , :business_unit ]
연관이 has_many
인 경우 쿼리에서 고유한 메소드가 호출됩니다.
제한 사항: 동일한 테이블에 대한 연결이 두 개 이상 있고 두 연결이 모두 쿼리의 일부일 수 있는 경우 중첩 필터를 직접 사용할 수 없습니다. 대신, 연관성을 명확하게 하는 범위를 구축한 다음 해당 범위에 대한 필터를 구축하세요.
필터 값의 유효성을 검사해야 하는 경우 ActiveModel 유효성 검사와 함께 validates
옵션을 사용하세요. 다음은 크기를 제한하는 데 사용되는 포함 유효성 검사기의 예입니다.
filter :size , validates : { inclusion : { in : %w[ Small Medium Large ] } }
추가 옵션 allow_multiple_values
을 제공하기 위해 inclusion
유효성 검사기가 재정의되었습니다. true인 경우 값은 배열일 수 있으며 배열의 각 항목이 검증됩니다. 필터가 하나 이상의 값을 지정할 수 있는 경우 이를 사용합니다.
filter :size , validates : { inclusion : { in : %w[ Small Medium Large ] , allow_multiple_values : true } }
필터가 부분 검색(SQL의 LIKE
)을 수행해야 하는 경우 부분 옵션을 지정합니다. 부분 옵션은 검색 동작을 지정하기 위해 해시를 허용합니다. 사용 가능한 옵션은 다음과 같습니다.
두 가지 단축키가 있습니다. : 부분 옵션은 기본값만 사용하는 true
로 선언할 수 있습니다. 또는 부분 옵션은 partial: :from_start
와 같이 일치 옵션을 사용하여 직접 선언할 수 있습니다.
filter :description , partial : true
filter :department_name , partial : :from_start
filter :reason , partial : { match : :dynamic , case_sensitive : true }
match
옵션은 검색할 위치를 정의합니다(와일드카드가 표시되는 위치를 제어함).
범위, 최소값 또는 최대값별로 검색을 활성화하려면 범위 옵션을 지정합니다. (이 모든 항목이 포함됩니다. 최소 $10.00를 검색하면 가격이 $10.00인 모든 항목이 포함됩니다.)
사용 가능한 옵션은 다음과 같습니다.
범위 옵션을 사용한다는 것은 속성 필터 외에 최소 및 최대 쿼리 매개변수도 지정할 수 있음을 의미합니다. 매개변수 이름은 속성 이름에 접미사 _min 또는 _max 를 더한 것입니다.
filter :price , range : true
filter :approved_at , range : :min_only
filter :sale_price , range : :max_only
첫 번째 예에서 쿼리 매개변수에는 가격 , 가격_최소 및 가격_최대가 포함될 수 있습니다.
기본적으로 대부분의 필터는 정렬 가능합니다. 속성 필터를 정렬할 수 없도록 하려면 옵션을 false로 설정하세요.
filter :price , sortable : false
다음 필터는 정렬할 수 없습니다.
인수를 사용하지 않는 범위의 경우 필터는 범위를 호출해야 하는지 여부를 나타내는 부울을 제공해야 합니다. 예를 들어, 우선순위가 높은 레코드를 식별하는 기준이 있는 high_priority
라는 범위를 상상해 보세요. 범위는 쿼리 매개변수 high_priority=true
에 의해 호출됩니다.
high_priority=false
전달하면 범위가 호출되지 않습니다. 이렇게 하면 확인란 UI가 있는 필터를 쉽게 포함할 수 있습니다.
인수를 취하는 범위는 인라인 범위가 아닌 클래스 메서드로 작성되어야 합니다. 예를 들어, 현재 날짜를 인수로 사용하는 recent
이라는 범위를 상상해 보세요. 그 모습은 다음과 같습니다.
def self . recent ( as_of_date )
where ( 'created_at > ?' , as_of_date )
end
위에서 언급한 것처럼 대부분의 속성 필터는 기본적으로 정렬 가능합니다. 속성에 대해 필터가 선언되지 않은 경우 sort
선언을 사용할 수 있습니다. 필요에 따라 동일한 name
과 association
옵션을 사용하십시오.
예를 들어, 활동 컨트롤러에서 다음 선언을 사용하면 생성된 프로젝트별로 활동을 정렬할 수 있습니다.
sort :project_created_at , name : :created_at , association : :project
옵션이 없는 정렬은 sorts
사용하여 한 번에 선언할 수 있습니다.
sorts :created_at ,
:updated_at ,
:description
범위는 정렬에 사용될 수 있지만 sort
(또는 sorts
)를 사용하여 선언되어야 합니다. 예를 들어 모델에 by_created_at
라는 범위가 포함된 경우 컨트롤러에 다음을 추가하여 이를 노출할 수 있습니다.
sort :by_created_at
name
및 association
옵션도 사용할 수 있습니다. 예를 들어 범위가 프로젝트 모델에 있는 경우 association
옵션을 사용하여 하위 활동 컨트롤러에서도 사용할 수 있습니다.
sort :by_created_at , association : :project
정렬에는 단일 연관만 유효합니다. 컬렉션 연결은 여러 값을 반환하여 정렬을 불확실하게 만들 수 있습니다.
정렬에 사용되는 범위는 단일 인수를 허용해야 합니다. 매개변수에 따라 :asc
또는 :desc
로 전달됩니다.
위의 예시 범위는 다음과 같이 정의될 수 있습니다.
def self . by_created_at ( dir )
order ( created_at : dir )
end
default_sort
사용하여 기본 정렬을 선언할 수 있습니다. 인수는 선언된 정렬 또는 정렬 가능한 필터 중 하나 이상을 이름별로 지정해야 합니다. 기본적으로 순서는 오름차순입니다. 내림차순을 원할 경우 열 이름 기호를 :desc에 매핑할 수 있습니다.
default_sort updated_at : :desc , :description
일관된 결과를 제공하기 위해 항상 정렬이 적용됩니다. 기본값이 지정되지 않으면 기본 키 내림차순을 사용합니다.
원하는 제어 및/또는 가시성의 정도에 따라 필터를 적용하고 쿼리를 작성하는 방법에는 두 가지가 있습니다.
build_filtered_query
를 사용하세요.build_query_from_filters
수동으로 호출하세요. build_filtered_query
를 사용하세요. 쿼리를 작성해야 하는 컨트롤러 작업에 대한 작업 콜백 build_filtered_query
추가합니다. 이는 ApplicationController
에서 또는 사례별로 수행될 수 있습니다.
콜백을 사용할 때 변수 이름은 복수형 모델 이름입니다. 예를 들어 Photo 모델은 @photos
변수를 사용하여 쿼리를 저장합니다. 변수 이름은 filter_query_var_name
사용하여 명시적으로 지정할 수 있습니다. 예를 들어 쿼리가 @data
로 저장된 경우 다음을 사용합니다.
filter_query_var_name :data
또한 filter_model
명령은 선택적 두 번째 매개변수를 사용하여 변수 이름을 지정합니다. 모델명과 변수명 모두 이 단축키로 지정할 수 있습니다. 예를 들어 Picture 모델을 사용하고 결과를 @data
로 저장하려면 다음을 사용합니다.
filter_model 'Picture' , :data
Happy 경로에서 WidgetsController는 위젯을 제공하고 크기와 색상을 필터링할 수 있습니다. 컨트롤러의 모양은 다음과 같습니다.
class WidgetsController < ApplicationController
include Filterameter :: DeclarativeFilters
before_action :build_filtered_query , only : :index
filter :size
filter :color
def index
render json : @widgets
end
end
build_query_from_filters
수동으로 호출하세요. 쿼리를 수동으로 생성하려면 콜백을 사용하는 대신 build_query_from_filters
직접 호출하면 됩니다.
여기에 다시 위젯 컨트롤러가 있습니다. 이번에는 쿼리를 수동으로 작성합니다.
class WidgetsController < ApplicationController
include Filterameter :: DeclarativeFilters
filter :size
filter :color
def index
@widgets = build_query_from_filters
end
end
이 메서드는 선택적으로 시작 쿼리를 사용합니다. 활성 위젯만 반환해야 하는 활성 위젯용 컨트롤러가 있는 경우 다음을 시작점으로 메서드에 전달할 수 있습니다.
def index
@widgets = build_query_from_filters ( Widget . where ( active : true ) )
end
시작 쿼리는 즉시 로드를 활성화하기 위해 포함을 제공하기에 좋은 장소이기도 합니다.
def index
@widgets = build_query_from_filters ( Widgets . includes ( :manufacturer ) )
end
시작 쿼리는 모델을 제공하므로 모델이 조회되지 않으며 model_name
선언이 필요하지 않습니다.
레일즈 규칙은 컨트롤러의 모델을 결정하는 데 사용됩니다. 예를 들어 PhotosController는 Photo 모델에 대한 쿼리를 작성합니다. 컨트롤러에 네임스페이스가 있는 경우 먼저 네임스페이스 없이 모델을 조회한 다음 네임스페이스를 사용하여 조회합니다.
규칙이 올바른 모델을 제공하지 않는 경우 다음을 사용하여 모델 이름을 명시적으로 지정할 수 있습니다.
filter_model 'Picture'
중요: filter_model
선언이 사용되는 경우 필터 또는 정렬 선언 앞에 와야 합니다.
세 가지 구성 옵션이 있습니다.
구성 옵션은 초기화 프로그램, 환경 파일 또는 application.rb
에서 설정할 수 있습니다.
옵션을 직접 설정할 수 있습니다...
Filterameter.configuration.action_on_undeclared_parameters = :log
...또는 구성이 생성될 수 있습니다.
Filterameter . configure do | config |
config . action_on_undeclared_parameters = :log
config . action_on_validation_failuer = :log
config . filter_key = :f
end
필터 매개변수에 정의되지 않은 키가 포함된 경우 발생합니다. 유효한 작업은 :log
, :raise
및 false
(작업을 수행하지 않음)입니다. 기본적으로 개발에서는 로그가 기록되고 테스트에서는 발생하며 프로덕션에서는 아무 작업도 수행되지 않습니다.
필터 매개변수가 유효성 검사에 실패할 때 발생합니다. 유효한 작업은 :log
, :raise
및 false
(작업을 수행하지 않음)입니다. 기본적으로 개발에서는 로그가 기록되고 테스트에서는 발생하며 프로덕션에서는 아무 작업도 수행되지 않습니다.
기본적으로 필터 매개변수는 :filter
키 아래에 중첩됩니다. 이 설정을 사용하여 키를 재정의합니다.
필터 매개변수가 중첩되지 않은 경우 false로 설정합니다. 그렇게 하면 필터 매개변수가 선언된 매개변수로만 제한됩니다. 즉, 선언되지 않은 매개변수는 무시됩니다(그리고 action_on_undeclared_parameters 구성 옵션은 작동하지 않습니다).
각 컨트롤러에 대해 선언을 테스트하여 오타, 잘못 정의된 범위 또는 기타 문제를 찾아낼 수 있습니다. 메소드 declarations_validator
각 컨트롤러에 추가되고 단일 컨트롤러 테스트를 추가하여 해당 컨트롤러에 대한 모든 선언의 유효성을 검사할 수 있습니다.
RSpec 테스트는 다음과 같습니다.
expect ( WidgetsController . declarations_validator ) . to be_valid
Minitest에서는 다음과 같이 보일 수 있습니다.
validator = WidgetsController . declarations_validator
assert_predicate validator , :valid? , -> { validator . errors }
필터 매개변수는 키 filter
아래에 중첩된 컨트롤러 매개변수에서 가져옵니다(기본적으로 필터 키를 변경하려면 구성 참조). 예를 들어 대형 파란색 위젯에 대한 요청의 URL에는 다음과 같은 쿼리 매개변수가 있을 수 있습니다.
?filter[size]=large&filter[color]=blue
일반 검색 양식에서 form_with
양식 도우미는 매개변수를 그룹화할 수 있는 옵션 scope
사용합니다.
<%= form_with url: "/search", scope: :filter, method: :get do |form| %>
<%= form.label :size, "Size:" %>
<%= form.text_field :size %>
<%= form.label :color, "Color:" %>
<%= form.text_field :color %>
<%= form.submit "Search" %>
<% end %>
정렬은 필터 키 아래에도 중첩되어 있습니다.
/widgets?filter[sort]=size
여러 정렬을 전달하려면 배열을 사용하세요. 매개변수의 순서는 정렬이 적용되는 순서입니다. 예를 들어 다음은 먼저 크기를 기준으로 정렬한 다음 색상을 기준으로 정렬합니다.
/widgets?filter[sort]=size&filter[sort]=color
정렬은 기본적으로 오름차순이지만 접두사를 사용하여 정렬을 제어할 수 있습니다.
+
오름차순(기본값)-
내림차순예를 들어 다음은 크기를 기준으로 내림차순으로 정렬됩니다.
/widgets?filter[sort]=-size
피드백, 기능 요청 및 제안된 변경 사항을 환영합니다. 피드백 및 기능 요청은 이슈 트래커를 사용하세요. 변경 사항을 직접 제안하려면 리포지토리를 포크하고 풀 요청을 열어주세요. 테스트와 Rubocop이 통과하는지 확인하기 위해 작업을 주시하세요. Code Climate은 코드라인을 평가하기 위해 수동으로 사용되기도 합니다.
버그를 신고하려면 이슈 트래커를 사용하고 다음 정보를 제공하십시오.
테스트를 통해 문제를 재현할 수 있으면 골드 스타가 수여됩니다.
테스트는 RSpec으로 작성되었으며 더미 앱은 Docker 데이터베이스를 사용합니다. bin/start_db.sh
스크립트는 테스트 데이터베이스를 시작하고 준비합니다. 테스트를 실행하기 전의 일회성 단계입니다.
bin/start_db.rb
bundle exec rspec
평가를 사용하여 모든 Ruby 및 Rails 조합에 대해 테스트를 실행할 수도 있습니다. 설치도 일회성 단계입니다.
bundle exec appraisal install
bundle exec appraisal rspec
이 gem은 MIT 라이선스 조건에 따라 오픈 소스로 제공됩니다.