query_string_interface 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +16 -0
- data/Gemfile.lock +61 -0
- data/MIT_LICENSE +20 -0
- data/README.rdoc +44 -0
- data/lib/query_string_interface.rb +69 -0
- data/lib/query_string_interface/filter.rb +148 -0
- data/lib/query_string_interface/filter_collection.rb +73 -0
- data/lib/query_string_interface/helpers.rb +22 -0
- data/lib/query_string_interface/parsers.rb +5 -0
- data/lib/query_string_interface/parsers/array_parser.rb +34 -0
- data/lib/query_string_interface/parsers/boolean_and_nil_parser.rb +28 -0
- data/lib/query_string_interface/parsers/date_time_parser.rb +15 -0
- data/lib/query_string_interface/parsers/number_parser.rb +26 -0
- data/lib/query_string_interface/parsers/regex_parser.rb +15 -0
- data/lib/query_string_interface/sorting_filters.rb +51 -0
- data/lib/query_string_interface/version.rb +3 -0
- metadata +93 -0
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
gem "rake", "0.8.7"
|
6
|
+
gem "i18n"
|
7
|
+
|
8
|
+
platforms :mri_18 do
|
9
|
+
gem "ruby-debug"
|
10
|
+
end
|
11
|
+
|
12
|
+
platforms :mri_19 do
|
13
|
+
gem "ruby-debug19", :require => 'ruby-debug' if RUBY_VERSION < "1.9.3"
|
14
|
+
gem 'simplecov', :require => false
|
15
|
+
end
|
16
|
+
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
query_string_interface (0.1.0)
|
5
|
+
activesupport (>= 3.0.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activesupport (3.1.0)
|
11
|
+
multi_json (~> 1.0)
|
12
|
+
archive-tar-minitar (0.5.2)
|
13
|
+
columnize (0.3.4)
|
14
|
+
diff-lcs (1.1.3)
|
15
|
+
i18n (0.6.0)
|
16
|
+
linecache (0.46)
|
17
|
+
rbx-require-relative (> 0.0.4)
|
18
|
+
linecache19 (0.5.12)
|
19
|
+
ruby_core_source (>= 0.1.4)
|
20
|
+
multi_json (1.0.3)
|
21
|
+
rake (0.8.7)
|
22
|
+
rbx-require-relative (0.0.5)
|
23
|
+
rspec (2.6.0)
|
24
|
+
rspec-core (~> 2.6.0)
|
25
|
+
rspec-expectations (~> 2.6.0)
|
26
|
+
rspec-mocks (~> 2.6.0)
|
27
|
+
rspec-core (2.6.4)
|
28
|
+
rspec-expectations (2.6.0)
|
29
|
+
diff-lcs (~> 1.1.2)
|
30
|
+
rspec-mocks (2.6.0)
|
31
|
+
ruby-debug (0.10.4)
|
32
|
+
columnize (>= 0.1)
|
33
|
+
ruby-debug-base (~> 0.10.4.0)
|
34
|
+
ruby-debug-base (0.10.4)
|
35
|
+
linecache (>= 0.3)
|
36
|
+
ruby-debug-base19 (0.11.25)
|
37
|
+
columnize (>= 0.3.1)
|
38
|
+
linecache19 (>= 0.5.11)
|
39
|
+
ruby_core_source (>= 0.1.4)
|
40
|
+
ruby-debug19 (0.11.6)
|
41
|
+
columnize (>= 0.3.1)
|
42
|
+
linecache19 (>= 0.5.11)
|
43
|
+
ruby-debug-base19 (>= 0.11.19)
|
44
|
+
ruby_core_source (0.1.5)
|
45
|
+
archive-tar-minitar (>= 0.5.2)
|
46
|
+
simplecov (0.5.3)
|
47
|
+
multi_json (~> 1.0.3)
|
48
|
+
simplecov-html (~> 0.5.3)
|
49
|
+
simplecov-html (0.5.3)
|
50
|
+
|
51
|
+
PLATFORMS
|
52
|
+
ruby
|
53
|
+
|
54
|
+
DEPENDENCIES
|
55
|
+
i18n
|
56
|
+
query_string_interface!
|
57
|
+
rake (= 0.8.7)
|
58
|
+
rspec (>= 2.6.0)
|
59
|
+
ruby-debug
|
60
|
+
ruby-debug19
|
61
|
+
simplecov
|
data/MIT_LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Vicente Mundim
|
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.rdoc
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
= Overview
|
2
|
+
|
3
|
+
== About QueryStringInterace
|
4
|
+
|
5
|
+
This gem extracts params given as a hash to structured data, that can be used when creating queries
|
6
|
+
|
7
|
+
== Repository
|
8
|
+
|
9
|
+
http://github.com/vicentemundim/query_string_interface
|
10
|
+
|
11
|
+
= Installing
|
12
|
+
|
13
|
+
This is a gem, so you can install it by:
|
14
|
+
|
15
|
+
sudo gem install query_string_interface
|
16
|
+
|
17
|
+
Or, if you are using rails, put this in your Gemfile:
|
18
|
+
|
19
|
+
gem 'query_string_interface'
|
20
|
+
|
21
|
+
= Usage
|
22
|
+
|
23
|
+
To use it, just extend QueryStringInterface in your document model:
|
24
|
+
|
25
|
+
class Document
|
26
|
+
include Mongoid::Document
|
27
|
+
extend QueryInterfaceString
|
28
|
+
|
29
|
+
# ... add fields here
|
30
|
+
end
|
31
|
+
|
32
|
+
Then, you can use some class methods with your ORM syntax:
|
33
|
+
|
34
|
+
def self.filter_by(params)
|
35
|
+
where(filtering_options(params)).order_by(*sorting_options(params))
|
36
|
+
end
|
37
|
+
|
38
|
+
= ORM Adapters
|
39
|
+
|
40
|
+
http://github.com/vicentemundim/mongoid_query_string_interface
|
41
|
+
|
42
|
+
= Credits
|
43
|
+
|
44
|
+
Vicente Mundim: vicente.mundim at gmail dot com
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "active_support/json"
|
2
|
+
require "active_support/core_ext/hash/indifferent_access"
|
3
|
+
|
4
|
+
module QueryStringInterface
|
5
|
+
NORMAL_CONDITIONAL_OPERATORS = [:exists, :gte, :gt, :lte, :lt, :ne, :size, :near, :within]
|
6
|
+
ARRAY_CONDITIONAL_OPERATORS = [:all, :in, :nin]
|
7
|
+
CONDITIONAL_OPERATORS = ARRAY_CONDITIONAL_OPERATORS + NORMAL_CONDITIONAL_OPERATORS
|
8
|
+
SORTING_OPERATORS = [:asc, :desc]
|
9
|
+
OR_OPERATOR = :or
|
10
|
+
|
11
|
+
OPERATORS = CONDITIONAL_OPERATORS + SORTING_OPERATORS + [OR_OPERATOR]
|
12
|
+
|
13
|
+
|
14
|
+
ATTRIBUTE_REGEX = /^(.*)\.(#{(OPERATORS).join('|')})$/
|
15
|
+
OPERATOR_REGEX = /^.*\.(#{CONDITIONAL_OPERATORS.join('|')})$/
|
16
|
+
|
17
|
+
ORDER_BY_PARAMETER = :order_by
|
18
|
+
PAGINATION_PARAMTERS = [:per_page, :page]
|
19
|
+
FRAMEWORK_PARAMETERS = [:controller, :action, :format]
|
20
|
+
CONTROL_PARAMETERS = [:disable_default_filters]
|
21
|
+
RESERVED_PARAMETERS = FRAMEWORK_PARAMETERS + PAGINATION_PARAMTERS + [ORDER_BY_PARAMETER] + CONTROL_PARAMETERS
|
22
|
+
|
23
|
+
def default_filtering_options
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_sorting_options
|
28
|
+
[]
|
29
|
+
end
|
30
|
+
|
31
|
+
def default_pagination_options
|
32
|
+
{ :per_page => 12, :page => 1 }
|
33
|
+
end
|
34
|
+
|
35
|
+
def sorting_attributes_to_replace
|
36
|
+
{}
|
37
|
+
end
|
38
|
+
|
39
|
+
def filtering_attributes_to_replace
|
40
|
+
{}
|
41
|
+
end
|
42
|
+
|
43
|
+
def pagination_options(options={})
|
44
|
+
default_pagination_options.with_indifferent_access.merge(options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def filtering_options(options={})
|
48
|
+
QueryStringInterface::FilterCollection.new(
|
49
|
+
only_filtering(options),
|
50
|
+
options.has_key?(:disable_default_filters) ? {} : default_filtering_options,
|
51
|
+
filtering_attributes_to_replace
|
52
|
+
).parse
|
53
|
+
end
|
54
|
+
|
55
|
+
def sorting_options(options={})
|
56
|
+
QueryStringInterface::SortingFilters.new(options, default_sorting_options, sorting_attributes_to_replace).parse
|
57
|
+
end
|
58
|
+
|
59
|
+
def only_filtering(options={})
|
60
|
+
options.with_indifferent_access.except(*RESERVED_PARAMETERS)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
require "query_string_interface/version"
|
65
|
+
require "query_string_interface/helpers"
|
66
|
+
require "query_string_interface/parsers"
|
67
|
+
require "query_string_interface/filter"
|
68
|
+
require "query_string_interface/filter_collection"
|
69
|
+
require "query_string_interface/sorting_filters"
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module QueryStringInterface
|
5
|
+
class Filter
|
6
|
+
include QueryStringInterface::Helpers
|
7
|
+
|
8
|
+
attr_reader :raw_attribute, :raw_value
|
9
|
+
|
10
|
+
PARSERS = [
|
11
|
+
QueryStringInterface::Parsers::ArrayParser.new,
|
12
|
+
QueryStringInterface::Parsers::DateTimeParser.new,
|
13
|
+
QueryStringInterface::Parsers::NumberParser.new,
|
14
|
+
QueryStringInterface::Parsers::RegexParser.new,
|
15
|
+
QueryStringInterface::Parsers::BooleanAndNilParser.new
|
16
|
+
]
|
17
|
+
|
18
|
+
def initialize(raw_attribute, raw_value, attributes_to_replace={}, raw_params={})
|
19
|
+
@raw_attribute = raw_attribute
|
20
|
+
@raw_value = raw_value
|
21
|
+
@attributes_to_replace = attributes_to_replace
|
22
|
+
@raw_params = raw_params
|
23
|
+
end
|
24
|
+
|
25
|
+
def attribute
|
26
|
+
@attribute ||= replaced_attribute_name(parsed_attribute, @attributes_to_replace).to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
def value
|
30
|
+
@value ||= expanded_value
|
31
|
+
end
|
32
|
+
|
33
|
+
def operator
|
34
|
+
@operator ||= operator_from(raw_attribute)
|
35
|
+
end
|
36
|
+
|
37
|
+
def or_attribute?
|
38
|
+
raw_attribute == 'or'
|
39
|
+
end
|
40
|
+
|
41
|
+
def include?(other_filter)
|
42
|
+
if or_attribute?
|
43
|
+
json_value.any? do |filters|
|
44
|
+
filters.filter_parsers.any? do |filter_parser|
|
45
|
+
filter_parser.attribute == other_filter.attribute &&
|
46
|
+
conditional_array_operators.include?(filter_parser.operator) &&
|
47
|
+
filter_parser.operator == other_filter.operator
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def merge(other_filter)
|
54
|
+
if or_attribute?
|
55
|
+
@value = json_value.map do |filters|
|
56
|
+
filters.filter_parsers << other_filter
|
57
|
+
filters.parse
|
58
|
+
end
|
59
|
+
elsif conditional_array_operators.include?(other_filter.operator) && operator == other_filter.operator
|
60
|
+
@value = value.inject({}) do |result, filter|
|
61
|
+
filter_operation, filter_value = filter
|
62
|
+
filter_value = filter_value.concat(other_filter.value[filter_operation]) if other_filter.value[filter_operation]
|
63
|
+
result[filter_operation] = filter_value
|
64
|
+
result
|
65
|
+
end
|
66
|
+
else
|
67
|
+
@value = value.merge(other_filter.value)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
def parsed_attribute
|
73
|
+
if raw_attribute.respond_to?(:key)
|
74
|
+
raw_attribute.key.to_s
|
75
|
+
elsif or_attribute?
|
76
|
+
'$or'
|
77
|
+
elsif raw_attribute =~ QueryStringInterface::ATTRIBUTE_REGEX
|
78
|
+
$1
|
79
|
+
else
|
80
|
+
raw_attribute
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def expanded_value
|
85
|
+
if operator
|
86
|
+
if or_attribute?
|
87
|
+
parsed_json_value
|
88
|
+
else
|
89
|
+
{ operator => replaced_attribute_value(parsed_attribute, parsed_value, @attributes_to_replace, @raw_params) }
|
90
|
+
end
|
91
|
+
else
|
92
|
+
replaced_attribute_value(parsed_attribute, parsed_value, @attributes_to_replace, @raw_params)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def parsed_value
|
97
|
+
if raw_value.is_a?(String)
|
98
|
+
PARSERS.each do |parser|
|
99
|
+
return parser.parse(unescaped_raw_value) if parser.parseable?(unescaped_raw_value, operator)
|
100
|
+
end
|
101
|
+
|
102
|
+
return nil
|
103
|
+
else
|
104
|
+
raw_value
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def parsed_json_value
|
109
|
+
if unescaped_raw_value.is_a?(String)
|
110
|
+
json_value.map(&:parse)
|
111
|
+
else
|
112
|
+
unescaped_raw_value
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def json_value
|
117
|
+
raw_or_data = ActiveSupport::JSON.decode(unescaped_raw_value)
|
118
|
+
|
119
|
+
raise "$or query filters must be given as an array of hashes" unless valid_or_filters?(raw_or_data)
|
120
|
+
|
121
|
+
raw_or_data.map do |filters|
|
122
|
+
FilterCollection.new(filters, {}, @attributes_to_replace, @raw_params)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def valid_or_filters?(raw_or_data)
|
127
|
+
raw_or_data.is_a?(Array) and raw_or_data.all? { |item| item.is_a?(Hash) }
|
128
|
+
end
|
129
|
+
|
130
|
+
def unescaped_raw_value
|
131
|
+
@unescaped_raw_value ||= raw_value.is_a?(String) ? CGI.unescape(raw_value) : raw_value
|
132
|
+
end
|
133
|
+
|
134
|
+
def conditional_array_operators
|
135
|
+
QueryStringInterface::ARRAY_CONDITIONAL_OPERATORS.map { |o| "$#{o}" }
|
136
|
+
end
|
137
|
+
|
138
|
+
def operator_from(attribute)
|
139
|
+
if attribute.respond_to?(:operator)
|
140
|
+
"$#{attribute.operator}"
|
141
|
+
elsif or_attribute?
|
142
|
+
'$or'
|
143
|
+
elsif attribute =~ QueryStringInterface::OPERATOR_REGEX
|
144
|
+
"$#{$1}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module QueryStringInterface
|
2
|
+
class FilterCollection
|
3
|
+
include QueryStringInterface::Helpers
|
4
|
+
|
5
|
+
attr_reader :filters, :default_filters
|
6
|
+
|
7
|
+
def initialize(filters, default_filters={}, attributes_to_replace={}, raw_filters=nil)
|
8
|
+
@filters = filters.with_indifferent_access
|
9
|
+
@default_filters = default_filters.with_indifferent_access
|
10
|
+
@attributes_to_replace = attributes_to_replace.with_indifferent_access
|
11
|
+
@raw_filters = raw_filters.nil? ? @filters : raw_filters.with_indifferent_access
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse
|
15
|
+
result = default_filters.inject({}) do |result, item|
|
16
|
+
raw_attribute, raw_value = item
|
17
|
+
result[replaced_attribute_name(raw_attribute, @attributes_to_replace).to_s] = replaced_attribute_value(raw_attribute, raw_value, @attributes_to_replace, @raw_filters)
|
18
|
+
result
|
19
|
+
end
|
20
|
+
result.merge(parsed_filters)
|
21
|
+
end
|
22
|
+
|
23
|
+
def filter_parsers
|
24
|
+
@filter_parsers ||= filters.map do |raw_attribute, raw_value|
|
25
|
+
Filter.new(raw_attribute, raw_value, @attributes_to_replace, @raw_filters)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def parsed_filters
|
32
|
+
filter_parsers_hash.inject({}) do |result, item|
|
33
|
+
attribute, filter_parser = item
|
34
|
+
|
35
|
+
result[attribute] = filter_parser.value
|
36
|
+
|
37
|
+
result
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def filter_parsers_hash
|
42
|
+
optimized_filter_parsers.inject({}) do |result, filter_parser|
|
43
|
+
if result.has_key?(filter_parser.attribute)
|
44
|
+
result[filter_parser.attribute].merge(filter_parser)
|
45
|
+
else
|
46
|
+
result[filter_parser.attribute] = filter_parser
|
47
|
+
end
|
48
|
+
|
49
|
+
result
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def optimized_filter_parsers
|
54
|
+
if or_filter_parser
|
55
|
+
filter_parsers.inject([]) do |result, filter_parser|
|
56
|
+
if filter_parser != or_filter_parser && or_filter_parser.include?(filter_parser)
|
57
|
+
or_filter_parser.merge(filter_parser)
|
58
|
+
else
|
59
|
+
result << filter_parser
|
60
|
+
end
|
61
|
+
|
62
|
+
result
|
63
|
+
end
|
64
|
+
else
|
65
|
+
filter_parsers
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def or_filter_parser
|
70
|
+
@or_filter_parser ||= filter_parsers.select { |filter_parser| filter_parser.or_attribute? }.first
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module QueryStringInterface
|
2
|
+
module Helpers
|
3
|
+
def hash_with_indifferent_access(params)
|
4
|
+
params.is_a?(HashWithIndifferentAccess) ? params : params.with_indifferent_access
|
5
|
+
end
|
6
|
+
|
7
|
+
def replace_attribute(attribute, hash_with_attributes_to_replace)
|
8
|
+
hash = hash_with_indifferent_access(hash_with_attributes_to_replace)
|
9
|
+
hash.has_key?(attribute) ? hash[attribute] : attribute
|
10
|
+
end
|
11
|
+
|
12
|
+
def replaced_attribute_name(attribute, hash_with_attributes_to_replace)
|
13
|
+
attribute = replace_attribute(attribute, hash_with_attributes_to_replace)
|
14
|
+
attribute.is_a?(Hash) ? attribute[:to] : attribute
|
15
|
+
end
|
16
|
+
|
17
|
+
def replaced_attribute_value(attribute, value, hash_with_attributes_to_replace, raw_params)
|
18
|
+
attribute = replace_attribute(attribute, hash_with_attributes_to_replace)
|
19
|
+
attribute.is_a?(Hash) ? attribute[:convert_value_to].call(value, raw_params) : value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
require "query_string_interface/parsers/date_time_parser"
|
2
|
+
require "query_string_interface/parsers/number_parser"
|
3
|
+
require "query_string_interface/parsers/array_parser"
|
4
|
+
require "query_string_interface/parsers/regex_parser"
|
5
|
+
require "query_string_interface/parsers/boolean_and_nil_parser"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module QueryStringInterface
|
2
|
+
module Parsers
|
3
|
+
class ArrayParser
|
4
|
+
ARRAY_SEPARATOR = '|'
|
5
|
+
|
6
|
+
def parseable?(value, operator)
|
7
|
+
operator && conditional_operators.include?(operator)
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(value)
|
11
|
+
value.split(ARRAY_SEPARATOR).map(&:strip).map do |item|
|
12
|
+
parse_item(item)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def parse_item(item)
|
18
|
+
other_parsers.each do |parser|
|
19
|
+
return parser.parse(item) if parser.parseable?(item, '')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def other_parsers
|
24
|
+
@other_parsers ||= QueryStringInterface::Filter::PARSERS.reject do |parser|
|
25
|
+
parser.is_a?(ArrayParser)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def conditional_operators
|
30
|
+
@conditional_operators ||= QueryStringInterface::ARRAY_CONDITIONAL_OPERATORS.map { |o| "$#{o}" }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module QueryStringInterface
|
2
|
+
module Parsers
|
3
|
+
class BooleanAndNilParser
|
4
|
+
def parseable?(value, operator)
|
5
|
+
!value.nil? && !value.empty?
|
6
|
+
end
|
7
|
+
|
8
|
+
def parse(value)
|
9
|
+
if boolean?(value)
|
10
|
+
value.strip == 'true'
|
11
|
+
elsif value.nil? or value.empty? or nil_value?(value)
|
12
|
+
nil
|
13
|
+
else
|
14
|
+
value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def boolean?(value)
|
20
|
+
value && ['true', 'false'].include?(value.strip)
|
21
|
+
end
|
22
|
+
|
23
|
+
def nil_value?(value)
|
24
|
+
value && ['nil', 'null'].include?(value.strip)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module QueryStringInterface
|
2
|
+
module Parsers
|
3
|
+
class DateTimeParser
|
4
|
+
DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?([ \t]*)(Z?|[-+]\d{2}?(:?\d{2})?))$/
|
5
|
+
|
6
|
+
def parseable?(value, operator)
|
7
|
+
DATE_REGEX.match(value)
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(value)
|
11
|
+
Time.parse(value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module QueryStringInterface
|
2
|
+
module Parsers
|
3
|
+
class NumberParser
|
4
|
+
def parseable?(value, operator)
|
5
|
+
integer?(value) or float?(value)
|
6
|
+
end
|
7
|
+
|
8
|
+
def parse(value)
|
9
|
+
if integer?(value)
|
10
|
+
value.to_i
|
11
|
+
elsif float?(value)
|
12
|
+
value.to_f
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def integer?(value)
|
18
|
+
value =~ /^\d+$/
|
19
|
+
end
|
20
|
+
|
21
|
+
def float?(value)
|
22
|
+
value =~ /^(\d+)(\.?\d*)$/
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module QueryStringInterface
|
2
|
+
class SortingFilters
|
3
|
+
include QueryStringInterface::Helpers
|
4
|
+
|
5
|
+
SORTING_FIELD_REGEXP = /(.*)\.(#{QueryStringInterface::SORTING_OPERATORS.join('|')})/
|
6
|
+
|
7
|
+
def initialize(raw_filters, default_filters, attributes_to_replace={})
|
8
|
+
@raw_filters = raw_filters.with_indifferent_access
|
9
|
+
@default_filters = default_filters
|
10
|
+
@attributes_to_replace = attributes_to_replace
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse
|
14
|
+
if @raw_filters.has_key?('order_by')
|
15
|
+
parse_filters(@raw_filters['order_by'])
|
16
|
+
else
|
17
|
+
parse_filters(@default_filters)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse_filters(filters)
|
22
|
+
filters = filters.split('|') if filters.is_a?(String)
|
23
|
+
filters.map do |filter|
|
24
|
+
replace(filter)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def replace(filter)
|
30
|
+
if filter.respond_to?(:key) && filter.respond_to?(:operator)
|
31
|
+
replaced_item_for filter.key, filter.operator
|
32
|
+
elsif filter.is_a?(String) or filter.is_a?(Symbol)
|
33
|
+
if match = matches?(filter)
|
34
|
+
replaced_item_for match[1], match[2]
|
35
|
+
else
|
36
|
+
replaced_item_for filter, :asc
|
37
|
+
end
|
38
|
+
elsif filter.is_a?(Hash)
|
39
|
+
replaced_item_for filter.keys.first, filter[filter.keys.first]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def replaced_item_for(key, value)
|
44
|
+
{ replace_attribute(key, @attributes_to_replace).to_sym => value.to_sym }
|
45
|
+
end
|
46
|
+
|
47
|
+
def matches?(filter)
|
48
|
+
filter.match(SORTING_FIELD_REGEXP)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: query_string_interface
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Vicente Mundim
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-09-26 00:00:00 -03:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: activesupport
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 3.0.0
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 2.6.0
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
description: This gem extracts params given as a hash to structured data, that can be used when creating queries
|
39
|
+
email:
|
40
|
+
- vicente.mundim@gmail.com
|
41
|
+
executables: []
|
42
|
+
|
43
|
+
extensions: []
|
44
|
+
|
45
|
+
extra_rdoc_files: []
|
46
|
+
|
47
|
+
files:
|
48
|
+
- lib/query_string_interface/filter.rb
|
49
|
+
- lib/query_string_interface/filter_collection.rb
|
50
|
+
- lib/query_string_interface/helpers.rb
|
51
|
+
- lib/query_string_interface/parsers/array_parser.rb
|
52
|
+
- lib/query_string_interface/parsers/boolean_and_nil_parser.rb
|
53
|
+
- lib/query_string_interface/parsers/date_time_parser.rb
|
54
|
+
- lib/query_string_interface/parsers/number_parser.rb
|
55
|
+
- lib/query_string_interface/parsers/regex_parser.rb
|
56
|
+
- lib/query_string_interface/parsers.rb
|
57
|
+
- lib/query_string_interface/sorting_filters.rb
|
58
|
+
- lib/query_string_interface/version.rb
|
59
|
+
- lib/query_string_interface.rb
|
60
|
+
- MIT_LICENSE
|
61
|
+
- README.rdoc
|
62
|
+
- Gemfile
|
63
|
+
- Gemfile.lock
|
64
|
+
has_rdoc: true
|
65
|
+
homepage: ""
|
66
|
+
licenses: []
|
67
|
+
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: "0"
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: "0"
|
85
|
+
requirements: []
|
86
|
+
|
87
|
+
rubyforge_project: query_string_interface
|
88
|
+
rubygems_version: 1.6.2
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: Extracts query string params to a structured data, suitable to use for model queries
|
92
|
+
test_files: []
|
93
|
+
|