forty_facets 0.0.14 → 0.1.0
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/forty_facets.gemspec +1 -1
- data/lib/forty_facets/facet_search.rb +4 -0
- data/lib/forty_facets/filter/sql_facet_filter_definition.rb +94 -0
- data/lib/forty_facets/version.rb +1 -1
- data/lib/forty_facets.rb +1 -0
- data/test/fixtures.rb +5 -1
- data/test/smoke_test.rb +48 -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: 2775c3dbeacb02d6a306671c1f46306f03baaa7c
         | 
| 4 | 
            +
              data.tar.gz: 007b804dc78ebe61b1aff29a41f276f2986d62ed
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 86d860f3463b9c264010489f2520cf4ac8e216f7f2cfe94ba0e1a5839439e1d799275ccc88281636e5b6a1f0911f26744a826e0a9ca93929eb6c0fb68082800b
         | 
| 7 | 
            +
              data.tar.gz: 51c6d45ef8c18c8185d142baac5cbb7bccc1cd54888fb2453f7157d9559657b86d151fc72b5da83bf0cb708c456f2691a593b66210b8e63b2f39392a9507489f
         | 
    
        data/forty_facets.gemspec
    CHANGED
    
    | @@ -24,5 +24,5 @@ Gem::Specification.new do |spec| | |
| 24 24 | 
             
              spec.add_development_dependency "sqlite3"
         | 
| 25 25 | 
             
              spec.add_development_dependency "coveralls"
         | 
| 26 26 | 
             
              spec.add_development_dependency "activerecord", "= 4.1.0"
         | 
| 27 | 
            -
              # | 
| 27 | 
            +
              #spec.add_development_dependency "byebug" # travis doenst like byebug
         | 
| 28 28 | 
             
            end
         | 
| @@ -40,6 +40,10 @@ module FortyFacets | |
| 40 40 | 
             
                    definitions << FacetFilterDefinition.new(self, path, opts)
         | 
| 41 41 | 
             
                  end
         | 
| 42 42 |  | 
| 43 | 
            +
                  def sql_facet(queries, opts = {})
         | 
| 44 | 
            +
                    definitions << SqlFacetFilterDefinition.new(self, queries, opts)
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 43 47 | 
             
                  def orders(name_and_order_options)
         | 
| 44 48 | 
             
                    @order_definitions = name_and_order_options.to_a.inject([]) {|ods, no| ods << OrderDefinition.new(no.first, no.last)}
         | 
| 45 49 | 
             
                  end
         | 
| @@ -0,0 +1,94 @@ | |
| 1 | 
            +
            module FortyFacets
         | 
| 2 | 
            +
              class SqlFacetFilterDefinition < FilterDefinition
         | 
| 3 | 
            +
                attr_reader(:queries)
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(search, queries, opts)
         | 
| 6 | 
            +
                  @search = search
         | 
| 7 | 
            +
                  @queries = queries
         | 
| 8 | 
            +
                  @path = Array(opts[:path]) if opts[:path].present?
         | 
| 9 | 
            +
                  @path ||= @queries.keys
         | 
| 10 | 
            +
                  @options = opts
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def request_param
         | 
| 14 | 
            +
                  path.join("-")
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def build_filter(search_instance, param_value)
         | 
| 18 | 
            +
                  ScopeFacetFilter.new(self, search_instance, param_value)
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                class ScopeFacetFilter < Filter
         | 
| 22 | 
            +
                  def values
         | 
| 23 | 
            +
                    @values ||= Array.wrap(value).sort.uniq
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def build_scope
         | 
| 27 | 
            +
                    return Proc.new { |base| base } if empty?
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    Proc.new do |base|
         | 
| 30 | 
            +
                      # intersection of values and definition queries
         | 
| 31 | 
            +
                      base.where(selected_queries.values.map do |query|
         | 
| 32 | 
            +
                        "(#{query})" 
         | 
| 33 | 
            +
                      end.join(" OR "))
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def selected
         | 
| 38 | 
            +
                    values
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  def remove(value)
         | 
| 42 | 
            +
                    new_params = search_instance.params || {}
         | 
| 43 | 
            +
                    old_values = new_params[definition.request_param]
         | 
| 44 | 
            +
                    old_values.delete(value.to_s)
         | 
| 45 | 
            +
                    new_params.delete(definition.request_param) if old_values.empty?
         | 
| 46 | 
            +
                    search_instance.class.new_unwrapped(new_params, search_instance.root)
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  def add(value)
         | 
| 50 | 
            +
                    new_params = search_instance.params || {}
         | 
| 51 | 
            +
                    old_values = new_params[definition.request_param] ||= []
         | 
| 52 | 
            +
                    old_values << value.to_s
         | 
| 53 | 
            +
                    search_instance.class.new_unwrapped(new_params, search_instance.root)
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  def facet
         | 
| 57 | 
            +
                    query = definition.queries.map do |key, sql_query|
         | 
| 58 | 
            +
                      "(#{sql_query}) as #{key}" 
         | 
| 59 | 
            +
                    end.join(", ")
         | 
| 60 | 
            +
                    query += ", count(*) as occurrences"
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    counts = without.result.reorder("")
         | 
| 63 | 
            +
                      .select(query)
         | 
| 64 | 
            +
                      .group(definition.queries.keys)
         | 
| 65 | 
            +
                    counts.includes_values = []
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    result = {}
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    counts.map do |count|
         | 
| 70 | 
            +
                      definition.queries.each do |key, _|
         | 
| 71 | 
            +
                        result[key] ||= 0
         | 
| 72 | 
            +
                        if [1, "1", true].include?(count[key])
         | 
| 73 | 
            +
                          result[key] += count.occurrences
         | 
| 74 | 
            +
                        end
         | 
| 75 | 
            +
                      end
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    result.map do |key, count|
         | 
| 79 | 
            +
                      key = key.to_sym
         | 
| 80 | 
            +
                      is_selected = selected_queries.keys.include?(key)
         | 
| 81 | 
            +
                      FacetValue.new(key, count, is_selected)
         | 
| 82 | 
            +
                    end
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  private
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  def selected_queries
         | 
| 88 | 
            +
                    @selected_queries ||= definition.queries.select do |key, _|
         | 
| 89 | 
            +
                      values.map(&:to_sym).include? key
         | 
| 90 | 
            +
                    end
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
              end
         | 
| 94 | 
            +
            end
         | 
    
        data/lib/forty_facets/version.rb
    CHANGED
    
    
    
        data/lib/forty_facets.rb
    CHANGED
    
    | @@ -10,4 +10,5 @@ require "forty_facets/filter_definition" | |
| 10 10 | 
             
            require "forty_facets/filter/range_filter_definition"
         | 
| 11 11 | 
             
            require "forty_facets/filter/text_filter_definition"
         | 
| 12 12 | 
             
            require "forty_facets/filter/facet_filter_definition"
         | 
| 13 | 
            +
            require "forty_facets/filter/sql_facet_filter_definition"
         | 
| 13 14 | 
             
            require "forty_facets/facet_search"
         | 
    
        data/test/fixtures.rb
    CHANGED
    
    | @@ -86,6 +86,9 @@ class Movie < ActiveRecord::Base | |
| 86 86 | 
             
              has_and_belongs_to_many :genres
         | 
| 87 87 | 
             
              has_and_belongs_to_many :actors
         | 
| 88 88 | 
             
              has_and_belongs_to_many :writers
         | 
| 89 | 
            +
             | 
| 90 | 
            +
              scope :classics, -> { where("year <= ?", 1980) }
         | 
| 91 | 
            +
              scope :non_classics, -> { where("year > ?", 1980) }
         | 
| 89 92 | 
             
            end
         | 
| 90 93 |  | 
| 91 94 | 
             
            LOREM = %w{Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren}
         | 
| @@ -133,7 +136,8 @@ end | |
| 133 136 |  | 
| 134 137 | 
             
            rand = Random.new
         | 
| 135 138 | 
             
            LOREM.each_with_index do |title, index|
         | 
| 136 | 
            -
              m = Movie.create!(title: title, studio: studios[index % studios.length], | 
| 139 | 
            +
              m = Movie.create!(title: title, studio: studios[index % studios.length],
         | 
| 140 | 
            +
                                price: rand.rand(20.0), year: (index + 1975))
         | 
| 137 141 | 
             
              3.times do
         | 
| 138 142 | 
             
                actor = actors[rand(actors.length)]
         | 
| 139 143 | 
             
                unless m.actors.include? actor
         | 
    
        data/test/smoke_test.rb
    CHANGED
    
    | @@ -23,6 +23,10 @@ class MovieSearch < FortyFacets::FacetSearch | |
| 23 23 | 
             
              facet [:studio, :country], name: 'Country'
         | 
| 24 24 | 
             
              facet [:studio, :status], name: 'Studio status'
         | 
| 25 25 | 
             
              facet [:studio, :producers], name: 'Producers'
         | 
| 26 | 
            +
              sql_facet({ classic: "year <= 1980", non_classic: "year > 1980" },
         | 
| 27 | 
            +
                        { name: "Classic", path: :classic })
         | 
| 28 | 
            +
              sql_facet({ classic: "year <= 1980", non_classic: "year > 1980" },
         | 
| 29 | 
            +
                        { name: "Classic" })
         | 
| 26 30 | 
             
              text [:studio, :description], name: 'Studio Description'
         | 
| 27 31 | 
             
            end
         | 
| 28 32 |  | 
| @@ -33,6 +37,50 @@ class SmokeTest < Minitest::Test | |
| 33 37 | 
             
                assert_equal Movie.all.size, search.result.size
         | 
| 34 38 | 
             
              end
         | 
| 35 39 |  | 
| 40 | 
            +
              def test_scope_filter
         | 
| 41 | 
            +
                search = MovieSearch.new("search" => {})
         | 
| 42 | 
            +
                assert_equal 40, search.result.size
         | 
| 43 | 
            +
                assert search.filter(:classic).facet.include? FortyFacets::FacetValue.new(:classic, 6, false)
         | 
| 44 | 
            +
                assert search.filter(:classic).facet.include? FortyFacets::FacetValue.new(:non_classic, 34, false)
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                search = MovieSearch.new("search" => { "classic" => "classic" })
         | 
| 47 | 
            +
                assert_equal 6, search.result.size
         | 
| 48 | 
            +
                assert search.filter(:classic).facet.include? FortyFacets::FacetValue.new(:classic, 6, true)
         | 
| 49 | 
            +
                assert search.filter(:classic).facet.include? FortyFacets::FacetValue.new(:non_classic, 34, false)
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                search = MovieSearch.new("search" => { "classic" => "non_classic" })
         | 
| 52 | 
            +
                assert_equal 34, search.result.size
         | 
| 53 | 
            +
                assert search.filter(:classic).facet.include? FortyFacets::FacetValue.new(:classic, 6, false)
         | 
| 54 | 
            +
                assert search.filter(:classic).facet.include? FortyFacets::FacetValue.new(:non_classic, 34, true)
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                search = MovieSearch.new("search" => { "classic" => ["non_classic", "classic"] })
         | 
| 57 | 
            +
                assert_equal 40, search.result.size
         | 
| 58 | 
            +
                assert search.filter(:classic).facet.include? FortyFacets::FacetValue.new(:classic, 6, true)
         | 
| 59 | 
            +
                assert search.filter(:classic).facet.include? FortyFacets::FacetValue.new(:non_classic, 34, true)
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
              def test_scope_filter_without_path
         | 
| 63 | 
            +
                search = MovieSearch.new("search" => {})
         | 
| 64 | 
            +
                assert_equal 40, search.result.size
         | 
| 65 | 
            +
                assert search.filter([:classic, :non_classic]).facet.include? FortyFacets::FacetValue.new(:classic, 6, false)
         | 
| 66 | 
            +
                assert search.filter([:classic, :non_classic]).facet.include? FortyFacets::FacetValue.new(:non_classic, 34, false)
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                search = MovieSearch.new("search" => { "classic-non_classic" => "classic" })
         | 
| 69 | 
            +
                assert_equal 6, search.result.size
         | 
| 70 | 
            +
                assert search.filter([:classic, :non_classic]).facet.include? FortyFacets::FacetValue.new(:classic, 6, true)
         | 
| 71 | 
            +
                assert search.filter([:classic, :non_classic]).facet.include? FortyFacets::FacetValue.new(:non_classic, 34, false)
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                search = MovieSearch.new("search" => { "classic-non_classic" => "non_classic" })
         | 
| 74 | 
            +
                assert_equal 34, search.result.size
         | 
| 75 | 
            +
                assert search.filter([:classic, :non_classic]).facet.include? FortyFacets::FacetValue.new(:classic, 6, false)
         | 
| 76 | 
            +
                assert search.filter([:classic, :non_classic]).facet.include? FortyFacets::FacetValue.new(:non_classic, 34, true)
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                search = MovieSearch.new("search" => { "classic-non_classic" => ["non_classic", "classic"] })
         | 
| 79 | 
            +
                assert_equal 40, search.result.size
         | 
| 80 | 
            +
                assert search.filter([:classic, :non_classic]).facet.include? FortyFacets::FacetValue.new(:classic, 6, true)
         | 
| 81 | 
            +
                assert search.filter([:classic, :non_classic]).facet.include? FortyFacets::FacetValue.new(:non_classic, 34, true)
         | 
| 82 | 
            +
              end
         | 
| 83 | 
            +
             | 
| 36 84 | 
             
              def test_text_filter
         | 
| 37 85 | 
             
                search = MovieSearch.new({'search' => { 'title' => 'ipsum' }})
         | 
| 38 86 | 
             
                assert_equal 1, search.result.size
         | 
    
        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.1.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Axel Tetzlaff
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2015- | 
| 11 | 
            +
            date: 2015-03-23 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bundler
         | 
| @@ -117,6 +117,7 @@ files: | |
| 117 117 | 
             
            - lib/forty_facets/filter.rb
         | 
| 118 118 | 
             
            - lib/forty_facets/filter/facet_filter_definition.rb
         | 
| 119 119 | 
             
            - lib/forty_facets/filter/range_filter_definition.rb
         | 
| 120 | 
            +
            - lib/forty_facets/filter/sql_facet_filter_definition.rb
         | 
| 120 121 | 
             
            - lib/forty_facets/filter/text_filter_definition.rb
         | 
| 121 122 | 
             
            - lib/forty_facets/filter_definition.rb
         | 
| 122 123 | 
             
            - lib/forty_facets/order.rb
         |