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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c21400fbc224e7d0575432c714970ed799cc0d4d
4
- data.tar.gz: 1f6e0062fdddf976f0b257becf262f5149cc45dc
3
+ metadata.gz: 73c6a1710bf109f92c675c82bfb51adbae2e83fe
4
+ data.tar.gz: f839fef7e88e8ca7632f3e9055467a217d7e2a96
5
5
  SHA512:
6
- metadata.gz: c74ee20c9c63598e6adf5af36d290a8397a0f7d0be20c4c0ed9c558dc92a0fbecabae17d3fbb3f857c79e86fbf6dd0cadd2071764def00ed1b5c2b97b870c1f2
7
- data.tar.gz: 6722154d0bfd8fc0ea142602c64caa2e8d786db9fec30c0d7407d3136c012ce6fedfbcf4ea93f3d5e03f6af79f45551349f6a91121ed8f0adec15e256b8ebc55
6
+ metadata.gz: be76ff4089fe1a2062ad37b001f1084f76b92401ca8011b356a480a8499036b4de359fc3ae6cf1e6b62da7a9655e914b805a0c028eba102758ec0145dbb0c853
7
+ data.tar.gz: d65155d4a62a460667c0e38e8fd1a3832bba1e0f6f9b0e43a837848f21d2e92f6610bed1bcaa286d769b9ee7c01682d59faf71580f0806f578042fefe48f88a1
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.5
5
+ - ruby-head
6
+
7
+ script: bundle exec rake spec
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
- Using the `sorted` method with the optional default order argument:
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(params[:sort], "email ASC").page(params[:page])
34
+ @users = User.sorted(sort: 'created_asc!orders_count_asc'
35
+ order: 'orders_count ASC, created_at DESC')
31
36
  ```
32
37
 
33
- A `resorted` method is also available and works the same way as the `reorder` method in Rails.
34
- It forces the order to be the one passed in:
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.order(:id).sorted(nil, 'name DESC').resorted(params[:sort], 'email ASC')
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 = Struct.new(:sort, :order, :quoter) do
6
- def uri
7
- ::Sorted::URIQuery.parse(sort)
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 sql
11
- ::Sorted::SQLQuery.parse(order)
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 set
15
- uri + (sql - uri)
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 to_s
19
- ::Sorted::SQLQuery.encode(set, quoter)
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
- def self.sorted(sort, order = nil)
11
- quote = ->(frag) { connection.quote_column_name(frag) }
12
- builder = ::Sorted::ActiveRecord::Builder.new(sort, order, quote)
13
- order(builder.to_s)
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 = nil)
17
- quote = ->(frag) { connection.quote_column_name(frag) }
18
- builder = ::Sorted::ActiveRecord::Builder.new(sort, order, quote)
19
- reorder(builder.to_s)
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
@@ -1,5 +1,5 @@
1
1
  module Sorted
2
2
  module Activerecord
3
- VERSION = '0.0.1'
3
+ VERSION = '0.1.0'
4
4
  end
5
5
  end
@@ -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(SortedActiveRecordTest.respond_to?(:sorted)).to eq(true)
20
- expect(SortedActiveRecordTest.respond_to?(:resorted)).to eq(true)
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(SortedActiveRecordTest.where(name: 'bob').page.sorted(nil, 'name ASC').to_sql).to eq(sql)
26
- expect(SortedActiveRecordTest.page.sorted(nil, 'name ASC').where(name: 'bob').to_sql).to eq(sql)
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(SortedActiveRecordTest.page.where(name: 'bob').order(:id).sorted(nil, 'name DESC').resorted(nil, 'name ASC').to_sql).to eq(sql)
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.1
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-01-31 00:00:00.000000000 Z
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