restful_query 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|