forty_facets 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|