order_query 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6d9ac479b13db9c6531719b51bca15d8079b2b37
4
+ data.tar.gz: 1c9e90573f1cf98650bdf2d2de5d9969e10ce5fb
5
+ SHA512:
6
+ metadata.gz: 38e902e5c4804d578d6b4817e9b428c4eb295f3fa87d6cf11efae6592630594f7df2d036c1242766ded5f5d58c703a7f01689d60332be0ef5c0bfe2da375f5e3
7
+ data.tar.gz: c7830348c5d536abd766fbd9d9834f3359a09f110c62bef57a69a74d4d7ee23f0b08df55650a1abf00247ea675d099d0afe6f5f2b93f4938a1544d66750451cf
data/Gemfile ADDED
@@ -0,0 +1,22 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :test, :development do
6
+ gem 'coveralls', require: false
7
+ end
8
+
9
+ platform :mri, :rbx do
10
+ # version locked because of rbx issue, see https://github.com/travis-ci/travis-ci/issues/2006#issuecomment-36275141
11
+ gem 'sqlite3', '=1.3.8'
12
+ end
13
+
14
+ platform :jruby do
15
+ gem 'activerecord-jdbcsqlite3-adapter'
16
+ end
17
+
18
+ platform :rbx do
19
+ gem 'rubysl-singleton', '~> 2.0'
20
+ gem 'rubysl-optparse', '~> 2.0'
21
+ gem 'rubysl-ostruct', '~> 2.0'
22
+ end
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2014 Gleb Mazovetskiy
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # order_query [![Build Status][travis-badge]][travis] [![Code Climate][codeclimate-badge]][codeclimate] [![Coverage Status][coveralls-badge]][coveralls]
2
+
3
+ order_query provides ActiveRecord methods to find items relative to the position of a given one for a particular ordering. These methods are useful for many navigation scenarios, e.g. links to the next / previous search result from the show page in a typical index/search -> show scenario.
4
+ order_query generates queries that only use `WHERE`, `ORDER BY`, and `LIMIT`, and *not* `OFFSET`. It only takes 1 query (returning 1 row) to get the record before or after the given one.
5
+
6
+ ```ruby
7
+ gem 'order_query', '~> 0.1.0'
8
+ ```
9
+
10
+ ## Usage
11
+
12
+ ```ruby
13
+ class Issue < ActiveRecord::Base
14
+ include OrderQuery
15
+ order_query :order_display, [
16
+ [:priority, %w(high medium low)],
17
+ [:valid_votes_count, :desc, sql: '(votes - suspicious_votes)'],
18
+ [:updated_at, :desc],
19
+ # pass unique: true for unique attributes to get more optimized queries
20
+ # default: true for primary_key, false otherwise
21
+ [:id, :desc, unique: true]
22
+ ]
23
+ def valid_votes_count
24
+ votes - suspicious_votes
25
+ end
26
+ end
27
+ ```
28
+
29
+ Order scopes:
30
+
31
+ ```ruby
32
+ Issue.order_display #=> ActiveRecord::Relation<...>
33
+ Issue.reverse_order_display #=> ActiveRecord::Relation<...>
34
+ ```
35
+
36
+ Relative order:
37
+
38
+ ```ruby
39
+ # get the order object, scope default: Issue.all
40
+ p = Issue.find(31).order_display(scope)
41
+ p.before #=> ActiveRecord::Relation<...>
42
+ p.previous #=> Issue<...>
43
+ # pass true to #next and #previous in order to loop onto the the first / last record
44
+ # will not loop onto itself
45
+ p.previous(true) #=> Issue<...>
46
+ p.position #=> 5
47
+ p.next #=> Issue<...>
48
+ p.after #=> ActiveRecord::Relation<...>
49
+ ```
50
+
51
+ `order_query` defines methods that call `.order_by_query` and `#relative_order_by_query`, also public:
52
+
53
+ ```ruby
54
+ Issue.order_by_query([[:id, :desc]]) #=> ActiveRecord::Relation<...>
55
+ Issue.reverse_order_by_query([[:id, :desc]]) #=> ActiveRecord::Relation<...>
56
+ Issue.find(31).relative_order_by_query([[:id, :desc]]).next #=> Issue<...>
57
+ Issue.find(31).relative_order_by_query(Issue.visible, [[:id, :desc]]).next #=> Issue<...>
58
+ ```
59
+
60
+ ## How it works
61
+
62
+ Internally this gem builds a query that depends on the current record's order values and looks like:
63
+
64
+ ```sql
65
+ SELECT ... WHERE
66
+ x0 OR
67
+ y0 AND (x1 OR
68
+ y1 AND (x2 OR
69
+ y2 AND ...))
70
+ ORDER BY ...
71
+ LIMIT 1
72
+ ```
73
+
74
+ Where `x` correspond to `>` / `<` terms, and `y` to `=` terms (for resolving ties), per order criterion.
75
+
76
+ A query may then look like this (with `?` for values):
77
+
78
+ ```sql
79
+ SELECT "issues".* FROM "issues" WHERE
80
+ ("issues"."priority" IN ('medium','low') OR
81
+ "issues"."priority" = 'high' AND (
82
+ (votes - suspicious_votes) < 4 OR
83
+ (votes - suspicious_votes) = 4 AND (
84
+ "issues"."updated_at" < '2014-03-19 10:23:18.671039' OR
85
+ "issues"."updated_at" = '2014-03-19 10:23:18.671039' AND
86
+ "issues"."id" < 9)))
87
+ ORDER BY
88
+ "issues"."priority"='high' DESC,
89
+ "issues"."priority"='medium' DESC,
90
+ "issues"."priority"='low' DESC,
91
+ (votes - suspicious_votes) DESC,
92
+ "issues"."updated_at" DESC,
93
+ "issues"."id" DESC
94
+ LIMIT 1
95
+ ```
96
+
97
+ This project uses MIT license.
98
+
99
+
100
+ [travis]: http://travis-ci.org/glebm/order_query
101
+ [travis-badge]: http://img.shields.io/travis/glebm/order_query.svg
102
+ [gemnasium]: https://gemnasium.com/glebm/order_query
103
+ [codeclimate]: https://codeclimate.com/github/glebm/order_query
104
+ [codeclimate-badge]: http://img.shields.io/codeclimate/github/glebm/order_query.svg
105
+ [coveralls]: https://coveralls.io/r/glebm/order_query
106
+ [coveralls-badge]: http://img.shields.io/coveralls/glebm/order_query.svg
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rspec/core/rake_task'
8
+ RSpec::Core::RakeTask.new(:spec)
9
+
10
+ task default: :spec
@@ -0,0 +1,29 @@
1
+ module OrderQuery
2
+ class OrderCondition
3
+ attr_reader :name, :order, :order_order, :options, :scope
4
+
5
+ def initialize(scope, line)
6
+ line = line.dup
7
+ @options = line.extract_options!
8
+ @name = line[0]
9
+ @order = line[1] || :asc
10
+ @order_order = line[2] || :desc
11
+ @scope = scope
12
+ @unique = @options.key?(:unique) ? !!@options[:unique] : (name.to_s == scope.primary_key)
13
+ end
14
+
15
+ def unique?
16
+ @unique
17
+ end
18
+
19
+ def col_name_sql
20
+ sql = options[:sql]
21
+ if sql
22
+ sql = sql.call if sql.respond_to?(:call)
23
+ sql
24
+ else
25
+ scope.connection.quote_table_name(scope.table_name) + '.' + scope.connection.quote_column_name(name)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,44 @@
1
+ require 'order_query/order_condition'
2
+ module OrderQuery
3
+ class OrderSpace
4
+ include Enumerable
5
+ attr_reader :order
6
+
7
+ delegate :each, :length, :size, to: :@order
8
+
9
+ def initialize(scope, order)
10
+ @scope = scope
11
+ @order = order.map { |line| OrderCondition.new(scope, line) }
12
+ end
13
+
14
+ def scope
15
+ @scope.order(order_by_sql)
16
+ end
17
+
18
+ def reverse_scope
19
+ @scope.order(order_by_reverse_sql)
20
+ end
21
+
22
+ def to_order_by_sql
23
+ @order.map { |spec|
24
+ ord = spec.order
25
+ if ord == :asc || ord == :desc
26
+ "#{spec.col_name_sql} #{ord.to_s.upcase}"
27
+ elsif ord.respond_to?(:map)
28
+ ord.map { |v| "#{spec.col_name_sql}=#{@scope.connection.quote v} #{spec.order_order.to_s.upcase}" } * ', '
29
+ else
30
+ raise "Unknown order #{spec.order.inspect} (#{spec.inspect})"
31
+ end
32
+ }
33
+ end
34
+
35
+ def order_by_reverse_sql
36
+ swap = {'DESC' => 'ASC', 'ASC' => 'DESC'}
37
+ to_order_by_sql.map { |s| s.gsub(/DESC|ASC/) { |m| swap[m] } } * ', '
38
+ end
39
+
40
+ def order_by_sql
41
+ to_order_by_sql * ', '
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,144 @@
1
+ require 'order_query/order_space'
2
+ module OrderQuery
3
+
4
+ class RelativeOrder
5
+ attr_reader :record, :scope, :order, :values, :options
6
+
7
+ def initialize(record, scope, order)
8
+ @record = record
9
+ @scope = scope
10
+ @order = order.is_a?(OrderSpace) ? order : OrderSpace.new(scope, order)
11
+ end
12
+
13
+ def first
14
+ order.scope.first
15
+ end
16
+
17
+ def last
18
+ order.scope.last
19
+ end
20
+
21
+ def count
22
+ @total ||= scope.count
23
+ end
24
+
25
+ def position
26
+ count - after.count
27
+ end
28
+
29
+ def next(loop = true)
30
+ record_unless_current after.first || (first if loop)
31
+ end
32
+
33
+ def previous(loop = true)
34
+ record_unless_current before.first || (last if loop)
35
+ end
36
+
37
+ def after
38
+ records :after
39
+ end
40
+
41
+ def before
42
+ records :before
43
+ end
44
+
45
+ def records(mode)
46
+ scope = (mode == :after ? order.scope : order.reverse_scope)
47
+ query, query_args = build_query(mode)
48
+ if query.present?
49
+ scope.where(query, *query_args)
50
+ else
51
+ scope
52
+ end
53
+ end
54
+
55
+ protected
56
+
57
+ def record_unless_current(record)
58
+ record unless record == @record
59
+ end
60
+
61
+ # @param [:before or :after] mode
62
+ # @return [query, parameters] conditions that exclude all elements not before / after the current one
63
+ def build_query(mode)
64
+ group_operators order.map { |term| [where_mode(term, mode), where_eq(term)] }
65
+ end
66
+
67
+ # Join conditions with operators and parenthesis
68
+ # @param [Array] term_pairs of query terms [[x0, y0], [x1, y1], ...],
69
+ # xi, yi are pairs of [query, parameters]
70
+ # @return [query, parameters]
71
+ # x0 OR
72
+ # y0 AND (x1 OR
73
+ # y1 AND (x2 OR
74
+ # y2 AND x3))
75
+ #
76
+ # Since x matches order criteria with values that come before / after the current record,
77
+ # and y matches order criteria with values equal to the current record's value (for resolving ties),
78
+ # the resulting condition matches just the elements that come before / after the record
79
+ def group_operators(term_pairs)
80
+ # create "x OR y" string
81
+ term = join_terms 'OR', *term_pairs[0]
82
+ rest = term_pairs.from(1)
83
+ if rest.present?
84
+ # nest the remaining pairs recursively, appending them with " AND "
85
+ rest_grouped = group_operators rest
86
+ rest_grouped[0] = "(#{rest_grouped[0]})" unless rest.length == 1
87
+ join_terms 'AND', term, rest_grouped
88
+ else
89
+ term
90
+ end
91
+ end
92
+
93
+ # joins terms with an operator
94
+ # @return [query, parameters]
95
+ def join_terms(op, *terms)
96
+ [terms.map { |t| t.first.presence }.compact.join(" #{op} "),
97
+ terms.map(&:second).reduce(:+) || []]
98
+ end
99
+
100
+ EMPTY_FILTER = ['', []]
101
+
102
+ # @return [query, params] Unless order attribute is unique, such as id, return ['WHERE value = ?', current value].
103
+ def where_eq(attr)
104
+ if attr.unique?
105
+ EMPTY_FILTER
106
+ else
107
+ [%Q(#{attr.col_name_sql} = ?), [attr_value(attr)]]
108
+ end
109
+ end
110
+
111
+ # @param [:before or :after] mode
112
+ # @return [query, params] return query conditions for attribute values before / after the current one
113
+ def where_mode(attr, mode)
114
+ ord = attr.order
115
+ value = attr_value attr
116
+ if ord.is_a?(Array)
117
+ # ord is an array of sort values, ordered first to last
118
+ pos = ord.index(value)
119
+ sort_values = if pos
120
+ dir = attr.order_order
121
+ if mode == :after && dir == :desc || mode == :before && dir == :asc
122
+ ord.from(pos + 1)
123
+ else
124
+ ord.first(pos)
125
+ end
126
+ else
127
+ # default to all if current is not in sort order values
128
+ ord
129
+ end
130
+ # if current not in result set, do not apply filter
131
+ return EMPTY_FILTER unless sort_values.present?
132
+ ["#{attr.col_name_sql} IN (?)", [sort_values]]
133
+ else
134
+ # ord is :asc or :desc
135
+ op = {before: {asc: '<', desc: '>'}, after: {asc: '>', desc: '<'}}[mode][ord || :asc]
136
+ ["#{attr.col_name_sql} #{op} ?", [value]]
137
+ end
138
+ end
139
+
140
+ def attr_value(attr)
141
+ record.send attr.name
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,3 @@
1
+ module OrderQuery
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,38 @@
1
+ require 'active_support'
2
+ require 'active_record'
3
+ require 'order_query/relative_order'
4
+
5
+ module OrderQuery
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ # @return [OrderSpace] order definition
10
+ # @example
11
+ # Issue.order_query [[:id, :desc]] #=> <ActiveRecord::Relation#...>
12
+ scope :order_by_query, ->(order) { OrderSpace.new(self, order).scope }
13
+ scope :reverse_order_by_query, ->(order) { OrderSpace.new(self, order).reverse_scope }
14
+ end
15
+
16
+ def relative_order_by_query(scope = self.class.all, order)
17
+ RelativeOrder.new(self, scope, order)
18
+ end
19
+
20
+ module ClassMethods
21
+ protected
22
+ # @example
23
+ # class Issue
24
+ # order_query :order_display, [[:created_at, :desc], [:id, :desc]]
25
+ # end
26
+ #
27
+ # Issue.order_display #=> <ActiveRecord::Relation#...>
28
+ # Issue.active.find(31).display_order(Issue.active).next #=> <Issue#...>
29
+ def order_query(name, order)
30
+ scope name, -> { order_by_query(order) }
31
+ scope :"reverse_#{name}", -> { reverse_order_by_query(order) }
32
+ define_method(name) do |scope = nil|
33
+ scope ||= self.class.all
34
+ relative_order_by_query(scope, order)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+
3
+ class Issue < ActiveRecord::Base
4
+ DISPLAY_ORDER = [
5
+ [:priority, %w(high medium low)],
6
+ [:valid_votes_count, :desc, sql: '(votes - suspicious_votes)'],
7
+ [:updated_at, :desc],
8
+ [:id, :desc]
9
+ ]
10
+
11
+ def valid_votes_count
12
+ votes - suspicious_votes
13
+ end
14
+
15
+ include OrderQuery
16
+ order_query :display_order, DISPLAY_ORDER
17
+ order_query :id_order_asc, [[:id, :asc]]
18
+ end
19
+
20
+ def create_issue(options = {})
21
+ Issue.create!({priority: 'high', votes: 3, suspicious_votes: 0, updated_at: Time.now}.merge(options))
22
+ end
23
+
24
+ describe 'OrderQuery.order_query' do
25
+
26
+ t = Time.now
27
+ datasets = [
28
+ [
29
+ ['high', 5, 0, t],
30
+ ['high', 5, 1, t],
31
+ ['high', 5, 1, t - 1.day],
32
+ ['medium', 10, 0, t],
33
+ ['medium', 10, 5, t - 12.hours],
34
+ ['low', 30, 0, t + 1.day]
35
+ ],
36
+ [
37
+ ['high', 5, 0, t],
38
+ ['high', 5, 1, t],
39
+ ['high', 5, 1, t - 1.day],
40
+ ['low', 30, 0, t + 1.day]
41
+ ],
42
+ [
43
+ ['high', 5, 1, t - 1.day],
44
+ ['low', 30, 0, t + 1.day]
45
+ ],
46
+ ]
47
+
48
+ datasets.each_with_index do |ds, i|
49
+ it "is ordered correctly (test data #{i})" do
50
+ issues = ds.map do |attr|
51
+ Issue.new(priority: attr[0], votes: attr[1], suspicious_votes: attr[2], updated_at: attr[3])
52
+ end
53
+ issues.reverse_each(&:save!)
54
+ expect(Issue.display_order.to_a).to eq(issues)
55
+ issues.each_slice(2) do |prev, cur|
56
+ cur ||= issues.first
57
+ expect(prev.display_order.next).to eq(cur)
58
+ expect(cur.display_order.previous).to eq(prev)
59
+ expect(cur.display_order.scope.count).to eq(Issue.count)
60
+ expect(cur.display_order.before.count + 1 + cur.display_order.after.count).to eq(cur.display_order.count)
61
+
62
+ expect(cur.display_order.before.to_a.reverse + [cur] + cur.display_order.after.to_a).to eq(Issue.display_order.to_a)
63
+ end
64
+ end
65
+ end
66
+
67
+ it '#next returns nil when there is only 1 record' do
68
+ p = create_issue.display_order
69
+ expect(p.next).to be_nil
70
+ expect(p.next(true)).to be_nil
71
+ end
72
+
73
+ it 'is ordered correctly for order query [[:id, :asc]]' do
74
+ a = create_issue
75
+ b = create_issue
76
+ expect(a.id_order_asc.next).to eq b
77
+ expect(b.id_order_asc.previous).to eq a
78
+ expect([a] + a.id_order_asc.after.to_a).to eq(Issue.id_order_asc.to_a)
79
+ expect(b.id_order_asc.before.reverse.to_a + [b]).to eq(Issue.id_order_asc.to_a)
80
+ expect(Issue.id_order_asc.count).to eq(2)
81
+ end
82
+
83
+ it '.order_by_query works on a list of ids' do
84
+ ids = (1..3).map { create_issue.id }
85
+ expect(Issue.order_by_query([[:id, ids]])).to have(ids.length).issues
86
+ end
87
+
88
+ it '.order_by_query preserves previous' do
89
+ create_issue(active: true)
90
+ expect(Issue.where(active: false).order_by_query([[:id, :desc]])).to have(0).records
91
+ expect(Issue.where(active: true).order_by_query([[:id, :desc]])).to have(1).record
92
+ end
93
+
94
+ it '#relative_order_by_query falls back to scope when order condition is missing self' do
95
+ a = create_issue(priority: 'medium')
96
+ b = create_issue(priority: 'high')
97
+ expect(a.relative_order_by_query(Issue.display_order, [[:priority, ['wontfix', 'askbob']], [:id, :desc]]).next).to eq(b)
98
+ end
99
+
100
+ before do
101
+ Issue.delete_all
102
+ end
103
+
104
+ before :all do
105
+ ActiveRecord::Schema.define do
106
+ self.verbose = false
107
+
108
+ create_table :issues do |t|
109
+ t.column :priority, :string
110
+ t.column :votes, :integer
111
+ t.column :suspicious_votes, :integer
112
+ t.column :announced_at, :datetime
113
+ t.column :updated_at, :datetime
114
+ t.column :active, :boolen, null: false, default: true
115
+ end
116
+ end
117
+
118
+ Issue.reset_column_information
119
+ end
120
+ end
@@ -0,0 +1,16 @@
1
+ # -*- encoding : utf-8 -*-
2
+ # Configure Rails Environment
3
+ ENV['RAILS_ENV'] = ENV['RACK_ENV'] = 'test'
4
+ unless defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
5
+ begin
6
+ require 'coveralls'
7
+ Coveralls.wear!
8
+ rescue LoadError
9
+ false
10
+ end
11
+ end
12
+ require 'order_query'
13
+
14
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
15
+
16
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: order_query
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gleb Mazovetskiy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email: glex.spb@gmail.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - Gemfile
76
+ - MIT-LICENSE
77
+ - README.md
78
+ - Rakefile
79
+ - lib/order_query.rb
80
+ - lib/order_query/order_condition.rb
81
+ - lib/order_query/order_space.rb
82
+ - lib/order_query/relative_order.rb
83
+ - lib/order_query/version.rb
84
+ - spec/order_query_spec.rb
85
+ - spec/spec_helper.rb
86
+ homepage: https://github.com/glebm/order_query
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.2.0
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Find next / previous record(s) in one query, for ActiveRecord
110
+ test_files:
111
+ - spec/order_query_spec.rb
112
+ - spec/spec_helper.rb