sortable-by 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +12 -1
- data/.travis.yml +0 -1
- data/Gemfile.lock +14 -14
- data/README.md +55 -18
- data/Rakefile +1 -1
- data/lib/sortable_by.rb +155 -63
- data/sortable-by.gemspec +1 -1
- data/spec/sortable_by_spec.rb +80 -22
- data/spec/spec_helper.rb +49 -21
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc4cdc4741e458addb43e7d95a66f12695cf601f4178d188de2fa5197d6dc199
|
4
|
+
data.tar.gz: b1c4bb549dcdea2ca6879d5c4e360b75d099171ed2a3b1bd4e28fa89e0b968d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 381a3fbb778b6540e44f76de77b733781f26686848342235b865608bcaa5aaa5878bbf184de2386570568642c637d419ffe7a68eb980ecd7e084eaf8564ad98c
|
7
|
+
data.tar.gz: 705e4e7a6eae3f496ae2e84f3d2f818c4a0c2341d2e93bdb7ebde678bbc32d0f8b7c4375447168025c222bd466dde5ba72cd2624690ef5c5c99c03cb66c094e3
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,15 +1,26 @@
|
|
1
1
|
AllCops:
|
2
2
|
TargetRubyVersion: 2.2
|
3
3
|
|
4
|
+
Naming/MemoizedInstanceVariableName:
|
5
|
+
Enabled: false
|
6
|
+
|
4
7
|
Metrics/AbcSize:
|
5
8
|
Enabled: false
|
9
|
+
Metrics/CyclomaticComplexity:
|
10
|
+
Enabled: false
|
11
|
+
Metrics/PerceivedComplexity:
|
12
|
+
Enabled: false
|
6
13
|
Naming/FileName:
|
7
14
|
Exclude: [lib/sortable-by.rb]
|
15
|
+
Metrics/BlockLength:
|
16
|
+
Exclude: [spec/**]
|
8
17
|
Metrics/MethodLength:
|
9
|
-
|
18
|
+
Enabled: false
|
10
19
|
Metrics/LineLength:
|
11
20
|
Max: 120
|
12
21
|
|
22
|
+
Style/NumericLiterals:
|
23
|
+
Exclude: [spec/**]
|
13
24
|
Style/TrailingCommaInArguments:
|
14
25
|
EnforcedStyleForMultiline: consistent_comma
|
15
26
|
Style/TrailingCommaInArrayLiteral:
|
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,37 +1,37 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
sortable-by (0.
|
4
|
+
sortable-by (0.11.0)
|
5
5
|
activerecord
|
6
6
|
activesupport
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: http://rubygems.org/
|
10
10
|
specs:
|
11
|
-
activemodel (5.
|
12
|
-
activesupport (= 5.
|
13
|
-
activerecord (5.
|
14
|
-
activemodel (= 5.
|
15
|
-
activesupport (= 5.
|
16
|
-
arel (
|
17
|
-
activesupport (5.
|
11
|
+
activemodel (5.2.0)
|
12
|
+
activesupport (= 5.2.0)
|
13
|
+
activerecord (5.2.0)
|
14
|
+
activemodel (= 5.2.0)
|
15
|
+
activesupport (= 5.2.0)
|
16
|
+
arel (>= 9.0)
|
17
|
+
activesupport (5.2.0)
|
18
18
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
19
|
-
i18n (
|
19
|
+
i18n (>= 0.7, < 2)
|
20
20
|
minitest (~> 5.1)
|
21
21
|
tzinfo (~> 1.1)
|
22
|
-
arel (
|
22
|
+
arel (9.0.0)
|
23
23
|
ast (2.4.0)
|
24
24
|
concurrent-ruby (1.0.5)
|
25
25
|
diff-lcs (1.3)
|
26
|
-
i18n (0.
|
26
|
+
i18n (1.0.0)
|
27
27
|
concurrent-ruby (~> 1.0)
|
28
28
|
minitest (5.11.3)
|
29
29
|
parallel (1.12.1)
|
30
|
-
parser (2.5.0.
|
30
|
+
parser (2.5.0.5)
|
31
31
|
ast (~> 2.4.0)
|
32
32
|
powerpack (0.1.1)
|
33
33
|
rainbow (3.0.0)
|
34
|
-
rake (12.3.
|
34
|
+
rake (12.3.1)
|
35
35
|
rspec (3.7.0)
|
36
36
|
rspec-core (~> 3.7.0)
|
37
37
|
rspec-expectations (~> 3.7.0)
|
@@ -45,7 +45,7 @@ GEM
|
|
45
45
|
diff-lcs (>= 1.2.0, < 2.0)
|
46
46
|
rspec-support (~> 3.7.0)
|
47
47
|
rspec-support (3.7.1)
|
48
|
-
rubocop (0.
|
48
|
+
rubocop (0.54.0)
|
49
49
|
parallel (~> 1.10)
|
50
50
|
parser (>= 2.5)
|
51
51
|
powerpack (~> 0.1)
|
data/README.md
CHANGED
@@ -13,38 +13,75 @@ Add `gem 'sortable-by'` to your Gemfile.
|
|
13
13
|
|
14
14
|
## Usage
|
15
15
|
|
16
|
-
Simple
|
16
|
+
Simple:
|
17
17
|
|
18
18
|
```ruby
|
19
|
-
class
|
20
|
-
sortable_by :title, :
|
19
|
+
class Post < ActiveRecord::Base
|
20
|
+
sortable_by :title, :id
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
Post.sorted_by('title') # => ORDER BY posts.title ASC
|
24
|
+
Post.sorted_by('-title') # => ORDER BY posts.title DESC
|
25
|
+
Post.sorted_by('bad,title') # => ORDER BY posts.title ASC
|
26
|
+
Post.sorted_by(nil) # => ORDER BY posts.title ASC
|
26
27
|
```
|
27
28
|
|
28
|
-
|
29
|
+
Case-insensitive:
|
29
30
|
|
30
31
|
```ruby
|
31
|
-
class
|
32
|
-
sortable_by
|
32
|
+
class Post < ActiveRecord::Base
|
33
|
+
sortable_by do |x|
|
34
|
+
x.field :title, as: arel_table[:title].lower
|
35
|
+
x.field :id
|
36
|
+
end
|
33
37
|
end
|
34
38
|
|
35
|
-
|
36
|
-
Foo.sorted_by "-semver" # => ORDER BY foos.major DESC, foos.minor DESC, foos.patch DESC
|
37
|
-
Foo.sorted_by nil # => ORDER BY foos.id ASC
|
39
|
+
Post.sorted_by('title') # => ORDER BY LOWER(posts.title) ASC
|
38
40
|
```
|
39
41
|
|
40
|
-
|
42
|
+
With custom default:
|
41
43
|
|
42
44
|
```ruby
|
43
|
-
class
|
44
|
-
sortable_by
|
45
|
+
class Post < ActiveRecord::Base
|
46
|
+
sortable_by :id, :topic, :created_at, default: 'topic,-created_at'
|
45
47
|
end
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
Post.sorted_by(nil) # => ORDER BY posts.topic ASC, posts.created_at DESC
|
50
|
+
```
|
51
|
+
|
52
|
+
Composition:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
class App < ActiveRecord::Base
|
56
|
+
sortable_by :name, default: '-version' do |x|
|
57
|
+
x.field :version, as: %i[major minor patch]]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
App.sorted_by('version') # => ORDER BY apps.major ASC, apps.minor ASC, apps.patch ASC
|
62
|
+
App.sorted_by(nil) # => ORDER BY apps.major DESC, apps.minor DESC, apps.patch DESC
|
63
|
+
```
|
64
|
+
|
65
|
+
Associations (eager load):
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
class Product < ActiveRecord::Base
|
69
|
+
belongs_to :shop
|
70
|
+
sortable_by do |x|
|
71
|
+
x.field :name, as: arel_table[:name].lower
|
72
|
+
x.field :shop, as: Shop.arel_table[:name].lower, eager_load: :shop
|
73
|
+
x.default 'shop,name'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
Associations (custom scope):
|
79
|
+
|
80
|
+
```
|
81
|
+
class Product < ActiveRecord::Base
|
82
|
+
belongs_to :shop
|
83
|
+
sortable_by do |x|
|
84
|
+
x.field :shop, as: Shop.arel_table[:name].lower, scope: -> { joins(:shop) }
|
85
|
+
end
|
86
|
+
end
|
50
87
|
```
|
data/Rakefile
CHANGED
data/lib/sortable_by.rb
CHANGED
@@ -1,71 +1,44 @@
|
|
1
1
|
require 'active_record'
|
2
|
-
require 'active_support/concern'
|
3
|
-
require 'set'
|
4
2
|
|
5
3
|
module ActiveRecord # :nodoc:
|
6
|
-
module
|
7
|
-
|
4
|
+
module SortableBy # :nodoc:
|
5
|
+
class Config # :nodoc:
|
6
|
+
attr_reader :_fields, :_default
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.validate_custom_scope!(custom, original = custom)
|
15
|
-
case custom
|
16
|
-
when true, Arel::Nodes::Node, String, Symbol
|
17
|
-
nil # OK
|
18
|
-
when Array
|
19
|
-
custom.each do |v|
|
20
|
-
validate_custom_scope!(v, original)
|
21
|
-
end
|
22
|
-
else
|
23
|
-
raise ArgumentError, "Option #{original.inspect} contains unexpected values."
|
8
|
+
def initialize
|
9
|
+
@_fields = {}
|
10
|
+
@_default = nil
|
24
11
|
end
|
25
|
-
end
|
26
12
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
{ value => rank }
|
33
|
-
when Arel::Nodes::Node
|
34
|
-
value.send(rank)
|
35
|
-
when Array
|
36
|
-
value.map { |v| order_clause(name, rank, v) }
|
13
|
+
def dup
|
14
|
+
duplicate = self.class.new
|
15
|
+
duplicate.instance_variable_set :@_fields, _fields.deep_dup
|
16
|
+
duplicate.instance_variable_set :@_default, _default.deep_dup
|
17
|
+
duplicate
|
37
18
|
end
|
38
|
-
end
|
39
19
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
super
|
20
|
+
def field(name, opts = {})
|
21
|
+
name = name.to_s
|
22
|
+
@_fields[name] = Field.new(name, opts)
|
23
|
+
@_default ||= name
|
45
24
|
end
|
46
25
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
self._sortable_by_scope_options[:default] = default if default
|
54
|
-
|
55
|
-
whitelist.each do |attr|
|
56
|
-
self._sortable_by_scope_options[:scopes][attr.to_s] = true
|
57
|
-
end
|
58
|
-
opts.each do |attr, custom|
|
59
|
-
SortableByHelper.validate_custom_scope!(custom)
|
60
|
-
self._sortable_by_scope_options[:scopes][attr.to_s] = custom
|
26
|
+
def default(expr)
|
27
|
+
# legacy support
|
28
|
+
if expr.is_a?(Hash)
|
29
|
+
expr = expr.map do |field, dir|
|
30
|
+
[(dir == :desc ? '-' : ''), field].join
|
31
|
+
end.join(',')
|
61
32
|
end
|
33
|
+
|
34
|
+
@_default = expr.to_s
|
62
35
|
end
|
63
36
|
|
64
|
-
|
65
|
-
|
66
|
-
def
|
67
|
-
|
68
|
-
|
37
|
+
protected
|
38
|
+
|
39
|
+
def order(relation, expr, fallback = true)
|
40
|
+
matched = false
|
41
|
+
expr.to_s.split(',').each do |name|
|
69
42
|
name.strip!
|
70
43
|
|
71
44
|
rank = :asc
|
@@ -74,23 +47,142 @@ module ActiveRecord # :nodoc:
|
|
74
47
|
name = name[1..-1]
|
75
48
|
end
|
76
49
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
50
|
+
field = _fields[name]
|
51
|
+
next unless field
|
52
|
+
|
53
|
+
matched = true
|
54
|
+
relation = field.order(relation, rank)
|
81
55
|
end
|
82
56
|
|
83
|
-
if
|
84
|
-
|
85
|
-
|
57
|
+
relation = order(relation, _default, false) if fallback && !matched && _default
|
58
|
+
relation
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Field # :nodoc:
|
63
|
+
def initialize(name, opts = {})
|
64
|
+
@cols = Array.wrap(opts[:as])
|
65
|
+
@eager_load = Array.wrap(opts[:eager_load]).presence
|
66
|
+
|
67
|
+
# validate custom_scope
|
68
|
+
@custom_scope = opts[:scope]
|
69
|
+
if @custom_scope && !@custom_scope.is_a?(Proc)
|
70
|
+
raise ArgumentError, "Invalid sortable-by field '#{name}': scope must be a Proc."
|
86
71
|
end
|
87
72
|
|
73
|
+
# normalize cols
|
74
|
+
@cols.push name if @cols.empty?
|
75
|
+
@cols.each do |col|
|
76
|
+
case col
|
77
|
+
when String, Symbol, Arel::Attributes::Attribute, Arel::Nodes::Node
|
78
|
+
next
|
79
|
+
when Proc
|
80
|
+
unless col.arity == 2
|
81
|
+
raise ArgumentError, "Invalid sortable-by field '#{name}': proc must accept 2 arguments."
|
82
|
+
end
|
83
|
+
else
|
84
|
+
raise ArgumentError, "Invalid sortable-by field '#{name}': invalid type #{col.class}."
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def order(relation, rank)
|
90
|
+
@cols.each do |col|
|
91
|
+
case col
|
92
|
+
when String, Symbol
|
93
|
+
relation = relation.order(col => rank)
|
94
|
+
when Arel::Nodes::Node, Arel::Attributes::Attribute
|
95
|
+
relation = relation.order(col.send(rank))
|
96
|
+
when Proc
|
97
|
+
relation = col.call(relation, rank)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
relation = relation.eager_load(*@eager_load) if @eager_load
|
102
|
+
relation = relation.instance_eval(&@custom_scope) if @custom_scope
|
88
103
|
relation
|
89
104
|
end
|
90
105
|
end
|
106
|
+
|
107
|
+
def self.extended(base) # :nodoc:
|
108
|
+
base.class_attribute :_sortable_by_config, instance_accessor: false, instance_predicate: false
|
109
|
+
base._sortable_by_config = Config.new
|
110
|
+
end
|
111
|
+
|
112
|
+
def inherited(base) # :nodoc:
|
113
|
+
base._sortable_by_config = _sortable_by_config.deep_dup
|
114
|
+
super
|
115
|
+
end
|
116
|
+
|
117
|
+
# Declare sortable attributes and scopes. Examples:
|
118
|
+
#
|
119
|
+
# # Simple
|
120
|
+
# class Post < ActiveRecord::Base
|
121
|
+
# sortable_by :title, :id
|
122
|
+
# end
|
123
|
+
#
|
124
|
+
# # Case-insensitive
|
125
|
+
# class Post < ActiveRecord::Base
|
126
|
+
# sortable_by do |x|
|
127
|
+
# x.field :title, as: arel_table[:title].lower
|
128
|
+
# x.field :id
|
129
|
+
# end
|
130
|
+
# end
|
131
|
+
#
|
132
|
+
# # With default
|
133
|
+
# class Post < ActiveRecord::Base
|
134
|
+
# sortable_by :id, :topic, :created_at,
|
135
|
+
# default: 'topic,-created_at'
|
136
|
+
# end
|
137
|
+
#
|
138
|
+
# # Composition
|
139
|
+
# class App < ActiveRecord::Base
|
140
|
+
# sortable_by :name, default: '-version' do |x|
|
141
|
+
# x.field :version, as: %i[major minor patch]]
|
142
|
+
# end
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# # Associations (eager load)
|
146
|
+
# class Product < ActiveRecord::Base
|
147
|
+
# belongs_to :shop
|
148
|
+
#
|
149
|
+
# sortable_by do |x|
|
150
|
+
# x.field :name
|
151
|
+
# x.field :shop, as: Shop.arel_table[:name].lower, eager_load: :shop
|
152
|
+
# x.default 'shop,name'
|
153
|
+
# end
|
154
|
+
# end
|
155
|
+
#
|
156
|
+
# # Associations (custom scope)
|
157
|
+
# class Product < ActiveRecord::Base
|
158
|
+
# belongs_to :shop
|
159
|
+
#
|
160
|
+
# sortable_by do |x|
|
161
|
+
# x.field :shop, as: Shop.arel_table[:name].lower, scope: -> { joins(:shop) }
|
162
|
+
# end
|
163
|
+
# end
|
164
|
+
#
|
165
|
+
def sortable_by(*attributes)
|
166
|
+
config = _sortable_by_config
|
167
|
+
opts = attributes.extract_options!
|
168
|
+
default = opts.delete(:default)
|
169
|
+
|
170
|
+
attributes.each do |name|
|
171
|
+
config.field(name, opts)
|
172
|
+
end
|
173
|
+
config.default(default) if default
|
174
|
+
yield config if block_given?
|
175
|
+
config
|
176
|
+
end
|
177
|
+
|
178
|
+
# @param [String] expr the sort expr
|
179
|
+
# @return [ActiveRecord::Relation] the scoped relation
|
180
|
+
def sorted_by(expr)
|
181
|
+
_sortable_by_config.send :order, self, expr
|
182
|
+
end
|
91
183
|
end
|
92
184
|
|
93
185
|
class Base # :nodoc:
|
94
|
-
|
186
|
+
extend SortableBy
|
95
187
|
end
|
96
188
|
end
|
data/sortable-by.gemspec
CHANGED
data/spec/sortable_by_spec.rb
CHANGED
@@ -1,28 +1,86 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path('./spec_helper', __dir__)
|
2
|
+
|
3
|
+
describe ActiveRecord::SortableBy do
|
4
|
+
before do
|
5
|
+
c = ActiveRecord::Base.connection
|
6
|
+
c.tables.each do |t|
|
7
|
+
c.update "DELETE FROM #{t}"
|
8
|
+
end
|
9
|
+
end
|
2
10
|
|
3
|
-
describe ActiveRecord::SortableByHelper do
|
4
11
|
it 'should have config' do
|
5
|
-
expect(
|
6
|
-
expect(
|
7
|
-
expect(
|
8
|
-
expect(
|
9
|
-
|
12
|
+
expect(Post._sortable_by_config._fields.keys).to match_array(%w[title created])
|
13
|
+
expect(Post._sortable_by_config._default).to eq('-created')
|
14
|
+
expect(SubPost._sortable_by_config._fields.keys).to match_array(%w[title created])
|
15
|
+
expect(SubPost._sortable_by_config._default).to eq('-created')
|
16
|
+
|
17
|
+
expect(App._sortable_by_config._fields.keys).to match_array(%w[name version])
|
18
|
+
expect(App._sortable_by_config._default).to eq('name')
|
19
|
+
|
20
|
+
expect(Shop._sortable_by_config._fields.keys).to be_empty
|
21
|
+
expect(Shop._sortable_by_config._default).to be_nil
|
22
|
+
|
23
|
+
expect(Product._sortable_by_config._fields.keys).to match_array(%w[name shop])
|
24
|
+
expect(Product._sortable_by_config._default).to eq('shop,name')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should simply order' do
|
28
|
+
Post.create! title: 'A', created_at: Time.at(1515151500)
|
29
|
+
Post.create! title: 'b', created_at: Time.at(1515151600)
|
30
|
+
Post.create! title: 'C', created_at: Time.at(1515151400)
|
31
|
+
|
32
|
+
expect(Post.sorted_by(nil).pluck(:title)).to eq(%w[b A C])
|
33
|
+
expect(Post.sorted_by('').pluck(:title)).to eq(%w[b A C])
|
34
|
+
expect(Post.sorted_by('invalid').pluck(:title)).to eq(%w[b A C])
|
35
|
+
|
36
|
+
expect(Post.sorted_by('-created').pluck(:title)).to eq(%w[b A C])
|
37
|
+
expect(Post.sorted_by('created').pluck(:title)).to eq(%w[C A b])
|
38
|
+
|
39
|
+
expect(Post.sorted_by('title').pluck(:title)).to eq(%w[A C b])
|
40
|
+
expect(Post.sorted_by('-title').pluck(:title)).to eq(%w[b C A])
|
41
|
+
expect(Post.sorted_by(' title ').pluck(:title)).to eq(%w[A C b])
|
10
42
|
end
|
11
43
|
|
12
|
-
it 'should
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
expect(
|
19
|
-
expect(
|
20
|
-
expect(
|
21
|
-
expect(
|
22
|
-
|
23
|
-
|
24
|
-
expect(
|
25
|
-
|
26
|
-
|
44
|
+
it 'should support STI inheritance and overrides' do
|
45
|
+
SubPost.create! title: 'A', created_at: Time.at(1515151700)
|
46
|
+
SubPost.create! title: 'b', created_at: Time.at(1515151600)
|
47
|
+
Post.create! title: 'C', created_at: Time.at(1515151400)
|
48
|
+
SubPost.create! title: 'D', created_at: Time.at(1515151500)
|
49
|
+
|
50
|
+
expect(Post.sorted_by(nil).pluck(:title)).to eq(%w[A b D C])
|
51
|
+
expect(SubPost.sorted_by(nil).pluck(:title)).to eq(%w[A b D])
|
52
|
+
expect(SubPost.sorted_by('-created').pluck(:title)).to eq(%w[A b D])
|
53
|
+
expect(SubPost.sorted_by('created').pluck(:title)).to eq(%w[D b A])
|
54
|
+
|
55
|
+
expect(Post.sorted_by('title').pluck(:title)).to eq(%w[A C D b])
|
56
|
+
expect(SubPost.sorted_by('title').pluck(:title)).to eq(%w[A b D])
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should support composition' do
|
60
|
+
App.create! name: 'E', major: 0, minor: 9, patch: 2
|
61
|
+
App.create! name: 'A', major: 1, minor: 0, patch: 1
|
62
|
+
App.create! name: 'D', major: 1, minor: 0, patch: 6
|
63
|
+
App.create! name: 'C', major: 1, minor: 1, patch: 0
|
64
|
+
App.create! name: 'B', major: 2, minor: 2, patch: 0
|
65
|
+
|
66
|
+
expect(App.sorted_by(nil).pluck(:name)).to eq(%w[A B C D E])
|
67
|
+
expect(App.sorted_by('version').pluck(:name)).to eq(%w[E A D C B])
|
68
|
+
expect(App.sorted_by('-version').pluck(:name)).to eq(%w[B C D A E])
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should support associations' do
|
72
|
+
y = Shop.create! name: 'Y'
|
73
|
+
x = Shop.create! name: 'X'
|
74
|
+
|
75
|
+
Product.create! name: 'a', shop_id: y.id
|
76
|
+
Product.create! name: 'B', shop_id: y.id, active: false
|
77
|
+
Product.create! name: 'c', shop_id: x.id
|
78
|
+
Product.create! name: 'D', shop_id: y.id
|
79
|
+
Product.create! name: 'e', shop_id: x.id
|
80
|
+
Product.create! name: 'f', shop_id: x.id, active: false
|
81
|
+
|
82
|
+
expect(Product.sorted_by(nil).pluck(:name)).to eq(%w[c e f a B D])
|
83
|
+
expect(Product.where(active: true).sorted_by(nil).pluck(:name)).to eq(%w[c e a D])
|
84
|
+
expect(Product.sorted_by('name').pluck(:name)).to eq(%w[a B c D e f])
|
27
85
|
end
|
28
86
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -7,36 +7,64 @@ ActiveRecord::Base.configurations['test'] = {
|
|
7
7
|
'database' => ':memory:',
|
8
8
|
}
|
9
9
|
ActiveRecord::Base.establish_connection :test
|
10
|
-
ActiveRecord::Base.connection.create_table :foos do |t|
|
11
|
-
t.string :title
|
12
|
-
t.integer :age
|
13
10
|
|
14
|
-
|
15
|
-
t.
|
11
|
+
ActiveRecord::Base.connection.create_table :posts do |t|
|
12
|
+
t.string :type
|
13
|
+
t.string :title, null: false
|
14
|
+
t.timestamp :created_at, null: false
|
16
15
|
end
|
17
16
|
|
18
|
-
ActiveRecord::Base
|
19
|
-
|
17
|
+
class Post < ActiveRecord::Base
|
18
|
+
sortable_by :title, default: '-created' do |s|
|
19
|
+
s.field :created, as: arel_table[:created_at]
|
20
|
+
end
|
20
21
|
end
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
class SubPost < Post
|
24
|
+
sortable_by do |s|
|
25
|
+
s.field :title, as: arel_table[:title].lower
|
26
|
+
end
|
24
27
|
end
|
25
28
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
# ---------------------------------------------------------------------
|
30
|
+
|
31
|
+
ActiveRecord::Base.connection.create_table :apps do |t|
|
32
|
+
t.string :name, null: false
|
33
|
+
t.integer :major, null: false
|
34
|
+
t.integer :minor, null: false
|
35
|
+
t.integer :patch, null: false
|
36
|
+
end
|
37
|
+
|
38
|
+
class App < ActiveRecord::Base
|
39
|
+
sortable_by :name do |s|
|
40
|
+
s.field :version, as: %i[major minor patch]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# ---------------------------------------------------------------------
|
45
|
+
|
46
|
+
ActiveRecord::Base.connection.create_table :shops do |t|
|
47
|
+
t.string :name, null: false
|
31
48
|
end
|
32
49
|
|
33
|
-
class
|
50
|
+
class Shop < ActiveRecord::Base
|
34
51
|
end
|
35
52
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
53
|
+
# ---------------------------------------------------------------------
|
54
|
+
|
55
|
+
ActiveRecord::Base.connection.create_table :products do |t|
|
56
|
+
t.string :name, null: false
|
57
|
+
t.integer :shop_id, null: false
|
58
|
+
t.boolean :active, null: false, default: true
|
59
|
+
t.foreign_key :shops
|
60
|
+
end
|
40
61
|
|
41
|
-
|
42
|
-
|
62
|
+
class Product < ActiveRecord::Base
|
63
|
+
belongs_to :shop
|
64
|
+
|
65
|
+
sortable_by do |s|
|
66
|
+
s.field :name, as: arel_table[:name].lower
|
67
|
+
s.field :shop, as: Shop.arel_table[:name].lower, eager_load: :shop
|
68
|
+
s.default 'shop,name'
|
69
|
+
end
|
70
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sortable-by
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitrij Denissenko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-04-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|