muster 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +132 -0
- data/Rakefile +9 -0
- data/lib/muster.rb +3 -0
- data/lib/muster/rack.rb +72 -0
- data/lib/muster/strategies.rb +6 -0
- data/lib/muster/strategies/active_record.rb +118 -0
- data/lib/muster/strategies/filter_expression.rb +142 -0
- data/lib/muster/strategies/hash.rb +93 -0
- data/lib/muster/strategies/pagination.rb +111 -0
- data/lib/muster/strategies/rack.rb +89 -0
- data/lib/muster/strategies/sort_expression.rb +75 -0
- data/lib/muster/version.rb +4 -0
- data/muster.gemspec +24 -0
- data/spec/muster/rack_spec.rb +37 -0
- data/spec/muster/strategies/active_record_spec.rb +137 -0
- data/spec/muster/strategies/filter_expression_spec.rb +126 -0
- data/spec/muster/strategies/hash_spec.rb +90 -0
- data/spec/muster/strategies/pagination_spec.rb +74 -0
- data/spec/muster/strategies/sort_expression_spec.rb +94 -0
- data/spec/spec_helper.rb +8 -0
- metadata +134 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'active_support/core_ext/array/wrap'
|
3
|
+
require 'muster/strategies/rack'
|
4
|
+
|
5
|
+
module Muster
|
6
|
+
module Strategies
|
7
|
+
|
8
|
+
# Query string parsing strategy with additional value handling options for separating values and uniqueness
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
#
|
12
|
+
# strategy = Muster::Strategies::Hash.new(:unique_values => true, :value_separator => ',')
|
13
|
+
# results = strategy.parse('name=value&choices=1,2,1') #=> { 'name' => 'value', 'choices' => ['1', '2'] }
|
14
|
+
class Hash < Muster::Strategies::Rack
|
15
|
+
|
16
|
+
# @attribute [r] value_separator
|
17
|
+
# @return [String,RegEx] when specified, each field value will be split into multiple values using the specified separator
|
18
|
+
attr_reader :value_separator
|
19
|
+
|
20
|
+
# @attribute [r] unique_values
|
21
|
+
# @return [Boolean] when specified, ensures a fields values do not contain duplicates
|
22
|
+
attr_reader :unique_values
|
23
|
+
|
24
|
+
# Create a new Hash parsing strategy
|
25
|
+
#
|
26
|
+
# @param [Hash] options the options available for this method
|
27
|
+
# @option options [optional,Array<Symbol>] :fields when specified, only parse the specified fields
|
28
|
+
# You may also use :field if you only intend to pass one field
|
29
|
+
# @option options [optional,String,RegEx] :value_separator (/,\s*/) when specified, splits the field value into multiple values
|
30
|
+
# You may pass the separator as a string or a regular expression
|
31
|
+
# @option options [optional,Boolean] :unique_values (true) when true, ensures field values do not contain duplicates
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
#
|
35
|
+
# strategy = Muster::Strategies::Hash.new(:fields => [:name, :state], :value_separator => '|')
|
36
|
+
# strategy = Muster::Strategies::Hash.new(:unique_values => false)
|
37
|
+
def initialize( options={} )
|
38
|
+
super
|
39
|
+
|
40
|
+
@unique_values = self.options.fetch(:unique_values, true)
|
41
|
+
@value_separator = self.options.fetch(:value_separator, /,\s*/)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Processes a query string and returns a hash of its fields/values
|
45
|
+
#
|
46
|
+
# @param query_string [String] the query string to parse
|
47
|
+
#
|
48
|
+
# @return [Hash]
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
#
|
52
|
+
# results = strategy.parse('name=value&choices=1,2') #=> { 'name' => 'value', 'choices' => ['1', '2'] }
|
53
|
+
def parse( query_string )
|
54
|
+
parameters = super
|
55
|
+
|
56
|
+
parameters.each do |key, value|
|
57
|
+
if self.value_separator.present?
|
58
|
+
parameters[key] = self.separate_values(value)
|
59
|
+
end
|
60
|
+
|
61
|
+
if self.unique_values == true && value.instance_of?(Array)
|
62
|
+
parameters[key].uniq!
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
return parameters
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
# Separates values into an Array of values using :values_separator
|
72
|
+
#
|
73
|
+
# @param value [String,Array] the original query string field value to separate
|
74
|
+
#
|
75
|
+
# @return [String,Array] String if a single value exists, Array otherwise
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
#
|
79
|
+
# value = self.separate_values('1') #=> '1'
|
80
|
+
# value = self.separate_values('1,2') #=> ['1', '2']
|
81
|
+
def separate_values( value )
|
82
|
+
values = Array.wrap(value)
|
83
|
+
|
84
|
+
values = values.map do |value|
|
85
|
+
value.split(self.value_separator)
|
86
|
+
end.flatten
|
87
|
+
|
88
|
+
return (values.size > 1) ? values : value
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'active_support/core_ext/hash/slice'
|
2
|
+
require 'muster/strategies/hash'
|
3
|
+
|
4
|
+
module Muster
|
5
|
+
module Strategies
|
6
|
+
|
7
|
+
# Query string parsing strategy with logic to handle pagination options
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# strategy = Muster::Strategies::Pagination.new
|
12
|
+
# results = strategy.parse('page=3&per_page=10') #=> { 'pagination' => {'page' => 3, 'per_page' => 10}, 'limit' => 10, 'offset' => 20 }
|
13
|
+
class Pagination < Muster::Strategies::Rack
|
14
|
+
|
15
|
+
# @attribute [r] default_page_size
|
16
|
+
# @return [Fixnum] when specified, will override the default page size of 30 when no page_size is parsed
|
17
|
+
attr_accessor :default_page_size
|
18
|
+
|
19
|
+
# Create a new Pagination parsing strategy
|
20
|
+
#
|
21
|
+
# @param [Hash] options the options available for this method
|
22
|
+
# @option options [optional,Array<Symbol>] :fields when specified, only parse the specified fields
|
23
|
+
# You may also use :field if you only intend to pass one field
|
24
|
+
# @option options [optional,String,RegEx] :value_separator (/,\s*/) when specified, splits the field value into multiple values
|
25
|
+
# You may pass the separator as a string or a regular expression
|
26
|
+
# @option options [optional,Boolean] :unique_values (true) when true, ensures field values do not contain duplicates
|
27
|
+
# @option options [options,Fixnum] :default_page_size (30) when specified, the default page size to use when no page size is parsed
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
#
|
31
|
+
# strategy = Muster::Strategies::Pagination.new
|
32
|
+
# strategy = Muster::Strategies::Pagination.new(:default_page_size => 10)
|
33
|
+
def initialize( options={} )
|
34
|
+
super
|
35
|
+
|
36
|
+
self.default_page_size = self.options[:default_page_size].to_i
|
37
|
+
if self.default_page_size < 1
|
38
|
+
self.default_page_size = 30
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Processes a query string and returns a hash of its fields/values
|
43
|
+
#
|
44
|
+
# @param query_string [String] the query string to parse
|
45
|
+
#
|
46
|
+
# @return [Hash]
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
#
|
50
|
+
# results = strategy.parse('page=3&per_page=10') #=> { 'pagination' => {'page' => 3, 'per_page' => 10}, 'limit' => 10, 'offset' => 20 }
|
51
|
+
def parse( query_string )
|
52
|
+
parameters = self.parse_query_string(query_string)
|
53
|
+
|
54
|
+
page = self.parse_page(parameters)
|
55
|
+
page_size = self.parse_page_size(parameters)
|
56
|
+
|
57
|
+
|
58
|
+
offset = (page - 1) * page_size
|
59
|
+
offset = nil if offset < 1
|
60
|
+
|
61
|
+
parameters = parameters.merge(:pagination => {:page => page, :per_page => page_size}, :limit => page_size, :offset => offset)
|
62
|
+
|
63
|
+
if self.fields.present?
|
64
|
+
parameters = parameters.slice(*self.fields).with_indifferent_access
|
65
|
+
end
|
66
|
+
|
67
|
+
parameters
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
# Returns the current page for the current query string.
|
73
|
+
#
|
74
|
+
# If page is not specified, or is not a positive number, 1 will be returned instead.
|
75
|
+
#
|
76
|
+
# @param parameters [Hash] the parameters parsed from the query string
|
77
|
+
#
|
78
|
+
# @return [Fixnum]
|
79
|
+
#
|
80
|
+
# @example
|
81
|
+
#
|
82
|
+
# page = self.parse_page(:page => 2) #=> 2
|
83
|
+
# page = self.parse_page(:page => nil) #=> 1
|
84
|
+
def parse_page( parameters )
|
85
|
+
page = parameters.delete(:page).to_i
|
86
|
+
page = 1 unless page > 0
|
87
|
+
page
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns the page size for the current query string.
|
91
|
+
#
|
92
|
+
# If per_page or page_size is not specified, or is not a positive number, :default_page_size will be returned instead.
|
93
|
+
#
|
94
|
+
# @param parameters [Hash] the parameters parsed from the query string
|
95
|
+
#
|
96
|
+
# @return [Fixnum]
|
97
|
+
#
|
98
|
+
# @example
|
99
|
+
#
|
100
|
+
# page_size = self.parse_page(:page_size => 10) #=> 10
|
101
|
+
# page_size = self.parse_page(:per_page => 10) #=> 10
|
102
|
+
# page_size = self.parse_page(:per_page => nil) #=> 30
|
103
|
+
def parse_page_size( parameters )
|
104
|
+
page_size = (parameters.delete(:page_size) || parameters.delete(:per_page)).to_i
|
105
|
+
page_size = self.default_page_size unless page_size > 0
|
106
|
+
page_size
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
3
|
+
require 'active_support/core_ext/array/wrap'
|
4
|
+
require 'rack/utils'
|
5
|
+
|
6
|
+
module Muster
|
7
|
+
module Strategies
|
8
|
+
|
9
|
+
# Query string parsing strategy based on Rack::Utils#parse_query
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
#
|
13
|
+
# strategy = Muster::Strategies::Rack.new
|
14
|
+
# results = strategy.parse('name=value&choices=1&choices=2') #=> { 'name' => 'value', 'choices' => ['1', '2'] }
|
15
|
+
class Rack
|
16
|
+
|
17
|
+
# @attribute [r] options
|
18
|
+
# @return [Hash] options specified during initialization
|
19
|
+
attr_reader :options
|
20
|
+
|
21
|
+
# @attribute [r] fields
|
22
|
+
# @return [Hash] list of fields to parse, ignoring all others
|
23
|
+
attr_reader :fields
|
24
|
+
|
25
|
+
# Create a new Rack parsing strategy
|
26
|
+
#
|
27
|
+
# @param [Hash] options the options available for this method
|
28
|
+
# @option options [optional,Array<Symbol>] :fields when specified, only parse the specified fields
|
29
|
+
# You may also use :field if you only intend to pass one field
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
#
|
33
|
+
# strategy = Muster::Strategies::Rack.new(:fields => [:name, :state])
|
34
|
+
# strategy = Muster::Strategies::Rack.new(:field => :name)
|
35
|
+
def initialize( options={} )
|
36
|
+
@options = options.with_indifferent_access
|
37
|
+
|
38
|
+
@fields = Array.wrap(@options[:field] || @options[:fields])
|
39
|
+
@fields.map!{ |field| field.to_sym }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Processes a query string and returns a hash of its fields/values
|
43
|
+
#
|
44
|
+
# @param query_string [String] the query string to parse
|
45
|
+
#
|
46
|
+
# @return [Hash]
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
#
|
50
|
+
# results = strategy.parse('name=value&choices=1&choices=1') #=> { 'name' => 'value', 'choices' => ['1', '2'] }
|
51
|
+
def parse( query_string )
|
52
|
+
self.fields_to_parse(query_string)
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
# Converts the query string into a hash for processing
|
58
|
+
#
|
59
|
+
# @param query_string [String] the query string being parsed
|
60
|
+
#
|
61
|
+
# @return [Hash]
|
62
|
+
#
|
63
|
+
# @example
|
64
|
+
#
|
65
|
+
# fields = self.parse_query_string('name=value&choices=1&choices=1') #=> { 'name' => 'value', 'choices' => ['1', '2'] }
|
66
|
+
def parse_query_string( query_string )
|
67
|
+
::Rack::Utils.parse_query(query_string).with_indifferent_access
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the list of fields to be parsed
|
71
|
+
#
|
72
|
+
# @param query_string [String] the query string to parse
|
73
|
+
#
|
74
|
+
# If the :fields option was specified, only those fields will be returned. Otherwise, all fields will be returned.
|
75
|
+
#
|
76
|
+
# @return [Hash]
|
77
|
+
def fields_to_parse( query_string )
|
78
|
+
fields = self.parse_query_string(query_string)
|
79
|
+
|
80
|
+
if self.fields.present?
|
81
|
+
fields = fields.select{ |key, value| self.fields.include?(key.to_sym) }
|
82
|
+
end
|
83
|
+
|
84
|
+
return fields.with_indifferent_access
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
2
|
+
require 'muster/strategies/hash'
|
3
|
+
|
4
|
+
module Muster
|
5
|
+
module Strategies
|
6
|
+
|
7
|
+
# Query string parsing strategy with additional value handling for sort orders
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# strategy = Muster::Strategies::SortExpression.new
|
12
|
+
# results = strategy.parse('sort=name:desc') #=> { 'sort' => 'name desc' }
|
13
|
+
class SortExpression < Muster::Strategies::Hash
|
14
|
+
|
15
|
+
# Processes a query string and returns a hash of its fields/values
|
16
|
+
#
|
17
|
+
# @param query_string [String] the query string to parse
|
18
|
+
#
|
19
|
+
# @return [Hash]
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
#
|
23
|
+
# results = strategy.parse('order=name') #=> { 'order' => 'name asc' }
|
24
|
+
# results = strategy.parse('order=name:desc') #=> { 'order' => 'name desc' }
|
25
|
+
# results = strategy.parse('order=name,date:desc') #=> { 'order' => ['name asc', 'date desc'] }
|
26
|
+
def parse( query_string )
|
27
|
+
parameters = super
|
28
|
+
|
29
|
+
parameters.each do |key, value|
|
30
|
+
parameters[key] = self.parse_sort_expression(value)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
# Separates the values into their field and direction
|
37
|
+
#
|
38
|
+
# @param value [String] the value being parsed
|
39
|
+
#
|
40
|
+
# @return [String,Arrary] String if a single value, Array otherwise
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
#
|
44
|
+
# value = self.parse_sort_expression('name:asc') #=> 'name asc'
|
45
|
+
# value = self.parse_sort_expression(['name:asc', 'date']) #=> ['name asc', 'date asc']
|
46
|
+
def parse_sort_expression( value )
|
47
|
+
values = Array.wrap(value)
|
48
|
+
|
49
|
+
values = values.map do |value|
|
50
|
+
name, direction = value.split(':', 2)
|
51
|
+
direction = self.parse_direction(direction)
|
52
|
+
|
53
|
+
"#{name} #{direction}"
|
54
|
+
end.flatten
|
55
|
+
|
56
|
+
return (values.size > 1) ? values : values.first
|
57
|
+
end
|
58
|
+
|
59
|
+
# Parse and normalize the sot expression direction
|
60
|
+
#
|
61
|
+
# @param direction [String] the direction to normalize
|
62
|
+
#
|
63
|
+
# @return [String]
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
#
|
67
|
+
# direction = self.parse_direction('ascending') #=> 'asc'
|
68
|
+
# direction = self.parse_direction('descending') #=> 'desc'
|
69
|
+
def parse_direction( direction )
|
70
|
+
direction.to_s.match(/^desc/i) ? 'desc' : 'asc'
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/muster.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/muster/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Christopher H. Laco"]
|
6
|
+
gem.email = ["claco@chrislaco.com"]
|
7
|
+
gem.description = %q{Muster is a gem that turns query string options in varying formats into data structures suitable for use in AR scopes and queryies.}
|
8
|
+
gem.summary = %q{Muster various query string options into AR query compatable options.}
|
9
|
+
gem.homepage = "https://github.com/claco/muster"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "muster"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Muster::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'activesupport', '~> 3.0'
|
19
|
+
gem.add_dependency 'rack', '~> 1.4'
|
20
|
+
|
21
|
+
gem.add_development_dependency 'rspec', '~> 2.10.0'
|
22
|
+
gem.add_development_dependency 'redcarpet', '~> 2.1'
|
23
|
+
gem.add_development_dependency 'yard', '~> 0.8.2'
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Muster::Rack do
|
4
|
+
let(:application) { lambda{|env| [200, {'Content-Type' => 'text/plain'}, '']} }
|
5
|
+
let(:environment) { Rack::MockRequest.env_for('/?name=value&order=name') }
|
6
|
+
let(:options) { {} }
|
7
|
+
let(:middleware) { Muster::Rack.new(application, Muster::Strategies::Hash, options) }
|
8
|
+
|
9
|
+
it 'parse query string with strategy' do
|
10
|
+
middleware.call(environment)
|
11
|
+
|
12
|
+
environment[Muster::Rack::QUERY].should == {'name' => 'value', 'order' => 'name'}
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'accepts options for middlewhere' do
|
16
|
+
options[:field] = :name
|
17
|
+
|
18
|
+
middleware.call(environment)
|
19
|
+
|
20
|
+
environment[Muster::Rack::QUERY].should == {'name' => 'value'}
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'accepts a strategy instance' do
|
24
|
+
strategy = Muster::Strategies::Hash.new(:field => :name)
|
25
|
+
Muster::Rack.new(application, strategy).call(environment)
|
26
|
+
|
27
|
+
environment[Muster::Rack::QUERY].should == {'name' => 'value'}
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'merges multiple strategies into one result' do
|
31
|
+
Muster::Rack.new(application, Muster::Strategies::Hash, :field => :name).call(environment)
|
32
|
+
environment[Muster::Rack::QUERY].should == {'name' => 'value'}
|
33
|
+
|
34
|
+
Muster::Rack.new(application, Muster::Strategies::Hash, :field => :order).call(environment)
|
35
|
+
environment[Muster::Rack::QUERY].should == {'name' => 'value', 'order' => 'name'}
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Muster::Strategies::ActiveRecord do
|
4
|
+
let(:options) { {} }
|
5
|
+
subject { Muster::Strategies::ActiveRecord.new(options) }
|
6
|
+
|
7
|
+
describe '#parse' do
|
8
|
+
context 'selects' do
|
9
|
+
it 'returns single value as Array' do
|
10
|
+
subject.parse('select=id')[:select].should == ['id']
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns values in Array' do
|
14
|
+
subject.parse('select=id&select=name')[:select].should == ['id', 'name']
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'supports comma separated values' do
|
18
|
+
subject.parse('select=id&select=guid,name')[:select].should == ['id', 'guid', 'name']
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'orders' do
|
24
|
+
it 'returns single value as Array' do
|
25
|
+
subject.parse('order=id')[:order].should == ['id asc']
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with direction' do
|
29
|
+
it 'supports asc' do
|
30
|
+
subject.parse('order=id:asc')[:order].should == ['id asc']
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'supports desc' do
|
34
|
+
subject.parse('order=id:desc')[:order].should == ['id desc']
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'supports ascending' do
|
38
|
+
subject.parse('order=id:ascending')[:order].should == ['id asc']
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'supports desc' do
|
42
|
+
subject.parse('order=id:descending')[:order].should == ['id desc']
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'pagination' do
|
48
|
+
it 'returns default will paginate compatible pagination' do
|
49
|
+
subject.parse('')[:pagination].should == {:page => 1, :per_page => 30}
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns default limit options' do
|
53
|
+
subject.parse('')[:limit].should eq 30
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'returns default offset options' do
|
57
|
+
subject.parse('')[:offset].should eq nil
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'accepts per_page option' do
|
61
|
+
results = subject.parse('per_page=10')
|
62
|
+
results[:pagination].should == {:page => 1, :per_page => 10}
|
63
|
+
results[:limit].should eq 10
|
64
|
+
results[:offset].should eq nil
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'ensures per_page is positive integer' do
|
68
|
+
results = subject.parse('per_page=-10')
|
69
|
+
results[:pagination].should == {:page => 1, :per_page => 30}
|
70
|
+
results[:limit].should eq 30
|
71
|
+
results[:offset].should eq nil
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'accepts page_size option' do
|
75
|
+
results = subject.parse('page_size=10')
|
76
|
+
results[:pagination].should == {:page => 1, :per_page => 10}
|
77
|
+
results[:limit].should eq 10
|
78
|
+
results[:offset].should eq nil
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'accepts page option' do
|
82
|
+
results = subject.parse('page=2')
|
83
|
+
results[:pagination].should == {:page => 2, :per_page => 30}
|
84
|
+
results[:limit].should eq 30
|
85
|
+
results[:offset].should eq 30
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'ensures page is positive integer' do
|
89
|
+
results = subject.parse('page=a')
|
90
|
+
results[:pagination].should == {:page => 1, :per_page => 30}
|
91
|
+
results[:limit].should eq 30
|
92
|
+
results[:offset].should eq nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'wheres' do
|
97
|
+
it 'returns a single value as a string in a hash' do
|
98
|
+
subject.parse('where=id:1')[:where].should == {'id' => '1'}
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'returns values as an Array in a hash' do
|
102
|
+
subject.parse('where=id:1&where=id:2')[:where].should == {'id' => ['1', '2']}
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'supports pipe for multiple values' do
|
106
|
+
subject.parse('where=id:1|2')[:where].should == {'id' => ['1', '2']}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'the full monty' do
|
111
|
+
it 'returns a hash of all options' do
|
112
|
+
query_string = 'select=id,guid,name&where=name:foop&order=id:desc&order=name&page=3&page_size=5'
|
113
|
+
results = subject.parse(query_string)
|
114
|
+
|
115
|
+
results[:select].should == ['id', 'guid', 'name']
|
116
|
+
results[:where].should == {'name' => 'foop'}
|
117
|
+
results[:order].should == ['id desc', 'name asc']
|
118
|
+
results[:pagination].should == {:page => 3, :per_page => 5}
|
119
|
+
results[:offset].should == 10
|
120
|
+
results[:limit].should == 5
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'supports indifferent access' do
|
124
|
+
query_string = 'select=id,guid,name&where=name:foop&order=id:desc&order=name&page=3&page_size=5'
|
125
|
+
results = subject.parse(query_string)
|
126
|
+
|
127
|
+
results['select'].should == ['id', 'guid', 'name']
|
128
|
+
results['where'].should == {'name' => 'foop'}
|
129
|
+
results['order'].should == ['id desc', 'name asc']
|
130
|
+
results['pagination'].should == {:page => 3, :per_page => 5}
|
131
|
+
results['offset'].should == 10
|
132
|
+
results['limit'].should == 5
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|