searchable-by 0.5.0 → 0.5.1
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/Gemfile.lock +1 -1
 - data/lib/searchable_by.rb +49 -28
 - data/searchable-by.gemspec +1 -1
 - data/spec/searchable_by_spec.rb +6 -0
 - metadata +1 -1
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 8c1c5cfdea465a6a9ff73159a062558c5475889d27e0119e81212bb8bb37bbc1
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: c77c158c890b37b67d1243c93b4d5c982731b55fb48d284ecc404de58142da26
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 1ba79022ae7056fc1cd6f6ef7af109ed6e0a7228601241ac2bc8f501818b6f487a6fd929f77bc3ce0dba9956b879def3dc705159c3696246d4653e3197f3d71f
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: c023dff5eb1bb4685dcfe7d10b7164242a090a930189ccc1b336c6de3437ab05d1de0dc90d13f52412e8e15b16a0a1386d5dacb289b208e00eefca5eab0cc2f7
         
     | 
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/lib/searchable_by.rb
    CHANGED
    
    | 
         @@ -3,24 +3,42 @@ require 'shellwords' 
     | 
|
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            module ActiveRecord
         
     | 
| 
       5 
5 
     | 
    
         
             
              module SearchableBy
         
     | 
| 
       6 
     | 
    
         
            -
                class  
     | 
| 
      
 6 
     | 
    
         
            +
                class Column
         
     | 
| 
      
 7 
     | 
    
         
            +
                  attr_reader :attr, :type
         
     | 
| 
      
 8 
     | 
    
         
            +
                  attr_accessor :node
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def initialize(attr, type: :string)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @attr = attr
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @type = type.to_sym
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                class Config
         
     | 
| 
      
 17 
     | 
    
         
            +
                  attr_reader :columns, :scoping
         
     | 
| 
      
 18 
     | 
    
         
            +
                  attr_accessor :max_terms
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
       7 
20 
     | 
    
         
             
                  def initialize
         
     | 
| 
       8 
     | 
    
         
            -
                     
     | 
| 
      
 21 
     | 
    
         
            +
                    @columns = []
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @max_terms = 5
         
     | 
| 
       9 
23 
     | 
    
         
             
                    scope { all }
         
     | 
| 
       10 
24 
     | 
    
         
             
                  end
         
     | 
| 
       11 
25 
     | 
    
         | 
| 
      
 26 
     | 
    
         
            +
                  def initialize_copy(other)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    @columns = other.columns.dup
         
     | 
| 
      
 28 
     | 
    
         
            +
                    super
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
       12 
31 
     | 
    
         
             
                  def column(*attrs, &block)
         
     | 
| 
       13 
32 
     | 
    
         
             
                    opts = attrs.extract_options!
         
     | 
| 
       14 
     | 
    
         
            -
                    cols = self[:columns]
         
     | 
| 
       15 
33 
     | 
    
         
             
                    attrs.each do |attr|
         
     | 
| 
       16 
     | 
    
         
            -
                       
     | 
| 
      
 34 
     | 
    
         
            +
                      columns.push Column.new(attr, opts)
         
     | 
| 
       17 
35 
     | 
    
         
             
                    end
         
     | 
| 
       18 
     | 
    
         
            -
                     
     | 
| 
       19 
     | 
    
         
            -
                     
     | 
| 
      
 36 
     | 
    
         
            +
                    columns.push Column.new(block, opts) if block
         
     | 
| 
      
 37 
     | 
    
         
            +
                    columns
         
     | 
| 
       20 
38 
     | 
    
         
             
                  end
         
     | 
| 
       21 
39 
     | 
    
         | 
| 
       22 
40 
     | 
    
         
             
                  def scope(&block)
         
     | 
| 
       23 
     | 
    
         
            -
                     
     | 
| 
      
 41 
     | 
    
         
            +
                    @scoping = block
         
     | 
| 
       24 
42 
     | 
    
         
             
                  end
         
     | 
| 
       25 
43 
     | 
    
         
             
                end
         
     | 
| 
       26 
44 
     | 
    
         | 
| 
         @@ -32,27 +50,30 @@ module ActiveRecord 
     | 
|
| 
       32 
50 
     | 
    
         
             
                  values
         
     | 
| 
       33 
51 
     | 
    
         
             
                end
         
     | 
| 
       34 
52 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
                def self.build_clauses( 
     | 
| 
      
 53 
     | 
    
         
            +
                def self.build_clauses(columns, values)
         
     | 
| 
       36 
54 
     | 
    
         
             
                  clauses = values.map do |value|
         
     | 
| 
       37 
55 
     | 
    
         
             
                    negate = value[0] == '-'
         
     | 
| 
       38 
56 
     | 
    
         
             
                    value.slice!(0) if negate || value[0] == '+'
         
     | 
| 
       39 
57 
     | 
    
         | 
| 
       40 
     | 
    
         
            -
                     
     | 
| 
       41 
     | 
    
         
            -
                      build_condition( 
     | 
| 
       42 
     | 
    
         
            -
                    end 
     | 
| 
       43 
     | 
    
         
            -
                     
     | 
| 
      
 58 
     | 
    
         
            +
                    grouping = columns.map do |column|
         
     | 
| 
      
 59 
     | 
    
         
            +
                      build_condition(column, value)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
                    grouping.compact!
         
     | 
| 
      
 62 
     | 
    
         
            +
                    next if grouping.empty?
         
     | 
| 
       44 
63 
     | 
    
         | 
| 
       45 
     | 
    
         
            -
                     
     | 
| 
      
 64 
     | 
    
         
            +
                    clause = grouping.inject(&:or)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    clause = clause.not if negate
         
     | 
| 
      
 66 
     | 
    
         
            +
                    clause
         
     | 
| 
       46 
67 
     | 
    
         
             
                  end
         
     | 
| 
       47 
68 
     | 
    
         
             
                  clauses.compact!
         
     | 
| 
       48 
69 
     | 
    
         
             
                  clauses
         
     | 
| 
       49 
70 
     | 
    
         
             
                end
         
     | 
| 
       50 
71 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
                def self.build_condition( 
     | 
| 
       52 
     | 
    
         
            -
                  case  
     | 
| 
      
 72 
     | 
    
         
            +
                def self.build_condition(column, value)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  case column.type
         
     | 
| 
       53 
74 
     | 
    
         
             
                  when :int, :integer
         
     | 
| 
       54 
75 
     | 
    
         
             
                    begin
         
     | 
| 
       55 
     | 
    
         
            -
                       
     | 
| 
      
 76 
     | 
    
         
            +
                      column.node.eq(Integer(value))
         
     | 
| 
       56 
77 
     | 
    
         
             
                    rescue ArgumentError
         
     | 
| 
       57 
78 
     | 
    
         
             
                      nil
         
     | 
| 
       58 
79 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -60,7 +81,7 @@ module ActiveRecord 
     | 
|
| 
       60 
81 
     | 
    
         
             
                    value = value.dup
         
     | 
| 
       61 
82 
     | 
    
         
             
                    value.gsub!('%', '\%')
         
     | 
| 
       62 
83 
     | 
    
         
             
                    value.gsub!('_', '\_')
         
     | 
| 
       63 
     | 
    
         
            -
                     
     | 
| 
      
 84 
     | 
    
         
            +
                    column.node.matches("%#{value}%")
         
     | 
| 
       64 
85 
     | 
    
         
             
                  end
         
     | 
| 
       65 
86 
     | 
    
         
             
                end
         
     | 
| 
       66 
87 
     | 
    
         | 
| 
         @@ -72,35 +93,35 @@ module ActiveRecord 
     | 
|
| 
       72 
93 
     | 
    
         
             
                  end
         
     | 
| 
       73 
94 
     | 
    
         | 
| 
       74 
95 
     | 
    
         
             
                  def inherited(base) # :nodoc:
         
     | 
| 
       75 
     | 
    
         
            -
                    base._searchable_by_config = _searchable_by_config. 
     | 
| 
      
 96 
     | 
    
         
            +
                    base._searchable_by_config = _searchable_by_config.dup
         
     | 
| 
       76 
97 
     | 
    
         
             
                    super
         
     | 
| 
       77 
98 
     | 
    
         
             
                  end
         
     | 
| 
       78 
99 
     | 
    
         | 
| 
       79 
100 
     | 
    
         
             
                  def searchable_by(max_terms: 5, &block)
         
     | 
| 
       80 
101 
     | 
    
         
             
                    _searchable_by_config.instance_eval(&block)
         
     | 
| 
       81 
     | 
    
         
            -
                    _searchable_by_config 
     | 
| 
      
 102 
     | 
    
         
            +
                    _searchable_by_config.max_terms = max_terms if max_terms
         
     | 
| 
       82 
103 
     | 
    
         
             
                  end
         
     | 
| 
       83 
104 
     | 
    
         | 
| 
       84 
105 
     | 
    
         
             
                  # @param [String] query the search query
         
     | 
| 
       85 
106 
     | 
    
         
             
                  # @return [ActiveRecord::Relation] the scoped relation
         
     | 
| 
       86 
107 
     | 
    
         
             
                  def search_by(query)
         
     | 
| 
       87 
     | 
    
         
            -
                    columns = _searchable_by_config 
     | 
| 
      
 108 
     | 
    
         
            +
                    columns = _searchable_by_config.columns
         
     | 
| 
       88 
109 
     | 
    
         
             
                    return all if columns.empty?
         
     | 
| 
       89 
110 
     | 
    
         | 
| 
       90 
     | 
    
         
            -
                    values = SearchableBy.norm_values(query).first(_searchable_by_config 
     | 
| 
      
 111 
     | 
    
         
            +
                    values = SearchableBy.norm_values(query).first(_searchable_by_config.max_terms)
         
     | 
| 
       91 
112 
     | 
    
         
             
                    return all if values.empty?
         
     | 
| 
       92 
113 
     | 
    
         | 
| 
       93 
     | 
    
         
            -
                     
     | 
| 
       94 
     | 
    
         
            -
                       
     | 
| 
       95 
     | 
    
         
            -
                      opts.merge(rel: rel)
         
     | 
| 
      
 114 
     | 
    
         
            +
                    columns.each do |col|
         
     | 
| 
      
 115 
     | 
    
         
            +
                      col.node ||= col.attr.is_a?(Proc) ? col.attr.call : arel_table[col.attr]
         
     | 
| 
       96 
116 
     | 
    
         
             
                    end
         
     | 
| 
       97 
     | 
    
         
            -
                    clauses = SearchableBy.build_clauses( 
     | 
| 
      
 117 
     | 
    
         
            +
                    clauses = SearchableBy.build_clauses(columns, values)
         
     | 
| 
       98 
118 
     | 
    
         
             
                    return all if clauses.empty?
         
     | 
| 
       99 
119 
     | 
    
         | 
| 
       100 
     | 
    
         
            -
                    scope = instance_exec(&_searchable_by_config 
     | 
| 
       101 
     | 
    
         
            -
                    clauses. 
     | 
| 
       102 
     | 
    
         
            -
                       
     | 
| 
      
 120 
     | 
    
         
            +
                    scope = instance_exec(&_searchable_by_config.scoping)
         
     | 
| 
      
 121 
     | 
    
         
            +
                    clauses.each do |clause|
         
     | 
| 
      
 122 
     | 
    
         
            +
                      scope = scope.where(clause)
         
     | 
| 
       103 
123 
     | 
    
         
             
                    end
         
     | 
| 
      
 124 
     | 
    
         
            +
                    scope
         
     | 
| 
       104 
125 
     | 
    
         
             
                  end
         
     | 
| 
       105 
126 
     | 
    
         
             
                end
         
     | 
| 
       106 
127 
     | 
    
         
             
              end
         
     | 
    
        data/searchable-by.gemspec
    CHANGED
    
    
    
        data/spec/searchable_by_spec.rb
    CHANGED
    
    | 
         @@ -1,11 +1,17 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'spec_helper'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            describe ActiveRecord::SearchableBy do
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       4 
5 
     | 
    
         
             
              it 'should ignore bad inputs' do
         
     | 
| 
       5 
6 
     | 
    
         
             
                expect(Post.search_by(nil).count).to eq(4)
         
     | 
| 
       6 
7 
     | 
    
         
             
                expect(Post.search_by('').count).to eq(4)
         
     | 
| 
       7 
8 
     | 
    
         
             
              end
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
      
 10 
     | 
    
         
            +
              it 'should configure correctly' do
         
     | 
| 
      
 11 
     | 
    
         
            +
                expect(AbstractModel._searchable_by_config.columns.size).to eq(1)
         
     | 
| 
      
 12 
     | 
    
         
            +
                expect(Post._searchable_by_config.columns.size).to eq(4)
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
       9 
15 
     | 
    
         
             
              it 'should generate SQL' do
         
     | 
| 
       10 
16 
     | 
    
         
             
                sql = Post.search_by('123').to_sql
         
     | 
| 
       11 
17 
     | 
    
         
             
                expect(sql).to include(%("posts"."id" = 123))
         
     |