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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2ebaa1971ee0830ae8723e8eec222fa2f4ea9227
4
- data.tar.gz: c6d056a6e4714b5aa21715fed17587cc467129fb
3
+ metadata.gz: c197c09ed3710813587e8fe9c937a581290470bd
4
+ data.tar.gz: 5b26596384c560a48a4bd823f2895afcf540582d
5
5
  SHA512:
6
- metadata.gz: 1fd1e8a57e598c2c5216a77a81824514c4a56e3bae3b54223b8cab467b60ed3eb63cf9b3e2cf4b979ad2bcff6a800fa5616f6eb78a0e1be7bb8c069325605c5f
7
- data.tar.gz: d960db30251ddbd60848c5dbba003631790018b4c3d24eddff4357f8ef32a7da7628550a2327a59ebef9cbe6b3b85bf4010c1ee2a5e4f2a8bbd0321a93276117
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
- reflection = self.root_class.reflect_on_association(model_field)
36
- if reflection
37
- if reflection.macro == :belongs_to
38
- definitions << BelongsToFilterDefinition.new(self, model_field, opts)
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 << HasManyFilterDefinition.new(self, model_field, opts)
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 "unknown filter #{filter_name}" unless filter
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
@@ -1,3 +1,3 @@
1
1
  module FortyFacets
2
- VERSION = "0.0.9"
2
+ VERSION = "0.0.10"
3
3
  end
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}.each do |suffix|
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.9
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-17 00:00:00.000000000 Z
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