muster 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/.rubocop.yml +14 -0
- data/Changes +4 -0
- data/Rakefile +6 -1
- data/lib/muster/rack.rb +8 -11
- data/lib/muster/results.rb +22 -25
- data/lib/muster/strategies/active_record.rb +28 -33
- data/lib/muster/strategies/filter_expression.rb +51 -40
- data/lib/muster/strategies/hash.rb +12 -15
- data/lib/muster/strategies/joins_expression.rb +14 -17
- data/lib/muster/strategies/pagination.rb +24 -27
- data/lib/muster/strategies/rack.rb +10 -13
- data/lib/muster/strategies/sort_expression.rb +10 -13
- data/lib/muster/version.rb +2 -1
- data/muster.gemspec +14 -12
- data/spec/muster/rack_spec.rb +8 -8
- data/spec/muster/results_spec.rb +10 -10
- data/spec/muster/strategies/active_record_spec.rb +49 -40
- data/spec/muster/strategies/filter_expression_spec.rb +16 -18
- data/spec/muster/strategies/hash_spec.rb +11 -13
- data/spec/muster/strategies/joins_expression_spec.rb +14 -17
- data/spec/muster/strategies/pagination_spec.rb +9 -9
- data/spec/muster/strategies/sort_expression_spec.rb +3 -5
- metadata +37 -22
@@ -4,7 +4,6 @@ require 'muster/strategies/rack'
|
|
4
4
|
|
5
5
|
module Muster
|
6
6
|
module Strategies
|
7
|
-
|
8
7
|
# Query string parsing strategy with additional value handling options for separating values and uniqueness
|
9
8
|
#
|
10
9
|
# @example
|
@@ -12,7 +11,6 @@ module Muster
|
|
12
11
|
# strategy = Muster::Strategies::Hash.new(:unique_values => true, :value_separator => ',')
|
13
12
|
# results = strategy.parse('name=value&choices=1,2,1') #=> { 'name' => 'value', 'choices' => ['1', '2'] }
|
14
13
|
class Hash < Muster::Strategies::Rack
|
15
|
-
|
16
14
|
# @attribute [r] value_separator
|
17
15
|
# @return [String,RegEx] when specified, each field value will be split into multiple values using the specified separator
|
18
16
|
attr_reader :value_separator
|
@@ -34,7 +32,7 @@ module Muster
|
|
34
32
|
#
|
35
33
|
# strategy = Muster::Strategies::Hash.new(:fields => [:name, :state], :value_separator => '|')
|
36
34
|
# strategy = Muster::Strategies::Hash.new(:unique_values => false)
|
37
|
-
def initialize(
|
35
|
+
def initialize(options = {})
|
38
36
|
super
|
39
37
|
|
40
38
|
@unique_values = self.options.fetch(:unique_values, true)
|
@@ -48,29 +46,29 @@ module Muster
|
|
48
46
|
# @return [Muster::Results]
|
49
47
|
#
|
50
48
|
# @example
|
51
|
-
#
|
49
|
+
#
|
52
50
|
# results = strategy.parse('name=value&choices=1,2') #=> { 'name' => 'value', 'choices' => ['1', '2'] }
|
53
|
-
def parse(
|
51
|
+
def parse(query_string)
|
54
52
|
parameters = super
|
55
53
|
|
56
54
|
parameters.each do |key, value|
|
57
|
-
if
|
58
|
-
parameters[key] =
|
55
|
+
if value_separator.present?
|
56
|
+
parameters[key] = separate_values(value)
|
59
57
|
end
|
60
58
|
|
61
|
-
if
|
59
|
+
if unique_values == true && value.instance_of?(Array)
|
62
60
|
parameters[key].uniq!
|
63
61
|
end
|
64
62
|
end
|
65
63
|
|
66
|
-
|
64
|
+
parameters
|
67
65
|
end
|
68
66
|
|
69
67
|
protected
|
70
68
|
|
71
69
|
# Separates values into an Array of values using :values_separator
|
72
70
|
#
|
73
|
-
# @param
|
71
|
+
# @param values_string [String,Array] the original query string field value to separate
|
74
72
|
#
|
75
73
|
# @return [String,Array] String if a single value exists, Array otherwise
|
76
74
|
#
|
@@ -78,16 +76,15 @@ module Muster
|
|
78
76
|
#
|
79
77
|
# value = self.separate_values('1') #=> '1'
|
80
78
|
# value = self.separate_values('1,2') #=> ['1', '2']
|
81
|
-
def separate_values(
|
82
|
-
values = Array.wrap(
|
79
|
+
def separate_values(values_string)
|
80
|
+
values = Array.wrap(values_string)
|
83
81
|
|
84
82
|
values = values.map do |value|
|
85
|
-
value.split(
|
83
|
+
value.split(value_separator)
|
86
84
|
end.flatten
|
87
85
|
|
88
|
-
|
86
|
+
(values.size > 1) ? values : values_string
|
89
87
|
end
|
90
|
-
|
91
88
|
end
|
92
89
|
end
|
93
90
|
end
|
@@ -2,7 +2,6 @@ require 'muster/strategies/hash'
|
|
2
2
|
|
3
3
|
module Muster
|
4
4
|
module Strategies
|
5
|
-
|
6
5
|
# Query string parsing strategy with additional value handling options for separating filtering expressions
|
7
6
|
#
|
8
7
|
# @example
|
@@ -10,7 +9,6 @@ module Muster
|
|
10
9
|
# strategy = Muster::Strategies::JoinsExpression.new
|
11
10
|
# results = strategy.parse('joins=author.name,activity') #=> { 'joins' => [{'author' => 'name'}, 'activity'] }
|
12
11
|
class JoinsExpression < Muster::Strategies::Hash
|
13
|
-
|
14
12
|
# @attribute [r] expression_separator
|
15
13
|
# @return [String,RegEx] when specified, each field value will be split into multiple expressions using the specified separator
|
16
14
|
attr_reader :expression_separator
|
@@ -34,12 +32,12 @@ module Muster
|
|
34
32
|
#
|
35
33
|
# strategy = Muster::Strategies::FilterExpression.new
|
36
34
|
# strategy = Muster::Strategies::FilterExpression.new(:unique_values => true)
|
37
|
-
def initialize(
|
35
|
+
def initialize(options = {})
|
38
36
|
super
|
39
37
|
|
40
|
-
@expression_separator =
|
41
|
-
@field_separator =
|
42
|
-
@unique_values =
|
38
|
+
@expression_separator = options.fetch(:expression_separator, /,\s*/)
|
39
|
+
@field_separator = options.fetch(:field_separator, '.')
|
40
|
+
@unique_values = options.fetch(:unique_values, true)
|
43
41
|
end
|
44
42
|
|
45
43
|
# Processes a query string and returns an array of hashes that represent an ActiveRecord joins expression
|
@@ -49,18 +47,19 @@ module Muster
|
|
49
47
|
# @return [Muster::Results]
|
50
48
|
#
|
51
49
|
# @example
|
52
|
-
#
|
50
|
+
#
|
53
51
|
# results = strategy.parse('joins=author.name,activity') #=> { 'joins' => [{'author' => 'name'}, 'activity'] }
|
54
|
-
def parse(
|
55
|
-
parameters = Muster::Results.new(
|
52
|
+
def parse(query_string)
|
53
|
+
parameters = Muster::Results.new(fields_to_parse(query_string))
|
56
54
|
|
57
55
|
parameters.each do |key, value|
|
58
|
-
value = value.uniq.first if
|
59
|
-
parameters[key] =
|
56
|
+
value = value.uniq.first if unique_values == true && value.instance_of?(Array)
|
57
|
+
parameters[key] = make_nested_hash(value)
|
60
58
|
end
|
61
59
|
end
|
62
60
|
|
63
61
|
protected
|
62
|
+
|
64
63
|
# Converts the array that represents the value to a nested hash
|
65
64
|
#
|
66
65
|
# @param value [Array] the value to convert
|
@@ -70,15 +69,13 @@ module Muster
|
|
70
69
|
# @example
|
71
70
|
#
|
72
71
|
# value = self.make_nested_hash('activity,author.country.name') #=> ['activity', {'author' => {'country' => 'name'}}]
|
73
|
-
def make_nested_hash(
|
72
|
+
def make_nested_hash(value)
|
74
73
|
expressions = value.split(expression_separator)
|
75
|
-
expressions.map do |
|
76
|
-
fields =
|
77
|
-
fields[0..-2].reverse.reduce(fields.last) { |a,
|
74
|
+
expressions.map do |expression|
|
75
|
+
fields = expression.split(field_separator)
|
76
|
+
fields[0..-2].reverse.reduce(fields.last) { |a, e| { e => a } }
|
78
77
|
end
|
79
78
|
end
|
80
|
-
|
81
79
|
end
|
82
80
|
end
|
83
81
|
end
|
84
|
-
|
@@ -4,7 +4,6 @@ require 'muster/strategies/hash'
|
|
4
4
|
|
5
5
|
module Muster
|
6
6
|
module Strategies
|
7
|
-
|
8
7
|
# Query string parsing strategy with logic to handle pagination options
|
9
8
|
#
|
10
9
|
# @example
|
@@ -12,7 +11,6 @@ module Muster
|
|
12
11
|
# strategy = Muster::Strategies::Pagination.new
|
13
12
|
# results = strategy.parse('page=3&per_page=10') #=> { 'pagination' => {'page' => 3, 'per_page' => 10}, 'limit' => 10, 'offset' => 20 }
|
14
13
|
class Pagination < Muster::Strategies::Rack
|
15
|
-
|
16
14
|
# @attribute [r] default_page_size
|
17
15
|
# @return [Fixnum] when specified, will override the default page size of 30 when no page_size is parsed
|
18
16
|
attr_accessor :default_page_size
|
@@ -31,13 +29,14 @@ module Muster
|
|
31
29
|
#
|
32
30
|
# strategy = Muster::Strategies::Pagination.new
|
33
31
|
# strategy = Muster::Strategies::Pagination.new(:default_page_size => 10)
|
34
|
-
def initialize(
|
32
|
+
def initialize(options = {})
|
35
33
|
super
|
36
34
|
|
37
|
-
self.default_page_size =
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
self.default_page_size = options[:default_page_size].to_i
|
36
|
+
|
37
|
+
return unless default_page_size < 3
|
38
|
+
|
39
|
+
self.default_page_size = 30
|
41
40
|
end
|
42
41
|
|
43
42
|
# Processes a query string and returns a hash of its fields/values
|
@@ -47,25 +46,24 @@ module Muster
|
|
47
46
|
# @return [Muster::Results]
|
48
47
|
#
|
49
48
|
# @example
|
50
|
-
#
|
49
|
+
#
|
51
50
|
# results = strategy.parse('page=3&per_page=10') #=> { 'pagination' => {'page' => 3, 'per_page' => 10}, 'limit' => 10, 'offset' => 20 }
|
52
|
-
def parse(
|
53
|
-
parameters =
|
54
|
-
|
55
|
-
page = self.parse_page(parameters)
|
56
|
-
page_size = self.parse_page_size(parameters)
|
51
|
+
def parse(query_string)
|
52
|
+
parameters = parse_query_string(query_string)
|
57
53
|
|
54
|
+
page = parse_page(parameters)
|
55
|
+
page_size = parse_page_size(parameters)
|
58
56
|
|
59
57
|
offset = (page - 1) * page_size
|
60
58
|
offset = nil if offset < 1
|
61
59
|
|
62
|
-
parameters = parameters.merge(:pagination => {:page => page, :per_page => page_size}, :limit => page_size, :offset => offset)
|
63
|
-
|
64
|
-
if
|
65
|
-
parameters = parameters.slice(*
|
60
|
+
parameters = parameters.merge(:pagination => { :page => page, :per_page => page_size }, :limit => page_size, :offset => offset)
|
61
|
+
|
62
|
+
if fields.present?
|
63
|
+
parameters = parameters.slice(*fields)
|
66
64
|
end
|
67
65
|
|
68
|
-
|
66
|
+
Muster::Results.new(parameters)
|
69
67
|
end
|
70
68
|
|
71
69
|
protected
|
@@ -80,9 +78,9 @@ module Muster
|
|
80
78
|
#
|
81
79
|
# @example
|
82
80
|
#
|
83
|
-
# page =
|
84
|
-
# page =
|
85
|
-
def parse_page(
|
81
|
+
# page = parse_page(:page => 2) #=> 2
|
82
|
+
# page = parse_page(:page => nil) #=> 1
|
83
|
+
def parse_page(parameters)
|
86
84
|
page = parameters.delete(:page).to_i
|
87
85
|
page = 1 unless page > 0
|
88
86
|
page
|
@@ -98,15 +96,14 @@ module Muster
|
|
98
96
|
#
|
99
97
|
# @example
|
100
98
|
#
|
101
|
-
# page_size =
|
102
|
-
# page_size =
|
103
|
-
# page_size =
|
104
|
-
def parse_page_size(
|
99
|
+
# page_size = parse_page(:page_size => 10) #=> 10
|
100
|
+
# page_size = parse_page(:per_page => 10) #=> 10
|
101
|
+
# page_size = parse_page(:per_page => nil) #=> 30
|
102
|
+
def parse_page_size(parameters)
|
105
103
|
page_size = (parameters.delete(:page_size) || parameters.delete(:per_page)).to_i
|
106
|
-
page_size =
|
104
|
+
page_size = default_page_size unless page_size > 0
|
107
105
|
page_size
|
108
106
|
end
|
109
|
-
|
110
107
|
end
|
111
108
|
end
|
112
109
|
end
|
@@ -6,7 +6,6 @@ require 'muster/results'
|
|
6
6
|
|
7
7
|
module Muster
|
8
8
|
module Strategies
|
9
|
-
|
10
9
|
# Query string parsing strategy based on Rack::Utils#parse_query
|
11
10
|
#
|
12
11
|
# @example
|
@@ -14,7 +13,6 @@ module Muster
|
|
14
13
|
# strategy = Muster::Strategies::Rack.new
|
15
14
|
# results = strategy.parse('name=value&choices=1&choices=2') #=> { 'name' => 'value', 'choices' => ['1', '2'] }
|
16
15
|
class Rack
|
17
|
-
|
18
16
|
# @attribute [r] options
|
19
17
|
# @return [Hash] options specified during initialization
|
20
18
|
attr_reader :options
|
@@ -33,11 +31,11 @@ module Muster
|
|
33
31
|
#
|
34
32
|
# strategy = Muster::Strategies::Rack.new(:fields => [:name, :state])
|
35
33
|
# strategy = Muster::Strategies::Rack.new(:field => :name)
|
36
|
-
def initialize(
|
34
|
+
def initialize(options = {})
|
37
35
|
@options = options.with_indifferent_access
|
38
36
|
|
39
37
|
@fields = Array.wrap(@options[:field] || @options[:fields])
|
40
|
-
@fields.map!
|
38
|
+
@fields.map!(&:to_sym)
|
41
39
|
end
|
42
40
|
|
43
41
|
# Processes a query string and returns a hash of its fields/values
|
@@ -47,10 +45,10 @@ module Muster
|
|
47
45
|
# @return [Muster::Results]
|
48
46
|
#
|
49
47
|
# @example
|
50
|
-
#
|
48
|
+
#
|
51
49
|
# results = strategy.parse('name=value&choices=1&choices=1') #=> { 'name' => 'value', 'choices' => ['1', '2'] }
|
52
|
-
def parse(
|
53
|
-
Muster::Results.new(
|
50
|
+
def parse(query_string)
|
51
|
+
Muster::Results.new(fields_to_parse(query_string))
|
54
52
|
end
|
55
53
|
|
56
54
|
protected
|
@@ -64,7 +62,7 @@ module Muster
|
|
64
62
|
# @example
|
65
63
|
#
|
66
64
|
# fields = self.parse_query_string('name=value&choices=1&choices=1') #=> { 'name' => 'value', 'choices' => ['1', '2'] }
|
67
|
-
def parse_query_string(
|
65
|
+
def parse_query_string(query_string)
|
68
66
|
::Rack::Utils.parse_query(query_string).with_indifferent_access
|
69
67
|
end
|
70
68
|
|
@@ -75,16 +73,15 @@ module Muster
|
|
75
73
|
# If the :fields option was specified, only those fields will be returned. Otherwise, all fields will be returned.
|
76
74
|
#
|
77
75
|
# @return [Hash]
|
78
|
-
def fields_to_parse(
|
79
|
-
fields =
|
76
|
+
def fields_to_parse(query_string)
|
77
|
+
fields = parse_query_string(query_string)
|
80
78
|
|
81
79
|
if self.fields.present?
|
82
|
-
fields = fields.select{ |key,
|
80
|
+
fields = fields.select { |key, _| self.fields.include?(key.to_sym) }
|
83
81
|
end
|
84
82
|
|
85
|
-
|
83
|
+
fields.with_indifferent_access
|
86
84
|
end
|
87
|
-
|
88
85
|
end
|
89
86
|
end
|
90
87
|
end
|
@@ -3,7 +3,6 @@ require 'muster/strategies/hash'
|
|
3
3
|
|
4
4
|
module Muster
|
5
5
|
module Strategies
|
6
|
-
|
7
6
|
# Query string parsing strategy with additional value handling for sort orders
|
8
7
|
#
|
9
8
|
# @example
|
@@ -11,7 +10,6 @@ module Muster
|
|
11
10
|
# strategy = Muster::Strategies::SortExpression.new
|
12
11
|
# results = strategy.parse('sort=name:desc') #=> { 'sort' => 'name desc' }
|
13
12
|
class SortExpression < Muster::Strategies::Hash
|
14
|
-
|
15
13
|
# Processes a query string and returns a hash of its fields/values
|
16
14
|
#
|
17
15
|
# @param query_string [String] the query string to parse
|
@@ -19,15 +17,15 @@ module Muster
|
|
19
17
|
# @return [Hash]
|
20
18
|
#
|
21
19
|
# @example
|
22
|
-
#
|
20
|
+
#
|
23
21
|
# results = strategy.parse('order=name') #=> { 'order' => 'name asc' }
|
24
22
|
# results = strategy.parse('order=name:desc') #=> { 'order' => 'name desc' }
|
25
23
|
# results = strategy.parse('order=name,date:desc') #=> { 'order' => ['name asc', 'date desc'] }
|
26
|
-
def parse(
|
27
|
-
parameters
|
24
|
+
def parse(query_string)
|
25
|
+
parameters = super
|
28
26
|
|
29
27
|
parameters.each do |key, value|
|
30
|
-
parameters[key] =
|
28
|
+
parameters[key] = parse_sort_expression(value)
|
31
29
|
end
|
32
30
|
end
|
33
31
|
|
@@ -35,7 +33,7 @@ module Muster
|
|
35
33
|
|
36
34
|
# Separates the values into their field and direction
|
37
35
|
#
|
38
|
-
# @param
|
36
|
+
# @param expression [String] the value being parsed
|
39
37
|
#
|
40
38
|
# @return [String,Arrary] String if a single value, Array otherwise
|
41
39
|
#
|
@@ -43,17 +41,17 @@ module Muster
|
|
43
41
|
#
|
44
42
|
# value = self.parse_sort_expression('name:asc') #=> 'name asc'
|
45
43
|
# value = self.parse_sort_expression(['name:asc', 'date']) #=> ['name asc', 'date asc']
|
46
|
-
def parse_sort_expression(
|
47
|
-
values = Array.wrap(
|
44
|
+
def parse_sort_expression(expression)
|
45
|
+
values = Array.wrap(expression)
|
48
46
|
|
49
47
|
values = values.map do |value|
|
50
48
|
name, direction = value.split(':', 2)
|
51
|
-
direction =
|
49
|
+
direction = parse_direction(direction)
|
52
50
|
|
53
51
|
"#{name} #{direction}"
|
54
52
|
end.flatten
|
55
53
|
|
56
|
-
|
54
|
+
(values.size > 1) ? values : values.first
|
57
55
|
end
|
58
56
|
|
59
57
|
# Parse and normalize the sot expression direction
|
@@ -66,10 +64,9 @@ module Muster
|
|
66
64
|
#
|
67
65
|
# direction = self.parse_direction('ascending') #=> 'asc'
|
68
66
|
# direction = self.parse_direction('descending') #=> 'desc'
|
69
|
-
def parse_direction(
|
67
|
+
def parse_direction(direction)
|
70
68
|
direction.to_s.match(/^desc/i) ? 'desc' : 'asc'
|
71
69
|
end
|
72
|
-
|
73
70
|
end
|
74
71
|
end
|
75
72
|
end
|
data/lib/muster/version.rb
CHANGED
data/muster.gemspec
CHANGED
@@ -1,26 +1,28 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
require File.expand_path('../lib/muster/version', __FILE__)
|
3
3
|
|
4
|
+
# rubocop:disable Metrics/LineLength
|
4
5
|
Gem::Specification.new do |gem|
|
5
|
-
gem.authors = [
|
6
|
-
gem.email = [
|
7
|
-
gem.description =
|
8
|
-
gem.summary =
|
9
|
-
gem.homepage =
|
6
|
+
gem.authors = ['Christopher H. Laco']
|
7
|
+
gem.email = ['claco@chrislaco.com']
|
8
|
+
gem.description = 'Muster is a gem that turns query strings of varying formats into data structures suitable for easier consumption in AR/DataMapper scopes and queries.'
|
9
|
+
gem.summary = 'Muster various query string formats into a more reusable data structure.'
|
10
|
+
gem.homepage = 'https://github.com/claco/muster'
|
10
11
|
|
11
|
-
gem.files = `git ls-files`.split(
|
12
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split($ORS)
|
13
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
13
14
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
-
gem.name =
|
15
|
-
gem.require_paths = [
|
15
|
+
gem.name = 'muster'
|
16
|
+
gem.require_paths = ['lib']
|
16
17
|
gem.version = Muster::VERSION
|
17
18
|
|
18
19
|
gem.add_dependency 'activesupport', '>= 3.0'
|
19
|
-
gem.add_dependency 'rack',
|
20
|
+
gem.add_dependency 'rack', '~> 1.4'
|
20
21
|
|
21
22
|
gem.add_development_dependency 'pry'
|
22
|
-
gem.add_development_dependency 'rspec',
|
23
|
+
gem.add_development_dependency 'rspec', '~> 2.11.0'
|
23
24
|
gem.add_development_dependency 'redcarpet', '~> 2.1'
|
25
|
+
gem.add_development_dependency 'rubocop', '~> 0.33.0'
|
24
26
|
gem.add_development_dependency 'simplecov'
|
25
|
-
gem.add_development_dependency 'yard',
|
27
|
+
gem.add_development_dependency 'yard', '~> 0.8.2'
|
26
28
|
end
|