sorted-activerecord 0.0.1 → 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 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