order 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ # Ignore simplecov generated files
2
+ /coverage
3
+ /doc
4
+ /pkg
5
+ .yardoc
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'rake'
7
+ gem 'sqlite3'
8
+ gem 'simplecov', :require => false
9
+ gem 'rspec', '~> 2.12'
10
+ end
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
@@ -0,0 +1,5 @@
1
+ require 'bundler/setup'
2
+ require 'rspec/core/rake_task'
3
+ Bundler::GemHelper.install_tasks
4
+ RSpec::Core::RakeTask.new
5
+ task :default => :spec
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
@@ -0,0 +1,13 @@
1
+ module Order
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ PATCH = 0
6
+ PRE = nil
7
+
8
+ def self.to_s
9
+ [MAJOR, MINOR, PATCH, PRE].compact.join '.'
10
+ end
11
+ end
12
+ end
13
+
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
@@ -0,0 +1,6 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'order'
5
+ require 'support/setup_sample_db'
6
+ require 'support/models'
@@ -0,0 +1,12 @@
1
+ class Person < ActiveRecord::Base
2
+ belongs_to :job
3
+ has_many :pet_relationships
4
+ has_many :pets, :through => :pet_relationships
5
+ end
6
+
7
+ class Job < ActiveRecord::Base
8
+ belongs_to :company
9
+ end
10
+
11
+ class Company < ActiveRecord::Base
12
+ end
@@ -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