sorted 1.0.1 → 1.1.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/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +50 -0
- data/.travis.yml +1 -0
- data/README.md +50 -13
- data/Rakefile +26 -8
- data/gemfiles/active_record_40.gemfile.lock +18 -1
- data/gemfiles/mongoid_30.gemfile.lock +18 -1
- data/lib/sorted.rb +181 -0
- data/lib/sorted/orms/mongoid.rb +2 -2
- data/lib/sorted/parser.rb +26 -40
- data/lib/sorted/railtie.rb +1 -1
- data/lib/sorted/toggler.rb +13 -48
- data/lib/sorted/version.rb +1 -1
- data/lib/sorted/view_helpers/action_view.rb +3 -3
- data/sorted.gemspec +17 -16
- data/spec/sorted/orms/active_record_spec.rb +6 -6
- data/spec/sorted/orms/mongoid_spec.rb +4 -4
- data/spec/sorted/parser_spec.rb +41 -41
- data/spec/sorted/toggler_spec.rb +41 -23
- data/spec/sorted/view_helpers/action_view_spec.rb +29 -11
- data/spec/spec_helper.rb +0 -8
- metadata +19 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b665b8a96ecf9464b3c3845dae2edbdf3a8fef8
|
4
|
+
data.tar.gz: f698bb91b0fa131eef2928a89d8125f19813dde9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4cd81b4bde74202c5ab1a6e4f6bce6696fc882c78392dcba6a59539e658819dcc8080683a2da19b939028a55f4c76be7f3533e35092944278151b4bde1c25b6
|
7
|
+
data.tar.gz: a5e122727f4e42a6756b5b08685e8cff49db754446bcd95f8e00d205fdf289a4699a46ee81d5d3e2d36846411532771b32a7b0d21b545f8a1a6a1b3dc1946474
|
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# This configuration was generated by `rubocop --auto-gen-config`
|
2
|
+
# on 2014-12-14 20:28:26 +1100 using RuboCop version 0.28.0.
|
3
|
+
# The point is for the user to remove these configuration records
|
4
|
+
# one by one as the offenses are removed from the code base.
|
5
|
+
# Note that changes in the inspected code, or installation of new
|
6
|
+
# versions of RuboCop, may require this file to be generated again.
|
7
|
+
|
8
|
+
# Offense count: 5
|
9
|
+
Lint/UselessAssignment:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
# Offense count: 2
|
13
|
+
Lint/Void:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
# Offense count: 2
|
17
|
+
Metrics/AbcSize:
|
18
|
+
Max: 29
|
19
|
+
|
20
|
+
# Offense count: 1
|
21
|
+
Metrics/CyclomaticComplexity:
|
22
|
+
Max: 8
|
23
|
+
|
24
|
+
# Offense count: 22
|
25
|
+
# Configuration parameters: AllowURI, URISchemes.
|
26
|
+
Metrics/LineLength:
|
27
|
+
Max: 177
|
28
|
+
|
29
|
+
# Offense count: 2
|
30
|
+
# Configuration parameters: CountComments.
|
31
|
+
Metrics/MethodLength:
|
32
|
+
Max: 18
|
33
|
+
|
34
|
+
# Offense count: 1
|
35
|
+
Metrics/PerceivedComplexity:
|
36
|
+
Max: 9
|
37
|
+
|
38
|
+
# Offense count: 14
|
39
|
+
Style/Documentation:
|
40
|
+
Enabled: false
|
41
|
+
|
42
|
+
# Offense count: 5
|
43
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
44
|
+
Style/For:
|
45
|
+
Enabled: false
|
46
|
+
|
47
|
+
# Offense count: 1
|
48
|
+
# Configuration parameters: MaxLineLength.
|
49
|
+
Style/IfUnlessModifier:
|
50
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,50 @@
|
|
1
1
|
# sorted
|
2
2
|
|
3
|
-
[](https://travis-ci.org/mynameisrufus/sorted)
|
4
|
+
[](http://badge.fury.io/rb/sorted)
|
4
5
|
|
5
|
-
Sorted
|
6
|
-
|
6
|
+
Sorted at it's core is a set of objects that let you sort many different
|
7
|
+
attributes in weird and wonderful ways.
|
8
|
+
|
9
|
+
## Example
|
10
|
+
|
11
|
+
The secret sauce is the `Sorted::Set` object, in this example we 'toggle' email:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
a = Sorted::Set.new([['email', 'asc'], ['name', 'asc']])
|
15
|
+
b = Sorted::Set.new([['email', 'asc'], ['phone', 'asc']])
|
16
|
+
|
17
|
+
s = a.direction_intersect(b)
|
18
|
+
|
19
|
+
s.uniq.to_a #=> [['email', 'desc'], ['phone', 'asc'], ['name', 'asc']]
|
20
|
+
```
|
21
|
+
|
22
|
+
The best way to think about this is to imagine a spreed sheet and what happens
|
23
|
+
when you sort by various columns, `Sorted::Set` pretty much just does that.
|
24
|
+
|
25
|
+
## Parsers/Encoders
|
26
|
+
|
27
|
+
Parsers return a `Sorted::Set` that can then be used by an encoder:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
set = Sorted::URIQuery.parse('name_asc!email_asc')
|
31
|
+
Sorted::SQLQuery.encode(set) #=> 'name ASC email ASC'
|
32
|
+
```
|
33
|
+
|
34
|
+
Currently implemented:
|
35
|
+
|
36
|
+
* `Sorted::SQLQuery`
|
37
|
+
* `Sorted::URIQuery`
|
38
|
+
|
39
|
+
TODO:
|
40
|
+
|
41
|
+
* `Sorted::JSONQuery` (Mongoid)
|
42
|
+
|
43
|
+
## Rails
|
44
|
+
|
45
|
+
Sorted comes with `ActionView` helpers and ORM scopes out of the box.
|
46
|
+
|
47
|
+
The ORM scopes will let you sort large datasets over many pages (using
|
7
48
|
[will_paginate](https://github.com/mislav/will_paginate) or
|
8
49
|
[kaminari](https://github.com/amatsuda/kaminari)) without losing state.
|
9
50
|
|
@@ -18,12 +59,6 @@ link_to_sorted "Email", :email
|
|
18
59
|
Works the same as the `link_to` method except a second argument for the
|
19
60
|
sort attribute is needed.
|
20
61
|
|
21
|
-
### Ruby 1.8.7 Rails 3.x
|
22
|
-
|
23
|
-
```ruby
|
24
|
-
gem 'sorted', '~> 0.4.3'
|
25
|
-
```
|
26
|
-
|
27
62
|
### Model
|
28
63
|
|
29
64
|
Using the `sorted` method with the optional default order argument:
|
@@ -32,12 +67,14 @@ Using the `sorted` method with the optional default order argument:
|
|
32
67
|
@users = User.sorted(params[:sort], "email ASC").page(params[:page])
|
33
68
|
```
|
34
69
|
|
35
|
-
|
70
|
+
A `resorted` method is also available and works the same way as the `reorder` method in Rails.
|
71
|
+
It forces the order to be the one passed in:
|
36
72
|
|
37
|
-
|
38
|
-
|
73
|
+
```ruby
|
74
|
+
@users = User.order(:id).sorted(nil, 'name DESC').resorted(params[:sort], 'email ASC')
|
75
|
+
```
|
39
76
|
|
40
|
-
|
77
|
+
## Supported ORMs
|
41
78
|
|
42
79
|
* ActiveRecord
|
43
80
|
* Mongoid
|
data/Rakefile
CHANGED
@@ -1,18 +1,18 @@
|
|
1
|
-
require 'bundler'
|
2
|
-
|
3
|
-
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'rubocop/rake_task'
|
4
4
|
require 'rspec/core'
|
5
5
|
require 'rspec/core/rake_task'
|
6
|
+
require 'rdoc/task'
|
7
|
+
require 'sorted'
|
8
|
+
|
6
9
|
RSpec::Core::RakeTask.new(:spec) do |spec|
|
7
10
|
spec.pattern = FileList['spec/**/*_spec.rb']
|
8
11
|
end
|
9
12
|
|
10
|
-
|
13
|
+
RuboCop::RakeTask.new
|
11
14
|
|
12
|
-
require 'rdoc/task'
|
13
15
|
Rake::RDocTask.new do |rdoc|
|
14
|
-
require 'sorted/version'
|
15
|
-
|
16
16
|
rdoc.rdoc_dir = 'rdoc'
|
17
17
|
rdoc.title = "sorted #{Sorted::VERSION}"
|
18
18
|
rdoc.rdoc_files.include('README*')
|
@@ -20,7 +20,7 @@ Rake::RDocTask.new do |rdoc|
|
|
20
20
|
end
|
21
21
|
|
22
22
|
namespace :spec do
|
23
|
-
desc
|
23
|
+
desc 'Run Tests against all ORMs'
|
24
24
|
task :all do
|
25
25
|
%w(active_record_40 mongoid_30).each do |gemfile|
|
26
26
|
sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle --quiet"
|
@@ -28,3 +28,21 @@ namespace :spec do
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
32
|
+
task :benchmark do
|
33
|
+
require 'benchmark'
|
34
|
+
|
35
|
+
sort = 'email_desc!name_desc'
|
36
|
+
order = 'email ASC, phone ASC, name DESC'
|
37
|
+
|
38
|
+
n = 50_000
|
39
|
+
Benchmark.bm do |x|
|
40
|
+
x.report(:lazy) { for i in 1..n; Sorted::Parser.new(sort, order); end }
|
41
|
+
x.report(:to_hash) { for i in 1..n; Sorted::Parser.new(sort, order).to_hash; end }
|
42
|
+
x.report(:to_sql) { for i in 1..n; Sorted::Parser.new(sort, order).to_sql; end }
|
43
|
+
x.report(:to_a) { for i in 1..n; Sorted::Parser.new(sort, order).to_a; end }
|
44
|
+
x.report(:toggle) { for i in 1..n; Sorted::Parser.new(sort, order).toggle; end }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
task default: ['rubocop', 'spec:all']
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: /Users/rufuspost/Projects/ruby/gems/sorted
|
3
3
|
specs:
|
4
|
-
sorted (1.
|
4
|
+
sorted (1.1.1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -33,6 +33,9 @@ GEM
|
|
33
33
|
thread_safe (~> 0.1)
|
34
34
|
tzinfo (~> 0.3.37)
|
35
35
|
arel (4.0.1)
|
36
|
+
ast (2.0.0)
|
37
|
+
astrolabe (1.3.0)
|
38
|
+
parser (>= 2.2.0.pre.3, < 3.0)
|
36
39
|
atomic (1.1.14)
|
37
40
|
atomic (1.1.14-java)
|
38
41
|
builder (3.1.4)
|
@@ -42,6 +45,10 @@ GEM
|
|
42
45
|
jdbc-sqlite3 (3.7.2.1)
|
43
46
|
minitest (4.7.5)
|
44
47
|
multi_json (1.8.2)
|
48
|
+
parser (2.2.0.pre.8)
|
49
|
+
ast (>= 1.1, < 3.0)
|
50
|
+
slop (~> 3.4, >= 3.4.5)
|
51
|
+
powerpack (0.0.9)
|
45
52
|
rack (1.5.2)
|
46
53
|
rack-test (0.6.2)
|
47
54
|
rack (>= 1.0)
|
@@ -50,6 +57,7 @@ GEM
|
|
50
57
|
activesupport (= 4.0.0)
|
51
58
|
rake (>= 0.8.7)
|
52
59
|
thor (>= 0.18.1, < 2.0)
|
60
|
+
rainbow (2.0.0)
|
53
61
|
rake (10.1.0)
|
54
62
|
rspec (2.14.1)
|
55
63
|
rspec-core (~> 2.14.0)
|
@@ -66,6 +74,14 @@ GEM
|
|
66
74
|
rspec-core (~> 2.14.0)
|
67
75
|
rspec-expectations (~> 2.14.0)
|
68
76
|
rspec-mocks (~> 2.14.0)
|
77
|
+
rubocop (0.28.0)
|
78
|
+
astrolabe (~> 1.3)
|
79
|
+
parser (>= 2.2.0.pre.7, < 3.0)
|
80
|
+
powerpack (~> 0.0.6)
|
81
|
+
rainbow (>= 1.99.1, < 3.0)
|
82
|
+
ruby-progressbar (~> 1.4)
|
83
|
+
ruby-progressbar (1.7.0)
|
84
|
+
slop (3.6.0)
|
69
85
|
sqlite3 (1.3.8)
|
70
86
|
thor (0.18.1)
|
71
87
|
thread_safe (0.1.3)
|
@@ -88,5 +104,6 @@ DEPENDENCIES
|
|
88
104
|
rake
|
89
105
|
rspec (>= 2.0.0)
|
90
106
|
rspec-rails (>= 2.0)
|
107
|
+
rubocop (>= 0.28)
|
91
108
|
sorted!
|
92
109
|
sqlite3 (>= 1.3.5)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../
|
3
3
|
specs:
|
4
|
-
sorted (1.
|
4
|
+
sorted (1.1.1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -24,6 +24,9 @@ GEM
|
|
24
24
|
minitest (~> 5.1)
|
25
25
|
thread_safe (~> 0.1)
|
26
26
|
tzinfo (~> 1.1)
|
27
|
+
ast (2.0.0)
|
28
|
+
astrolabe (1.3.0)
|
29
|
+
parser (>= 2.2.0.pre.3, < 3.0)
|
27
30
|
bson (2.3.0)
|
28
31
|
bson (2.3.0-java)
|
29
32
|
builder (3.2.2)
|
@@ -45,6 +48,10 @@ GEM
|
|
45
48
|
optionable (~> 0.2.0)
|
46
49
|
optionable (0.2.0)
|
47
50
|
origin (2.1.1)
|
51
|
+
parser (2.2.0.pre.8)
|
52
|
+
ast (>= 1.1, < 3.0)
|
53
|
+
slop (~> 3.4, >= 3.4.5)
|
54
|
+
powerpack (0.0.9)
|
48
55
|
rack (1.5.2)
|
49
56
|
rack-test (0.6.2)
|
50
57
|
rack (>= 1.0)
|
@@ -53,6 +60,7 @@ GEM
|
|
53
60
|
activesupport (= 4.1.4)
|
54
61
|
rake (>= 0.8.7)
|
55
62
|
thor (>= 0.18.1, < 2.0)
|
63
|
+
rainbow (2.0.0)
|
56
64
|
rake (10.3.2)
|
57
65
|
rspec (3.0.0)
|
58
66
|
rspec-core (~> 3.0.0)
|
@@ -74,6 +82,14 @@ GEM
|
|
74
82
|
rspec-mocks (~> 3.0.0)
|
75
83
|
rspec-support (~> 3.0.0)
|
76
84
|
rspec-support (3.0.2)
|
85
|
+
rubocop (0.28.0)
|
86
|
+
astrolabe (~> 1.3)
|
87
|
+
parser (>= 2.2.0.pre.7, < 3.0)
|
88
|
+
powerpack (~> 0.0.6)
|
89
|
+
rainbow (>= 1.99.1, < 3.0)
|
90
|
+
ruby-progressbar (~> 1.4)
|
91
|
+
ruby-progressbar (1.7.0)
|
92
|
+
slop (3.6.0)
|
77
93
|
thor (0.19.1)
|
78
94
|
thread_safe (0.3.4)
|
79
95
|
thread_safe (0.3.4-java)
|
@@ -93,4 +109,5 @@ DEPENDENCIES
|
|
93
109
|
rake
|
94
110
|
rspec (>= 2.0.0)
|
95
111
|
rspec-rails (>= 2.0)
|
112
|
+
rubocop (>= 0.28)
|
96
113
|
sorted!
|
data/lib/sorted.rb
CHANGED
@@ -1,8 +1,189 @@
|
|
1
1
|
require 'sorted/parser'
|
2
2
|
|
3
3
|
module Sorted
|
4
|
+
class Set
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(set = [])
|
8
|
+
@set = set
|
9
|
+
end
|
10
|
+
|
11
|
+
def each(&block)
|
12
|
+
@set.each(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Gets the keys from the array pairs
|
17
|
+
#
|
18
|
+
# set = [["email", "name"], ["desc", "desc"]]
|
19
|
+
# set.transpose #=> [["email", "name"], ["desc", "desc"]]
|
20
|
+
# set.transpose.first #=> ["email", "name"]
|
21
|
+
|
22
|
+
def keys
|
23
|
+
@set.transpose.first || []
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Returns a resulting set with specific keys flipped
|
28
|
+
#
|
29
|
+
# a = Sorted::Set.new([['email', 'asc'], ['name', 'asc']])
|
30
|
+
# b = Sorted::Set.new([['email', 'asc'], ['phone', 'asc']])
|
31
|
+
# s = a.direction_intersect(b)
|
32
|
+
# s.to_a #=> [['email', 'desc'], ['phone', 'asc'], ['name', 'asc']]
|
33
|
+
|
34
|
+
def direction_intersect(other)
|
35
|
+
self.class.new.tap do |memo|
|
36
|
+
unless other.keys.empty?
|
37
|
+
a(memo, other)
|
38
|
+
b(memo, other)
|
39
|
+
end
|
40
|
+
c(memo)
|
41
|
+
d(memo, other)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def -(other)
|
46
|
+
self.class.new.tap do |memo|
|
47
|
+
each do |a|
|
48
|
+
b = other.assoc(a.first)
|
49
|
+
next if b
|
50
|
+
memo << a
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def +(other)
|
56
|
+
self.class.new(@set + other.to_a)
|
57
|
+
end
|
58
|
+
|
59
|
+
def <<(a)
|
60
|
+
self.class.new(@set << a)
|
61
|
+
end
|
62
|
+
|
63
|
+
def uniq
|
64
|
+
self.class.new(@set.uniq)
|
65
|
+
end
|
66
|
+
|
67
|
+
def assoc(o)
|
68
|
+
@set.assoc(o)
|
69
|
+
end
|
70
|
+
|
71
|
+
def at(i)
|
72
|
+
@set.at(i)
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_a
|
76
|
+
@set
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_hash
|
80
|
+
@set.inject({}) { |a, e| a.merge(Hash[e[0], e[1]]) }
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# If the order of keys match upto the size of the set then flip them
|
86
|
+
def a(memo, other)
|
87
|
+
if keys == other.keys.take(keys.size)
|
88
|
+
keys.each do |order|
|
89
|
+
if other.keys.include?(order)
|
90
|
+
memo << [order, flip_direction(other.assoc(order).last)]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
else
|
94
|
+
keys.each do |order|
|
95
|
+
if other.keys.include?(order)
|
96
|
+
memo << other.assoc(order)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Add items from other that are common and not already added
|
103
|
+
def b(memo, other)
|
104
|
+
other.keys.each do |sort|
|
105
|
+
if keys.include?(sort) && !memo.keys.include?(sort)
|
106
|
+
memo << other.assoc(sort)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Add items not in memo
|
112
|
+
def c(memo)
|
113
|
+
each do |order|
|
114
|
+
unless memo.keys.include?(order[0])
|
115
|
+
memo << order
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Add items from other not in memo
|
121
|
+
def d(memo, other)
|
122
|
+
other.each do |sort|
|
123
|
+
unless memo.keys.include?(sort[0])
|
124
|
+
memo << sort
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def flip_direction(direction)
|
130
|
+
case direction
|
131
|
+
when 'asc' then 'desc'
|
132
|
+
when 'desc'then 'asc'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
module Parse
|
138
|
+
def split(raw, delim, &block)
|
139
|
+
return Set.new if raw.nil?
|
140
|
+
raw.to_s.split(delim).inject(Set.new, &block)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class URIQuery
|
145
|
+
extend Parse
|
146
|
+
|
147
|
+
REGEXP = /([a-zA-Z0-9._]+)_(asc|desc)$/
|
148
|
+
|
149
|
+
def self.parse(raw)
|
150
|
+
split(raw, /!/) do |set, part|
|
151
|
+
m = part.match(REGEXP)
|
152
|
+
return set unless m
|
153
|
+
set << [m[1], m[2].downcase]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.encode(set)
|
158
|
+
set.map { |a| a.join('_') }.join('!')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class SQLQuery
|
163
|
+
extend Parse
|
164
|
+
|
165
|
+
REGEXP = /(([a-z0-9._]+)\s([asc|desc]+)|[a-z0-9._]+)/i
|
166
|
+
|
167
|
+
def self.parse(raw)
|
168
|
+
split(raw, /,/) do |set, part|
|
169
|
+
m = part.match(REGEXP)
|
170
|
+
return set unless m
|
171
|
+
set << [(m[2].nil? ? m[1] : m[2]), (m[3].nil? ? 'asc' : m[3].downcase)]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.encode(set, quote_proc = ->(f) { f })
|
176
|
+
set.map { |a| "#{column(a[0], quote_proc)} #{a[1].upcase}" }.join(', ')
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.column(parts, quote_proc)
|
180
|
+
parts.split('.').map { |frag| quote_proc.call(frag) }.join('.')
|
181
|
+
end
|
182
|
+
private_class_method :column
|
183
|
+
end
|
4
184
|
end
|
5
185
|
|
6
186
|
if defined?(::Rails::Railtie)
|
187
|
+
ActiveSupport::Deprecation.warn('Rails helpers will be removed in version 2.0 of the sorted gem, use sorted-rails instead.')
|
7
188
|
require 'sorted/railtie'
|
8
189
|
end
|
data/lib/sorted/orms/mongoid.rb
CHANGED
@@ -5,12 +5,12 @@ module Sorted
|
|
5
5
|
module Orms
|
6
6
|
module Mongoid
|
7
7
|
extend ActiveSupport::Concern
|
8
|
-
SQL_TO_MONGO = {
|
8
|
+
SQL_TO_MONGO = { 'asc' => 1, 'desc' => -1 }
|
9
9
|
|
10
10
|
included do
|
11
11
|
def self.sorted(sort, default_order = nil)
|
12
12
|
sorter = ::Sorted::Parser.new(sort, default_order)
|
13
|
-
order_by sorter.to_hash.merge(sorter) { |
|
13
|
+
order_by sorter.to_hash.merge(sorter) { |_key, val| SQL_TO_MONGO[val] }
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
data/lib/sorted/parser.rb
CHANGED
@@ -1,82 +1,68 @@
|
|
1
1
|
require 'sorted/toggler'
|
2
2
|
|
3
3
|
module Sorted
|
4
|
+
##
|
4
5
|
# Takes a sort query string and an SQL order string and parses the
|
6
|
+
#
|
5
7
|
# values to produce key value pairs.
|
6
8
|
#
|
7
9
|
# Example:
|
8
|
-
# Sorted::Parser.new('phone_desc', 'name ASC').to_s
|
9
|
-
|
10
|
-
|
10
|
+
# Sorted::Parser.new('phone_desc', 'name ASC').to_s #=> "phone_desc!name_asc"
|
11
|
+
#
|
12
|
+
# TODO A more helpfull name than `Parser` because it only deals with URI and
|
13
|
+
# SQL. Shoud be refactored before 2.x
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
+
Parser = Struct.new(:sort, :order) do
|
16
|
+
def uri
|
17
|
+
URIQuery.parse(sort)
|
18
|
+
end
|
15
19
|
|
16
|
-
def
|
17
|
-
|
18
|
-
@order = order
|
19
|
-
@sorts = parse_sort
|
20
|
-
@orders = parse_order
|
20
|
+
def sql
|
21
|
+
SQLQuery.parse(order)
|
21
22
|
end
|
22
23
|
|
23
|
-
def
|
24
|
-
|
25
|
-
if m = sort_string.match(SORTED_QUERY_REGEX)
|
26
|
-
[m[1], m[2].downcase]
|
27
|
-
end
|
28
|
-
end.compact
|
24
|
+
def sorts
|
25
|
+
uri.to_a
|
29
26
|
end
|
30
27
|
|
31
|
-
def
|
32
|
-
|
33
|
-
if m = order_string.match(SQL_REGEX)
|
34
|
-
[(m[2].nil? ? m[1] : m[2]),(m[3].nil? ? "asc" : m[3].downcase)]
|
35
|
-
end
|
36
|
-
end.compact
|
28
|
+
def orders
|
29
|
+
sql.to_a
|
37
30
|
end
|
38
31
|
|
39
32
|
def to_hash
|
40
|
-
|
33
|
+
set.to_hash
|
41
34
|
end
|
42
35
|
|
43
|
-
def to_sql(
|
44
|
-
|
45
|
-
column = a[0].split('.').map{ |frag| quoter.call(frag) }.join('.')
|
46
|
-
"#{column} #{a[1].upcase}"
|
47
|
-
end.join(', ')
|
36
|
+
def to_sql(quote_proc = ->(f) { f })
|
37
|
+
SQLQuery.encode(set, quote_proc)
|
48
38
|
end
|
49
39
|
|
50
40
|
def to_s
|
51
|
-
|
41
|
+
URIQuery.encode(set)
|
52
42
|
end
|
53
43
|
|
54
44
|
def to_a
|
55
|
-
|
45
|
+
set.to_a
|
56
46
|
end
|
57
47
|
|
58
48
|
def toggle
|
59
|
-
@
|
49
|
+
@set = Toggler.new(sorts, orders).toggle
|
60
50
|
self
|
61
51
|
end
|
62
52
|
|
63
53
|
def reset
|
64
|
-
@
|
54
|
+
@set = default
|
65
55
|
self
|
66
56
|
end
|
67
57
|
|
68
58
|
private
|
69
59
|
|
70
|
-
def
|
71
|
-
@
|
60
|
+
def set
|
61
|
+
@set ||= default
|
72
62
|
end
|
73
63
|
|
74
64
|
def default
|
75
|
-
|
76
|
-
orders.each do |o|
|
77
|
-
sorts_new << o unless sorts_new.flatten.include?(o[0])
|
78
|
-
end
|
79
|
-
sorts_new
|
65
|
+
uri + (sql - uri)
|
80
66
|
end
|
81
67
|
end
|
82
68
|
end
|
data/lib/sorted/railtie.rb
CHANGED
data/lib/sorted/toggler.rb
CHANGED
@@ -1,62 +1,27 @@
|
|
1
1
|
module Sorted
|
2
|
-
|
3
|
-
#
|
2
|
+
##
|
3
|
+
# Takes a parsed arrays of sorts and orders, it then will reorder the pairs
|
4
|
+
# and flip the ascendance of the first sort pair.
|
4
5
|
#
|
5
6
|
# Example:
|
6
7
|
# sorts = [['name', 'asc'], ['phone', 'desc']]
|
7
8
|
# orders = [['name', 'asc']]
|
8
|
-
# Sorted::Toggler.new(sorts, orders).to_a
|
9
|
+
# Sorted::Toggler.new(sorts, orders).to_a
|
10
|
+
#
|
11
|
+
# TODO Remove this in 2.x, it's only here for backwards compatibility.
|
12
|
+
|
9
13
|
class Toggler
|
10
14
|
def initialize(sorts, orders)
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@orders = orders
|
14
|
-
@sort_keys = sorts.transpose.first
|
15
|
-
@order_keys = orders.transpose.first
|
16
|
-
toggle_sorts unless @sort_keys.nil?
|
17
|
-
add_remaining_orders
|
18
|
-
add_remaining_sorts
|
19
|
-
end
|
20
|
-
|
21
|
-
def to_a
|
22
|
-
@array
|
23
|
-
end
|
24
|
-
|
25
|
-
def toggle_sorts
|
26
|
-
if @order_keys == @sort_keys.take(@order_keys.size)
|
27
|
-
@order_keys.select do |order|
|
28
|
-
@sort_keys.include?(order)
|
29
|
-
end.each do |order|
|
30
|
-
@array << [order, (case @sorts.assoc(order).last; when "asc"; "desc"; when "desc"; "asc" end)]
|
31
|
-
end
|
32
|
-
else
|
33
|
-
@order_keys.select do |order|
|
34
|
-
@sort_keys.include?(order)
|
35
|
-
end.each do |order|
|
36
|
-
@array << [order, @sorts.assoc(order).last]
|
37
|
-
end
|
38
|
-
end
|
39
|
-
@sort_keys.select do |sort|
|
40
|
-
@order_keys.include?(sort) && !@array.flatten.include?(sort)
|
41
|
-
end.each do |sort|
|
42
|
-
@array << [sort, @sorts.assoc(sort).last]
|
43
|
-
end
|
15
|
+
@sorts = Set.new(sorts)
|
16
|
+
@orders = Set.new(orders)
|
44
17
|
end
|
45
18
|
|
46
|
-
def
|
47
|
-
@orders.
|
48
|
-
!@array.flatten.include?(order[0])
|
49
|
-
end.each do |order|
|
50
|
-
@array << order
|
51
|
-
end
|
19
|
+
def toggle
|
20
|
+
@orders.direction_intersect(@sorts)
|
52
21
|
end
|
53
22
|
|
54
|
-
def
|
55
|
-
|
56
|
-
!@array.flatten.include?(sort[0])
|
57
|
-
end.each do |sort|
|
58
|
-
@array << sort
|
59
|
-
end
|
23
|
+
def to_a
|
24
|
+
toggle.to_a
|
60
25
|
end
|
61
26
|
end
|
62
27
|
end
|
data/lib/sorted/version.rb
CHANGED
@@ -18,7 +18,7 @@ module Sorted
|
|
18
18
|
if @parser.sorts.flatten.include? @parser.orders[0][0]
|
19
19
|
"sorted #{@parser.sorts.assoc(@parser.orders[0][0]).last}"
|
20
20
|
else
|
21
|
-
|
21
|
+
'sorted'
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -71,10 +71,10 @@ module Sorted
|
|
71
71
|
# sortable_by :author_name, :title, ["Date of Publication", :published_at]
|
72
72
|
#
|
73
73
|
def sortable_by(*columns)
|
74
|
-
links = content_tag :span,
|
74
|
+
links = content_tag :span, 'Sort by: '
|
75
75
|
columns.each do |c|
|
76
76
|
if c.is_a? Array
|
77
|
-
links += link_to_sorted(c[0],c[1].to_sym)
|
77
|
+
links += link_to_sorted(c[0], c[1].to_sym)
|
78
78
|
else
|
79
79
|
links += link_to_sorted(c.to_s.titleize, c.to_sym)
|
80
80
|
end
|
data/sorted.gemspec
CHANGED
@@ -1,27 +1,28 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
require File.expand_path(
|
2
|
+
require File.expand_path('../lib/sorted/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
|
-
s.name =
|
5
|
+
s.name = 'sorted'
|
6
6
|
s.version = Sorted::VERSION
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
|
-
s.authors = [
|
9
|
-
s.email = [
|
10
|
-
s.homepage =
|
11
|
-
s.summary =
|
12
|
-
s.description =
|
13
|
-
s.license =
|
8
|
+
s.authors = ['Rufus Post', 'Daniel Leavitt']
|
9
|
+
s.email = ['rufuspost@gmail.com', 'daniel.leavitt@gmail.com']
|
10
|
+
s.homepage = 'http://rubygems.org/gems/sorted'
|
11
|
+
s.summary = 'Data sorting library'
|
12
|
+
s.description = 'Allows you to sort large datasets over many pages, without losing state.'
|
13
|
+
s.license = 'MIT'
|
14
14
|
|
15
|
-
s.required_rubygems_version =
|
16
|
-
s.rubyforge_project =
|
15
|
+
s.required_rubygems_version = '>= 1.3.6'
|
16
|
+
s.rubyforge_project = 'sorted'
|
17
17
|
|
18
|
-
s.add_development_dependency
|
19
|
-
s.add_development_dependency
|
20
|
-
s.add_development_dependency
|
21
|
-
s.add_development_dependency
|
22
|
-
s.add_development_dependency
|
18
|
+
s.add_development_dependency 'rake', '>= 0'
|
19
|
+
s.add_development_dependency 'bundler', '>= 1.0.0'
|
20
|
+
s.add_development_dependency 'rspec', '>= 2.0.0'
|
21
|
+
s.add_development_dependency 'activesupport', '>= 3.0.0'
|
22
|
+
s.add_development_dependency 'actionpack', '>= 3.0.0'
|
23
|
+
s.add_development_dependency 'rubocop', '>= 0.28'
|
23
24
|
|
24
25
|
s.files = `git ls-files`.split("\n")
|
25
|
-
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ?
|
26
|
+
s.executables = `git ls-files`.split("\n").map { |f| f =~ /^bin\/(.*)/ ? Regexp.last_match[1] : nil }.compact
|
26
27
|
s.require_path = 'lib'
|
27
28
|
end
|
@@ -14,20 +14,20 @@ if defined? ActiveRecord
|
|
14
14
|
scope :page, -> { limit(50) }
|
15
15
|
end
|
16
16
|
|
17
|
-
it
|
17
|
+
it 'should integrate with ActiveRecord::Base' do
|
18
18
|
SortedActiveRecordTest.should respond_to(:sorted)
|
19
19
|
SortedActiveRecordTest.should respond_to(:resorted)
|
20
20
|
end
|
21
21
|
|
22
|
-
it
|
22
|
+
it 'should play nice with other scopes' do
|
23
23
|
sql = "SELECT \"sorted_active_record_tests\".* FROM \"sorted_active_record_tests\" WHERE \"sorted_active_record_tests\".\"name\" = 'bob' ORDER BY \"name\" ASC LIMIT 50"
|
24
|
-
SortedActiveRecordTest.where(:
|
25
|
-
SortedActiveRecordTest.page.sorted(nil, 'name ASC').where(:
|
24
|
+
SortedActiveRecordTest.where(name: 'bob').page.sorted(nil, 'name ASC').to_sql.should == sql
|
25
|
+
SortedActiveRecordTest.page.sorted(nil, 'name ASC').where(name: 'bob').to_sql.should == sql
|
26
26
|
end
|
27
27
|
|
28
|
-
it
|
28
|
+
it 'should override the provided order' do
|
29
29
|
sql = "SELECT \"sorted_active_record_tests\".* FROM \"sorted_active_record_tests\" WHERE \"sorted_active_record_tests\".\"name\" = 'bob' ORDER BY \"name\" ASC LIMIT 50"
|
30
|
-
SortedActiveRecordTest.page.where(:
|
30
|
+
SortedActiveRecordTest.page.where(name: 'bob').order(:id).sorted(nil, 'name DESC').resorted(nil, 'name ASC').to_sql.should == sql
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
@@ -15,16 +15,16 @@ if defined? Mongoid
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
it
|
18
|
+
it 'should integrate with Mongoid::Document' do
|
19
19
|
SortedMongoidTest.should respond_to(:sorted)
|
20
20
|
end
|
21
21
|
|
22
|
-
it
|
22
|
+
it 'should integrate with Mongoid::Criteria' do
|
23
23
|
SortedMongoidTest.page.should respond_to(:sorted)
|
24
24
|
end
|
25
25
|
|
26
|
-
it
|
27
|
-
query_options = { limit: 50, sort: {
|
26
|
+
it 'should play nice with other scopes' do
|
27
|
+
query_options = { limit: 50, sort: { 'name' => 1 } }
|
28
28
|
SortedMongoidTest.page.sorted(nil, 'name ASC').options.should == query_options
|
29
29
|
SortedMongoidTest.page.sorted(nil, 'name ASC').options.should == query_options
|
30
30
|
end
|
data/spec/sorted/parser_spec.rb
CHANGED
@@ -1,66 +1,66 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Sorted::Parser,
|
4
|
-
it
|
5
|
-
|
3
|
+
describe Sorted::Parser, 'params parsing' do
|
4
|
+
it 'should not raise if pased nil arguments' do
|
5
|
+
-> { Sorted::Parser.new(nil, nil).toggle }.should_not raise_error
|
6
6
|
end
|
7
7
|
|
8
|
-
it
|
8
|
+
it 'should return a nice array from the order sql' do
|
9
9
|
sort = nil
|
10
|
-
order =
|
11
|
-
result = [[
|
10
|
+
order = 'email ASC, phone ASC, name DESC'
|
11
|
+
result = [['email', 'asc'], ['phone', 'asc'], ['name', 'desc']]
|
12
12
|
|
13
13
|
sorter = Sorted::Parser.new(sort, order)
|
14
14
|
sorter.orders.should eq result
|
15
15
|
end
|
16
16
|
|
17
|
-
it
|
18
|
-
sort =
|
17
|
+
it 'should return a nice array from the sort params' do
|
18
|
+
sort = 'email_desc!name_desc'
|
19
19
|
order = nil
|
20
|
-
result = [[
|
20
|
+
result = [['email', 'desc'], ['name', 'desc']]
|
21
21
|
|
22
22
|
sorter = Sorted::Parser.new(sort, order)
|
23
23
|
sorter.sorts.should eq result
|
24
24
|
end
|
25
25
|
|
26
|
-
it
|
27
|
-
sort =
|
28
|
-
order =
|
29
|
-
result = [[
|
26
|
+
it 'should combine sort and order params with sort params being of higer importance' do
|
27
|
+
sort = 'email_desc!name_desc'
|
28
|
+
order = 'email ASC, phone ASC, name DESC'
|
29
|
+
result = [['email', 'desc'], ['name', 'desc'], ['phone', 'asc']]
|
30
30
|
|
31
31
|
sorter = Sorted::Parser.new(sort, order)
|
32
32
|
sorter.to_a.should eq result
|
33
33
|
end
|
34
34
|
|
35
|
-
it
|
36
|
-
sort =
|
35
|
+
it 'should allow numbers, underscores and full stops in sort params' do
|
36
|
+
sort = 'assessmentsTable.name_desc!users_300.name_5_desc'
|
37
37
|
order = nil
|
38
|
-
result = [[
|
38
|
+
result = [['assessmentsTable.name', 'desc'], ['users_300.name_5', 'desc']]
|
39
39
|
|
40
40
|
sorter = Sorted::Parser.new(sort, order)
|
41
41
|
sorter.sorts.should eq result
|
42
42
|
end
|
43
43
|
|
44
|
-
it
|
44
|
+
it 'should allow numbers, underscores and full stops in order params' do
|
45
45
|
sort = nil
|
46
|
-
order =
|
47
|
-
result = [[
|
46
|
+
order = 'assessmentsTable.name ASC, users_300.name_5 ASC'
|
47
|
+
result = [['assessmentsTable.name', 'asc'], ['users_300.name_5', 'asc']]
|
48
48
|
|
49
49
|
sorter = Sorted::Parser.new(sort, order)
|
50
50
|
sorter.orders.should eq result
|
51
51
|
end
|
52
52
|
|
53
|
-
it
|
53
|
+
it 'should default to asc if sort params order is ommited' do
|
54
54
|
sort = nil
|
55
55
|
order = :email
|
56
|
-
result = [[
|
56
|
+
result = [['email', 'asc']]
|
57
57
|
|
58
58
|
sorter = Sorted::Parser.new(sort, order)
|
59
59
|
sorter.orders.should eq result
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
-
describe Sorted::Parser,
|
63
|
+
describe Sorted::Parser, 'return types' do
|
64
64
|
module FakeConnection
|
65
65
|
def self.quote_column_name(column_name)
|
66
66
|
"`#{column_name}`"
|
@@ -71,45 +71,45 @@ describe Sorted::Parser, "return types" do
|
|
71
71
|
->(frag) { FakeConnection.quote_column_name(frag) }
|
72
72
|
}
|
73
73
|
|
74
|
-
it
|
75
|
-
order =
|
76
|
-
result =
|
74
|
+
it 'should properly escape sql column names' do
|
75
|
+
order = 'users.name DESC'
|
76
|
+
result = '`users`.`name` DESC'
|
77
77
|
|
78
78
|
sorter = Sorted::Parser.new(nil, order)
|
79
79
|
sorter.to_sql(quoter).should eq result
|
80
80
|
end
|
81
81
|
|
82
|
-
it
|
83
|
-
sort =
|
84
|
-
order =
|
85
|
-
result =
|
82
|
+
it 'should return an sql sort string' do
|
83
|
+
sort = 'email_desc!name_desc'
|
84
|
+
order = 'email ASC, phone ASC, name DESC'
|
85
|
+
result = '`email` DESC, `name` DESC, `phone` ASC'
|
86
86
|
|
87
87
|
sorter = Sorted::Parser.new(sort, order)
|
88
88
|
sorter.to_sql(quoter).should eq result
|
89
89
|
end
|
90
90
|
|
91
|
-
it
|
92
|
-
sort =
|
93
|
-
order =
|
94
|
-
result = {
|
91
|
+
it 'should return an hash' do
|
92
|
+
sort = 'email_desc!name_desc'
|
93
|
+
order = 'email ASC, phone ASC, name DESC'
|
94
|
+
result = { 'email' => 'desc', 'name' => 'desc', 'phone' => 'asc' }
|
95
95
|
|
96
96
|
sorter = Sorted::Parser.new(sort, order)
|
97
97
|
sorter.to_hash.should eq result
|
98
98
|
end
|
99
99
|
|
100
|
-
it
|
101
|
-
sort =
|
102
|
-
order =
|
103
|
-
result =
|
100
|
+
it 'should return an the encoded sort string' do
|
101
|
+
sort = 'email_desc!name_desc'
|
102
|
+
order = 'email ASC, phone ASC, name DESC'
|
103
|
+
result = 'email_desc!name_desc!phone_asc'
|
104
104
|
|
105
105
|
sorter = Sorted::Parser.new(sort, order)
|
106
106
|
sorter.to_s.should eq result
|
107
107
|
end
|
108
108
|
|
109
|
-
it
|
110
|
-
sort =
|
111
|
-
order =
|
112
|
-
result =
|
109
|
+
it 'sql injection using order by clause should not work' do
|
110
|
+
sort = '(case+when+((ASCII(SUBSTR((select+table_name+from+all_tables+where+rownum%3d1),1))>%3D128))+then+id+else+something+end)'
|
111
|
+
order = 'email ASC, phone ASC, name DESC'
|
112
|
+
result = '`email` ASC, `phone` ASC, `name` DESC'
|
113
113
|
|
114
114
|
sorter = Sorted::Parser.new(sort, order)
|
115
115
|
sorter.to_sql(quoter).should eq result
|
data/spec/sorted/toggler_spec.rb
CHANGED
@@ -1,55 +1,73 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Sorted::Toggler do
|
4
|
-
it
|
5
|
-
orders = [[
|
6
|
-
sorts = [[
|
7
|
-
result = [[
|
4
|
+
it 'should bring phone to first order importance but not toggle ascendance' do
|
5
|
+
orders = [['email', 'asc'], ['phone', 'asc']]
|
6
|
+
sorts = [['phone', 'asc']]
|
7
|
+
result = [['phone', 'asc'], ['email', 'asc']]
|
8
8
|
|
9
9
|
toggler = Sorted::Toggler.new(sorts, orders)
|
10
10
|
toggler.to_a.should eq result
|
11
11
|
end
|
12
12
|
|
13
|
-
it
|
14
|
-
orders = [[
|
15
|
-
sorts = [[
|
16
|
-
result = [[
|
13
|
+
it 'should toggle ascendance of email' do
|
14
|
+
orders = [['email', 'desc']]
|
15
|
+
sorts = [['email', 'asc']]
|
16
|
+
result = [['email', 'desc']]
|
17
17
|
|
18
18
|
toggler = Sorted::Toggler.new(sorts, orders)
|
19
19
|
toggler.to_a.should eq result
|
20
20
|
end
|
21
21
|
|
22
|
-
it
|
23
|
-
orders = [[
|
22
|
+
it 'should return both order params un-toggled with no sort param' do
|
23
|
+
orders = [['email', 'asc'], ['phone', 'asc']]
|
24
24
|
sorts = []
|
25
|
-
result = [[
|
25
|
+
result = [['email', 'asc'], ['phone', 'asc']]
|
26
26
|
|
27
27
|
toggler = Sorted::Toggler.new(sorts, orders)
|
28
28
|
toggler.to_a.should eq result
|
29
29
|
end
|
30
30
|
|
31
|
-
it
|
32
|
-
orders = [[
|
33
|
-
sorts = [[
|
34
|
-
result = [[
|
31
|
+
it 'should toggle the email ascendance' do
|
32
|
+
orders = [['email', 'asc']]
|
33
|
+
sorts = [['email', 'asc'], ['phone', 'asc']]
|
34
|
+
result = [['email', 'desc'], ['phone', 'asc']]
|
35
35
|
|
36
36
|
toggler = Sorted::Toggler.new(sorts, orders)
|
37
37
|
toggler.to_a.should eq result
|
38
38
|
end
|
39
39
|
|
40
|
-
it
|
41
|
-
orders = [[
|
42
|
-
sorts = [[
|
43
|
-
result = [[
|
40
|
+
it 'should toggle the email ascendance' do
|
41
|
+
orders = [['email', 'desc']]
|
42
|
+
sorts = [['email', 'asc'], ['phone', 'asc']]
|
43
|
+
result = [['email', 'desc'], ['phone', 'asc']]
|
44
44
|
|
45
45
|
toggler = Sorted::Toggler.new(sorts, orders)
|
46
46
|
toggler.to_a.should eq result
|
47
47
|
end
|
48
48
|
|
49
|
-
it
|
50
|
-
orders = [[
|
51
|
-
sorts = [[
|
52
|
-
result = [[
|
49
|
+
it 'should toggle two 1..n sort values' do
|
50
|
+
orders = [['email', 'asc'], ['phone', 'asc']]
|
51
|
+
sorts = [['email', 'asc'], ['phone', 'asc']]
|
52
|
+
result = [['email', 'desc'], ['phone', 'desc']]
|
53
|
+
|
54
|
+
toggler = Sorted::Toggler.new(sorts, orders)
|
55
|
+
toggler.to_a.should eq result
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should toggle based on sorts and not orders' do
|
59
|
+
orders = [['email', 'desc'], ['phone', 'desc']]
|
60
|
+
sorts = [['email', 'asc'], ['phone', 'asc']]
|
61
|
+
result = [['email', 'desc'], ['phone', 'desc']]
|
62
|
+
|
63
|
+
toggler = Sorted::Toggler.new(sorts, orders)
|
64
|
+
toggler.to_a.should eq result
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should toggle based on sorts and not orders' do
|
68
|
+
orders = [['email', 'asc']]
|
69
|
+
sorts = [['name', 'asc']]
|
70
|
+
result = [['email', 'asc'], ['name', 'asc']]
|
53
71
|
|
54
72
|
toggler = Sorted::Toggler.new(sorts, orders)
|
55
73
|
toggler.to_a.should eq result
|
@@ -1,45 +1,63 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Sorted::ViewHelpers::ActionView do
|
4
|
-
it
|
4
|
+
it 'should integrate with ActiveRecord::Base' do
|
5
5
|
ActionView::Base.send(:include, Sorted::ViewHelpers::ActionView)
|
6
6
|
ActionView::Base.new.should respond_to(:link_to_sorted)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
10
|
describe Sorted::ViewHelpers::ActionView::SortedViewHelper do
|
11
|
-
it
|
11
|
+
it 'should return the default sort order and preserve the existing params' do
|
12
12
|
order = :email
|
13
|
-
params = { :
|
14
|
-
result = { :
|
13
|
+
params = { page: 10 }
|
14
|
+
result = { page: 10, sort: 'email_asc' }
|
15
15
|
|
16
16
|
sorter = Sorted::ViewHelpers::ActionView::SortedViewHelper.new order, params
|
17
17
|
sorter.params.should eq result
|
18
18
|
end
|
19
19
|
|
20
|
-
it
|
20
|
+
it 'should only return the sorted css class if email has not yet been sorted' do
|
21
21
|
order = :email
|
22
22
|
params = {}
|
23
|
-
result =
|
23
|
+
result = 'sorted'
|
24
24
|
|
25
25
|
sorter = Sorted::ViewHelpers::ActionView::SortedViewHelper.new order, params
|
26
26
|
sorter.css.should eq result
|
27
27
|
end
|
28
28
|
|
29
|
-
it
|
29
|
+
it 'should only return the sorted css class if email has not yet been sorted' do
|
30
30
|
order = :email
|
31
|
-
params = { :
|
32
|
-
result =
|
31
|
+
params = { sort: 'email_asc' }
|
32
|
+
result = 'sorted asc'
|
33
33
|
|
34
34
|
sorter = Sorted::ViewHelpers::ActionView::SortedViewHelper.new order, params
|
35
35
|
sorter.css.should eq result
|
36
36
|
end
|
37
37
|
|
38
|
-
it
|
38
|
+
it 'should return the default order when params are empty' do
|
39
39
|
order = :email
|
40
|
-
result = { :
|
40
|
+
result = { sort: 'email_asc' }
|
41
41
|
|
42
42
|
sorter = Sorted::ViewHelpers::ActionView::SortedViewHelper.new order, {}
|
43
43
|
sorter.params.should eq result
|
44
44
|
end
|
45
|
+
|
46
|
+
it 'should correctly toggle multiple params' do
|
47
|
+
order = 'email DESC, name DESC'
|
48
|
+
params = { sort: 'email_asc!name_asc' }
|
49
|
+
result = { sort: 'email_desc!name_desc' }
|
50
|
+
|
51
|
+
sorter = Sorted::ViewHelpers::ActionView::SortedViewHelper.new order, params
|
52
|
+
sorter.params.should eq result
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should have sort order over existing params' do
|
56
|
+
order = :email
|
57
|
+
params = { sort: 'name_asc' }
|
58
|
+
result = { sort: 'email_asc!name_asc' }
|
59
|
+
|
60
|
+
sorter = Sorted::ViewHelpers::ActionView::SortedViewHelper.new order, params
|
61
|
+
sorter.params.should eq result
|
62
|
+
end
|
45
63
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,14 +1,6 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
2
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
3
|
|
4
|
-
begin
|
5
|
-
require 'rails'
|
6
|
-
rescue LoadError
|
7
|
-
end
|
8
|
-
|
9
|
-
require 'bundler/setup'
|
10
|
-
Bundler.require
|
11
|
-
|
12
4
|
require 'sorted'
|
13
5
|
require 'sorted/orms/mongoid'
|
14
6
|
require 'sorted/orms/active_record'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sorted
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rufus Post
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-01-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -81,6 +81,20 @@ dependencies:
|
|
81
81
|
- - ">="
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: 3.0.0
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rubocop
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0.28'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0.28'
|
84
98
|
description: Allows you to sort large datasets over many pages, without losing state.
|
85
99
|
email:
|
86
100
|
- rufuspost@gmail.com
|
@@ -91,6 +105,8 @@ extra_rdoc_files: []
|
|
91
105
|
files:
|
92
106
|
- ".gitignore"
|
93
107
|
- ".rspec"
|
108
|
+
- ".rubocop.yml"
|
109
|
+
- ".rubocop_todo.yml"
|
94
110
|
- ".travis.yml"
|
95
111
|
- Gemfile
|
96
112
|
- LICENSE
|
@@ -135,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
151
|
version: 1.3.6
|
136
152
|
requirements: []
|
137
153
|
rubyforge_project: sorted
|
138
|
-
rubygems_version: 2.
|
154
|
+
rubygems_version: 2.4.5
|
139
155
|
signing_key:
|
140
156
|
specification_version: 4
|
141
157
|
summary: Data sorting library
|