forty_facets 0.0.9 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/lib/forty_facets/facet_search.rb +12 -8
- data/lib/forty_facets/filter/belongs_to_chain_filter_definition.rb +79 -0
- data/lib/forty_facets/version.rb +1 -1
- data/lib/forty_facets.rb +1 -0
- data/test/fixtures.rb +15 -2
- data/test/smoke_test.rb +19 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c197c09ed3710813587e8fe9c937a581290470bd
|
4
|
+
data.tar.gz: 5b26596384c560a48a4bd823f2895afcf540582d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ed2ec2abcd495e4bd0c0984f94be6164bc1f196907f07ae71e00ef28e836c43e8175ff65bf5bb1d82aa67fd65c9f6195e41489897784f05a0fa56049650fec9
|
7
|
+
data.tar.gz: 3b6f68acb231546772f9cc886e651178929b72cdd42dfb0623203a9eddd1f29b62a78103724933d7db3e843fe4dce01a14c67bd0dab7f2d13cb78fe1b9e69e15
|
data/README.md
CHANGED
@@ -38,7 +38,7 @@ Or install it yourself as:
|
|
38
38
|
|
39
39
|
You can clone a working example at https://github.com/fortytools/forty_facets_demo
|
40
40
|
|
41
|
-
If you have Movies with a textual title, categotized by genre, studio and year
|
41
|
+
If you have Movies with a textual title, categotized by genre, studio and year with studios belonging to a country...
|
42
42
|
|
43
43
|
class Movie < ActiveRecord::Base
|
44
44
|
belongs_to :year
|
@@ -58,6 +58,7 @@ class HomeController < ApplicationController
|
|
58
58
|
facet :year, name: 'Releaseyear', order: :year # additionally order values in the year field
|
59
59
|
facet :studio, name: 'Studio', order: :name
|
60
60
|
facet :genres, name: 'Genre' # generate a filter with all values of 'genre' occuring in the result
|
61
|
+
facet [:studio, :country], name: 'Country' # generate a filter several belongs_to 'hops' away
|
61
62
|
|
62
63
|
orders 'Title' => :title,
|
63
64
|
'price, cheap first' => "price asc",
|
@@ -32,15 +32,19 @@ module FortyFacets
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def facet(model_field, opts = {})
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
if model_field.is_a? Array
|
36
|
+
definitions << BelongsToChainFilterDefinition.new(self, model_field, opts)
|
37
|
+
else
|
38
|
+
reflection = self.root_class.reflect_on_association(model_field)
|
39
|
+
if reflection
|
40
|
+
if reflection.macro == :belongs_to
|
41
|
+
definitions << BelongsToFilterDefinition.new(self, model_field, opts)
|
42
|
+
else
|
43
|
+
definitions << HasManyFilterDefinition.new(self, model_field, opts)
|
44
|
+
end
|
39
45
|
else
|
40
|
-
definitions <<
|
46
|
+
definitions << AttributeFilterDefinition.new(self, model_field, opts)
|
41
47
|
end
|
42
|
-
else
|
43
|
-
definitions << AttributeFilterDefinition.new(self, model_field, opts)
|
44
48
|
end
|
45
49
|
end
|
46
50
|
|
@@ -93,7 +97,7 @@ module FortyFacets
|
|
93
97
|
|
94
98
|
def filter(filter_name)
|
95
99
|
filter = @filters.find { |f| f.filter_definition.model_field == filter_name }
|
96
|
-
raise "
|
100
|
+
raise "Unknown filter #{filter_name}" unless filter
|
97
101
|
filter
|
98
102
|
end
|
99
103
|
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module FortyFacets
|
2
|
+
class BelongsToChainFilterDefinition < FilterDefinition
|
3
|
+
class BelongsToChainFilter < FacetFilter
|
4
|
+
def association
|
5
|
+
current_association = nil
|
6
|
+
current_class = filter_definition.search.root_class
|
7
|
+
|
8
|
+
filter_definition.model_field.each do |field|
|
9
|
+
current_association = current_class.reflect_on_association(field)
|
10
|
+
current_class = current_association.klass
|
11
|
+
end
|
12
|
+
|
13
|
+
current_association
|
14
|
+
end
|
15
|
+
|
16
|
+
# class objects in this filter
|
17
|
+
def klass
|
18
|
+
association.klass
|
19
|
+
end
|
20
|
+
|
21
|
+
def selected
|
22
|
+
@selected ||= klass.find(values)
|
23
|
+
end
|
24
|
+
|
25
|
+
def joins
|
26
|
+
fields = filter_definition.model_field
|
27
|
+
fields.reverse.drop(1).inject(fields.last) { |a, n| { n => a } }
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_scope
|
31
|
+
return Proc.new { |base| base } if empty?
|
32
|
+
|
33
|
+
Proc.new do |base|
|
34
|
+
condition = {association.klass.table_name => {id: values}}
|
35
|
+
base.joins(joins).where(condition)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def facet
|
40
|
+
my_column = association.association_foreign_key
|
41
|
+
counts = without.result.reorder('').joins(joins).select("#{my_column} AS foreign_id, count(#{my_column}) AS occurrences").group(my_column)
|
42
|
+
entities_by_id = klass.find(counts.map(&:foreign_id)).group_by(&:id)
|
43
|
+
|
44
|
+
facet = counts.map do |count|
|
45
|
+
facet_entity = entities_by_id[count.foreign_id].first
|
46
|
+
is_selected = selected.include?(facet_entity)
|
47
|
+
FacetValue.new(facet_entity, count.occurrences, is_selected)
|
48
|
+
end
|
49
|
+
|
50
|
+
order_facet!(facet)
|
51
|
+
end
|
52
|
+
|
53
|
+
def remove(entity)
|
54
|
+
new_params = search_instance.params || {}
|
55
|
+
old_values = new_params[filter_definition.request_param]
|
56
|
+
old_values.delete(entity.id.to_s)
|
57
|
+
new_params.delete(filter_definition.request_param) if old_values.empty?
|
58
|
+
search_instance.class.new_unwrapped(new_params, search_instance.root)
|
59
|
+
end
|
60
|
+
|
61
|
+
def add(entity)
|
62
|
+
new_params = search_instance.params || {}
|
63
|
+
old_values = new_params[filter_definition.request_param] ||= []
|
64
|
+
old_values << entity.id.to_s
|
65
|
+
search_instance.class.new_unwrapped(new_params, search_instance.root)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
def build_filter(search_instance, param_value)
|
71
|
+
BelongsToChainFilter.new(self, search_instance, param_value)
|
72
|
+
end
|
73
|
+
|
74
|
+
def request_param
|
75
|
+
model_field.join('-')
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
data/lib/forty_facets/version.rb
CHANGED
data/lib/forty_facets.rb
CHANGED
@@ -8,6 +8,7 @@ require "forty_facets/order"
|
|
8
8
|
require "forty_facets/filter"
|
9
9
|
require "forty_facets/filter_definition"
|
10
10
|
require "forty_facets/filter/belongs_to_filter_definition"
|
11
|
+
require "forty_facets/filter/belongs_to_chain_filter_definition"
|
11
12
|
require "forty_facets/filter/has_many_filter_definition"
|
12
13
|
require "forty_facets/filter/range_filter_definition"
|
13
14
|
require "forty_facets/filter/text_filter_definition"
|
data/test/fixtures.rb
CHANGED
@@ -5,8 +5,12 @@ ActiveRecord::Base.logger = Logger.new(nil)
|
|
5
5
|
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
6
6
|
|
7
7
|
ActiveRecord::Schema.define do
|
8
|
+
create_table :countries do |t|
|
9
|
+
t.string :name
|
10
|
+
end
|
8
11
|
|
9
12
|
create_table :studios do |t|
|
13
|
+
t.integer :country_id
|
10
14
|
t.string :name
|
11
15
|
end
|
12
16
|
|
@@ -55,7 +59,11 @@ end
|
|
55
59
|
class Genre < ActiveRecord::Base
|
56
60
|
end
|
57
61
|
|
62
|
+
class Country < ActiveRecord::Base
|
63
|
+
end
|
64
|
+
|
58
65
|
class Studio < ActiveRecord::Base
|
66
|
+
belongs_to :country
|
59
67
|
end
|
60
68
|
|
61
69
|
class Movie < ActiveRecord::Base
|
@@ -65,9 +73,14 @@ class Movie < ActiveRecord::Base
|
|
65
73
|
has_and_belongs_to_many :writers
|
66
74
|
end
|
67
75
|
|
76
|
+
countries = []
|
77
|
+
%w{US UK}.each do |code|
|
78
|
+
countries << Country.create!(name: code)
|
79
|
+
end
|
80
|
+
|
68
81
|
studios = []
|
69
|
-
%w{A B C D}.
|
70
|
-
studios << Studio.create!(name: "Studio #{suffix}")
|
82
|
+
%w{A B C D}.each_with_index do |suffix, index|
|
83
|
+
studios << Studio.create!(name: "Studio #{suffix}", country: countries[index % countries.length])
|
71
84
|
end
|
72
85
|
|
73
86
|
genres = []
|
data/test/smoke_test.rb
CHANGED
@@ -20,6 +20,7 @@ class MovieSearch < FortyFacets::FacetSearch
|
|
20
20
|
facet :actors, name: 'Actor'
|
21
21
|
range :price, name: 'Price'
|
22
22
|
facet :writers, name: 'Writer'
|
23
|
+
facet [:studio, :country], name: 'Country'
|
23
24
|
end
|
24
25
|
|
25
26
|
class SmokeTest < Minitest::Test
|
@@ -43,6 +44,24 @@ class SmokeTest < Minitest::Test
|
|
43
44
|
assert_equal Movie.count, facet.map(&:count).sum
|
44
45
|
end
|
45
46
|
|
47
|
+
def test_country_filter
|
48
|
+
search = MovieSearch.new('search' => { 'studio-country' => Country.first.id.to_s})
|
49
|
+
assert_equal [Country.first], search.result.map{|m| m.studio.country}.uniq
|
50
|
+
assert_equal Movie.count / 2, search.result.count
|
51
|
+
|
52
|
+
search = MovieSearch.new('search' => { 'studio-country' => Country.last.id.to_s})
|
53
|
+
assert_equal [Country.last], search.result.map{|m| m.studio.country}.uniq
|
54
|
+
assert_equal Movie.count / 2, search.result.count
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_selected_country_filter
|
58
|
+
search = MovieSearch.new('search' => { 'studio-country' => Country.first.id.to_s})
|
59
|
+
filter = search.filter([:studio, :country])
|
60
|
+
assert_equal [Country.first], filter.selected
|
61
|
+
|
62
|
+
assert_equal Movie.count / 2, filter.facet.reject(&:selected).first.count
|
63
|
+
end
|
64
|
+
|
46
65
|
def test_year_add_remove_filter
|
47
66
|
|
48
67
|
search = MovieSearch.new()
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: forty_facets
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Axel Tetzlaff
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- lib/forty_facets/facet_search.rb
|
117
117
|
- lib/forty_facets/filter.rb
|
118
118
|
- lib/forty_facets/filter/attribute_filter_definition.rb
|
119
|
+
- lib/forty_facets/filter/belongs_to_chain_filter_definition.rb
|
119
120
|
- lib/forty_facets/filter/belongs_to_filter_definition.rb
|
120
121
|
- lib/forty_facets/filter/has_many_filter_definition.rb
|
121
122
|
- lib/forty_facets/filter/range_filter_definition.rb
|