muster 0.0.1
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/.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
|