sorted-activerecord 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.travis.yml +7 -0
- data/Guardfile +14 -0
- data/README.md +22 -5
- data/lib/sorted/activerecord/builder.rb +46 -9
- data/lib/sorted/activerecord/helper.rb +11 -8
- data/lib/sorted/activerecord/version.rb +1 -1
- data/sorted-activerecord.gemspec +1 -0
- data/spec/sorted/activerecord_spec.rb +57 -7
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 73c6a1710bf109f92c675c82bfb51adbae2e83fe
|
4
|
+
data.tar.gz: f839fef7e88e8ca7632f3e9055467a217d7e2a96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be76ff4089fe1a2062ad37b001f1084f76b92401ca8011b356a480a8499036b4de359fc3ae6cf1e6b62da7a9655e914b805a0c028eba102758ec0145dbb0c853
|
7
|
+
data.tar.gz: d65155d4a62a460667c0e38e8fd1a3832bba1e0f6f9b0e43a837848f21d2e92f6610bed1bcaa286d769b9ee7c01682d59faf71580f0806f578042fefe48f88a1
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
2
|
+
require "guard/rspec/dsl"
|
3
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
4
|
+
|
5
|
+
# RSpec files
|
6
|
+
rspec = dsl.rspec
|
7
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
8
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
9
|
+
watch(rspec.spec_files)
|
10
|
+
|
11
|
+
# Ruby files
|
12
|
+
ruby = dsl.ruby
|
13
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
14
|
+
end
|
data/README.md
CHANGED
@@ -24,17 +24,34 @@ Or install it yourself as:
|
|
24
24
|
|
25
25
|
## Usage
|
26
26
|
|
27
|
-
|
27
|
+
The sorted scope takes two optional arguments, `sort` and `order`, they both
|
28
|
+
conform to the [AR query
|
29
|
+
interface](http://guides.rubyonrails.org/active_record_querying.html#ordering)
|
30
|
+
except that if you provide a string to the sort argument it expects a
|
31
|
+
`Sorted::URIQuery` encoded string:
|
28
32
|
|
29
33
|
```ruby
|
30
|
-
@users = User.sorted(
|
34
|
+
@users = User.sorted(sort: 'created_asc!orders_count_asc'
|
35
|
+
order: 'orders_count ASC, created_at DESC')
|
31
36
|
```
|
32
37
|
|
33
|
-
|
34
|
-
|
38
|
+
See https://github.com/mynameisrufus/sorted-actionview for a view helper to
|
39
|
+
generate the sort string or roll your own.
|
40
|
+
|
41
|
+
A `resorted` method is also available and works the same way as the `reorder`
|
42
|
+
method in Rails. It forces the order to be the one passed in:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
@users = User.order(:id).sorted(order: 'name DESC')
|
46
|
+
.resorted(sort: params[:sort], order: 'email ASC')
|
47
|
+
```
|
48
|
+
|
49
|
+
If you want to prevent people creating 500s by messing with the sort url string
|
50
|
+
you can use a white list:
|
35
51
|
|
36
52
|
```ruby
|
37
|
-
@users = User.
|
53
|
+
@users = User.sorted(sort: 'created_asc!explode_me_asc'
|
54
|
+
whitelist: %w(created_at))
|
38
55
|
```
|
39
56
|
|
40
57
|
## Contributing
|
@@ -2,21 +2,58 @@ require 'sorted'
|
|
2
2
|
|
3
3
|
module Sorted
|
4
4
|
module ActiveRecord
|
5
|
-
Builder
|
6
|
-
|
7
|
-
|
5
|
+
class Builder
|
6
|
+
class ParseError < StandardError; end
|
7
|
+
|
8
|
+
attr_reader :set
|
9
|
+
|
10
|
+
def initialize(sort: [], order: [], whitelist: [])
|
11
|
+
uri = parse_sort(sort)
|
12
|
+
if whitelist.length > 0
|
13
|
+
uri = ::Sorted::Set.new(uri.select { |o| whitelist.include?(o[0]) })
|
14
|
+
end
|
15
|
+
sql = parse_order(order)
|
16
|
+
@set = uri + (sql - uri)
|
8
17
|
end
|
9
18
|
|
10
|
-
def
|
11
|
-
|
19
|
+
def parse_sort(sort)
|
20
|
+
case sort.class.name
|
21
|
+
when 'String'
|
22
|
+
::Sorted::URIQuery.parse(sort)
|
23
|
+
when 'Array'
|
24
|
+
parse(sort)
|
25
|
+
else
|
26
|
+
raise ParseError, "could not parse sort - #{sort}"
|
27
|
+
end
|
12
28
|
end
|
13
29
|
|
14
|
-
def
|
15
|
-
|
30
|
+
def parse_order(order)
|
31
|
+
case order.class.name
|
32
|
+
when 'String'
|
33
|
+
::Sorted::SQLQuery.parse(order)
|
34
|
+
when 'Array'
|
35
|
+
parse(order)
|
36
|
+
else
|
37
|
+
raise ParseError, "could not parse sort - #{order}"
|
38
|
+
end
|
16
39
|
end
|
17
40
|
|
18
|
-
def
|
19
|
-
|
41
|
+
def parse(values)
|
42
|
+
values.inject(Sorted::Set.new) do |memo, value|
|
43
|
+
case value.class.name
|
44
|
+
when 'Hash'
|
45
|
+
memo = memo + parse(value.to_a)
|
46
|
+
when 'String'
|
47
|
+
memo = memo + ::Sorted::SQLQuery.parse(value)
|
48
|
+
when 'Symbol'
|
49
|
+
memo = memo << [value.to_s, 'asc']
|
50
|
+
when 'Array'
|
51
|
+
memo = memo << [value[0].to_s, value[1].to_s]
|
52
|
+
else
|
53
|
+
raise ParseError, "could not parse - #{value}"
|
54
|
+
end
|
55
|
+
memo
|
56
|
+
end
|
20
57
|
end
|
21
58
|
end
|
22
59
|
end
|
@@ -7,16 +7,19 @@ module Sorted
|
|
7
7
|
module Helper
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
included do
|
10
|
-
|
11
|
-
|
12
|
-
builder = ::Sorted::ActiveRecord::Builder.new(sort
|
13
|
-
|
10
|
+
|
11
|
+
def self.sorted(sort: [], order: [], whitelist: [])
|
12
|
+
builder = ::Sorted::ActiveRecord::Builder.new(sort: sort,
|
13
|
+
order: order,
|
14
|
+
whitelist: whitelist)
|
15
|
+
order(builder.set.to_hash)
|
14
16
|
end
|
15
17
|
|
16
|
-
def self.resorted(sort, order
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
def self.resorted(sort: [], order: [], whitelist: [])
|
19
|
+
builder = ::Sorted::ActiveRecord::Builder.new(sort: sort,
|
20
|
+
order: order,
|
21
|
+
whitelist: whitelist)
|
22
|
+
reorder(builder.set.to_hash)
|
20
23
|
end
|
21
24
|
end
|
22
25
|
end
|
data/sorted-activerecord.gemspec
CHANGED
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.add_development_dependency 'railties', '>= 4.0.0'
|
28
28
|
spec.add_development_dependency 'rubocop'
|
29
29
|
spec.add_development_dependency 'rspec'
|
30
|
+
spec.add_development_dependency 'guard-rspec'
|
30
31
|
# spec.add_development_dependency 'activerecord-jdbcsqlite3-adapter'
|
31
32
|
spec.add_development_dependency 'sqlite3', '>= 1.3.5'
|
32
33
|
end
|
@@ -15,19 +15,69 @@ describe Sorted::ActiveRecord do
|
|
15
15
|
scope :page, -> { limit(50) }
|
16
16
|
end
|
17
17
|
|
18
|
+
let(:subject) { SortedActiveRecordTest }
|
19
|
+
|
18
20
|
it 'should integrate with ActiveRecord::Base' do
|
19
|
-
expect(
|
20
|
-
expect(
|
21
|
+
expect(subject.respond_to?(:sorted)).to eq(true)
|
22
|
+
expect(subject.respond_to?(:resorted)).to eq(true)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should allow symbols and strings order' do
|
26
|
+
order_by = 'ORDER BY "sorted_active_record_tests"."created_at" ASC'
|
27
|
+
expect(subject.sorted(order: [:created_at]).to_sql).to match(order_by)
|
28
|
+
expect(subject.sorted(order: ['created_at']).to_sql).to match(order_by)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should allow symbols and strings as sort' do
|
32
|
+
order_by = 'ORDER BY "sorted_active_record_tests"."created_at" ASC'
|
33
|
+
expect(subject.sorted(sort: [:created_at]).to_sql).to match(order_by)
|
34
|
+
expect(subject.sorted(sort: ['created_at']).to_sql).to match(order_by)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should allow hash as order' do
|
38
|
+
order_by = 'ORDER BY "sorted_active_record_tests"."created_at" DESC'
|
39
|
+
expect(subject.sorted(order: [{ created_at: :desc }]).to_sql).to match(order_by)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should allow hash as sort' do
|
43
|
+
order_by = 'ORDER BY "sorted_active_record_tests"."created_at" DESC'
|
44
|
+
expect(subject.sorted(sort: [{ created_at: :desc }]).to_sql).to match(order_by)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should allow symbols and hash as order' do
|
48
|
+
order_by = 'ORDER BY "sorted_active_record_tests"."orders_count" ASC, "sorted_active_record_tests"."created_at" DESC'
|
49
|
+
expect(subject.sorted(order: [:orders_count, { created_at: :desc }]).to_sql).to match(order_by)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should allow sql' do
|
53
|
+
order_by = 'ORDER BY "sorted_active_record_tests"."orders_count" ASC, "sorted_active_record_tests"."created_at" DESC'
|
54
|
+
expect(subject.sorted(order: 'orders_count ASC, created_at DESC').to_sql).to match(order_by)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should allow uri' do
|
58
|
+
order_by = 'ORDER BY "sorted_active_record_tests"."orders_count" ASC, "sorted_active_record_tests"."created_at" DESC'
|
59
|
+
expect(subject.sorted(sort: 'orders_count_asc!created_at_desc').to_sql).to match(order_by)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should allow an array of sql' do
|
63
|
+
order_by = 'ORDER BY "sorted_active_record_tests"."orders_count" ASC, "sorted_active_record_tests"."created_at" DESC'
|
64
|
+
expect(subject.sorted(order: ['orders_count ASC', 'created_at DESC']).to_sql).to match(order_by)
|
21
65
|
end
|
22
66
|
|
23
67
|
it 'should play nice with other scopes' do
|
24
|
-
sql = "SELECT \"sorted_active_record_tests\".* FROM \"sorted_active_record_tests\" WHERE \"sorted_active_record_tests\".\"name\" = 'bob' ORDER BY \"name\" ASC LIMIT 50"
|
25
|
-
expect(
|
26
|
-
expect(
|
68
|
+
sql = "SELECT \"sorted_active_record_tests\".* FROM \"sorted_active_record_tests\" WHERE \"sorted_active_record_tests\".\"name\" = 'bob' ORDER BY \"sorted_active_record_tests\".\"name\" ASC LIMIT 50"
|
69
|
+
expect(subject.where(name: 'bob').page.sorted(order: 'name ASC').to_sql).to eq(sql)
|
70
|
+
expect(subject.page.sorted(order: 'name ASC').where(name: 'bob').to_sql).to eq(sql)
|
27
71
|
end
|
28
72
|
|
29
73
|
it 'should override the provided order' do
|
30
|
-
sql = "SELECT \"sorted_active_record_tests\".* FROM \"sorted_active_record_tests\" WHERE \"sorted_active_record_tests\".\"name\" = 'bob' ORDER BY \"name\" ASC LIMIT 50"
|
31
|
-
expect(
|
74
|
+
sql = "SELECT \"sorted_active_record_tests\".* FROM \"sorted_active_record_tests\" WHERE \"sorted_active_record_tests\".\"name\" = 'bob' ORDER BY \"sorted_active_record_tests\".\"name\" ASC LIMIT 50"
|
75
|
+
expect(subject.page.where(name: 'bob').order(:id).sorted(order: 'name DESC').resorted(order: 'name ASC').to_sql).to eq(sql)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should work with whitelist' do
|
79
|
+
order_by = 'ORDER BY "sorted_active_record_tests"."created_at" DESC'
|
80
|
+
expect(subject.sorted(sort: 'orders_count_asc!created_at_desc',
|
81
|
+
whitelist: %(created_at)).to_sql).to match(order_by)
|
32
82
|
end
|
33
83
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sorted-activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rufus Post
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: guard-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: sqlite3
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -144,8 +158,11 @@ extensions: []
|
|
144
158
|
extra_rdoc_files: []
|
145
159
|
files:
|
146
160
|
- ".gitignore"
|
161
|
+
- ".rspec"
|
147
162
|
- ".rubocop.yml"
|
163
|
+
- ".travis.yml"
|
148
164
|
- Gemfile
|
165
|
+
- Guardfile
|
149
166
|
- LICENSE.txt
|
150
167
|
- README.md
|
151
168
|
- Rakefile
|