order 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.
- data/.gitignore +5 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +50 -0
- data/LICENSE +20 -0
- data/README.markdown +84 -0
- data/Rakefile +5 -0
- data/lib/order.rb +67 -0
- data/lib/order/version.rb +13 -0
- data/order.gemspec +19 -0
- data/spec/lib/order_spec.rb +60 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/support/models.rb +12 -0
- data/spec/support/setup_sample_db.rb +28 -0
- metadata +83 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
order (0.1.0)
|
5
|
+
activerecord (~> 3.2)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (3.2.9)
|
11
|
+
activesupport (= 3.2.9)
|
12
|
+
builder (~> 3.0.0)
|
13
|
+
activerecord (3.2.9)
|
14
|
+
activemodel (= 3.2.9)
|
15
|
+
activesupport (= 3.2.9)
|
16
|
+
arel (~> 3.0.2)
|
17
|
+
tzinfo (~> 0.3.29)
|
18
|
+
activesupport (3.2.9)
|
19
|
+
i18n (~> 0.6)
|
20
|
+
multi_json (~> 1.0)
|
21
|
+
arel (3.0.2)
|
22
|
+
builder (3.0.4)
|
23
|
+
diff-lcs (1.1.3)
|
24
|
+
i18n (0.6.1)
|
25
|
+
multi_json (1.5.0)
|
26
|
+
rake (10.0.2)
|
27
|
+
rspec (2.12.0)
|
28
|
+
rspec-core (~> 2.12.0)
|
29
|
+
rspec-expectations (~> 2.12.0)
|
30
|
+
rspec-mocks (~> 2.12.0)
|
31
|
+
rspec-core (2.12.1)
|
32
|
+
rspec-expectations (2.12.0)
|
33
|
+
diff-lcs (~> 1.1.3)
|
34
|
+
rspec-mocks (2.12.0)
|
35
|
+
simplecov (0.7.1)
|
36
|
+
multi_json (~> 1.0)
|
37
|
+
simplecov-html (~> 0.7.1)
|
38
|
+
simplecov-html (0.7.1)
|
39
|
+
sqlite3 (1.3.6)
|
40
|
+
tzinfo (0.3.35)
|
41
|
+
|
42
|
+
PLATFORMS
|
43
|
+
ruby
|
44
|
+
|
45
|
+
DEPENDENCIES
|
46
|
+
order!
|
47
|
+
rake
|
48
|
+
rspec (~> 2.12)
|
49
|
+
simplecov
|
50
|
+
sqlite3
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Jay Hayes
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# Order
|
2
|
+
Convenient ActiveRecord ordering.
|
3
|
+
|
4
|
+
## Introduction
|
5
|
+
|
6
|
+
Order provides a convenient DSL for adding ordering scopes to ActiveRecord
|
7
|
+
models. Additionally it provides an `order_by` method that allows you to
|
8
|
+
get records by multiple orderings using a simple syntax. This is great for
|
9
|
+
APIs. Take this model for example:
|
10
|
+
|
11
|
+
## Getting Started
|
12
|
+
|
13
|
+
Add `order` to your `Gemfile`:
|
14
|
+
|
15
|
+
gem 'order', '~> 0.1'
|
16
|
+
|
17
|
+
Bundle it up:
|
18
|
+
|
19
|
+
# From the command line
|
20
|
+
bundle install
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
Add some ordering to your models:
|
25
|
+
|
26
|
+
Person < ActiveRecord::Base
|
27
|
+
belongs_to :job
|
28
|
+
attr_accessible :first_name, :last_name
|
29
|
+
|
30
|
+
orderable :first_name, :last_name
|
31
|
+
orderable :name => [:last_name, :first_name]
|
32
|
+
orderable :job_title do |direction|
|
33
|
+
joins(:job).order "jobs.title #{direction}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Now you can retrieve people in order!
|
38
|
+
|
39
|
+
Person.order_by_first_name
|
40
|
+
=> SELECT "people".* FROM "people" ORDER BY "first_name" ASC
|
41
|
+
|
42
|
+
Person.order_by_first_name(:desc)
|
43
|
+
=> SELECT "people".* FROM "people" ORDER BY "first_name" DESC
|
44
|
+
|
45
|
+
Person.order_by_name
|
46
|
+
=> SELECT "people".* FROM "people" ORDER BY "last_name" ASC, "first_name" ASC
|
47
|
+
|
48
|
+
Person.order_by_job_title
|
49
|
+
=> SELECT "people".* FROM "people" INNER JOIN "jobs" ON "jobs"."id" = "person"."job_id" ORDER BY jobs.title ASC
|
50
|
+
|
51
|
+
You can also get at ordering using the `order_by` method:
|
52
|
+
|
53
|
+
Person.order_by 'name.desc, job_title'
|
54
|
+
=> SELECT "people".* FROM "people" INNER JOIN "jobs" ON "jobs"."id" = "person"."job_id" ORDER BY "last_name" DESC, "first_name" DESC, jobs.title ASC
|
55
|
+
|
56
|
+
This is super helpful when developing APIs that need to provide ordered
|
57
|
+
results. Consider the following:
|
58
|
+
|
59
|
+
PeopleController < ApplicationController
|
60
|
+
respond_to :html
|
61
|
+
def index
|
62
|
+
@requests = Person.order_by params[:order]
|
63
|
+
respond_with @requests
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
A simple request to "/people?order=name.desc,job_title" will give you ordered results! :)
|
68
|
+
|
69
|
+
## Contributing
|
70
|
+
|
71
|
+
This is a baby library! _Cute_? Absolutely. But also hoping to grow up into a
|
72
|
+
big, useful tool to be take advantage of! (Something about that really
|
73
|
+
doesn't sound right)
|
74
|
+
|
75
|
+
* Use it.
|
76
|
+
* Pick it apart.
|
77
|
+
* Let me know what you think.
|
78
|
+
* Tell others.
|
79
|
+
|
80
|
+
Please add issues and send pull requests!
|
81
|
+
|
82
|
+
## Copyright
|
83
|
+
|
84
|
+
Copyright © 2012 Jay Hayes
|
data/Rakefile
ADDED
data/lib/order.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'order/version'
|
3
|
+
|
4
|
+
module Order
|
5
|
+
DIRECTION_DESC = /\Adesc/i
|
6
|
+
|
7
|
+
# Given and ordering such as "state.desc,site,name.descending", it orders the results as such
|
8
|
+
# using the +order_by_*+ scopes.
|
9
|
+
def order_by(ordering)
|
10
|
+
ordering.split(',').inject(self) do |relation, orderer|
|
11
|
+
attribute, direction = *orderer.split('.')
|
12
|
+
relation.send "order_by_#{attribute.strip}", direction
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Generates +order_by_*+ scopes used for ordering resulting records.
|
17
|
+
# @overload orderable(*attribute_names)
|
18
|
+
# Generates simple ordering scopes that can accept a direction such as "desc"
|
19
|
+
# @param [Array] of attribute names
|
20
|
+
# @overload orderable(attribute_map)
|
21
|
+
# Generates simple ordering scope using aliased mapping to one or more attributes
|
22
|
+
# @param [Hash] mapping ordering name to attribute(s)
|
23
|
+
# @overload orderable(order_name, &block)
|
24
|
+
# Generates an ordering scope using a custom strategy specified by the given block
|
25
|
+
# @example Custom ordering strategy
|
26
|
+
# orderable(:category_name){ |direction| joins(:category).order "category.name #{direction}" }
|
27
|
+
def orderable(*attributes, &block)
|
28
|
+
if block_given?
|
29
|
+
raise ArgumentError.new('Cannot provide mapped naming for custom ordering strategy') if attributes.first.is_a? Hash
|
30
|
+
order_scope attributes.first, &block
|
31
|
+
elsif attributes.first.is_a? Hash
|
32
|
+
attributes.first.each{ |name, attribute| order_scope name, attribute }
|
33
|
+
else
|
34
|
+
attributes.each{ |attribute| order_scope attribute }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Adds an +order_by_(name)+ scope. Optionally you can provide an +attribute+ to order on in case
|
39
|
+
# the +name+ is to be an alias for the ordering. You may also provide a block that defines a
|
40
|
+
# custom ordering strategy.
|
41
|
+
def order_scope(name, attribute = nil, &block)
|
42
|
+
scope "order_by_#{name}", lambda{ |direction = nil|
|
43
|
+
if block_given?
|
44
|
+
yield normalize_direction(direction)
|
45
|
+
else
|
46
|
+
if attribute.is_a? Array
|
47
|
+
order attribute.map{ |bute| order_clause bute, direction }.join ', '
|
48
|
+
else
|
49
|
+
order order_clause attribute || name, direction
|
50
|
+
end
|
51
|
+
end
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
# Builds an ordering clause string such as "some_table.some_attribute DESC". The direction will
|
56
|
+
# be normalized to either "ASC" or "DESC".
|
57
|
+
def order_clause(attribute, direction)
|
58
|
+
"\"#{attribute || name}\" #{normalize_direction direction}"
|
59
|
+
end
|
60
|
+
|
61
|
+
# Converts a direction into either "ASC" or "DESC". It uses +DIRECTION_DESC+ to match the +String+.
|
62
|
+
def normalize_direction(direction)
|
63
|
+
(direction =~ DIRECTION_DESC) ? 'DESC' : 'ASC'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
ActiveRecord::Base.extend Order
|
data/order.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'order/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'order'
|
7
|
+
s.version = Order::Version
|
8
|
+
s.authors = ['Jay Hayes']
|
9
|
+
s.email = 'ur@iamvery.com'
|
10
|
+
s.homepage = 'http://github.com/iamvery/order'
|
11
|
+
s.summary = %q(DSL for defining ActiveRecord orderings)
|
12
|
+
s.description = %q(Provides a simple DSL for creating named scopes for ordering records)
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split "\n"
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split "\n"
|
16
|
+
s.require_paths = ['lib']
|
17
|
+
|
18
|
+
s.add_dependency 'activerecord', '~> 3.2'
|
19
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# Re-opened. See support/models.rb
|
4
|
+
class Person
|
5
|
+
orderable :first_name, :last_name
|
6
|
+
orderable :fname => :first_name,
|
7
|
+
:created => :created_at,
|
8
|
+
:updated => :updated_at,
|
9
|
+
:name => [:last_name, :first_name]
|
10
|
+
|
11
|
+
orderable :job do |dir|
|
12
|
+
joins(:job).
|
13
|
+
order "jobs.title #{dir}"
|
14
|
+
end
|
15
|
+
|
16
|
+
orderable :company do |dir|
|
17
|
+
joins(:job => :company).
|
18
|
+
order "companies.name #{dir}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe Order do
|
23
|
+
|
24
|
+
describe '#orderable' do
|
25
|
+
it 'handles attributes' do
|
26
|
+
Person.order_by_first_name.to_sql.should match /"first_name" ASC/
|
27
|
+
Person.order_by_first_name('desc').to_sql.should match /"first_name" DESC/
|
28
|
+
Person.order_by_last_name.to_sql.should match /"last_name" ASC/
|
29
|
+
Person.order_by_last_name('desc').to_sql.should match /"last_name" DESC/
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'handles aliased attributes' do
|
33
|
+
Person.order_by_fname.to_sql.should match /"first_name" ASC/
|
34
|
+
Person.order_by_fname('desc').to_sql.should match /"first_name" DESC/
|
35
|
+
Person.order_by_created.to_sql.should match /"created_at" ASC/
|
36
|
+
Person.order_by_created('desc').to_sql.should match /"created_at" DESC/
|
37
|
+
Person.order_by_updated.to_sql.should match /"updated_at" ASC/
|
38
|
+
Person.order_by_updated('desc').to_sql.should match /"updated_at" DESC/
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'handles multiple aliased attributes' do
|
42
|
+
Person.order_by_name.to_sql.should match /"last_name" ASC, "first_name" ASC/
|
43
|
+
Person.order_by_name('desc').to_sql.should match /"last_name" DESC, "first_name" DESC/
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'handles custom ordering strategies' do
|
47
|
+
Person.order_by_job.to_sql.should match /jobs.title ASC/
|
48
|
+
Person.order_by_job('desc').to_sql.should match /jobs.title DESC/
|
49
|
+
Person.order_by_company.to_sql.should match /companies.name ASC/
|
50
|
+
Person.order_by_company('desc').to_sql.should match /companies.name DESC/
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#order_by' do
|
55
|
+
it 'chains multiple orderings together from comma-separated string' do
|
56
|
+
Person.order_by('name.desc, job').to_sql.should match /"last_name" DESC, "first_name" DESC, jobs.title ASC/
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(
|
4
|
+
:adapter => 'sqlite3',
|
5
|
+
:database => ':memory:'
|
6
|
+
)
|
7
|
+
|
8
|
+
ActiveRecord::Base.silence do
|
9
|
+
ActiveRecord::Migration.verbose = false
|
10
|
+
|
11
|
+
ActiveRecord::Schema.define do
|
12
|
+
create_table :people, :force => true do |t|
|
13
|
+
t.string :first_name
|
14
|
+
t.string :last_name
|
15
|
+
t.references :job
|
16
|
+
t.timestamps
|
17
|
+
end
|
18
|
+
|
19
|
+
create_table :jobs, :force => true do |t|
|
20
|
+
t.string :title
|
21
|
+
t.references :company
|
22
|
+
end
|
23
|
+
|
24
|
+
create_table :companies, :force => true do |t|
|
25
|
+
t.string :name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: order
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jay Hayes
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.2'
|
30
|
+
description: Provides a simple DSL for creating named scopes for ordering records
|
31
|
+
email: ur@iamvery.com
|
32
|
+
executables: []
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- .gitignore
|
37
|
+
- Gemfile
|
38
|
+
- Gemfile.lock
|
39
|
+
- LICENSE
|
40
|
+
- README.markdown
|
41
|
+
- Rakefile
|
42
|
+
- lib/order.rb
|
43
|
+
- lib/order/version.rb
|
44
|
+
- order.gemspec
|
45
|
+
- spec/lib/order_spec.rb
|
46
|
+
- spec/spec_helper.rb
|
47
|
+
- spec/support/models.rb
|
48
|
+
- spec/support/setup_sample_db.rb
|
49
|
+
homepage: http://github.com/iamvery/order
|
50
|
+
licenses: []
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
hash: -2075213003283050010
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
hash: -2075213003283050010
|
73
|
+
requirements: []
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 1.8.24
|
76
|
+
signing_key:
|
77
|
+
specification_version: 3
|
78
|
+
summary: DSL for defining ActiveRecord orderings
|
79
|
+
test_files:
|
80
|
+
- spec/lib/order_spec.rb
|
81
|
+
- spec/spec_helper.rb
|
82
|
+
- spec/support/models.rb
|
83
|
+
- spec/support/setup_sample_db.rb
|