mirror of
https://github.com/manyfold3d/manyfold.git
synced 2026-10-22 03:32:18 -05:00
refactor filterable controller code into a filter service object
This commit is contained in:
2 files changed
+237
No files matched your search
@@ -0,0 +1,133 @@
|
||||
class Search::FilterService
|
||||
attr_reader :collection
|
||||
attr_reader :creator
|
||||
|
||||
# Get list filters from URL
|
||||
def initialize(params)
|
||||
@filters = params.permit(
|
||||
:library,
|
||||
:collection,
|
||||
:q,
|
||||
:creator,
|
||||
:link,
|
||||
:missingtag,
|
||||
tag: []
|
||||
).compact || {}
|
||||
@collection = Collection.find_param(parameter(:collection)) if parameter(:collection).present?
|
||||
@creator = Creator.find_param(parameter(:creator)) if parameter(:creator).present?
|
||||
end
|
||||
|
||||
def any?
|
||||
!@filters.empty?
|
||||
end
|
||||
|
||||
def filtering_by?(key)
|
||||
parameter(key).present?
|
||||
end
|
||||
|
||||
def parameter(key)
|
||||
@filters[key]
|
||||
end
|
||||
|
||||
def to_params(except: nil)
|
||||
ActionController::Parameters.new(@filters.except(except))
|
||||
end
|
||||
|
||||
def tags
|
||||
end
|
||||
|
||||
def models(scope)
|
||||
scope = scope.all
|
||||
scope = filter_by_library(scope)
|
||||
scope = filter_by_missing_tag(scope)
|
||||
scope = filter_by_tag(scope)
|
||||
scope = filter_by_collection(scope)
|
||||
scope = filter_by_creator(scope)
|
||||
scope = filter_by_url(scope)
|
||||
filter_by_search(scope)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Filter by library
|
||||
def filter_by_library(scope)
|
||||
filtering_by?(:library) ? scope.where(library: Library.find_param(parameter(:library))) : scope
|
||||
end
|
||||
|
||||
# Filter by collection
|
||||
def filter_by_collection(scope)
|
||||
case parameter(:collection)
|
||||
when nil
|
||||
scope # No collection, move along
|
||||
when ""
|
||||
scope.where(collection_id: nil)
|
||||
else
|
||||
scope.where(collection: Collection.tree_down(@collection.id))
|
||||
end
|
||||
end
|
||||
|
||||
# Filter by creator
|
||||
def filter_by_creator(scope)
|
||||
case parameter(:creator)
|
||||
when nil
|
||||
scope # No creator specified, nothing to do
|
||||
when ""
|
||||
scope.where(creator_id: nil)
|
||||
else
|
||||
scope.where(creator: creator)
|
||||
end
|
||||
end
|
||||
|
||||
# Filter by tag
|
||||
def filter_by_tag(scope)
|
||||
case parameter(:tag)
|
||||
when nil
|
||||
scope # No tags, move along
|
||||
when [""]
|
||||
scope.where("(select count(*) from taggings where taggings.taggable_id=models.id and taggings.context='tags')<1")
|
||||
else
|
||||
# Build query directly rather than using tagged_with, which parses the tag list again using default separators
|
||||
::ActsAsTaggableOn::Taggable::TaggedWithQuery.build(scope, ActsAsTaggableOn::Tag, ActsAsTaggableOn::Tagging, parameter(:tag), {})
|
||||
end
|
||||
end
|
||||
|
||||
# Filter by url
|
||||
def filter_by_url(scope)
|
||||
case parameter(:link)
|
||||
when nil
|
||||
scope # no filter
|
||||
when ""
|
||||
scope.where("(select count(*) from links where linkable_id=models.id and linkable_type='Model')<1")
|
||||
else
|
||||
scope.where("(select count(*) from links where linkable_id=models.id and linkable_type='Model' and url like ?)>0", "%#{parameter(:link)}%")
|
||||
end
|
||||
end
|
||||
|
||||
# Filter by search query
|
||||
def filter_by_search(scope)
|
||||
if parameter(:q)
|
||||
Search::ModelSearchService.new(scope).search(parameter(:q))
|
||||
else
|
||||
scope
|
||||
end
|
||||
end
|
||||
|
||||
def filter_by_missing_tag(scope)
|
||||
# Missing tags (If specific tag is not specified, require library to be set)
|
||||
if filtering_by?(:missingtag) || (filtering_by?(:missingtag) && parameter(:library))
|
||||
tag_regex_build = []
|
||||
regexes = ((parameter(:missingtag) != "") ? [parameter(:missingtag)] : Library.find_param(parameter(:library)).tag_regex)
|
||||
# Regexp match syntax - postgres is different from MySQL and SQLite
|
||||
regact = (ApplicationRecord.connection.adapter_name == "PostgreSQL") ? "~" : "REGEXP"
|
||||
regexes.each do |reg|
|
||||
qreg = ActiveRecord::Base.connection.quote(reg)
|
||||
tag_regex_build.push "(select count(*) from tags join taggings on tags.id=taggings.tag_id where tags.name #{regact} #{qreg} and taggings.taggable_id=models.id and taggings.taggable_type='Model')<1"
|
||||
end
|
||||
qreg = ActiveRecord::Base.connection.quote(parameter(:missingtag))
|
||||
tag_regex_build.push "(select count(*) from tags join taggings on tags.id=taggings.tag_id where tags.name #{regact} #{qreg} and taggings.taggable_id=models.id and taggings.taggable_type='Model')<1"
|
||||
scope.where("(" + tag_regex_build.join(" OR ") + ")")
|
||||
else
|
||||
scope
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,104 @@
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe Search::FilterService do
|
||||
context "with a range of models" do
|
||||
let(:creator) { create(:creator, name: "Dr Seuss") }
|
||||
let(:collection) { create(:collection, name: "Chiroptera") }
|
||||
|
||||
before do
|
||||
create(:model, name: "cat in the hat", tag_list: ["dog", "log", "frog", "cat"], creator: creator, notes: "lorem ipsum", caption: nil)
|
||||
create(:model, name: "hat on the cat", tag_list: [], creator: creator, notes: nil, caption: "dolor sit amet")
|
||||
create(:model, name: "bat on a mat", tag_list: ["log"], collection: collection, notes: nil, caption: nil, links_attributes: [{url: "https://thingiverse.com/thing:1234"}])
|
||||
create(:model, name: "bat on a hat", tag_list: ["frog"], collection: collection, notes: nil, caption: nil)
|
||||
end
|
||||
|
||||
it "returns all models with no filters" do
|
||||
filter = described_class.new(ActionController::Parameters.new)
|
||||
expect(filter.models(Model).count).to eq 4
|
||||
end
|
||||
|
||||
it "filters on a simple search query" do
|
||||
service = described_class.new(ActionController::Parameters.new(q: "cat"))
|
||||
expect(service.models(Model.all).pluck(:name)).to eq [
|
||||
"cat in the hat",
|
||||
"hat on the cat"
|
||||
]
|
||||
end
|
||||
|
||||
it "filters by collection" do
|
||||
service = described_class.new(ActionController::Parameters.new(collection: collection.to_param))
|
||||
expect(service.models(Model.all).pluck(:name)).to eq [
|
||||
"bat on a mat",
|
||||
"bat on a hat"
|
||||
]
|
||||
end
|
||||
|
||||
it "filters by missing collection" do
|
||||
service = described_class.new(ActionController::Parameters.new(collection: ""))
|
||||
expect(service.models(Model.all).pluck(:name)).to eq [
|
||||
"cat in the hat",
|
||||
"hat on the cat"
|
||||
]
|
||||
end
|
||||
|
||||
it "filters by creator" do
|
||||
service = described_class.new(ActionController::Parameters.new(creator: creator.to_param))
|
||||
expect(service.models(Model.all).pluck(:name)).to eq [
|
||||
"cat in the hat",
|
||||
"hat on the cat"
|
||||
]
|
||||
end
|
||||
|
||||
it "filters by missing creator" do
|
||||
service = described_class.new(ActionController::Parameters.new(creator: ""))
|
||||
expect(service.models(Model.all).pluck(:name)).to eq [
|
||||
"bat on a mat",
|
||||
"bat on a hat"
|
||||
]
|
||||
end
|
||||
|
||||
it "filters by single tag" do
|
||||
service = described_class.new(ActionController::Parameters.new(tag: ["log"]))
|
||||
expect(service.models(Model.all).pluck(:name)).to eq [
|
||||
"cat in the hat",
|
||||
"bat on a mat"
|
||||
]
|
||||
end
|
||||
|
||||
it "filters by no tags" do
|
||||
service = described_class.new(ActionController::Parameters.new(tag: [""]))
|
||||
expect(service.models(Model.all).pluck(:name)).to eq [
|
||||
"hat on the cat"
|
||||
]
|
||||
end
|
||||
|
||||
it "filters by multiple tags" do
|
||||
service = described_class.new(ActionController::Parameters.new(tag: ["log", "frog"]))
|
||||
expect(service.models(Model.all).pluck(:name)).to eq [
|
||||
"cat in the hat"
|
||||
]
|
||||
end
|
||||
|
||||
it "filters by missing tag" do
|
||||
service = described_class.new(ActionController::Parameters.new(missingtag: "log"))
|
||||
expect(service.models(Model.all).pluck(:name)).to eq [
|
||||
"hat on the cat",
|
||||
"bat on a hat"
|
||||
]
|
||||
end
|
||||
|
||||
it "filters by library" do
|
||||
service = described_class.new(ActionController::Parameters.new(library: Library.last.to_param))
|
||||
expect(service.models(Model.all).pluck(:name)).to eq [
|
||||
"bat on a hat"
|
||||
]
|
||||
end
|
||||
|
||||
it "filters by link" do
|
||||
service = described_class.new(ActionController::Parameters.new(link: "thingiverse.com"))
|
||||
expect(service.models(Model.all).pluck(:name)).to eq [
|
||||
"bat on a mat"
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user