search_cop 1.0.9 → 1.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 +5 -5
- data/.travis.yml +18 -29
- data/CHANGELOG.md +12 -0
- data/Gemfile +0 -12
- data/README.md +58 -2
- data/docker-compose.yml +11 -0
- data/gemfiles/4.2.gemfile +4 -16
- data/gemfiles/5.1.gemfile +13 -0
- data/lib/search_cop.rb +30 -10
- data/lib/search_cop/grammar_parser.rb +2 -1
- data/lib/search_cop/hash_parser.rb +3 -1
- data/lib/search_cop/query_builder.rb +2 -2
- data/lib/search_cop/version.rb +1 -1
- data/lib/search_cop/visitors/visitor.rb +2 -2
- data/lib/search_cop_grammar.rb +12 -1
- data/lib/search_cop_grammar.treetop +6 -4
- data/lib/search_cop_grammar/attributes.rb +11 -3
- data/search_cop.gemspec +1 -2
- data/test/and_test.rb +1 -1
- data/test/database.yml +3 -1
- data/test/date_test.rb +28 -0
- data/test/datetime_test.rb +35 -0
- data/test/default_operator_test.rb +47 -0
- data/test/float_test.rb +7 -0
- data/test/search_cop_test.rb +1 -2
- data/test/test_helper.rb +7 -3
- metadata +8 -23
- data/Appraisals +0 -21
- data/gemfiles/3.2.gemfile +0 -25
- data/gemfiles/4.0.gemfile +0 -25
- data/gemfiles/4.1.gemfile +0 -25
- data/gemfiles/5.0.gemfile +0 -25
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 | 
            -
             | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: bf37a33f6c9998a09a09817fe5f14024947a1f83ed63682c066dcf35c385ac57
         | 
| 4 | 
            +
              data.tar.gz: 36df6f6110f2126240702636d93c0ed54322794ff5f558fa6dcb27484c3b2b28
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 4c5233ee11b096ec18e1a53f1e76a6dbb0880a99de9ac41ec836993f317612cf728d81a1332c3f7bfeb42766cb7e28e388061d85cabec8355e7c32e969468761
         | 
| 7 | 
            +
              data.tar.gz: f766262804751519e3566ea80af0358dba148540883d74b5947804e70876ae90555dcb539032f5e59ff1d64fb03b6b10e293e487842b47eeea31246c14a69024
         | 
    
        data/.travis.yml
    CHANGED
    
    | @@ -1,45 +1,34 @@ | |
| 1 1 |  | 
| 2 | 
            -
             | 
| 3 | 
            -
              postgresql | 
| 2 | 
            +
            services:
         | 
| 3 | 
            +
              - postgresql
         | 
| 4 | 
            +
              - mysql
         | 
| 4 5 |  | 
| 5 6 | 
             
            before_script:
         | 
| 6 7 | 
             
              - mysql -e 'create database search_cop;'
         | 
| 7 | 
            -
              - psql -c  | 
| 8 | 
            +
              - psql -c "create user search_cop password 'secret';" -U postgres
         | 
| 9 | 
            +
              - psql -c 'create database search_cop owner search_cop;' -U postgres
         | 
| 8 10 |  | 
| 9 | 
            -
            rvm:
         | 
| 10 | 
            -
              - 2.1.10
         | 
| 11 | 
            -
              - 2.2.5
         | 
| 12 | 
            -
              - 2.3.1
         | 
| 13 | 
            -
              - rbx-3
         | 
| 14 | 
            -
              - jruby
         | 
| 15 | 
            -
            env:
         | 
| 16 | 
            -
              - DATABASE=sqlite
         | 
| 17 | 
            -
              - DATABASE=mysql
         | 
| 18 | 
            -
              - DATABASE=postgres
         | 
| 19 11 | 
             
            matrix:
         | 
| 20 12 | 
             
              include:
         | 
| 21 | 
            -
                - rvm:  | 
| 22 | 
            -
                  gemfile: gemfiles/ | 
| 13 | 
            +
                - rvm: ruby-head
         | 
| 14 | 
            +
                  gemfile: gemfiles/4.2.gemfile
         | 
| 23 15 | 
             
                  env: DATABASE=sqlite
         | 
| 24 | 
            -
                - rvm:  | 
| 25 | 
            -
                  gemfile: gemfiles/ | 
| 16 | 
            +
                - rvm: ruby-head
         | 
| 17 | 
            +
                  gemfile: gemfiles/4.2.gemfile
         | 
| 26 18 | 
             
                  env: DATABASE=mysql
         | 
| 27 | 
            -
                - rvm:  | 
| 28 | 
            -
                  gemfile: gemfiles/5. | 
| 19 | 
            +
                - rvm: ruby-head
         | 
| 20 | 
            +
                  gemfile: gemfiles/5.1.gemfile
         | 
| 21 | 
            +
                  env: DATABASE=mysql
         | 
| 22 | 
            +
                - rvm: ruby-head
         | 
| 23 | 
            +
                  gemfile: gemfiles/4.2.gemfile
         | 
| 24 | 
            +
                  env: DATABASE=postgres
         | 
| 25 | 
            +
                - rvm: ruby-head
         | 
| 26 | 
            +
                  gemfile: gemfiles/5.1.gemfile
         | 
| 29 27 | 
             
                  env: DATABASE=postgres
         | 
| 30 | 
            -
              allow_failures:
         | 
| 31 | 
            -
                - rvm: rbx-3
         | 
| 32 | 
            -
                - rvm: jruby
         | 
| 33 | 
            -
             | 
| 34 | 
            -
            gemfile:
         | 
| 35 | 
            -
              - gemfiles/3.2.gemfile
         | 
| 36 | 
            -
              - gemfiles/4.0.gemfile
         | 
| 37 | 
            -
              - gemfiles/4.1.gemfile
         | 
| 38 | 
            -
              - gemfiles/4.2.gemfile
         | 
| 39 28 |  | 
| 40 29 | 
             
            install:
         | 
| 41 30 | 
             
              - "travis_retry bundle install"
         | 
| 42 31 |  | 
| 43 | 
            -
            script: " | 
| 32 | 
            +
            script: "rake test --trace"
         | 
| 44 33 | 
             
            sudo: false
         | 
| 45 34 |  | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,6 +1,18 @@ | |
| 1 1 |  | 
| 2 2 | 
             
            # Changelog
         | 
| 3 3 |  | 
| 4 | 
            +
            Version 1.2.0:
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            * Adds customizable default operator to concatenate conditions (#49)
         | 
| 7 | 
            +
            * Make the postgis adapter use the postgres extensions
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            Version 1.0.9:
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            * Use [:blank:] instead of \s for space (#46)
         | 
| 12 | 
            +
            * Updated SearchCop::Visitors::Visitor to check the connection's adapter_name when extending. (#47)
         | 
| 13 | 
            +
            * Fix for negative numeric values
         | 
| 14 | 
            +
            * allow searching for relative dates, like hours, days, weeks, months or years ago
         | 
| 15 | 
            +
             | 
| 4 16 | 
             
            Version 1.0.8:
         | 
| 5 17 |  | 
| 6 18 | 
             
            * No longer add search scope methods globally #34
         | 
    
        data/Gemfile
    CHANGED
    
    | @@ -3,21 +3,9 @@ source 'https://rubygems.org' | |
| 3 3 | 
             
            # Specify your gem's dependencies in search_cop.gemspec
         | 
| 4 4 | 
             
            gemspec
         | 
| 5 5 |  | 
| 6 | 
            -
            platforms :jruby do
         | 
| 7 | 
            -
              gem 'activerecord-jdbcmysql-adapter'
         | 
| 8 | 
            -
              gem 'activerecord-jdbcsqlite3-adapter'
         | 
| 9 | 
            -
              gem 'activerecord-jdbcpostgresql-adapter'
         | 
| 10 | 
            -
            end
         | 
| 11 | 
            -
             | 
| 12 6 | 
             
            platforms :ruby do
         | 
| 13 7 | 
             
              gem 'sqlite3'
         | 
| 14 8 | 
             
              gem 'mysql2'
         | 
| 15 9 | 
             
              gem 'pg'
         | 
| 16 10 | 
             
            end
         | 
| 17 11 |  | 
| 18 | 
            -
            platforms :rbx do
         | 
| 19 | 
            -
              gem 'racc'
         | 
| 20 | 
            -
              gem 'rubysl', '~> 2.0'
         | 
| 21 | 
            -
              gem 'psych'
         | 
| 22 | 
            -
            end
         | 
| 23 | 
            -
             | 
    
        data/README.md
    CHANGED
    
    | @@ -2,7 +2,6 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            [](http://travis-ci.org/mrkamel/search_cop)
         | 
| 4 4 | 
             
            [](https://codeclimate.com/github/mrkamel/search_cop)
         | 
| 5 | 
            -
            [](https://gemnasium.com/mrkamel/search_cop)
         | 
| 6 5 | 
             
            [](http://badge.fury.io/rb/search_cop)
         | 
| 7 6 |  | 
| 8 7 | 
             
            
         | 
| @@ -118,6 +117,24 @@ or post-process the search results in every possible way: | |
| 118 117 | 
             
            Book.where(available: true).search("Harry Potter").order("books.id desc").paginate(page: params[:page])
         | 
| 119 118 | 
             
            ```
         | 
| 120 119 |  | 
| 120 | 
            +
            ## Security
         | 
| 121 | 
            +
             | 
| 122 | 
            +
            When you pass a query string to SearchCop, it gets parsed, analyzed and mapped
         | 
| 123 | 
            +
            to finally build up an SQL query. To be more precise, when SearchCop parses the
         | 
| 124 | 
            +
            query, it creates objects (nodes), which represent the query expressions (And-,
         | 
| 125 | 
            +
            Or-, Not-, String-, Date-, etc Nodes). To build the SQL query, SearchCop uses
         | 
| 126 | 
            +
            the concept of visitors like e.g. used in
         | 
| 127 | 
            +
            [Arel](https://github.com/rails/arel), such that, for every node there must be
         | 
| 128 | 
            +
            a [visitor](https://github.com/mrkamel/search_cop/blob/master/lib/search_cop/visitors/visitor.rb),
         | 
| 129 | 
            +
            which transforms the node to SQL. When there is no visitor, an exception is
         | 
| 130 | 
            +
            raised when the query builder tries to "visit" the node. The visitors are
         | 
| 131 | 
            +
            responsible for sanitizing the user supplied input. This is primilarly done via
         | 
| 132 | 
            +
            quoting (string-, table-name-, column-quoting, etc). SearchCop is using the
         | 
| 133 | 
            +
            methods provided by the ActiveRecord connection adapter for sanitizing/quoting
         | 
| 134 | 
            +
            to prevent SQL injection. While we can never be 100% safe from security issues,
         | 
| 135 | 
            +
            SearchCop takes security issues seriously. Please report responsibly via
         | 
| 136 | 
            +
            security at flakks dot com in case you find any security related issues.
         | 
| 137 | 
            +
             | 
| 121 138 | 
             
            ## Fulltext index capabilities
         | 
| 122 139 |  | 
| 123 140 | 
             
            By default, i.e. if you don't tell SearchCop about your fulltext indices,
         | 
| @@ -283,7 +300,7 @@ For more details about PostgreSQL fulltext indices visit | |
| 283 300 |  | 
| 284 301 | 
             
            In case you expose non-fulltext attributes to search queries (price, stock,
         | 
| 285 302 | 
             
            etc.), the respective queries, like `Book.search("stock > 0")`, will profit
         | 
| 286 | 
            -
            from  | 
| 303 | 
            +
            from the usual non-fulltext indices. Thus, you should add a usual index on
         | 
| 287 304 | 
             
            every column you expose to search queries plus a fulltext index for every
         | 
| 288 305 | 
             
            fulltext attribute.
         | 
| 289 306 |  | 
| @@ -312,6 +329,41 @@ User.search("admin") | |
| 312 329 | 
             
            # ... WHERE users.username LIKE 'admin%'
         | 
| 313 330 | 
             
            ```
         | 
| 314 331 |  | 
| 332 | 
            +
            ## Default operator
         | 
| 333 | 
            +
             | 
| 334 | 
            +
            When you define multiple fields on a search scope, SearcCop will use
         | 
| 335 | 
            +
            by default the AND operator to concatenate the conditions, e.g:
         | 
| 336 | 
            +
             | 
| 337 | 
            +
            ```ruby
         | 
| 338 | 
            +
            class User < ActiveRecord::Base
         | 
| 339 | 
            +
              include SearchCop
         | 
| 340 | 
            +
             | 
| 341 | 
            +
              search_scope :search do
         | 
| 342 | 
            +
                attributes :username, :fullname
         | 
| 343 | 
            +
              end
         | 
| 344 | 
            +
             | 
| 345 | 
            +
              # ...
         | 
| 346 | 
            +
            end
         | 
| 347 | 
            +
            ```
         | 
| 348 | 
            +
             | 
| 349 | 
            +
            So a search like `User.search("something")` will generate a query
         | 
| 350 | 
            +
            with the following conditions:
         | 
| 351 | 
            +
             | 
| 352 | 
            +
            ```sql
         | 
| 353 | 
            +
            ... WHERE username LIKE '%something%' AND fullname LIKE '%something%'
         | 
| 354 | 
            +
            ```
         | 
| 355 | 
            +
             | 
| 356 | 
            +
            However, there are cases where using AND as the default operator is not desired,
         | 
| 357 | 
            +
            so SearchCop allows you to override it and use OR as the default operator instead.
         | 
| 358 | 
            +
            A query like `User.search("something", default_operator: :or)` will
         | 
| 359 | 
            +
            generate the query using OR to concatenate the conditions
         | 
| 360 | 
            +
             | 
| 361 | 
            +
            ```sql
         | 
| 362 | 
            +
            ... WHERE username LIKE '%something%' OR fullname LIKE '%something%'
         | 
| 363 | 
            +
            ```
         | 
| 364 | 
            +
             | 
| 365 | 
            +
            Finally, please note that you can apply it to fulltext indices/queries as well.
         | 
| 366 | 
            +
             | 
| 315 367 | 
             
            ## Associations
         | 
| 316 368 |  | 
| 317 369 | 
             
            If you specify searchable attributes from another model, like
         | 
| @@ -454,6 +506,10 @@ SearchCop also provides the ability to define custom operators by defining a | |
| 454 506 | 
             
            search. This is useful when you want to use database operators that are not
         | 
| 455 507 | 
             
            supported by SearchCop.
         | 
| 456 508 |  | 
| 509 | 
            +
            Please note, when using generators, you are responsible for sanitizing/quoting
         | 
| 510 | 
            +
            the values (see example below). Otherwise your generator will allow SQL
         | 
| 511 | 
            +
            injection. Thus, please only use generators if you know what you're doing.
         | 
| 512 | 
            +
             | 
| 457 513 | 
             
            For example, if you wanted to perform a `LIKE` query where a book title starts
         | 
| 458 514 | 
             
            with a string, you can define the search scope like so:
         | 
| 459 515 |  | 
    
        data/docker-compose.yml
    ADDED
    
    
    
        data/gemfiles/4.2.gemfile
    CHANGED
    
    | @@ -2,24 +2,12 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            source "https://rubygems.org"
         | 
| 4 4 |  | 
| 5 | 
            -
            gem "activerecord", "~> 4.2 | 
| 6 | 
            -
             | 
| 7 | 
            -
            platforms :jruby do
         | 
| 8 | 
            -
              gem "activerecord-jdbcmysql-adapter"
         | 
| 9 | 
            -
              gem "activerecord-jdbcsqlite3-adapter"
         | 
| 10 | 
            -
              gem "activerecord-jdbcpostgresql-adapter"
         | 
| 11 | 
            -
            end
         | 
| 5 | 
            +
            gem "activerecord", "~> 4.2"
         | 
| 12 6 |  | 
| 13 7 | 
             
            platforms :ruby do
         | 
| 14 | 
            -
              gem "sqlite3"
         | 
| 15 | 
            -
              gem "mysql2", " | 
| 16 | 
            -
              gem "pg"
         | 
| 17 | 
            -
            end
         | 
| 18 | 
            -
             | 
| 19 | 
            -
            platforms :rbx do
         | 
| 20 | 
            -
              gem "racc"
         | 
| 21 | 
            -
              gem "rubysl", "~> 2.0"
         | 
| 22 | 
            -
              gem "psych"
         | 
| 8 | 
            +
              gem "sqlite3", "1.3.13"
         | 
| 9 | 
            +
              gem "mysql2", "0.4.10"
         | 
| 10 | 
            +
              gem "pg", "0.21.0"
         | 
| 23 11 | 
             
            end
         | 
| 24 12 |  | 
| 25 13 | 
             
            gemspec :path => "../"
         | 
    
        data/lib/search_cop.rb
    CHANGED
    
    | @@ -8,21 +8,24 @@ require "search_cop/hash_parser" | |
| 8 8 | 
             
            require "search_cop/visitors"
         | 
| 9 9 |  | 
| 10 10 | 
             
            module SearchCop
         | 
| 11 | 
            -
              class  | 
| 11 | 
            +
              class Error < StandardError; end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              class SpecificationError < Error; end
         | 
| 12 14 | 
             
              class UnknownAttribute < SpecificationError; end
         | 
| 15 | 
            +
              class UnknownDefaultOperator < SpecificationError; end
         | 
| 13 16 |  | 
| 14 | 
            -
              class RuntimeError <  | 
| 17 | 
            +
              class RuntimeError < Error; end
         | 
| 15 18 | 
             
              class UnknownColumn < RuntimeError; end
         | 
| 16 19 | 
             
              class NoSearchableAttributes < RuntimeError; end
         | 
| 17 20 | 
             
              class IncompatibleDatatype < RuntimeError; end
         | 
| 18 21 | 
             
              class ParseError < RuntimeError; end
         | 
| 19 22 |  | 
| 20 23 | 
             
              module Parser
         | 
| 21 | 
            -
                def self.parse(query, query_info)
         | 
| 24 | 
            +
                def self.parse(query, query_info, query_options = {})
         | 
| 22 25 | 
             
                  if query.is_a?(Hash)
         | 
| 23 26 | 
             
                    SearchCop::HashParser.new(query_info).parse(query)
         | 
| 24 27 | 
             
                  else
         | 
| 25 | 
            -
                    SearchCop::GrammarParser.new(query_info).parse(query)
         | 
| 28 | 
            +
                    SearchCop::GrammarParser.new(query_info).parse(query, query_options)
         | 
| 26 29 | 
             
                  end
         | 
| 27 30 | 
             
                end
         | 
| 28 31 | 
             
              end
         | 
| @@ -41,24 +44,24 @@ module SearchCop | |
| 41 44 | 
             
                  search_scopes[name] = SearchScope.new(name, self)
         | 
| 42 45 | 
             
                  search_scopes[name].instance_exec(&block)
         | 
| 43 46 |  | 
| 44 | 
            -
                  self.send(:define_singleton_method, name) { |query| search_cop query, name }
         | 
| 45 | 
            -
                  self.send(:define_singleton_method, "unsafe_#{name}") { |query| unsafe_search_cop query, name }
         | 
| 47 | 
            +
                  self.send(:define_singleton_method, name) { |query, query_options={}| search_cop query, name, query_options }
         | 
| 48 | 
            +
                  self.send(:define_singleton_method, "unsafe_#{name}") { |query, query_options={}| unsafe_search_cop query, name, query_options }
         | 
| 46 49 | 
             
                end
         | 
| 47 50 |  | 
| 48 51 | 
             
                def search_reflection(scope_name)
         | 
| 49 52 | 
             
                  search_scopes[scope_name].reflection
         | 
| 50 53 | 
             
                end
         | 
| 51 54 |  | 
| 52 | 
            -
                def search_cop(query, scope_name)
         | 
| 53 | 
            -
                  unsafe_search_cop query, scope_name
         | 
| 55 | 
            +
                def search_cop(query, scope_name, query_options)
         | 
| 56 | 
            +
                  unsafe_search_cop query, scope_name, query_options
         | 
| 54 57 | 
             
                rescue SearchCop::RuntimeError
         | 
| 55 58 | 
             
                  respond_to?(:none) ? none : where("1 = 0")
         | 
| 56 59 | 
             
                end
         | 
| 57 60 |  | 
| 58 | 
            -
                def unsafe_search_cop(query, scope_name)
         | 
| 61 | 
            +
                def unsafe_search_cop(query, scope_name, query_options)
         | 
| 59 62 | 
             
                  return respond_to?(:scoped) ? scoped : all if query.blank?
         | 
| 60 63 |  | 
| 61 | 
            -
                  query_builder = QueryBuilder.new(self, query, search_scopes[scope_name])
         | 
| 64 | 
            +
                  query_builder = QueryBuilder.new(self, query, search_scopes[scope_name], query_options)
         | 
| 62 65 |  | 
| 63 66 | 
             
                  scope = instance_exec(&search_scopes[scope_name].reflection.scope) if search_scopes[scope_name].reflection.scope
         | 
| 64 67 | 
             
                  scope ||= eager_load(query_builder.associations) if query_builder.associations.any?
         | 
| @@ -66,5 +69,22 @@ module SearchCop | |
| 66 69 | 
             
                  (scope || self).where(query_builder.sql)
         | 
| 67 70 | 
             
                end
         | 
| 68 71 | 
             
              end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              module Helpers
         | 
| 74 | 
            +
                def self.sanitize_default_operator(hash, delete_hash_option=false)
         | 
| 75 | 
            +
                  default_operator = :and
         | 
| 76 | 
            +
                  if hash.member?(:default_operator)
         | 
| 77 | 
            +
                    unless [:and, :or].include?(hash[:default_operator])
         | 
| 78 | 
            +
                      raise(SearchCop::UnknownDefaultOperator, "Unknown default operator value #{hash[:default_operator]}")
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                    default_operator = hash[:default_operator]
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  hash.delete(:default_operator) if delete_hash_option
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  default_operator
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
              end
         | 
| 69 89 | 
             
            end
         | 
| 70 90 |  | 
| @@ -12,9 +12,10 @@ module SearchCop | |
| 12 12 | 
             
                  @query_info = query_info
         | 
| 13 13 | 
             
                end
         | 
| 14 14 |  | 
| 15 | 
            -
                def parse(string)
         | 
| 15 | 
            +
                def parse(string, query_options)
         | 
| 16 16 | 
             
                  node = SearchCopGrammarParser.new.parse(string) || raise(ParseError)
         | 
| 17 17 | 
             
                  node.query_info = query_info
         | 
| 18 | 
            +
                  node.query_options = query_options
         | 
| 18 19 | 
             
                  node.evaluate
         | 
| 19 20 | 
             
                end
         | 
| 20 21 | 
             
              end
         | 
| @@ -7,6 +7,8 @@ class SearchCop::HashParser | |
| 7 7 | 
             
              end
         | 
| 8 8 |  | 
| 9 9 | 
             
              def parse(hash)
         | 
| 10 | 
            +
                default_operator = SearchCop::Helpers.sanitize_default_operator(hash, true)
         | 
| 11 | 
            +
             | 
| 10 12 | 
             
                res = hash.collect do |key, value|
         | 
| 11 13 | 
             
                  case key
         | 
| 12 14 | 
             
                    when :and
         | 
| @@ -22,7 +24,7 @@ class SearchCop::HashParser | |
| 22 24 | 
             
                  end
         | 
| 23 25 | 
             
                end
         | 
| 24 26 |  | 
| 25 | 
            -
                res.inject  | 
| 27 | 
            +
                res.inject default_operator
         | 
| 26 28 | 
             
              end
         | 
| 27 29 |  | 
| 28 30 | 
             
              private
         | 
| @@ -3,11 +3,11 @@ module SearchCop | |
| 3 3 | 
             
              class QueryBuilder
         | 
| 4 4 | 
             
                attr_accessor :query_info, :scope, :sql
         | 
| 5 5 |  | 
| 6 | 
            -
                def initialize(model, query, scope)
         | 
| 6 | 
            +
                def initialize(model, query, scope, query_options)
         | 
| 7 7 | 
             
                  self.scope = scope
         | 
| 8 8 | 
             
                  self.query_info = QueryInfo.new(model, scope)
         | 
| 9 9 |  | 
| 10 | 
            -
                  arel = SearchCop::Parser.parse(query, query_info).optimize!
         | 
| 10 | 
            +
                  arel = SearchCop::Parser.parse(query, query_info, query_options).optimize!
         | 
| 11 11 |  | 
| 12 12 | 
             
                  self.sql = SearchCop::Visitors::Visitor.new(model.connection).visit(arel)
         | 
| 13 13 | 
             
                end
         | 
    
        data/lib/search_cop/version.rb
    CHANGED
    
    
| @@ -7,8 +7,8 @@ module SearchCop | |
| 7 7 | 
             
                  def initialize(connection)
         | 
| 8 8 | 
             
                    @connection = connection
         | 
| 9 9 |  | 
| 10 | 
            -
                    extend(SearchCop::Visitors::Mysql) if @connection. | 
| 11 | 
            -
                    extend(SearchCop::Visitors::Postgres) if @connection. | 
| 10 | 
            +
                    extend(SearchCop::Visitors::Mysql) if @connection.adapter_name =~ /mysql/i
         | 
| 11 | 
            +
                    extend(SearchCop::Visitors::Postgres) if @connection.adapter_name =~ /postgres|postgis/i
         | 
| 12 12 | 
             
                  end
         | 
| 13 13 |  | 
| 14 14 | 
             
                  def visit(visit_node = node)
         | 
    
        data/lib/search_cop_grammar.rb
    CHANGED
    
    | @@ -4,12 +4,16 @@ require "search_cop_grammar/nodes" | |
| 4 4 |  | 
| 5 5 | 
             
            module SearchCopGrammar
         | 
| 6 6 | 
             
              class BaseNode < Treetop::Runtime::SyntaxNode
         | 
| 7 | 
            -
                 | 
| 7 | 
            +
                attr_writer :query_info, :query_options
         | 
| 8 8 |  | 
| 9 9 | 
             
                def query_info
         | 
| 10 10 | 
             
                  (@query_info ||= nil) || parent.query_info
         | 
| 11 11 | 
             
                end
         | 
| 12 12 |  | 
| 13 | 
            +
                def query_options
         | 
| 14 | 
            +
                  (@query_options ||= nil) || parent.query_options
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 13 17 | 
             
                def evaluate
         | 
| 14 18 | 
             
                  elements.collect(&:evaluate).inject(:and)
         | 
| 15 19 | 
             
                end
         | 
| @@ -110,6 +114,13 @@ module SearchCopGrammar | |
| 110 114 | 
             
                end
         | 
| 111 115 | 
             
              end
         | 
| 112 116 |  | 
| 117 | 
            +
              class AndOrExpression < BaseNode
         | 
| 118 | 
            +
                def evaluate
         | 
| 119 | 
            +
                  default_operator = SearchCop::Helpers.sanitize_default_operator(query_options)
         | 
| 120 | 
            +
                  [elements.first.evaluate, elements.last.evaluate].inject(default_operator)
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
              end
         | 
| 123 | 
            +
             | 
| 113 124 | 
             
              class OrExpression < BaseNode
         | 
| 114 125 | 
             
                def evaluate
         | 
| 115 126 | 
             
                  [elements.first.evaluate, elements.last.evaluate].inject(:or)
         | 
| @@ -9,7 +9,9 @@ grammar SearchCopGrammar | |
| 9 9 | 
             
              end
         | 
| 10 10 |  | 
| 11 11 | 
             
              rule and_expression
         | 
| 12 | 
            -
                or_expression  | 
| 12 | 
            +
                or_expression space? ('AND' / 'and') space? complex_expression <AndExpression> /
         | 
| 13 | 
            +
                or_expression space !('OR' / 'or') complex_expression <AndOrExpression> /
         | 
| 14 | 
            +
                or_expression
         | 
| 13 15 | 
             
              end
         | 
| 14 16 |  | 
| 15 17 | 
             
              rule or_expression
         | 
| @@ -37,7 +39,7 @@ grammar SearchCopGrammar | |
| 37 39 | 
             
              end
         | 
| 38 40 |  | 
| 39 41 | 
             
              rule anywhere_expression
         | 
| 40 | 
            -
                "'" [^\']* "'" <SingleQuotedAnywhereExpression> / '"' [^\"]* '"' <DoubleQuotedAnywhereExpression> / [ | 
| 42 | 
            +
                "'" [^\']* "'" <SingleQuotedAnywhereExpression> / '"' [^\"]* '"' <DoubleQuotedAnywhereExpression> / [^[:blank:]()]+ <AnywhereExpression>
         | 
| 41 43 | 
             
              end
         | 
| 42 44 |  | 
| 43 45 | 
             
              rule simple_column
         | 
| @@ -45,11 +47,11 @@ grammar SearchCopGrammar | |
| 45 47 | 
             
              end
         | 
| 46 48 |  | 
| 47 49 | 
             
              rule value
         | 
| 48 | 
            -
                "'" [^\']* "'" <SingleQuotedValue> / '"' [^\"]* '"' <DoubleQuotedValue> / [ | 
| 50 | 
            +
                "'" [^\']* "'" <SingleQuotedValue> / '"' [^\"]* '"' <DoubleQuotedValue> / [^[:blank:]()]+ <Value>
         | 
| 49 51 | 
             
              end
         | 
| 50 52 |  | 
| 51 53 | 
             
              rule space
         | 
| 52 | 
            -
                [ | 
| 54 | 
            +
                [[:blank:]]+
         | 
| 53 55 | 
             
              end
         | 
| 54 56 | 
             
            end
         | 
| 55 57 |  | 
| @@ -176,7 +176,7 @@ module SearchCopGrammar | |
| 176 176 |  | 
| 177 177 | 
             
                class Float < WithoutMatches
         | 
| 178 178 | 
             
                  def compatible?(value)
         | 
| 179 | 
            -
                    return true if value.to_s =~  | 
| 179 | 
            +
                    return true if value.to_s =~ /^\-?[0-9]+(\.[0-9]+)?$/
         | 
| 180 180 |  | 
| 181 181 | 
             
                    false
         | 
| 182 182 | 
             
                  end
         | 
| @@ -198,7 +198,11 @@ module SearchCopGrammar | |
| 198 198 | 
             
                  def parse(value)
         | 
| 199 199 | 
             
                    return value .. value unless value.is_a?(::String)
         | 
| 200 200 |  | 
| 201 | 
            -
                    if value =~ /^[0-9]{ | 
| 201 | 
            +
                    if value =~ /^[0-9]+ (hour|day|week|month|year)s{0,1} (ago)$/
         | 
| 202 | 
            +
                      number,period,ago = value.split(' ')
         | 
| 203 | 
            +
                      time = number.to_i.send(period.to_sym).send(ago.to_sym)
         | 
| 204 | 
            +
                      time .. ::Time.now
         | 
| 205 | 
            +
                    elsif value =~ /^[0-9]{4}$/
         | 
| 202 206 | 
             
                      ::Time.new(value).beginning_of_year .. ::Time.new(value).end_of_year
         | 
| 203 207 | 
             
                    elsif value =~ /^([0-9]{4})(\.|-|\/)([0-9]{1,2})$/
         | 
| 204 208 | 
             
                      ::Time.new($1, $3, 15).beginning_of_month .. ::Time.new($1, $3, 15).end_of_month
         | 
| @@ -244,7 +248,11 @@ module SearchCopGrammar | |
| 244 248 | 
             
                  def parse(value)
         | 
| 245 249 | 
             
                    return value .. value unless value.is_a?(::String)
         | 
| 246 250 |  | 
| 247 | 
            -
                    if value =~ /^[0-9]{ | 
| 251 | 
            +
                    if value =~ /^[0-9]+ (day|week|month|year)s{0,1} (ago)$/
         | 
| 252 | 
            +
                      number,period,ago = value.split(' ')
         | 
| 253 | 
            +
                      time = number.to_i.send(period.to_sym).send(ago.to_sym)
         | 
| 254 | 
            +
                      time.to_date .. ::Date.today
         | 
| 255 | 
            +
                    elsif value =~ /^[0-9]{4}$/
         | 
| 248 256 | 
             
                      ::Date.new(value.to_i).beginning_of_year .. ::Date.new(value.to_i).end_of_year
         | 
| 249 257 | 
             
                    elsif value =~ /^([0-9]{4})(\.|-|\/)([0-9]{1,2})$/
         | 
| 250 258 | 
             
                      ::Date.new($1.to_i, $3.to_i, 15).beginning_of_month .. ::Date.new($1.to_i, $3.to_i, 15).end_of_month
         | 
    
        data/search_cop.gemspec
    CHANGED
    
    | @@ -23,7 +23,6 @@ Gem::Specification.new do |spec| | |
| 23 23 | 
             
              spec.add_development_dependency "bundler"
         | 
| 24 24 | 
             
              spec.add_development_dependency "rake"
         | 
| 25 25 | 
             
              spec.add_development_dependency "activerecord", ">= 3.0.0"
         | 
| 26 | 
            -
              spec.add_development_dependency " | 
| 27 | 
            -
              spec.add_development_dependency "appraisal"
         | 
| 26 | 
            +
              spec.add_development_dependency "factory_bot"
         | 
| 28 27 | 
             
              spec.add_development_dependency "minitest"
         | 
| 29 28 | 
             
            end
         | 
    
        data/test/and_test.rb
    CHANGED
    
    | @@ -3,7 +3,7 @@ require File.expand_path("../test_helper", __FILE__) | |
| 3 3 |  | 
| 4 4 | 
             
            class AndTest < SearchCop::TestCase
         | 
| 5 5 | 
             
              def test_and_string
         | 
| 6 | 
            -
                expected = create(:product, :title => " | 
| 6 | 
            +
                expected = create(:product, :title => "expected title", :description => "Description")
         | 
| 7 7 | 
             
                rejected = create(:product, :title => "Rejected title", :description => "Description")
         | 
| 8 8 |  | 
| 9 9 | 
             
                results = Product.search("title: 'Expected title' description: Description")
         | 
    
        data/test/database.yml
    CHANGED
    
    
    
        data/test/date_test.rb
    CHANGED
    
    | @@ -66,6 +66,34 @@ class DateTest < SearchCop::TestCase | |
| 66 66 | 
             
                refute_includes Product.search("created_on <= 2014-05-01"), product
         | 
| 67 67 | 
             
              end
         | 
| 68 68 |  | 
| 69 | 
            +
              def test_days_ago
         | 
| 70 | 
            +
                product = create(:product, :created_at => 2.days.ago.to_date)
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                assert_includes Product.search("created_at <= '1 day ago'"), product
         | 
| 73 | 
            +
                refute_includes Product.search("created_at <= '3 days ago'"), product
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              def test_weeks_ago
         | 
| 77 | 
            +
                product = create(:product, :created_at => 2.weeks.ago.to_date)
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                assert_includes Product.search("created_at <= '1 weeks ago'"), product
         | 
| 80 | 
            +
                refute_includes Product.search("created_at <= '3 weeks ago'"), product
         | 
| 81 | 
            +
              end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
              def test_months_ago
         | 
| 84 | 
            +
                product = create(:product, :created_at => 2.months.ago.to_date)
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                assert_includes Product.search("created_at <= '1 months ago'"), product
         | 
| 87 | 
            +
                refute_includes Product.search("created_at <= '3 months ago'"), product
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
              def test_years_ago
         | 
| 91 | 
            +
                product = create(:product, :created_at => 2.years.ago.to_date)
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                assert_includes Product.search("created_at <= '1 years ago'"), product
         | 
| 94 | 
            +
                refute_includes Product.search("created_at <= '3 years ago'"), product
         | 
| 95 | 
            +
              end
         | 
| 96 | 
            +
             | 
| 69 97 | 
             
              def test_no_overflow
         | 
| 70 98 | 
             
                assert_nothing_raised do
         | 
| 71 99 | 
             
                  Product.search("created_on: 1000000").to_a
         | 
    
        data/test/datetime_test.rb
    CHANGED
    
    | @@ -67,6 +67,41 @@ class DatetimeTest < SearchCop::TestCase | |
| 67 67 | 
             
                refute_includes Product.search("created_at <= 2014-05-01"), product
         | 
| 68 68 | 
             
              end
         | 
| 69 69 |  | 
| 70 | 
            +
              def test_hours_ago
         | 
| 71 | 
            +
                product = create(:product, :created_at => 5.hours.ago)
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                assert_includes Product.search("created_at <= '4 hours ago'"), product
         | 
| 74 | 
            +
                refute_includes Product.search("created_at <= '6 hours ago'"), product
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              def test_days_ago
         | 
| 78 | 
            +
                product = create(:product, :created_at => 2.days.ago)
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                assert_includes Product.search("created_at <= '1 day ago'"), product
         | 
| 81 | 
            +
                refute_includes Product.search("created_at <= '3 days ago'"), product
         | 
| 82 | 
            +
              end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
              def test_weeks_ago
         | 
| 85 | 
            +
                product = create(:product, :created_at => 2.weeks.ago)
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                assert_includes Product.search("created_at <= '1 weeks ago'"), product
         | 
| 88 | 
            +
                refute_includes Product.search("created_at <= '3 weeks ago'"), product
         | 
| 89 | 
            +
              end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
              def test_months_ago
         | 
| 92 | 
            +
                product = create(:product, :created_at => 2.months.ago)
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                assert_includes Product.search("created_at <= '1 months ago'"), product
         | 
| 95 | 
            +
                refute_includes Product.search("created_at <= '3 months ago'"), product
         | 
| 96 | 
            +
              end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
              def test_years_ago
         | 
| 99 | 
            +
                product = create(:product, :created_at => 2.years.ago)
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                assert_includes Product.search("created_at <= '1 years ago'"), product
         | 
| 102 | 
            +
                refute_includes Product.search("created_at <= '3 years ago'"), product
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
             | 
| 70 105 | 
             
              def test_no_overflow
         | 
| 71 106 | 
             
                assert_nothing_raised do
         | 
| 72 107 | 
             
                  Product.search("created_at: 1000000").to_a
         | 
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            require File.expand_path('../test_helper', __FILE__)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class DefaultOperatorTest < SearchCop::TestCase
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              def test_without_default_operator
         | 
| 6 | 
            +
                avengers = create(:product, title: "Title Avengers", description: "2012")
         | 
| 7 | 
            +
                inception = create(:product, title: "Title Inception", description: "2010")
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                results = Product.search_multi_columns("Title Avengers")
         | 
| 10 | 
            +
                assert_includes results, avengers
         | 
| 11 | 
            +
                refute_includes results, inception
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                results = Product.search_multi_columns("Title AND Avengers")
         | 
| 14 | 
            +
                assert_includes results, avengers
         | 
| 15 | 
            +
                refute_includes results, inception
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                results = Product.search_multi_columns("Title OR Avengers")
         | 
| 18 | 
            +
                assert_includes results, avengers
         | 
| 19 | 
            +
                assert_includes results, inception
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                results = Product.search(title: "Avengers", description: "2012")
         | 
| 22 | 
            +
                assert_includes results, avengers
         | 
| 23 | 
            +
                refute_includes results, inception
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              def test_with_specific_default_operator
         | 
| 27 | 
            +
                matrix = create(:product, title: "Matrix", description: "1999 Fantasy Sci-fi 2h 30m")
         | 
| 28 | 
            +
                start_wars = create(:product, title: "Start Wars", description: "2010 Sci-fi Thriller 2h 28m")
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                results = Product.search_multi_columns("Matrix movie", default_operator: :or)
         | 
| 31 | 
            +
                assert_includes results, matrix
         | 
| 32 | 
            +
                refute_includes results, start_wars
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                results = Product.search(title: "Matrix", description: "2000", default_operator: :or)
         | 
| 35 | 
            +
                assert_includes results, matrix
         | 
| 36 | 
            +
                refute_includes results, start_wars
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              def test_with_invalid_default_operator
         | 
| 40 | 
            +
                assert_raises SearchCop::UnknownDefaultOperator do
         | 
| 41 | 
            +
                  Product.search_multi_columns('Matrix movie', default_operator: :xpto)
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
                assert_raises SearchCop::UnknownDefaultOperator do
         | 
| 44 | 
            +
                  Product.search_multi_columns(title: 'Matrix movie', default_operator: :xpto)
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         | 
    
        data/test/float_test.rb
    CHANGED
    
    | @@ -58,6 +58,13 @@ class FloatTest < SearchCop::TestCase | |
| 58 58 | 
             
                refute_includes Product.search("price <= 10.4"), product
         | 
| 59 59 | 
             
              end
         | 
| 60 60 |  | 
| 61 | 
            +
              def test_negative
         | 
| 62 | 
            +
                product = create(:product, price: -10)
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                assert_includes Product.search("price = -10"), product
         | 
| 65 | 
            +
                refute_includes Product.search("price = -11"), product
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
             | 
| 61 68 | 
             
              def test_incompatible_datatype
         | 
| 62 69 | 
             
                assert_raises SearchCop::IncompatibleDatatype do
         | 
| 63 70 | 
             
                  Product.unsafe_search "price: Value"
         | 
    
        data/test/search_cop_test.rb
    CHANGED
    
    
    
        data/test/test_helper.rb
    CHANGED
    
    | @@ -13,7 +13,7 @@ end | |
| 13 13 |  | 
| 14 14 | 
             
            require "minitest/autorun"
         | 
| 15 15 | 
             
            require "active_record"
         | 
| 16 | 
            -
            require " | 
| 16 | 
            +
            require "factory_bot"
         | 
| 17 17 | 
             
            require "yaml"
         | 
| 18 18 |  | 
| 19 19 | 
             
            DATABASE = ENV["DATABASE"] || "sqlite"
         | 
| @@ -68,13 +68,17 @@ class Product < ActiveRecord::Base | |
| 68 68 | 
             
                aliases :users_products => User
         | 
| 69 69 | 
             
              end
         | 
| 70 70 |  | 
| 71 | 
            +
              search_scope :search_multi_columns do
         | 
| 72 | 
            +
                attributes all: [:title, :description]
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
             | 
| 71 75 | 
             
              has_many :comments
         | 
| 72 76 | 
             
              has_many :users, :through => :comments
         | 
| 73 77 |  | 
| 74 78 | 
             
              belongs_to :user
         | 
| 75 79 | 
             
            end
         | 
| 76 80 |  | 
| 77 | 
            -
             | 
| 81 | 
            +
            FactoryBot.define do
         | 
| 78 82 | 
             
              factory :product do
         | 
| 79 83 | 
             
              end
         | 
| 80 84 |  | 
| @@ -122,7 +126,7 @@ if DATABASE == "mysql" | |
| 122 126 | 
             
            end
         | 
| 123 127 |  | 
| 124 128 | 
             
            class SearchCop::TestCase
         | 
| 125 | 
            -
              include  | 
| 129 | 
            +
              include FactoryBot::Syntax::Methods
         | 
| 126 130 |  | 
| 127 131 | 
             
              def teardown
         | 
| 128 132 | 
             
                Product.delete_all
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: search_cop
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.0 | 
| 4 | 
            +
              version: 1.1.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Benjamin Vetter
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2019-10-16 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: treetop
         | 
| @@ -67,21 +67,7 @@ dependencies: | |
| 67 67 | 
             
                  - !ruby/object:Gem::Version
         | 
| 68 68 | 
             
                    version: 3.0.0
         | 
| 69 69 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 70 | 
            -
              name:  | 
| 71 | 
            -
              requirement: !ruby/object:Gem::Requirement
         | 
| 72 | 
            -
                requirements:
         | 
| 73 | 
            -
                - - ">="
         | 
| 74 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 75 | 
            -
                    version: '0'
         | 
| 76 | 
            -
              type: :development
         | 
| 77 | 
            -
              prerelease: false
         | 
| 78 | 
            -
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 79 | 
            -
                requirements:
         | 
| 80 | 
            -
                - - ">="
         | 
| 81 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 82 | 
            -
                    version: '0'
         | 
| 83 | 
            -
            - !ruby/object:Gem::Dependency
         | 
| 84 | 
            -
              name: appraisal
         | 
| 70 | 
            +
              name: factory_bot
         | 
| 85 71 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 86 72 | 
             
                requirements:
         | 
| 87 73 | 
             
                - - ">="
         | 
| @@ -117,7 +103,6 @@ extra_rdoc_files: [] | |
| 117 103 | 
             
            files:
         | 
| 118 104 | 
             
            - ".gitignore"
         | 
| 119 105 | 
             
            - ".travis.yml"
         | 
| 120 | 
            -
            - Appraisals
         | 
| 121 106 | 
             
            - CHANGELOG.md
         | 
| 122 107 | 
             
            - CONTRIBUTING.md
         | 
| 123 108 | 
             
            - Gemfile
         | 
| @@ -125,11 +110,9 @@ files: | |
| 125 110 | 
             
            - MIGRATION.md
         | 
| 126 111 | 
             
            - README.md
         | 
| 127 112 | 
             
            - Rakefile
         | 
| 128 | 
            -
            -  | 
| 129 | 
            -
            - gemfiles/4.0.gemfile
         | 
| 130 | 
            -
            - gemfiles/4.1.gemfile
         | 
| 113 | 
            +
            - docker-compose.yml
         | 
| 131 114 | 
             
            - gemfiles/4.2.gemfile
         | 
| 132 | 
            -
            - gemfiles/5. | 
| 115 | 
            +
            - gemfiles/5.1.gemfile
         | 
| 133 116 | 
             
            - lib/search_cop.rb
         | 
| 134 117 | 
             
            - lib/search_cop/grammar_parser.rb
         | 
| 135 118 | 
             
            - lib/search_cop/hash_parser.rb
         | 
| @@ -151,6 +134,7 @@ files: | |
| 151 134 | 
             
            - test/database.yml
         | 
| 152 135 | 
             
            - test/date_test.rb
         | 
| 153 136 | 
             
            - test/datetime_test.rb
         | 
| 137 | 
            +
            - test/default_operator_test.rb
         | 
| 154 138 | 
             
            - test/error_test.rb
         | 
| 155 139 | 
             
            - test/float_test.rb
         | 
| 156 140 | 
             
            - test/fulltext_test.rb
         | 
| @@ -183,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 183 167 | 
             
                  version: '0'
         | 
| 184 168 | 
             
            requirements: []
         | 
| 185 169 | 
             
            rubyforge_project: 
         | 
| 186 | 
            -
            rubygems_version: 2. | 
| 170 | 
            +
            rubygems_version: 2.7.3
         | 
| 187 171 | 
             
            signing_key: 
         | 
| 188 172 | 
             
            specification_version: 4
         | 
| 189 173 | 
             
            summary: Easily perform complex search engine like fulltext queries on your ActiveRecord
         | 
| @@ -194,6 +178,7 @@ test_files: | |
| 194 178 | 
             
            - test/database.yml
         | 
| 195 179 | 
             
            - test/date_test.rb
         | 
| 196 180 | 
             
            - test/datetime_test.rb
         | 
| 181 | 
            +
            - test/default_operator_test.rb
         | 
| 197 182 | 
             
            - test/error_test.rb
         | 
| 198 183 | 
             
            - test/float_test.rb
         | 
| 199 184 | 
             
            - test/fulltext_test.rb
         | 
    
        data/Appraisals
    DELETED
    
    | @@ -1,21 +0,0 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            appraise "3.2" do
         | 
| 3 | 
            -
              gem "activerecord", "~> 3.2"
         | 
| 4 | 
            -
              gem "search_cop", :path => "../"
         | 
| 5 | 
            -
            end
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            appraise "4.1" do
         | 
| 8 | 
            -
              gem "activerecord", "~> 4.1"
         | 
| 9 | 
            -
              gem "search_cop", :path => "../"
         | 
| 10 | 
            -
            end
         | 
| 11 | 
            -
             | 
| 12 | 
            -
            appraise "4.2" do
         | 
| 13 | 
            -
              gem "activerecord", "~> 4.2"
         | 
| 14 | 
            -
              gem "search_cop", :path => "../"
         | 
| 15 | 
            -
            end
         | 
| 16 | 
            -
             | 
| 17 | 
            -
            appraise "5.0" do
         | 
| 18 | 
            -
              gem "activerecord", "~> 5.0"
         | 
| 19 | 
            -
              gem "search_cop", :path => "../"
         | 
| 20 | 
            -
            end
         | 
| 21 | 
            -
             | 
    
        data/gemfiles/3.2.gemfile
    DELETED
    
    | @@ -1,25 +0,0 @@ | |
| 1 | 
            -
            # This file was generated by Appraisal
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            source "https://rubygems.org"
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            gem "activerecord", "~> 3.2.22"
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            platforms :jruby do
         | 
| 8 | 
            -
              gem "activerecord-jdbcmysql-adapter"
         | 
| 9 | 
            -
              gem "activerecord-jdbcsqlite3-adapter"
         | 
| 10 | 
            -
              gem "activerecord-jdbcpostgresql-adapter"
         | 
| 11 | 
            -
            end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
            platforms :ruby do
         | 
| 14 | 
            -
              gem "sqlite3"
         | 
| 15 | 
            -
              gem "mysql2", "~> 0.3.20"
         | 
| 16 | 
            -
              gem "pg"
         | 
| 17 | 
            -
            end
         | 
| 18 | 
            -
             | 
| 19 | 
            -
            platforms :rbx do
         | 
| 20 | 
            -
              gem "racc"
         | 
| 21 | 
            -
              gem "rubysl", "~> 2.0"
         | 
| 22 | 
            -
              gem "psych"
         | 
| 23 | 
            -
            end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
            gemspec :path => "../"
         | 
    
        data/gemfiles/4.0.gemfile
    DELETED
    
    | @@ -1,25 +0,0 @@ | |
| 1 | 
            -
            # This file was generated by Appraisal
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            source "https://rubygems.org"
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            gem "activerecord", "~> 4.0.13"
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            platforms :jruby do
         | 
| 8 | 
            -
              gem "activerecord-jdbcmysql-adapter"
         | 
| 9 | 
            -
              gem "activerecord-jdbcsqlite3-adapter"
         | 
| 10 | 
            -
              gem "activerecord-jdbcpostgresql-adapter"
         | 
| 11 | 
            -
            end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
            platforms :ruby do
         | 
| 14 | 
            -
              gem "sqlite3"
         | 
| 15 | 
            -
              gem "mysql2", "~> 0.3.20"
         | 
| 16 | 
            -
              gem "pg"
         | 
| 17 | 
            -
            end
         | 
| 18 | 
            -
             | 
| 19 | 
            -
            platforms :rbx do
         | 
| 20 | 
            -
              gem "racc"
         | 
| 21 | 
            -
              gem "rubysl", "~> 2.0"
         | 
| 22 | 
            -
              gem "psych"
         | 
| 23 | 
            -
            end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
            gemspec :path => "../"
         | 
    
        data/gemfiles/4.1.gemfile
    DELETED
    
    | @@ -1,25 +0,0 @@ | |
| 1 | 
            -
            # This file was generated by Appraisal
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            source "https://rubygems.org"
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            gem "activerecord", "~> 4.1.13"
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            platforms :jruby do
         | 
| 8 | 
            -
              gem "activerecord-jdbcmysql-adapter"
         | 
| 9 | 
            -
              gem "activerecord-jdbcsqlite3-adapter"
         | 
| 10 | 
            -
              gem "activerecord-jdbcpostgresql-adapter"
         | 
| 11 | 
            -
            end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
            platforms :ruby do
         | 
| 14 | 
            -
              gem "sqlite3"
         | 
| 15 | 
            -
              gem "mysql2", "~> 0.3.20"
         | 
| 16 | 
            -
              gem "pg"
         | 
| 17 | 
            -
            end
         | 
| 18 | 
            -
             | 
| 19 | 
            -
            platforms :rbx do
         | 
| 20 | 
            -
              gem "racc"
         | 
| 21 | 
            -
              gem "rubysl", "~> 2.0"
         | 
| 22 | 
            -
              gem "psych"
         | 
| 23 | 
            -
            end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
            gemspec :path => "../"
         | 
    
        data/gemfiles/5.0.gemfile
    DELETED
    
    | @@ -1,25 +0,0 @@ | |
| 1 | 
            -
            # This file was generated by Appraisal
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            source "https://rubygems.org"
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            gem "activerecord", "~> 5.0"
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            platforms :jruby do
         | 
| 8 | 
            -
              gem "activerecord-jdbcmysql-adapter"
         | 
| 9 | 
            -
              gem "activerecord-jdbcsqlite3-adapter"
         | 
| 10 | 
            -
              gem "activerecord-jdbcpostgresql-adapter"
         | 
| 11 | 
            -
            end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
            platforms :ruby do
         | 
| 14 | 
            -
              gem "sqlite3"
         | 
| 15 | 
            -
              gem "mysql2", "~> 0.3.20"
         | 
| 16 | 
            -
              gem "pg"
         | 
| 17 | 
            -
            end
         | 
| 18 | 
            -
             | 
| 19 | 
            -
            platforms :rbx do
         | 
| 20 | 
            -
              gem "racc"
         | 
| 21 | 
            -
              gem "rubysl", "~> 2.0"
         | 
| 22 | 
            -
              gem "psych"
         | 
| 23 | 
            -
            end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
            gemspec :path => "../"
         |