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 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