order_query 0.3.0 → 0.3.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/CHANGES.md +7 -1
 - data/MIT-LICENSE +1 -1
 - data/README.md +7 -8
 - data/lib/order_query/{condition.rb → column.rb} +6 -13
 - data/lib/order_query/space.rb +13 -8
 - data/lib/order_query/sql/{condition.rb → column.rb} +6 -6
 - data/lib/order_query/sql/order_by.rb +5 -5
 - data/lib/order_query/sql/where.rb +31 -37
 - data/lib/order_query/version.rb +1 -1
 - data/lib/order_query.rb +3 -3
 - data/spec/order_query_spec.rb +9 -5
 - metadata +3 -3
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: be66ba349bb0a4819d39ee8f01d8bfa12e82177e
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 56637889ea73d1f90e70dcd72e09431eb357f1e1
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: c8f9fe60869ae746f9d095dfc549f651b994ded8b75875cd60761e9e7683cdcb6968834916ae6eb881b3dc684f26220adf4e873948923b8e41daee90b11d6671
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: b068a5d7bcc50af28dbdff396adca897f8f502640fcad0f162f1ea9ee4424c9a9cf1afde85837261908bd984a9339bbb6fdce50477d0aa9ed99b394cabf2e53a
         
     | 
    
        data/CHANGES.md
    CHANGED
    
    | 
         @@ -1,6 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ## 0.3.1
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            * Automatically add primary key when there is no unique column for the order
         
     | 
| 
      
 4 
     | 
    
         
            +
            * Remove `complete` option
         
     | 
| 
      
 5 
     | 
    
         
            +
            * Fix Rubinius compatibility
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
       1 
7 
     | 
    
         
             
            ## 0.3.0
         
     | 
| 
       2 
8 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            * `order_query` now accepts  
     | 
| 
      
 9 
     | 
    
         
            +
            * `order_query` now accepts columns as varargs. Array form is still supported.
         
     | 
| 
       4 
10 
     | 
    
         
             
            * `order_by` renamed to `seek`
         
     | 
| 
       5 
11 
     | 
    
         | 
| 
       6 
12 
     | 
    
         
             
            ## 0.2.1
         
     | 
    
        data/MIT-LICENSE
    CHANGED
    
    | 
         @@ -6,7 +6,7 @@ a copy of this software and associated documentation files (the 
     | 
|
| 
       6 
6 
     | 
    
         
             
            without limitation the rights to use, copy, modify, merge, publish,
         
     | 
| 
       7 
7 
     | 
    
         
             
            distribute, sublicense, and/or sell copies of the Software, and to
         
     | 
| 
       8 
8 
     | 
    
         
             
            permit persons to whom the Software is furnished to do so, subject to
         
     | 
| 
       9 
     | 
    
         
            -
            the following  
     | 
| 
      
 9 
     | 
    
         
            +
            the following columns:
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
            The above copyright notice and this permission notice shall be
         
     | 
| 
       12 
12 
     | 
    
         
             
            included in all copies or substantial portions of the Software.
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -12,12 +12,12 @@ It uses [keyset pagination](http://use-the-index-luke.com/no-offset) to achieve 
     | 
|
| 
       12 
12 
     | 
    
         
             
            Add to Gemfile:
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
            ```ruby
         
     | 
| 
       15 
     | 
    
         
            -
            gem 'order_query', '~> 0.3. 
     | 
| 
      
 15 
     | 
    
         
            +
            gem 'order_query', '~> 0.3.1'
         
     | 
| 
       16 
16 
     | 
    
         
             
            ```
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
18 
     | 
    
         
             
            ## Usage
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
            Define named order  
     | 
| 
      
 20 
     | 
    
         
            +
            Define named order columns with `order_query`:
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
            ```ruby
         
     | 
| 
       23 
23 
     | 
    
         
             
            class Post < ActiveRecord::Base
         
     | 
| 
         @@ -29,7 +29,7 @@ class Post < ActiveRecord::Base 
     | 
|
| 
       29 
29 
     | 
    
         
             
            end
         
     | 
| 
       30 
30 
     | 
    
         
             
            ```
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
            Order query accepts a list of order  
     | 
| 
      
 32 
     | 
    
         
            +
            Order query accepts a list of order columns as varargs or one array, each one specified as:
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
34 
     | 
    
         
             
            ```ruby
         
     | 
| 
       35 
35 
     | 
    
         
             
            [<attribute name>, (attribute values in order), (:asc or :desc), (options hash)]
         
     | 
| 
         @@ -40,7 +40,6 @@ Available options: 
     | 
|
| 
       40 
40 
     | 
    
         
             
            | option     | description                                                                |
         
     | 
| 
       41 
41 
     | 
    
         
             
            |------------|----------------------------------------------------------------------------|
         
     | 
| 
       42 
42 
     | 
    
         
             
            | unique     | Unique attribute. Default: `true` for primary key, `false` otherwise.      |
         
     | 
| 
       43 
     | 
    
         
            -
            | complete   | Specified attribute values are the only possible values. Default: `true`.  |
         
     | 
| 
       44 
43 
     | 
    
         
             
            | sql        | Customize attribute value SQL                                              |
         
     | 
| 
       45 
44 
     | 
    
         | 
| 
       46 
45 
     | 
    
         | 
| 
         @@ -87,9 +86,9 @@ post  = posts.find(42) 
     | 
|
| 
       87 
86 
     | 
    
         
             
            post.order_home(posts) #=> #<OrderQuery::Point>
         
     | 
| 
       88 
87 
     | 
    
         
             
            ```
         
     | 
| 
       89 
88 
     | 
    
         | 
| 
       90 
     | 
    
         
            -
            ### Dynamic  
     | 
| 
      
 89 
     | 
    
         
            +
            ### Dynamic columns
         
     | 
| 
       91 
90 
     | 
    
         | 
| 
       92 
     | 
    
         
            -
            Query with dynamic order  
     | 
| 
      
 91 
     | 
    
         
            +
            Query with dynamic order columns using the `seek(*spec)` class method:
         
     | 
| 
       93 
92 
     | 
    
         | 
| 
       94 
93 
     | 
    
         
             
            ```ruby
         
     | 
| 
       95 
94 
     | 
    
         
             
            space = Post.visible.seek([:id, :desc]) #=> #<OrderQuery::Space>
         
     | 
| 
         @@ -124,7 +123,7 @@ class Post < ActiveRecord::Base 
     | 
|
| 
       124 
123 
     | 
    
         
             
                [:priority, %w(high medium low)],
         
     | 
| 
       125 
124 
     | 
    
         
             
                # A method and custom SQL can be used instead of an attribute
         
     | 
| 
       126 
125 
     | 
    
         
             
                [:valid_votes_count, :desc, sql: '(votes - suspicious_votes)'],
         
     | 
| 
       127 
     | 
    
         
            -
                # Default sort order for non-array  
     | 
| 
      
 126 
     | 
    
         
            +
                # Default sort order for non-array columns is :asc, just like SQL
         
     | 
| 
       128 
127 
     | 
    
         
             
                [:updated_at, :desc],
         
     | 
| 
       129 
128 
     | 
    
         
             
                # pass unique: true for unique attributes to get more optimized queries
         
     | 
| 
       130 
129 
     | 
    
         
             
                # unique is true by default for primary_key
         
     | 
| 
         @@ -153,7 +152,7 @@ ORDER BY 
     | 
|
| 
       153 
152 
     | 
    
         
             
            LIMIT 1
         
     | 
| 
       154 
153 
     | 
    
         
             
            ```
         
     | 
| 
       155 
154 
     | 
    
         | 
| 
       156 
     | 
    
         
            -
            The actual query is a bit different because `order_query` wraps the top-level `OR` with a (redundant) non-strict  
     | 
| 
      
 155 
     | 
    
         
            +
            The actual query is a bit different because `order_query` wraps the top-level `OR` with a (redundant) non-strict column `x0' AND (x0 OR ...)`
         
     | 
| 
       157 
156 
     | 
    
         
             
            for [performance reasons](https://github.com/glebm/order_query/issues/3).
         
     | 
| 
       158 
157 
     | 
    
         
             
            This can be disabled with `OrderQuery.wrap_top_level_or = false`.
         
     | 
| 
       159 
158 
     | 
    
         | 
| 
         @@ -1,13 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # coding: utf-8
         
     | 
| 
       2 
     | 
    
         
            -
            require 'order_query/sql/ 
     | 
| 
      
 2 
     | 
    
         
            +
            require 'order_query/sql/column'
         
     | 
| 
       3 
3 
     | 
    
         
             
            module OrderQuery
         
     | 
| 
       4 
     | 
    
         
            -
              # An order  
     | 
| 
       5 
     | 
    
         
            -
              class  
     | 
| 
      
 4 
     | 
    
         
            +
              # An order column (sort column)
         
     | 
| 
      
 5 
     | 
    
         
            +
              class Column
         
     | 
| 
       6 
6 
     | 
    
         
             
                attr_reader :name, :order, :order_enum, :options
         
     | 
| 
       7 
7 
     | 
    
         
             
                delegate :column_name, :quote, to: :@sql
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                # @option spec [String] :unique    Mark the attribute as unique to avoid redundant  
     | 
| 
       10 
     | 
    
         
            -
                # @option spec [String] :complete  Mark the condition's domain as complete to avoid redundant conditions (only for array conditions)
         
     | 
| 
      
 9 
     | 
    
         
            +
                # @option spec [String] :unique    Mark the attribute as unique to avoid redundant columns
         
     | 
| 
       11 
10 
     | 
    
         
             
                def initialize(spec, scope)
         
     | 
| 
       12 
11 
     | 
    
         
             
                  spec    = spec.dup
         
     | 
| 
       13 
12 
     | 
    
         
             
                  options = spec.extract_options!
         
     | 
| 
         @@ -24,18 +23,13 @@ module OrderQuery 
     | 
|
| 
       24 
23 
     | 
    
         
             
                      complete: true
         
     | 
| 
       25 
24 
     | 
    
         
             
                  )
         
     | 
| 
       26 
25 
     | 
    
         
             
                  @unique   = @options[:unique]
         
     | 
| 
       27 
     | 
    
         
            -
                  @ 
     | 
| 
       28 
     | 
    
         
            -
                  @sql      = SQL::Condition.new(self, scope)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @sql      = SQL::Column.new(self, scope)
         
     | 
| 
       29 
27 
     | 
    
         
             
                end
         
     | 
| 
       30 
28 
     | 
    
         | 
| 
       31 
29 
     | 
    
         
             
                def unique?
         
     | 
| 
       32 
30 
     | 
    
         
             
                  @unique
         
     | 
| 
       33 
31 
     | 
    
         
             
                end
         
     | 
| 
       34 
32 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
                def complete?
         
     | 
| 
       36 
     | 
    
         
            -
                  @complete
         
     | 
| 
       37 
     | 
    
         
            -
                end
         
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
33 
     | 
    
         
             
                # @param [Object] value
         
     | 
| 
       40 
34 
     | 
    
         
             
                # @param [:before, :after] side
         
     | 
| 
       41 
35 
     | 
    
         
             
                # @return [Array] valid order values before / after passed (depending on the side)
         
     | 
| 
         @@ -54,7 +48,7 @@ module OrderQuery 
     | 
|
| 
       54 
48 
     | 
    
         
             
                    end
         
     | 
| 
       55 
49 
     | 
    
         
             
                  else
         
     | 
| 
       56 
50 
     | 
    
         
             
                    # default to all if current is not in sort order values
         
     | 
| 
       57 
     | 
    
         
            -
                     
     | 
| 
      
 51 
     | 
    
         
            +
                    []
         
     | 
| 
       58 
52 
     | 
    
         
             
                  end
         
     | 
| 
       59 
53 
     | 
    
         
             
                end
         
     | 
| 
       60 
54 
     | 
    
         | 
| 
         @@ -63,7 +57,6 @@ module OrderQuery 
     | 
|
| 
       63 
57 
     | 
    
         
             
                      @name,
         
     | 
| 
       64 
58 
     | 
    
         
             
                      (@order_enum.inspect if order_enum),
         
     | 
| 
       65 
59 
     | 
    
         
             
                      ('unique' if @unique),
         
     | 
| 
       66 
     | 
    
         
            -
                      ('complete' if order_enum && complete?),
         
     | 
| 
       67 
60 
     | 
    
         
             
                      (column_name if options[:sql]),
         
     | 
| 
       68 
61 
     | 
    
         
             
                      {desc: '▼', asc: '▲'}[@order]
         
     | 
| 
       69 
62 
     | 
    
         
             
                  ].compact
         
     | 
    
        data/lib/order_query/space.rb
    CHANGED
    
    | 
         @@ -1,17 +1,22 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'order_query/ 
     | 
| 
      
 1 
     | 
    
         
            +
            require 'order_query/column'
         
     | 
| 
       2 
2 
     | 
    
         
             
            require 'order_query/sql/order_by'
         
     | 
| 
       3 
3 
     | 
    
         
             
            module OrderQuery
         
     | 
| 
       4 
4 
     | 
    
         
             
              # Order specification and a scope
         
     | 
| 
       5 
5 
     | 
    
         
             
              class Space
         
     | 
| 
       6 
     | 
    
         
            -
                # @return [Array<OrderQuery:: 
     | 
| 
       7 
     | 
    
         
            -
                attr_reader : 
     | 
| 
      
 6 
     | 
    
         
            +
                # @return [Array<OrderQuery::Column>]
         
     | 
| 
      
 7 
     | 
    
         
            +
                attr_reader :columns
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
                # @param [ActiveRecord::Relation] base_scope
         
     | 
| 
       10 
10 
     | 
    
         
             
                # @param [Array<Array<Symbol,String>>, OrderQuery::Spec] order_spec
         
     | 
| 
       11 
11 
     | 
    
         
             
                def initialize(base_scope, order_spec)
         
     | 
| 
       12 
12 
     | 
    
         
             
                  @base_scope   = base_scope
         
     | 
| 
       13 
     | 
    
         
            -
                  @ 
     | 
| 
       14 
     | 
    
         
            -
                   
     | 
| 
      
 13 
     | 
    
         
            +
                  @columns   = order_spec.map { |cond_spec| Column.new(cond_spec, base_scope) }
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # add primary key if columns are not unique
         
     | 
| 
      
 15 
     | 
    
         
            +
                  unless @columns.last.unique?
         
     | 
| 
      
 16 
     | 
    
         
            +
                    raise ArgumentError.new('Unique column must be last') if @columns.detect(&:unique?)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @columns << Column.new([base_scope.primary_key], base_scope)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @order_by_sql = SQL::OrderBy.new(@columns)
         
     | 
| 
       15 
20 
     | 
    
         
             
                end
         
     | 
| 
       16 
21 
     | 
    
         | 
| 
       17 
22 
     | 
    
         
             
                # @return [Point]
         
     | 
| 
         @@ -19,12 +24,12 @@ module OrderQuery 
     | 
|
| 
       19 
24 
     | 
    
         
             
                  Point.new(record, self)
         
     | 
| 
       20 
25 
     | 
    
         
             
                end
         
     | 
| 
       21 
26 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
                # @return [ActiveRecord::Relation] scope ordered by  
     | 
| 
      
 27 
     | 
    
         
            +
                # @return [ActiveRecord::Relation] scope ordered by columns
         
     | 
| 
       23 
28 
     | 
    
         
             
                def scope
         
     | 
| 
       24 
29 
     | 
    
         
             
                  @scope ||= @base_scope.order(@order_by_sql.build)
         
     | 
| 
       25 
30 
     | 
    
         
             
                end
         
     | 
| 
       26 
31 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
                # @return [ActiveRecord::Relation] scope ordered by  
     | 
| 
      
 32 
     | 
    
         
            +
                # @return [ActiveRecord::Relation] scope ordered by columns in reverse
         
     | 
| 
       28 
33 
     | 
    
         
             
                def scope_reverse
         
     | 
| 
       29 
34 
     | 
    
         
             
                  @scope_reverse ||= @base_scope.order(@order_by_sql.build_reverse)
         
     | 
| 
       30 
35 
     | 
    
         
             
                end
         
     | 
| 
         @@ -42,7 +47,7 @@ module OrderQuery 
     | 
|
| 
       42 
47 
     | 
    
         
             
                delegate :count, :empty?, to: :@base_scope
         
     | 
| 
       43 
48 
     | 
    
         | 
| 
       44 
49 
     | 
    
         
             
                def inspect
         
     | 
| 
       45 
     | 
    
         
            -
                  "#<OrderQuery::Space @ 
     | 
| 
      
 50 
     | 
    
         
            +
                  "#<OrderQuery::Space @columns=#{@columns.inspect} @base_scope=#{@base_scope.inspect}>"
         
     | 
| 
       46 
51 
     | 
    
         
             
                end
         
     | 
| 
       47 
52 
     | 
    
         
             
              end
         
     | 
| 
       48 
53 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,20 +1,20 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module OrderQuery
         
     | 
| 
       2 
2 
     | 
    
         
             
              module SQL
         
     | 
| 
       3 
     | 
    
         
            -
                class  
     | 
| 
       4 
     | 
    
         
            -
                  attr_reader : 
     | 
| 
      
 3 
     | 
    
         
            +
                class Column
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :column, :scope
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
                  def initialize( 
     | 
| 
       7 
     | 
    
         
            -
                    @ 
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(column, scope)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @column = column
         
     | 
| 
       8 
8 
     | 
    
         
             
                    @scope = scope
         
     | 
| 
       9 
9 
     | 
    
         
             
                  end
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
                  def column_name
         
     | 
| 
       12 
12 
     | 
    
         
             
                    @column_name ||= begin
         
     | 
| 
       13 
     | 
    
         
            -
                      sql =  
     | 
| 
      
 13 
     | 
    
         
            +
                      sql = column.options[:sql]
         
     | 
| 
       14 
14 
     | 
    
         
             
                      if sql
         
     | 
| 
       15 
15 
     | 
    
         
             
                        sql.respond_to?(:call) ? sql.call : sql
         
     | 
| 
       16 
16 
     | 
    
         
             
                      else
         
     | 
| 
       17 
     | 
    
         
            -
                        connection.quote_table_name(scope.table_name) + '.' + connection.quote_column_name( 
     | 
| 
      
 17 
     | 
    
         
            +
                        connection.quote_table_name(scope.table_name) + '.' + connection.quote_column_name(column.name)
         
     | 
| 
       18 
18 
     | 
    
         
             
                      end
         
     | 
| 
       19 
19 
     | 
    
         
             
                    end
         
     | 
| 
       20 
20 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -1,9 +1,9 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module OrderQuery
         
     | 
| 
       2 
2 
     | 
    
         
             
              module SQL
         
     | 
| 
       3 
3 
     | 
    
         
             
                class OrderBy
         
     | 
| 
       4 
     | 
    
         
            -
                  # @param [Array< 
     | 
| 
       5 
     | 
    
         
            -
                  def initialize( 
     | 
| 
       6 
     | 
    
         
            -
                    @ 
     | 
| 
      
 4 
     | 
    
         
            +
                  # @param [Array<Column>]
         
     | 
| 
      
 5 
     | 
    
         
            +
                  def initialize(columns)
         
     | 
| 
      
 6 
     | 
    
         
            +
                    @columns = columns
         
     | 
| 
       7 
7 
     | 
    
         
             
                  end
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
                  # @return [String]
         
     | 
| 
         @@ -20,10 +20,10 @@ module OrderQuery 
     | 
|
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
                  # @return [Array<String>]
         
     | 
| 
       22 
22 
     | 
    
         
             
                  def order_by_sql_clauses
         
     | 
| 
       23 
     | 
    
         
            -
                    @ 
     | 
| 
      
 23 
     | 
    
         
            +
                    @columns.map { |cond| column_clause cond }
         
     | 
| 
       24 
24 
     | 
    
         
             
                  end
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
                  def  
     | 
| 
      
 26 
     | 
    
         
            +
                  def column_clause(cond)
         
     | 
| 
       27 
27 
     | 
    
         
             
                    dir_sql = sort_direction_sql cond.order
         
     | 
| 
       28 
28 
     | 
    
         
             
                    col_sql = cond.column_name
         
     | 
| 
       29 
29 
     | 
    
         
             
                    if cond.order_enum
         
     | 
| 
         @@ -7,13 +7,13 @@ module OrderQuery 
     | 
|
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                  # @param [OrderQuery::Point] point
         
     | 
| 
       9 
9 
     | 
    
         
             
                  def initialize(point)
         
     | 
| 
       10 
     | 
    
         
            -
                    @point 
     | 
| 
       11 
     | 
    
         
            -
                    @ 
     | 
| 
      
 10 
     | 
    
         
            +
                    @point   = point
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @columns = point.space.columns
         
     | 
| 
       12 
12 
     | 
    
         
             
                  end
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
                  # Join  
     | 
| 
      
 14 
     | 
    
         
            +
                  # Join column pairs with OR, and nest within each other with AND
         
     | 
| 
       15 
15 
     | 
    
         
             
                  # @param [:before or :after] side
         
     | 
| 
       16 
     | 
    
         
            -
                  # @return [query, parameters] WHERE  
     | 
| 
      
 16 
     | 
    
         
            +
                  # @return [query, parameters] WHERE columns matching records strictly before / after this one
         
     | 
| 
       17 
17 
     | 
    
         
             
                  #   sales < 5 OR
         
     | 
| 
       18 
18 
     | 
    
         
             
                  #   sales = 5 AND (
         
     | 
| 
       19 
19 
     | 
    
         
             
                  #     invoice < 3 OR
         
     | 
| 
         @@ -21,14 +21,14 @@ module OrderQuery 
     | 
|
| 
       21 
21 
     | 
    
         
             
                  #       ... ))
         
     | 
| 
       22 
22 
     | 
    
         
             
                  def build(side)
         
     | 
| 
       23 
23 
     | 
    
         
             
                    # generate pairs of terms such as sales < 5, sales = 5
         
     | 
| 
       24 
     | 
    
         
            -
                     
     | 
| 
       25 
     | 
    
         
            -
                      [where_side( 
     | 
| 
      
 24 
     | 
    
         
            +
                    terms = @columns.map { |col|
         
     | 
| 
      
 25 
     | 
    
         
            +
                      [where_side(col, side, true), where_tie(col)].reject { |x| x == WHERE_IDENTITY }
         
     | 
| 
       26 
26 
     | 
    
         
             
                    }
         
     | 
| 
       27 
27 
     | 
    
         
             
                    # group pairwise with OR, and nest with AND
         
     | 
| 
       28 
     | 
    
         
            -
                    query = foldr_terms  
     | 
| 
      
 28 
     | 
    
         
            +
                    query = foldr_terms terms.map { |pair| join_terms 'OR'.freeze, *pair }, 'AND'.freeze
         
     | 
| 
       29 
29 
     | 
    
         
             
                    if ::OrderQuery.wrap_top_level_or
         
     | 
| 
       30 
30 
     | 
    
         
             
                      # wrap in a redundant AND clause for performance
         
     | 
| 
       31 
     | 
    
         
            -
                      query = wrap_top_level_or query,  
     | 
| 
      
 31 
     | 
    
         
            +
                      query = wrap_top_level_or query, terms, side
         
     | 
| 
       32 
32 
     | 
    
         
             
                    end
         
     | 
| 
       33 
33 
     | 
    
         
             
                    query
         
     | 
| 
       34 
34 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -44,7 +44,7 @@ module OrderQuery 
     | 
|
| 
       44 
44 
     | 
    
         
             
                    end
         
     | 
| 
       45 
45 
     | 
    
         
             
                  end
         
     | 
| 
       46 
46 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
                  # joins terms with an operator
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # joins terms with an operator, empty terms are skipped
         
     | 
| 
       48 
48 
     | 
    
         
             
                  # @return [query, parameters]
         
     | 
| 
       49 
49 
     | 
    
         
             
                  def join_terms(op, *terms)
         
     | 
| 
       50 
50 
     | 
    
         
             
                    [terms.map(&:first).reject(&:empty?).join(" #{op} "), terms.map(&:second).reduce([], :+)]
         
     | 
| 
         @@ -63,61 +63,55 @@ module OrderQuery 
     | 
|
| 
       63 
63 
     | 
    
         
             
                  #    (sales < 5 OR
         
     | 
| 
       64 
64 
     | 
    
         
             
                  #       (sales = 5 AND ...)))
         
     | 
| 
       65 
65 
     | 
    
         
             
                  # Read more at https://github.com/glebm/order_query/issues/3
         
     | 
| 
       66 
     | 
    
         
            -
                  def wrap_top_level_or(query,  
     | 
| 
       67 
     | 
    
         
            -
                     
     | 
| 
       68 
     | 
    
         
            -
                    if  
     | 
| 
       69 
     | 
    
         
            -
                        ( 
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
                      join_terms 'AND'.freeze, redundant_cond, wrap_term_with_parens(query)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  def wrap_top_level_or(query, terms, side)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    top_term_i = terms.index(&:present?)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    if top_term_i && terms[top_term_i].length == 2 &&
         
     | 
| 
      
 69 
     | 
    
         
            +
                        (cond = where_side(@columns[top_term_i], side, false)) != WHERE_IDENTITY
         
     | 
| 
      
 70 
     | 
    
         
            +
                      join_terms 'AND'.freeze, cond, wrap_term_with_parens(query)
         
     | 
| 
       72 
71 
     | 
    
         
             
                    else
         
     | 
| 
       73 
72 
     | 
    
         
             
                      query
         
     | 
| 
       74 
73 
     | 
    
         
             
                    end
         
     | 
| 
       75 
74 
     | 
    
         
             
                  end
         
     | 
| 
       76 
75 
     | 
    
         | 
| 
       77 
     | 
    
         
            -
                  # @return [query, params] tie-breaker unless  
     | 
| 
       78 
     | 
    
         
            -
                  def where_tie( 
     | 
| 
       79 
     | 
    
         
            -
                    if  
     | 
| 
      
 76 
     | 
    
         
            +
                  # @return [query, params] tie-breaker unless column is unique
         
     | 
| 
      
 77 
     | 
    
         
            +
                  def where_tie(col)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    if col.unique?
         
     | 
| 
       80 
79 
     | 
    
         
             
                      WHERE_IDENTITY
         
     | 
| 
       81 
80 
     | 
    
         
             
                    else
         
     | 
| 
       82 
     | 
    
         
            -
                      where_eq( 
     | 
| 
      
 81 
     | 
    
         
            +
                      where_eq(col)
         
     | 
| 
       83 
82 
     | 
    
         
             
                    end
         
     | 
| 
       84 
83 
     | 
    
         
             
                  end
         
     | 
| 
       85 
84 
     | 
    
         | 
| 
       86 
85 
     | 
    
         
             
                  # @param [:before or :after] side
         
     | 
| 
       87 
     | 
    
         
            -
                  # @return [query, params] return query  
     | 
| 
       88 
     | 
    
         
            -
                  def where_side( 
     | 
| 
       89 
     | 
    
         
            -
                    if  
     | 
| 
       90 
     | 
    
         
            -
                       
     | 
| 
       91 
     | 
    
         
            -
                      if cond.complete? && values.length == cond.order_enum.length
         
     | 
| 
       92 
     | 
    
         
            -
                        WHERE_IDENTITY
         
     | 
| 
       93 
     | 
    
         
            -
                      else
         
     | 
| 
       94 
     | 
    
         
            -
                        where_in cond, values
         
     | 
| 
       95 
     | 
    
         
            -
                      end
         
     | 
| 
      
 86 
     | 
    
         
            +
                  # @return [query, params] return query fragment for column values before / after the current one
         
     | 
| 
      
 87 
     | 
    
         
            +
                  def where_side(col, side, strict = true, value = point.value(col))
         
     | 
| 
      
 88 
     | 
    
         
            +
                    if col.order_enum
         
     | 
| 
      
 89 
     | 
    
         
            +
                      where_in col, col.enum_side(value, side, strict)
         
     | 
| 
       96 
90 
     | 
    
         
             
                    else
         
     | 
| 
       97 
     | 
    
         
            -
                      where_ray  
     | 
| 
      
 91 
     | 
    
         
            +
                      where_ray col, value, side, strict
         
     | 
| 
       98 
92 
     | 
    
         
             
                    end
         
     | 
| 
       99 
93 
     | 
    
         
             
                  end
         
     | 
| 
       100 
94 
     | 
    
         | 
| 
       101 
     | 
    
         
            -
                  def where_in( 
     | 
| 
      
 95 
     | 
    
         
            +
                  def where_in(col, values)
         
     | 
| 
       102 
96 
     | 
    
         
             
                    case values.length
         
     | 
| 
       103 
97 
     | 
    
         
             
                      when 0
         
     | 
| 
       104 
98 
     | 
    
         
             
                        WHERE_IDENTITY
         
     | 
| 
       105 
99 
     | 
    
         
             
                      when 1
         
     | 
| 
       106 
     | 
    
         
            -
                        where_eq  
     | 
| 
      
 100 
     | 
    
         
            +
                        where_eq col, values[0]
         
     | 
| 
       107 
101 
     | 
    
         
             
                      else
         
     | 
| 
       108 
     | 
    
         
            -
                        ["#{ 
     | 
| 
      
 102 
     | 
    
         
            +
                        ["#{col.column_name} IN (?)".freeze, [values]]
         
     | 
| 
       109 
103 
     | 
    
         
             
                    end
         
     | 
| 
       110 
104 
     | 
    
         
             
                  end
         
     | 
| 
       111 
105 
     | 
    
         | 
| 
       112 
     | 
    
         
            -
                  def where_eq( 
     | 
| 
       113 
     | 
    
         
            -
                    [%Q(#{ 
     | 
| 
      
 106 
     | 
    
         
            +
                  def where_eq(col, value = point.value(col))
         
     | 
| 
      
 107 
     | 
    
         
            +
                    [%Q(#{col.column_name} = ?).freeze, [value]]
         
     | 
| 
       114 
108 
     | 
    
         
             
                  end
         
     | 
| 
       115 
109 
     | 
    
         | 
| 
       116 
     | 
    
         
            -
                  def where_ray( 
     | 
| 
      
 110 
     | 
    
         
            +
                  def where_ray(col, from, mode, strict = true)
         
     | 
| 
       117 
111 
     | 
    
         
             
                    ops = %w(< >)
         
     | 
| 
       118 
112 
     | 
    
         
             
                    ops = ops.reverse if mode == :after
         
     | 
| 
       119 
     | 
    
         
            -
                    op  = {asc: ops[0], desc: ops[1]}[ 
     | 
| 
       120 
     | 
    
         
            -
                    ["#{ 
     | 
| 
      
 113 
     | 
    
         
            +
                    op  = {asc: ops[0], desc: ops[1]}[col.order || :asc]
         
     | 
| 
      
 114 
     | 
    
         
            +
                    ["#{col.column_name} #{op}#{'=' unless strict} ?".freeze, [from]]
         
     | 
| 
       121 
115 
     | 
    
         
             
                  end
         
     | 
| 
       122 
116 
     | 
    
         | 
| 
       123 
117 
     | 
    
         
             
                  WHERE_IDENTITY = [''.freeze, [].freeze].freeze
         
     | 
    
        data/lib/order_query/version.rb
    CHANGED
    
    
    
        data/lib/order_query.rb
    CHANGED
    
    | 
         @@ -66,10 +66,10 @@ module OrderQuery 
     | 
|
| 
       66 
66 
     | 
    
         
             
                  scope name, -> {
         
     | 
| 
       67 
67 
     | 
    
         
             
                    send(space_method).scope
         
     | 
| 
       68 
68 
     | 
    
         
             
                  }
         
     | 
| 
       69 
     | 
    
         
            -
                  scope  
     | 
| 
      
 69 
     | 
    
         
            +
                  scope "#{name}_reverse", -> {
         
     | 
| 
       70 
70 
     | 
    
         
             
                    send(space_method).scope_reverse
         
     | 
| 
       71 
71 
     | 
    
         
             
                  }
         
     | 
| 
       72 
     | 
    
         
            -
                  define_singleton_method "#{name}_at", -> 
     | 
| 
      
 72 
     | 
    
         
            +
                  define_singleton_method "#{name}_at", ->(record) {
         
     | 
| 
       73 
73 
     | 
    
         
             
                    send(space_method).at(record)
         
     | 
| 
       74 
74 
     | 
    
         
             
                  }
         
     | 
| 
       75 
75 
     | 
    
         
             
                  define_method(name) { |scope = nil|
         
     | 
| 
         @@ -81,6 +81,6 @@ module OrderQuery 
     | 
|
| 
       81 
81 
     | 
    
         
             
              class << self
         
     | 
| 
       82 
82 
     | 
    
         
             
                attr_accessor :wrap_top_level_or
         
     | 
| 
       83 
83 
     | 
    
         
             
              end
         
     | 
| 
       84 
     | 
    
         
            -
              # Wrap top-level or with an AND and a redundant  
     | 
| 
      
 84 
     | 
    
         
            +
              # Wrap top-level or with an AND and a redundant column for performance
         
     | 
| 
       85 
85 
     | 
    
         
             
              self.wrap_top_level_or = true
         
     | 
| 
       86 
86 
     | 
    
         
             
            end
         
     | 
    
        data/spec/order_query_spec.rb
    CHANGED
    
    | 
         @@ -9,7 +9,7 @@ end 
     | 
|
| 
       9 
9 
     | 
    
         
             
            class Post < ActiveRecord::Base
         
     | 
| 
       10 
10 
     | 
    
         
             
              include OrderQuery
         
     | 
| 
       11 
11 
     | 
    
         
             
              order_query :order_list,
         
     | 
| 
       12 
     | 
    
         
            -
                          [:pinned, [true, false] 
     | 
| 
      
 12 
     | 
    
         
            +
                          [:pinned, [true, false]],
         
     | 
| 
       13 
13 
     | 
    
         
             
                          [:published_at, :desc],
         
     | 
| 
       14 
14 
     | 
    
         
             
                          [:id, :desc]
         
     | 
| 
       15 
15 
     | 
    
         
             
            end
         
     | 
| 
         @@ -21,7 +21,8 @@ end 
     | 
|
| 
       21 
21 
     | 
    
         
             
            # Advanced model
         
     | 
| 
       22 
22 
     | 
    
         
             
            class Issue < ActiveRecord::Base
         
     | 
| 
       23 
23 
     | 
    
         
             
              DISPLAY_ORDER = [
         
     | 
| 
       24 
     | 
    
         
            -
                  [: 
     | 
| 
      
 24 
     | 
    
         
            +
                  [:pinned, [true, false]],
         
     | 
| 
      
 25 
     | 
    
         
            +
                  [:priority, %w(high medium low)],
         
     | 
| 
       25 
26 
     | 
    
         
             
                  [:valid_votes_count, :desc, sql: '(votes - suspicious_votes)'],
         
     | 
| 
       26 
27 
     | 
    
         
             
                  [:updated_at, :desc],
         
     | 
| 
       27 
28 
     | 
    
         
             
                  [:id, :desc]
         
     | 
| 
         @@ -63,9 +64,11 @@ describe 'OrderQuery' do 
     | 
|
| 
       63 
64 
     | 
    
         
             
                    t        = Time.now
         
     | 
| 
       64 
65 
     | 
    
         
             
                    datasets = [
         
     | 
| 
       65 
66 
     | 
    
         
             
                        [
         
     | 
| 
      
 67 
     | 
    
         
            +
                            ['high', 5, 0, t, true],
         
     | 
| 
      
 68 
     | 
    
         
            +
                            ['high', 5, 1, t, true],
         
     | 
| 
       66 
69 
     | 
    
         
             
                            ['high', 5, 0, t],
         
     | 
| 
      
 70 
     | 
    
         
            +
                            ['high', 5, 0, t - 1.day],
         
     | 
| 
       67 
71 
     | 
    
         
             
                            ['high', 5, 1, t],
         
     | 
| 
       68 
     | 
    
         
            -
                            ['high', 5, 1, t - 1.day],
         
     | 
| 
       69 
72 
     | 
    
         
             
                            ['medium', 10, 0, t],
         
     | 
| 
       70 
73 
     | 
    
         
             
                            ['medium', 10, 5, t - 12.hours],
         
     | 
| 
       71 
74 
     | 
    
         
             
                            ['low', 30, 0, t + 1.day]
         
     | 
| 
         @@ -85,7 +88,7 @@ describe 'OrderQuery' do 
     | 
|
| 
       85 
88 
     | 
    
         
             
                    datasets.each_with_index do |ds, i|
         
     | 
| 
       86 
89 
     | 
    
         
             
                      it "is ordered correctly (test data #{i})" do
         
     | 
| 
       87 
90 
     | 
    
         
             
                        issues = ds.map do |attr|
         
     | 
| 
       88 
     | 
    
         
            -
                          Issue.new(priority: attr[0], votes: attr[1], suspicious_votes: attr[2], updated_at: attr[3])
         
     | 
| 
      
 91 
     | 
    
         
            +
                          Issue.new(priority: attr[0], votes: attr[1], suspicious_votes: attr[2], updated_at: attr[3], pinned: attr[4] || false)
         
     | 
| 
       89 
92 
     | 
    
         
             
                        end
         
     | 
| 
       90 
93 
     | 
    
         
             
                        issues.shuffle.reverse_each(&:save!)
         
     | 
| 
       91 
94 
     | 
    
         
             
                        expect(Issue.display_order.to_a).to eq(issues)
         
     | 
| 
         @@ -161,7 +164,7 @@ describe 'OrderQuery' do 
     | 
|
| 
       161 
164 
     | 
    
         
             
                      end
         
     | 
| 
       162 
165 
     | 
    
         
             
                    end
         
     | 
| 
       163 
166 
     | 
    
         | 
| 
       164 
     | 
    
         
            -
                    it '#seek falls back to scope when order  
     | 
| 
      
 167 
     | 
    
         
            +
                    it '#seek falls back to scope when order column is missing self' do
         
     | 
| 
       165 
168 
     | 
    
         
             
                      a = create_issue(priority: 'medium')
         
     | 
| 
       166 
169 
     | 
    
         
             
                      b = create_issue(priority: 'high')
         
     | 
| 
       167 
170 
     | 
    
         
             
                      expect(a.seek(Issue.display_order, [[:priority, ['wontfix', 'askbob']], [:id, :desc]]).next).to eq(b)
         
     | 
| 
         @@ -176,6 +179,7 @@ describe 'OrderQuery' do 
     | 
|
| 
       176 
179 
     | 
    
         
             
                        self.verbose = false
         
     | 
| 
       177 
180 
     | 
    
         | 
| 
       178 
181 
     | 
    
         
             
                        create_table :issues do |t|
         
     | 
| 
      
 182 
     | 
    
         
            +
                          t.column :pinned, :boolean, null: false, default: false
         
     | 
| 
       179 
183 
     | 
    
         
             
                          t.column :priority, :string
         
     | 
| 
       180 
184 
     | 
    
         
             
                          t.column :votes, :integer
         
     | 
| 
       181 
185 
     | 
    
         
             
                          t.column :suspicious_votes, :integer
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: order_query
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0.3. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.3.1
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Gleb Mazovetskiy
         
     | 
| 
         @@ -78,10 +78,10 @@ files: 
     | 
|
| 
       78 
78 
     | 
    
         
             
            - README.md
         
     | 
| 
       79 
79 
     | 
    
         
             
            - Rakefile
         
     | 
| 
       80 
80 
     | 
    
         
             
            - lib/order_query.rb
         
     | 
| 
       81 
     | 
    
         
            -
            - lib/order_query/ 
     | 
| 
      
 81 
     | 
    
         
            +
            - lib/order_query/column.rb
         
     | 
| 
       82 
82 
     | 
    
         
             
            - lib/order_query/point.rb
         
     | 
| 
       83 
83 
     | 
    
         
             
            - lib/order_query/space.rb
         
     | 
| 
       84 
     | 
    
         
            -
            - lib/order_query/sql/ 
     | 
| 
      
 84 
     | 
    
         
            +
            - lib/order_query/sql/column.rb
         
     | 
| 
       85 
85 
     | 
    
         
             
            - lib/order_query/sql/order_by.rb
         
     | 
| 
       86 
86 
     | 
    
         
             
            - lib/order_query/sql/where.rb
         
     | 
| 
       87 
87 
     | 
    
         
             
            - lib/order_query/version.rb
         
     |