restful_query 0.2.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/History.txt +10 -0
- data/LICENSE +20 -0
- data/Manifest.txt +19 -0
- data/README.rdoc +29 -0
- data/Rakefile +32 -0
- data/init.rb +1 -0
- data/lib/restful_query/can_query.rb +36 -0
- data/lib/restful_query/condition.rb +77 -0
- data/lib/restful_query/parser.rb +132 -0
- data/lib/restful_query/sort.rb +65 -0
- data/lib/restful_query.rb +22 -0
- data/rails/init.rb +3 -0
- data/restful_query.gemspec +50 -0
- data/tasks/restful_query_tasks.rake +4 -0
- data/test/test_helper.rb +34 -0
- data/test/test_restful_query_can_query.rb +20 -0
- data/test/test_restful_query_condition.rb +112 -0
- data/test/test_restful_query_parser.rb +392 -0
- data/test/test_restful_query_sort.rb +72 -0
- metadata +138 -0
data/History.txt
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Aaron Quint, Quirkey NYC, LLC
|
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 NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
History.txt
|
2
|
+
LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
init.rb
|
7
|
+
lib/restful_query.rb
|
8
|
+
lib/restful_query/can_query.rb
|
9
|
+
lib/restful_query/condition.rb
|
10
|
+
lib/restful_query/parser.rb
|
11
|
+
lib/restful_query/sort.rb
|
12
|
+
rails/init.rb
|
13
|
+
restful_query.gemspec
|
14
|
+
tasks/restful_query_tasks.rake
|
15
|
+
test/test_helper.rb
|
16
|
+
test/test_restful_query_can_query.rb
|
17
|
+
test/test_restful_query_condition.rb
|
18
|
+
test/test_restful_query_parser.rb
|
19
|
+
test/test_restful_query_sort.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
= restful_query
|
2
|
+
|
3
|
+
http://github.com/quirkey/restful_query
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
RestfulQuery provides a RESTful interface for easily and safely querying ActiveRecord data.
|
8
|
+
|
9
|
+
== USAGE:
|
10
|
+
|
11
|
+
Please see the project homepage for usage and more info:
|
12
|
+
|
13
|
+
http://code.quirkey.com/restful_query
|
14
|
+
|
15
|
+
== INSTALL:
|
16
|
+
|
17
|
+
To install as a gem:
|
18
|
+
|
19
|
+
sudo gem install restful_query
|
20
|
+
|
21
|
+
or from github:
|
22
|
+
|
23
|
+
sudo gem install quirkey-restful_query -s http://gems.github.com
|
24
|
+
|
25
|
+
To install as a rails plugin
|
26
|
+
|
27
|
+
== LICENSE:
|
28
|
+
|
29
|
+
Free for use under the terms of the MIT License - see LICENSE for details
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/restful_query'
|
3
|
+
|
4
|
+
# Generate all the Rake tasks
|
5
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
|
+
$hoe = Hoe.new('restful_query', RestfulQuery::VERSION) do |p|
|
7
|
+
p.developer('Aaron Quint', 'aaron@quirkey.com')
|
8
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
9
|
+
p.rubyforge_name = 'quirkey'
|
10
|
+
p.description = p.summary = 'Simple ActiveRecord queries from a RESTful and safe interface'
|
11
|
+
p.url = 'http://code.quirkey.com/restful_query'
|
12
|
+
p.extra_deps = [
|
13
|
+
['activesupport','>= 2.2.0'],
|
14
|
+
['activerecord','>= 2.2.0'],
|
15
|
+
['chronic','>= 0.2.3']
|
16
|
+
]
|
17
|
+
p.extra_dev_deps = [
|
18
|
+
['newgem', ">= #{::Newgem::VERSION}"],
|
19
|
+
['Shoulda', '>= 1.2.0']
|
20
|
+
]
|
21
|
+
|
22
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
23
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
24
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
25
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
29
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
30
|
+
|
31
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
32
|
+
# task :default => [:spec, :features]
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'rails', 'init.rb')
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module RestfulQuery
|
2
|
+
module CanQuery
|
3
|
+
|
4
|
+
def self.included(klass)
|
5
|
+
klass.extend MacroMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module MacroMethods
|
9
|
+
def can_query(options = {})
|
10
|
+
@include = options.delete(:include) || []
|
11
|
+
@query_options = options
|
12
|
+
@can_query = true
|
13
|
+
module_eval do
|
14
|
+
def self.restful_query_parser(query_hash, options = {})
|
15
|
+
RestfulQuery::Parser.new(query_hash, @query_options.merge(options))
|
16
|
+
end
|
17
|
+
|
18
|
+
named_scope :restful_query, lambda {|query_hash|
|
19
|
+
parser = self.restful_query_parser(query_hash)
|
20
|
+
query_hash = {}
|
21
|
+
query_hash[:conditions] = parser.to_conditions_array if parser.has_conditions?
|
22
|
+
query_hash[:include] = @include if @include && !@include.empty?
|
23
|
+
query_hash[:order] = parser.sort_sql if parser.has_sort?
|
24
|
+
logger.info 'Rest query:' + query_hash.inspect
|
25
|
+
query_hash
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def can_query?
|
31
|
+
@can_query
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module RestfulQuery
|
2
|
+
class InvalidOperator < Error; end;
|
3
|
+
|
4
|
+
class Condition
|
5
|
+
attr_reader :column, :value, :operator, :options
|
6
|
+
|
7
|
+
OPERATOR_MAPPING = {
|
8
|
+
'lt' => '<',
|
9
|
+
'gt' => '>',
|
10
|
+
'gteq' => '>=',
|
11
|
+
'lteq' => '<=',
|
12
|
+
'eq' => '=',
|
13
|
+
'like' => 'LIKE'
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
REVERSE_OPERATOR_MAPPING = {
|
17
|
+
'<' => 'lt',
|
18
|
+
'>' => 'gt',
|
19
|
+
'>=' => 'gteq',
|
20
|
+
'<=' => 'lteq',
|
21
|
+
'=' => 'eq',
|
22
|
+
'LIKE' => 'like'
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
def initialize(column, value, operator = '=', options = {})
|
26
|
+
@options = {}
|
27
|
+
@options = options if options.is_a?(Hash)
|
28
|
+
self.column = column
|
29
|
+
self.value = value
|
30
|
+
self.operator = operator
|
31
|
+
end
|
32
|
+
|
33
|
+
def map_operator(operator_to_look_up, reverse = false)
|
34
|
+
mapping = reverse ? REVERSE_OPERATOR_MAPPING.dup : OPERATOR_MAPPING.dup
|
35
|
+
return operator_to_look_up if mapping.values.include?(operator_to_look_up)
|
36
|
+
found = mapping[operator_to_look_up.to_s]
|
37
|
+
end
|
38
|
+
|
39
|
+
def operator=(operator)
|
40
|
+
@operator = map_operator(operator)
|
41
|
+
raise(RestfulQuery::InvalidOperator, "#{@operator} is not a valid operator") unless @operator
|
42
|
+
end
|
43
|
+
|
44
|
+
def column=(column)
|
45
|
+
@column = column.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
def value=(value)
|
49
|
+
@value = parse_value(value)
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_hash
|
53
|
+
{column => {map_operator(operator, true) => value}}
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_condition_array
|
57
|
+
parsed_value = if operator == 'LIKE'
|
58
|
+
"%#{value}%"
|
59
|
+
elsif options[:integer]
|
60
|
+
value.to_i
|
61
|
+
else
|
62
|
+
value
|
63
|
+
end
|
64
|
+
["#{column} #{operator} ?", parsed_value]
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
def parse_value(value)
|
69
|
+
if options[:chronic]
|
70
|
+
Chronic.parse(value.to_s)
|
71
|
+
else
|
72
|
+
value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module RestfulQuery
|
2
|
+
class Parser
|
3
|
+
attr_reader :query_hash, :exclude_columns, :integer_columns, :options
|
4
|
+
|
5
|
+
def initialize(query_hash, options = {})
|
6
|
+
@options = options || {}
|
7
|
+
@exclude_columns = options[:exclude_columns] ? [options.delete(:exclude_columns)].flatten.collect {|c| c.to_s } : []
|
8
|
+
@integer_columns = options[:integer_columns] ? [options.delete(:integer_columns)].flatten.collect {|c| c.to_s } : []
|
9
|
+
@default_sort = options[:default_sort] ? [Sort.parse(options[:default_sort])] : []
|
10
|
+
@query_hash = (query_hash || {}).dup
|
11
|
+
@default_join = @query_hash.delete(:join) || :and
|
12
|
+
extract_sorts_from_conditions
|
13
|
+
map_hash_to_conditions
|
14
|
+
end
|
15
|
+
|
16
|
+
def conditions
|
17
|
+
conditions_hash.values.flatten
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_conditions?
|
21
|
+
!conditions.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
def conditions_for(column)
|
25
|
+
conditions_hash[column.to_s]
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_conditions_array(join = nil)
|
29
|
+
join ||= @default_join
|
30
|
+
join_string = (join == :or) ? ' OR ' : ' AND '
|
31
|
+
conditions_string = []
|
32
|
+
conditions_values = []
|
33
|
+
conditions.each do |c|
|
34
|
+
ca = c.to_condition_array
|
35
|
+
conditions_string << ca[0]
|
36
|
+
conditions_values << ca[1]
|
37
|
+
end
|
38
|
+
conditions_values.unshift(conditions_string.join(join_string))
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_query_hash
|
42
|
+
hash = @query_hash
|
43
|
+
hash['join'] = @default_join
|
44
|
+
hash['_sort'] = sorts.collect {|s| s.to_s }
|
45
|
+
hash
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.sorts_from_hash(sorts)
|
49
|
+
sort_conditions = [sorts].flatten.compact
|
50
|
+
sort_conditions.collect {|c| Sort.parse(c) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def sort_sql
|
54
|
+
@sorts.collect {|s| s.to_sql }.join(', ')
|
55
|
+
end
|
56
|
+
|
57
|
+
def has_sort?
|
58
|
+
!sorts.empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
def sorts
|
62
|
+
@sorts ||= []
|
63
|
+
end
|
64
|
+
|
65
|
+
def sorted_columns
|
66
|
+
sorts.collect {|s| s.column }
|
67
|
+
end
|
68
|
+
|
69
|
+
def sorted_by?(column)
|
70
|
+
sorted_columns.include?(column.to_s)
|
71
|
+
end
|
72
|
+
|
73
|
+
def sort(column)
|
74
|
+
sorts.detect {|s| s && s.column == column.to_s }
|
75
|
+
end
|
76
|
+
|
77
|
+
def set_sort(column, direction)
|
78
|
+
if new_sort = self.sort(column)
|
79
|
+
if direction.nil?
|
80
|
+
self.sorts.reject! {|s| s.column == column.to_s }
|
81
|
+
else
|
82
|
+
new_sort.direction = direction
|
83
|
+
end
|
84
|
+
else
|
85
|
+
new_sort = Sort.new(column, direction)
|
86
|
+
self.sorts << new_sort
|
87
|
+
end
|
88
|
+
new_sort
|
89
|
+
end
|
90
|
+
|
91
|
+
protected
|
92
|
+
def add_condition_for(column, condition)
|
93
|
+
conditions_hash[column.to_s] ||= []
|
94
|
+
conditions_hash[column.to_s] << condition
|
95
|
+
end
|
96
|
+
|
97
|
+
def conditions_hash
|
98
|
+
@conditions_hash ||= {}
|
99
|
+
end
|
100
|
+
|
101
|
+
def chronic_columns
|
102
|
+
if chronic = options[:chronic]
|
103
|
+
chronic.is_a?(Array) ? chronic.collect {|c| c.to_s } : ['created_at', 'updated_at']
|
104
|
+
else
|
105
|
+
[]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def extract_sorts_from_conditions
|
110
|
+
@sorts = self.class.sorts_from_hash(@query_hash.delete('_sort'))
|
111
|
+
@sorts = @default_sort if @sorts.empty?
|
112
|
+
end
|
113
|
+
|
114
|
+
def map_hash_to_conditions
|
115
|
+
@query_hash.each do |column, hash_conditions|
|
116
|
+
unless exclude_columns.include?(column.to_s)
|
117
|
+
condition_options = {}
|
118
|
+
condition_options[:chronic] = true if chronic_columns.include?(column.to_s)
|
119
|
+
condition_options[:integer] = true if integer_columns.include?(column.to_s)
|
120
|
+
if hash_conditions.is_a?(Hash)
|
121
|
+
hash_conditions.each do |operator, value|
|
122
|
+
add_condition_for(column, Condition.new(column, value, operator, condition_options))
|
123
|
+
end
|
124
|
+
else
|
125
|
+
add_condition_for(column, Condition.new(column, hash_conditions, '=', condition_options))
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module RestfulQuery
|
2
|
+
class InvalidDirection < Error; end
|
3
|
+
|
4
|
+
class Sort
|
5
|
+
attr_reader :column, :direction
|
6
|
+
|
7
|
+
DIRECTIONS = {
|
8
|
+
'up' => 'ASC',
|
9
|
+
'asc' => 'ASC',
|
10
|
+
'ASC' => 'ASC',
|
11
|
+
'down' => 'DESC',
|
12
|
+
'desc' => 'DESC',
|
13
|
+
'DESC' => 'DESC'
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
|
17
|
+
def initialize(column, direction)
|
18
|
+
self.column = column
|
19
|
+
self.direction = direction
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.parse(sort_string, split_on = /-|\ /)
|
23
|
+
return unless sort_string
|
24
|
+
column, direction = sort_string.split(split_on)
|
25
|
+
new(column, direction)
|
26
|
+
end
|
27
|
+
|
28
|
+
def column=(column)
|
29
|
+
@column = column.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
def direction=(direction)
|
33
|
+
@direction = DIRECTIONS[direction.to_s]
|
34
|
+
raise(InvalidDirection, "'#{direction}' is not a valid order direction") unless @direction
|
35
|
+
end
|
36
|
+
|
37
|
+
def reverse_direction
|
38
|
+
direction == 'ASC' ? 'DESC' : 'ASC'
|
39
|
+
end
|
40
|
+
|
41
|
+
# Makes a roundabout for directions nil -> desc -> asc -> nil
|
42
|
+
def self.next_direction(current_direction)
|
43
|
+
case current_direction.to_s.downcase
|
44
|
+
when 'desc'
|
45
|
+
'asc'
|
46
|
+
when 'asc'
|
47
|
+
nil
|
48
|
+
else
|
49
|
+
'desc'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def next_direction
|
54
|
+
self.class.next_direction(direction)
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_s(join = '-')
|
58
|
+
"#{column}#{join}#{direction.downcase}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_sql
|
62
|
+
"#{column} #{direction}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'chronic'
|
6
|
+
unless defined?(ActiveSupport)
|
7
|
+
require 'active_support'
|
8
|
+
end
|
9
|
+
rescue LoadError
|
10
|
+
warn 'In order to use the time parsing functionalities you must install the Chronic gem: sudo gem install chronic'
|
11
|
+
end
|
12
|
+
|
13
|
+
module RestfulQuery
|
14
|
+
VERSION = '0.2.0'
|
15
|
+
|
16
|
+
class Error < RuntimeError; end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
%w{condition sort parser can_query}.each do |lib|
|
21
|
+
require File.join("restful_query","#{lib}.rb")
|
22
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{restful_query}
|
5
|
+
s.version = "0.2.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Aaron Quint"]
|
9
|
+
s.date = %q{2009-03-21}
|
10
|
+
s.description = %q{Simple ActiveRecord queries from a RESTful and safe interface}
|
11
|
+
s.email = ["aaron@quirkey.com"]
|
12
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.rdoc"]
|
13
|
+
s.files = ["History.txt", "LICENSE", "Manifest.txt", "README.rdoc", "Rakefile", "init.rb", "lib/restful_query.rb", "lib/restful_query/can_query.rb", "lib/restful_query/condition.rb", "lib/restful_query/parser.rb", "lib/restful_query/sort.rb", "rails/init.rb", "restful_query.gemspec", "tasks/restful_query_tasks.rake", "test/test_helper.rb", "test/test_restful_query_can_query.rb", "test/test_restful_query_condition.rb", "test/test_restful_query_parser.rb", "test/test_restful_query_sort.rb"]
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.homepage = %q{http://code.quirkey.com/restful_query}
|
16
|
+
s.rdoc_options = ["--main", "README.rdoc"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = %q{quirkey}
|
19
|
+
s.rubygems_version = %q{1.3.1}
|
20
|
+
s.summary = %q{Simple ActiveRecord queries from a RESTful and safe interface}
|
21
|
+
s.test_files = ["test/test_helper.rb", "test/test_restful_query_can_query.rb", "test/test_restful_query_condition.rb", "test/test_restful_query_parser.rb", "test/test_restful_query_sort.rb"]
|
22
|
+
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
25
|
+
s.specification_version = 2
|
26
|
+
|
27
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
28
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 2.2.0"])
|
29
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 2.2.0"])
|
30
|
+
s.add_runtime_dependency(%q<chronic>, [">= 0.2.3"])
|
31
|
+
s.add_development_dependency(%q<newgem>, [">= 1.2.3"])
|
32
|
+
s.add_development_dependency(%q<Shoulda>, [">= 1.2.0"])
|
33
|
+
s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
|
34
|
+
else
|
35
|
+
s.add_dependency(%q<activesupport>, [">= 2.2.0"])
|
36
|
+
s.add_dependency(%q<activerecord>, [">= 2.2.0"])
|
37
|
+
s.add_dependency(%q<chronic>, [">= 0.2.3"])
|
38
|
+
s.add_dependency(%q<newgem>, [">= 1.2.3"])
|
39
|
+
s.add_dependency(%q<Shoulda>, [">= 1.2.0"])
|
40
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
41
|
+
end
|
42
|
+
else
|
43
|
+
s.add_dependency(%q<activesupport>, [">= 2.2.0"])
|
44
|
+
s.add_dependency(%q<activerecord>, [">= 2.2.0"])
|
45
|
+
s.add_dependency(%q<chronic>, [">= 0.2.3"])
|
46
|
+
s.add_dependency(%q<newgem>, [">= 1.2.3"])
|
47
|
+
s.add_dependency(%q<Shoulda>, [">= 1.2.0"])
|
48
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
49
|
+
end
|
50
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'shoulda'
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), '..', 'lib','restful_query.rb')
|
6
|
+
|
7
|
+
|
8
|
+
unless defined?(ActiveRecord)
|
9
|
+
module ActiveRecord
|
10
|
+
class Base
|
11
|
+
class << self
|
12
|
+
attr_accessor :pluralize_table_names
|
13
|
+
|
14
|
+
def protected_attributes
|
15
|
+
[]
|
16
|
+
end
|
17
|
+
|
18
|
+
def named_scope(name, options = {})
|
19
|
+
end
|
20
|
+
end
|
21
|
+
self.pluralize_table_names = true
|
22
|
+
|
23
|
+
include RestfulQuery::CanQuery
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class ClassWithQuery < ActiveRecord::Base
|
29
|
+
can_query
|
30
|
+
end
|
31
|
+
|
32
|
+
class ClassWithoutQuery < ActiveRecord::Base
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestRestfulQueryCanQuery < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "CanQuery" do
|
6
|
+
context "A class with the can_query macro" do
|
7
|
+
should "can_query?" do
|
8
|
+
assert ClassWithQuery.can_query?
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context "A class without the can_query macro" do
|
13
|
+
should "not can_query?" do
|
14
|
+
assert !ClassWithoutQuery.can_query?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RestfulQueryConditionTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "Condition" do
|
6
|
+
|
7
|
+
context "initializing" do
|
8
|
+
context "with column, value, operator" do
|
9
|
+
setup do
|
10
|
+
@condition = RestfulQuery::Condition.new('created_at', '1 week ago', '>')
|
11
|
+
end
|
12
|
+
|
13
|
+
should "save column" do
|
14
|
+
assert_equal 'created_at', @condition.column
|
15
|
+
end
|
16
|
+
|
17
|
+
should "save value" do
|
18
|
+
assert_equal '1 week ago', @condition.value
|
19
|
+
end
|
20
|
+
|
21
|
+
should "save operator as string" do
|
22
|
+
assert_equal '>', @condition.operator
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with no operator" do
|
28
|
+
setup do
|
29
|
+
@condition = RestfulQuery::Condition.new('created_at', '1 week ago')
|
30
|
+
end
|
31
|
+
|
32
|
+
should "assume =" do
|
33
|
+
assert_equal '=', @condition.operator
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with an operator as a string" do
|
39
|
+
setup do
|
40
|
+
@condition = RestfulQuery::Condition.new('created_at', '1 week ago', 'gteq')
|
41
|
+
end
|
42
|
+
|
43
|
+
should "translate string to operator" do
|
44
|
+
assert_equal '>=', @condition.operator
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
context "with chronic => true" do
|
50
|
+
setup do
|
51
|
+
@condition = RestfulQuery::Condition.new('created_at', '1 week ago', 'gteq', :chronic => true)
|
52
|
+
end
|
53
|
+
|
54
|
+
should "save option to options" do
|
55
|
+
assert @condition.options[:chronic]
|
56
|
+
end
|
57
|
+
|
58
|
+
should "parse value with chronic" do
|
59
|
+
assert_equal(1.week.ago.to_s, @condition.value.to_s)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
context "a Condition" do
|
66
|
+
setup do
|
67
|
+
@condition = RestfulQuery::Condition.new('title', 'Bossman', 'lt')
|
68
|
+
end
|
69
|
+
|
70
|
+
context "with operator = like to condition array" do
|
71
|
+
setup do
|
72
|
+
@condition = RestfulQuery::Condition.new('title', 'Bossman', 'like')
|
73
|
+
@to_condition_array = @condition.to_condition_array
|
74
|
+
end
|
75
|
+
|
76
|
+
should "wrap value with %" do
|
77
|
+
assert_equal "%Bossman%", @to_condition_array[1]
|
78
|
+
end
|
79
|
+
|
80
|
+
should "translate operator to LIKE" do
|
81
|
+
assert_equal("title LIKE ?", @to_condition_array[0])
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
context "to_condition_array" do
|
87
|
+
setup do
|
88
|
+
@to_condition = @condition.to_condition_array
|
89
|
+
end
|
90
|
+
|
91
|
+
should "return array" do
|
92
|
+
assert @to_condition.is_a?(Array)
|
93
|
+
end
|
94
|
+
|
95
|
+
should "have conditional string first" do
|
96
|
+
assert_equal 'title < ?', @to_condition[0]
|
97
|
+
end
|
98
|
+
|
99
|
+
should "have value as [1]" do
|
100
|
+
assert_equal @condition.value, @to_condition[1]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "to_hash" do
|
105
|
+
should "return hash like params" do
|
106
|
+
assert_equal({'title' => {'lt' => 'Bossman'}}, @condition.to_hash)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,392 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RestfulQueryParserTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "Parser" do
|
6
|
+
setup do
|
7
|
+
@base_query_hash = {'created_at' => {'gt' => '1 week ago', 'lt' => '1 hour ago'}, 'updated_at' => {'lt' => '1 day ago'}, 'title' => {'eq' => 'Test'}, 'other_time' => {'gt' => 'oct 1'}, 'name' => 'Aaron'}
|
8
|
+
end
|
9
|
+
|
10
|
+
context "from_hash" do
|
11
|
+
|
12
|
+
context "without hash" do
|
13
|
+
setup do
|
14
|
+
@parser = RestfulQuery::Parser.new(nil)
|
15
|
+
end
|
16
|
+
|
17
|
+
should "return Parser object" do
|
18
|
+
assert @parser.is_a?(RestfulQuery::Parser)
|
19
|
+
end
|
20
|
+
|
21
|
+
should "have a blank hash for query hash" do
|
22
|
+
assert_equal({}, @parser.query_hash)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "with a hash of columns and operations" do
|
27
|
+
setup do
|
28
|
+
new_parser_from_hash
|
29
|
+
end
|
30
|
+
|
31
|
+
should "return parser object" do
|
32
|
+
assert @parser.is_a?(RestfulQuery::Parser)
|
33
|
+
end
|
34
|
+
|
35
|
+
should "save hash to query_hash" do
|
36
|
+
assert_equal @base_query_hash, @parser.query_hash.to_hash
|
37
|
+
end
|
38
|
+
|
39
|
+
should "save each condition as a condition object" do
|
40
|
+
assert @parser.conditions.is_a?(Array)
|
41
|
+
assert @parser.conditions.first.is_a?(RestfulQuery::Condition)
|
42
|
+
end
|
43
|
+
|
44
|
+
should "save condition without operator with default operator" do
|
45
|
+
assert @parser.conditions_for(:name)
|
46
|
+
assert @parser.conditions_for(:name).first.is_a?(RestfulQuery::Condition)
|
47
|
+
assert_equal '=', @parser.conditions_for(:name).first.operator
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with exclude columns" do
|
53
|
+
setup do
|
54
|
+
new_parser_from_hash({}, :exclude_columns => [:other_time,'name'])
|
55
|
+
end
|
56
|
+
|
57
|
+
should "return parser object" do
|
58
|
+
assert @parser.is_a?(RestfulQuery::Parser)
|
59
|
+
end
|
60
|
+
|
61
|
+
should "exclude columns from conditions" do
|
62
|
+
assert @parser.conditions_for('created_at')
|
63
|
+
assert_nil @parser.conditions_for('other_time')
|
64
|
+
assert_nil @parser.conditions_for(:name)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
context "with chronic => true" do
|
70
|
+
setup do
|
71
|
+
new_parser_from_hash({}, :chronic => true)
|
72
|
+
end
|
73
|
+
|
74
|
+
should "return parser object" do
|
75
|
+
assert @parser.is_a?(RestfulQuery::Parser)
|
76
|
+
end
|
77
|
+
|
78
|
+
should "parse created at and updated with chronic" do
|
79
|
+
assert_equal Chronic.parse('1 week ago').to_s, @parser.conditions_for(:created_at).first.value.to_s
|
80
|
+
assert_equal Chronic.parse('1 day ago').to_s, @parser.conditions_for(:updated_at).first.value.to_s
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
context "with chronic => []" do
|
86
|
+
setup do
|
87
|
+
new_parser_from_hash({}, :chronic => [:other_time])
|
88
|
+
end
|
89
|
+
|
90
|
+
should "return parser object" do
|
91
|
+
assert @parser.is_a?(RestfulQuery::Parser)
|
92
|
+
end
|
93
|
+
|
94
|
+
should "parse selected attributes in array with chronic" do
|
95
|
+
assert_equal Chronic.parse('oct 1').to_s, @parser.conditions_for(:other_time).first.value.to_s
|
96
|
+
end
|
97
|
+
|
98
|
+
should "not parse created at/updated at if not specified" do
|
99
|
+
assert_not_equal Chronic.parse('1 week ago').to_s, @parser.conditions_for(:created_at).first.value.to_s
|
100
|
+
assert_not_equal Chronic.parse('1 day ago').to_s, @parser.conditions_for(:updated_at).first.value.to_s
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "with sort as a single string" do
|
105
|
+
setup do
|
106
|
+
new_parser_from_hash({'_sort' => 'created_at-up'})
|
107
|
+
end
|
108
|
+
|
109
|
+
should "return parser object" do
|
110
|
+
assert @parser.is_a?(RestfulQuery::Parser)
|
111
|
+
end
|
112
|
+
|
113
|
+
should "parse sort string" do
|
114
|
+
@sort = @parser.sorts.first
|
115
|
+
assert @sort.is_a?(RestfulQuery::Sort)
|
116
|
+
assert_equal 'ASC', @sort.direction
|
117
|
+
assert_equal 'created_at', @sort.column
|
118
|
+
end
|
119
|
+
|
120
|
+
should "add sort to sorts" do
|
121
|
+
assert @parser.sorts
|
122
|
+
assert_equal 1, @parser.sorts.length
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
context "with sort as an array of strings" do
|
128
|
+
setup do
|
129
|
+
new_parser_from_hash({'_sort' => ['created_at-up','title-desc']})
|
130
|
+
end
|
131
|
+
|
132
|
+
should "return parser object" do
|
133
|
+
assert @parser.is_a?(RestfulQuery::Parser)
|
134
|
+
end
|
135
|
+
|
136
|
+
should "add sorts to sorts" do
|
137
|
+
assert @parser.sorts
|
138
|
+
assert_equal 2, @parser.sorts.length
|
139
|
+
@parser.sorts.each do |sort|
|
140
|
+
assert sort.is_a?(RestfulQuery::Sort)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
context "with a default_sort" do
|
147
|
+
context "with no sorts defined in the query hash" do
|
148
|
+
setup do
|
149
|
+
new_parser_from_hash({}, {:default_sort => 'created_at DESC'})
|
150
|
+
end
|
151
|
+
|
152
|
+
should "return parser object" do
|
153
|
+
assert @parser.is_a?(RestfulQuery::Parser)
|
154
|
+
end
|
155
|
+
|
156
|
+
should "have default sort in sorts" do
|
157
|
+
assert @parser.sorts
|
158
|
+
assert_equal 1, @parser.sorts.length
|
159
|
+
assert_equal 'created_at DESC', @parser.sort_sql
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "with sorts defined in the query hash" do
|
164
|
+
setup do
|
165
|
+
new_parser_from_hash({'_sort' => 'created_at-up'})
|
166
|
+
end
|
167
|
+
|
168
|
+
should "return parser object" do
|
169
|
+
assert @parser.is_a?(RestfulQuery::Parser)
|
170
|
+
end
|
171
|
+
|
172
|
+
should "have query hash sorts in sorts and not default sort" do
|
173
|
+
assert @parser.sorts
|
174
|
+
assert_equal 1, @parser.sorts.length
|
175
|
+
assert_equal 'created_at ASC', @parser.sort_sql
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context "a loaded parser" do
|
182
|
+
setup do
|
183
|
+
new_parser_from_hash
|
184
|
+
end
|
185
|
+
|
186
|
+
context "conditions" do
|
187
|
+
setup do
|
188
|
+
@conditions = @parser.conditions
|
189
|
+
end
|
190
|
+
|
191
|
+
should "return array of all conditions objects" do
|
192
|
+
assert @conditions.is_a?(Array)
|
193
|
+
@conditions.each do |condition|
|
194
|
+
assert condition.is_a?(RestfulQuery::Condition)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
should "include conditions for every attribute" do
|
199
|
+
assert_equal @base_query_hash.keys.length + 1, @conditions.length
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
context "conditions_for" do
|
204
|
+
should "return nil for columns without conditions" do
|
205
|
+
assert_nil @parser.conditions_for(:blah)
|
206
|
+
end
|
207
|
+
|
208
|
+
should "return array of conditions for column that exists" do
|
209
|
+
@conditions = @parser.conditions_for(:created_at)
|
210
|
+
assert @conditions.is_a?(Array)
|
211
|
+
@conditions.each do |condition|
|
212
|
+
assert condition.is_a?(RestfulQuery::Condition)
|
213
|
+
assert_equal 'created_at', condition.column
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
context "to conditions array" do
|
220
|
+
setup do
|
221
|
+
@conditions = @parser.to_conditions_array
|
222
|
+
end
|
223
|
+
|
224
|
+
should "return array" do
|
225
|
+
assert @conditions.is_a?(Array)
|
226
|
+
end
|
227
|
+
|
228
|
+
should "first element should be a condition string" do
|
229
|
+
assert @conditions[0].is_a?(String)
|
230
|
+
end
|
231
|
+
|
232
|
+
should "include operators for all querys" do
|
233
|
+
assert_match(/(([a-z_]) (\<|\>|\=|\<\=|\>\=) \? AND)+/,@conditions[0])
|
234
|
+
end
|
235
|
+
|
236
|
+
should "join query hash with AND" do
|
237
|
+
assert_match(/AND/,@conditions[0])
|
238
|
+
end
|
239
|
+
|
240
|
+
should "include values for each conditions" do
|
241
|
+
assert_equal @base_query_hash.keys.length + 2, @conditions.length
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
245
|
+
|
246
|
+
context "to conditions with :or" do
|
247
|
+
setup do
|
248
|
+
@conditions = @parser.to_conditions_array(:or)
|
249
|
+
end
|
250
|
+
|
251
|
+
should "join query hash with OR" do
|
252
|
+
assert_match(/(([a-z_]) (\<|\>|\=|\<\=|\>\=) \? OR)+/,@conditions[0])
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
context "to_query_hash" do
|
257
|
+
context "with no altering" do
|
258
|
+
setup do
|
259
|
+
@query_hash = @parser.to_query_hash
|
260
|
+
end
|
261
|
+
|
262
|
+
should "return hash" do
|
263
|
+
assert @query_hash.is_a?(Hash)
|
264
|
+
end
|
265
|
+
|
266
|
+
should "return initial query hash" do
|
267
|
+
assert_equal({'gt' => '1 week ago', 'lt' => '1 hour ago'}, @query_hash['created_at'])
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
context "with altered sorts" do
|
272
|
+
setup do
|
273
|
+
@parser.set_sort('title', 'up')
|
274
|
+
@parser.set_sort('created_at', 'down')
|
275
|
+
@query_hash = @parser.to_query_hash
|
276
|
+
end
|
277
|
+
|
278
|
+
should "include unaltered sort conditions" do
|
279
|
+
assert_equal({'gt' => '1 week ago', 'lt' => '1 hour ago'}, @query_hash['created_at'])
|
280
|
+
end
|
281
|
+
|
282
|
+
should "include altered sorts" do
|
283
|
+
assert_equal(['title-asc','created_at-desc'], @query_hash['_sort'])
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
context "sorts" do
|
289
|
+
setup do
|
290
|
+
new_parser_from_hash({'_sort' => ['title-down', 'updated_at-asc']})
|
291
|
+
@sorts = @parser.sorts
|
292
|
+
end
|
293
|
+
|
294
|
+
should "return an array of sort objects" do
|
295
|
+
assert @sorts
|
296
|
+
assert_equal 2, @sorts.length
|
297
|
+
@sorts.each do |sort|
|
298
|
+
assert sort.is_a?(RestfulQuery::Sort)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
context "sorted_columns" do
|
303
|
+
should "return an array of columns" do
|
304
|
+
@sorted_columns = @parser.sorted_columns
|
305
|
+
assert @sorted_columns.is_a?(Array)
|
306
|
+
assert @sorted_columns.include?('title')
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
context "sorted_by?" do
|
311
|
+
should "return true if column is sorted" do
|
312
|
+
assert @parser.sorted_by?('title')
|
313
|
+
end
|
314
|
+
|
315
|
+
should "return false if column is not sorted" do
|
316
|
+
assert !@parser.sorted_by?('created_at')
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
context "sort()" do
|
321
|
+
should "return Sort object if column is sorted" do
|
322
|
+
sort = @parser.sort('title')
|
323
|
+
assert sort.is_a?(RestfulQuery::Sort)
|
324
|
+
assert_equal 'title', sort.column
|
325
|
+
end
|
326
|
+
|
327
|
+
should "return nil if col" do
|
328
|
+
assert_nil @parser.sort('created_at')
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
context "set_sort" do
|
333
|
+
context "with an existing sort" do
|
334
|
+
setup do
|
335
|
+
@parser.set_sort('title','up')
|
336
|
+
end
|
337
|
+
|
338
|
+
should "not add new sort" do
|
339
|
+
assert_equal 2, @parser.sorts.length
|
340
|
+
end
|
341
|
+
|
342
|
+
should "update sort direction" do
|
343
|
+
assert_equal 'ASC', @parser.sort('title').direction
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
context "with direction: nil" do
|
348
|
+
setup do
|
349
|
+
@parser.set_sort('title', nil)
|
350
|
+
end
|
351
|
+
|
352
|
+
should "remove sort" do
|
353
|
+
assert_equal 1, @parser.sorts.length
|
354
|
+
assert !@parser.sorted_by?('title')
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
context "with a new sort" do
|
359
|
+
setup do
|
360
|
+
@parser.set_sort('name', 'down')
|
361
|
+
end
|
362
|
+
|
363
|
+
should "add sort to sorts" do
|
364
|
+
assert_equal 3, @parser.sorts.length
|
365
|
+
end
|
366
|
+
|
367
|
+
should "set sort direction" do
|
368
|
+
assert_equal 'DESC', @parser.sort('name').direction
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
end
|
373
|
+
|
374
|
+
end
|
375
|
+
|
376
|
+
|
377
|
+
context "sort_sql" do
|
378
|
+
should "join order with ," do
|
379
|
+
new_parser_from_hash({'_sort' => ['title-down', 'updated_at-asc']})
|
380
|
+
assert_equal 'title DESC, updated_at ASC', @parser.sort_sql
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
end
|
385
|
+
|
386
|
+
end
|
387
|
+
|
388
|
+
protected
|
389
|
+
def new_parser_from_hash(params = {}, options = {})
|
390
|
+
@parser = RestfulQuery::Parser.new(@base_query_hash.merge(params), options)
|
391
|
+
end
|
392
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RestfulQuerySortTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "Sort" do
|
6
|
+
context "initializing" do
|
7
|
+
context "with valid column and direction" do
|
8
|
+
setup do
|
9
|
+
@sort = RestfulQuery::Sort.new(:attribute, 'up')
|
10
|
+
end
|
11
|
+
|
12
|
+
should "save column name as string" do
|
13
|
+
assert_equal 'attribute', @sort.column
|
14
|
+
end
|
15
|
+
|
16
|
+
should "interpret direction" do
|
17
|
+
assert_equal 'ASC', @sort.direction
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "with an invalid direction" do
|
22
|
+
should "raise error" do
|
23
|
+
assert_raise(RestfulQuery::InvalidDirection) do
|
24
|
+
RestfulQuery::Sort.new('column', 'blarg')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
context "parse" do
|
32
|
+
context "with a query hash like condition" do
|
33
|
+
setup do
|
34
|
+
@sort = RestfulQuery::Sort.parse('long_name_attribute-down')
|
35
|
+
end
|
36
|
+
|
37
|
+
should "return sort object" do
|
38
|
+
assert @sort.is_a?(RestfulQuery::Sort)
|
39
|
+
end
|
40
|
+
|
41
|
+
should "set column and direction" do
|
42
|
+
assert_equal 'long_name_attribute', @sort.column
|
43
|
+
assert_equal 'DESC', @sort.direction
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "with a standard SQL like condition" do
|
48
|
+
setup do
|
49
|
+
@sort = RestfulQuery::Sort.parse('long_name_attribute DESC')
|
50
|
+
end
|
51
|
+
|
52
|
+
should "return sort object" do
|
53
|
+
assert @sort.is_a?(RestfulQuery::Sort)
|
54
|
+
end
|
55
|
+
|
56
|
+
should "set column and direction" do
|
57
|
+
assert_equal 'long_name_attribute', @sort.column
|
58
|
+
assert_equal 'DESC', @sort.direction
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "to_sql" do
|
64
|
+
should "join the column and attribute" do
|
65
|
+
@sort = RestfulQuery::Sort.new(:attribute, 'down')
|
66
|
+
assert_equal 'attribute DESC', @sort.to_sql
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: restful_query
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aaron Quint
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-21 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.2.0
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: activerecord
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.2.0
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: chronic
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.2.3
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: newgem
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.2.3
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: Shoulda
|
57
|
+
type: :development
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 1.2.0
|
64
|
+
version:
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: hoe
|
67
|
+
type: :development
|
68
|
+
version_requirement:
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 1.8.0
|
74
|
+
version:
|
75
|
+
description: Simple ActiveRecord queries from a RESTful and safe interface
|
76
|
+
email:
|
77
|
+
- aaron@quirkey.com
|
78
|
+
executables: []
|
79
|
+
|
80
|
+
extensions: []
|
81
|
+
|
82
|
+
extra_rdoc_files:
|
83
|
+
- History.txt
|
84
|
+
- Manifest.txt
|
85
|
+
- README.rdoc
|
86
|
+
files:
|
87
|
+
- History.txt
|
88
|
+
- LICENSE
|
89
|
+
- Manifest.txt
|
90
|
+
- README.rdoc
|
91
|
+
- Rakefile
|
92
|
+
- init.rb
|
93
|
+
- lib/restful_query.rb
|
94
|
+
- lib/restful_query/can_query.rb
|
95
|
+
- lib/restful_query/condition.rb
|
96
|
+
- lib/restful_query/parser.rb
|
97
|
+
- lib/restful_query/sort.rb
|
98
|
+
- rails/init.rb
|
99
|
+
- restful_query.gemspec
|
100
|
+
- tasks/restful_query_tasks.rake
|
101
|
+
- test/test_helper.rb
|
102
|
+
- test/test_restful_query_can_query.rb
|
103
|
+
- test/test_restful_query_condition.rb
|
104
|
+
- test/test_restful_query_parser.rb
|
105
|
+
- test/test_restful_query_sort.rb
|
106
|
+
has_rdoc: true
|
107
|
+
homepage: http://code.quirkey.com/restful_query
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options:
|
110
|
+
- --main
|
111
|
+
- README.rdoc
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: "0"
|
119
|
+
version:
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: "0"
|
125
|
+
version:
|
126
|
+
requirements: []
|
127
|
+
|
128
|
+
rubyforge_project: quirkey
|
129
|
+
rubygems_version: 1.3.1
|
130
|
+
signing_key:
|
131
|
+
specification_version: 2
|
132
|
+
summary: Simple ActiveRecord queries from a RESTful and safe interface
|
133
|
+
test_files:
|
134
|
+
- test/test_helper.rb
|
135
|
+
- test/test_restful_query_can_query.rb
|
136
|
+
- test/test_restful_query_condition.rb
|
137
|
+
- test/test_restful_query_parser.rb
|
138
|
+
- test/test_restful_query_sort.rb
|