graphiti 1.0.alpha.5 → 1.0.alpha.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphiti/api_test_generator.rb +71 -0
- data/lib/generators/graphiti/generator_mixin.rb +45 -0
- data/lib/generators/graphiti/resource_generator.rb +99 -0
- data/lib/generators/graphiti/resource_test_generator.rb +57 -0
- data/lib/generators/{jsonapi → graphiti}/templates/application_resource.rb.erb +0 -0
- data/lib/generators/{jsonapi → graphiti}/templates/controller.rb.erb +0 -0
- data/lib/generators/{jsonapi → graphiti}/templates/create_request_spec.rb.erb +3 -3
- data/lib/generators/{jsonapi → graphiti}/templates/destroy_request_spec.rb.erb +5 -5
- data/lib/generators/graphiti/templates/index_request_spec.rb.erb +22 -0
- data/lib/generators/{jsonapi → graphiti}/templates/resource.rb.erb +0 -0
- data/lib/generators/{jsonapi → graphiti}/templates/resource_reads_spec.rb.erb +12 -12
- data/lib/generators/{jsonapi → graphiti}/templates/resource_writes_spec.rb.erb +12 -12
- data/lib/generators/graphiti/templates/show_request_spec.rb.erb +21 -0
- data/lib/generators/{jsonapi → graphiti}/templates/update_request_spec.rb.erb +5 -5
- data/lib/graphiti.rb +0 -6
- data/lib/graphiti/adapters/abstract.rb +0 -1
- data/lib/graphiti/adapters/active_record/inferrence.rb +8 -1
- data/lib/graphiti/base.rb +1 -1
- data/lib/graphiti/errors.rb +70 -5
- data/lib/graphiti/jsonapi_serializable_ext.rb +18 -6
- data/lib/graphiti/query.rb +28 -17
- data/lib/graphiti/resource.rb +14 -0
- data/lib/graphiti/resource/configuration.rb +22 -3
- data/lib/graphiti/resource/dsl.rb +17 -2
- data/lib/graphiti/resource/interface.rb +10 -8
- data/lib/graphiti/resource/links.rb +6 -3
- data/lib/graphiti/runner.rb +2 -1
- data/lib/graphiti/schema.rb +33 -5
- data/lib/graphiti/schema_diff.rb +71 -1
- data/lib/graphiti/scope.rb +4 -9
- data/lib/graphiti/scoping/base.rb +2 -2
- data/lib/graphiti/scoping/filter.rb +5 -0
- data/lib/graphiti/scoping/filterable.rb +21 -6
- data/lib/graphiti/scoping/paginate.rb +4 -4
- data/lib/graphiti/scoping/sort.rb +26 -5
- data/lib/graphiti/sideload.rb +13 -22
- data/lib/graphiti/sideload/belongs_to.rb +11 -2
- data/lib/graphiti/sideload/has_many.rb +1 -1
- data/lib/graphiti/sideload/polymorphic_belongs_to.rb +2 -3
- data/lib/graphiti/types.rb +1 -1
- data/lib/graphiti/util/class.rb +5 -2
- data/lib/graphiti/util/persistence.rb +1 -1
- data/lib/graphiti/version.rb +1 -1
- metadata +16 -13
- data/lib/generators/jsonapi/resource_generator.rb +0 -169
- data/lib/generators/jsonapi/templates/index_request_spec.rb.erb +0 -22
- data/lib/generators/jsonapi/templates/show_request_spec.rb.erb +0 -21
@@ -5,38 +5,40 @@ module Graphiti
|
|
5
5
|
|
6
6
|
class_methods do
|
7
7
|
def all(params = {}, base_scope = nil)
|
8
|
-
validate!
|
8
|
+
validate!(params)
|
9
9
|
_all(params, {}, base_scope)
|
10
10
|
end
|
11
11
|
|
12
12
|
# @api private
|
13
13
|
def _all(params, opts, base_scope)
|
14
|
-
runner = Runner.new(self, params)
|
14
|
+
runner = Runner.new(self, params, opts.delete(:query))
|
15
15
|
runner.proxy(base_scope, opts)
|
16
16
|
end
|
17
17
|
|
18
|
-
def find(params, base_scope = nil)
|
19
|
-
validate!
|
18
|
+
def find(params = {}, base_scope = nil)
|
19
|
+
validate!(params)
|
20
20
|
id = params[:data].try(:[], :id) || params.delete(:id)
|
21
21
|
params[:filter] ||= {}
|
22
|
-
params[:filter].merge!(id: id)
|
22
|
+
params[:filter].merge!(id: id) if id
|
23
23
|
|
24
24
|
runner = Runner.new(self, params)
|
25
25
|
runner.proxy(base_scope, single: true, raise_on_missing: true)
|
26
26
|
end
|
27
27
|
|
28
28
|
def build(params, base_scope = nil)
|
29
|
-
validate!
|
29
|
+
validate!(params)
|
30
30
|
runner = Runner.new(self, params)
|
31
31
|
runner.proxy(base_scope, single: true, raise_on_missing: true)
|
32
32
|
end
|
33
33
|
|
34
34
|
private
|
35
35
|
|
36
|
-
def validate!
|
36
|
+
def validate!(params)
|
37
|
+
return unless validate_endpoints?
|
38
|
+
|
37
39
|
if context && context.respond_to?(:request)
|
38
40
|
path = context.request.env['PATH_INFO']
|
39
|
-
unless allow_request?(path, context_namespace)
|
41
|
+
unless allow_request?(path, params, context_namespace)
|
40
42
|
raise Errors::InvalidEndpoint.new(self, path, context_namespace)
|
41
43
|
end
|
42
44
|
end
|
@@ -19,9 +19,11 @@ module Graphiti
|
|
19
19
|
:base_url,
|
20
20
|
:endpoint_namespace,
|
21
21
|
:secondary_endpoints,
|
22
|
-
:autolink
|
22
|
+
:autolink,
|
23
|
+
:validate_endpoints
|
23
24
|
self.secondary_endpoints = []
|
24
25
|
self.autolink = true
|
26
|
+
self.validate_endpoints = true
|
25
27
|
|
26
28
|
class << self
|
27
29
|
prepend Overrides
|
@@ -66,9 +68,10 @@ module Graphiti
|
|
66
68
|
([endpoint] + secondary_endpoints).compact
|
67
69
|
end
|
68
70
|
|
69
|
-
def allow_request?(path, action)
|
71
|
+
def allow_request?(path, params, action)
|
70
72
|
endpoints.any? do |e|
|
71
|
-
|
73
|
+
has_id = params[:id] || params[:data].try(:[], :id)
|
74
|
+
if [:update, :show, :destroy].include?(context_namespace) && has_id
|
72
75
|
path = path.split('/')
|
73
76
|
path.pop
|
74
77
|
path = path.join('/')
|
data/lib/graphiti/runner.rb
CHANGED
@@ -3,9 +3,10 @@ module Graphiti
|
|
3
3
|
attr_reader :params
|
4
4
|
include Graphiti::Base
|
5
5
|
|
6
|
-
def initialize(resource_class, params)
|
6
|
+
def initialize(resource_class, params, query = nil)
|
7
7
|
@resource_class = resource_class
|
8
8
|
@params = params
|
9
|
+
@query = query
|
9
10
|
end
|
10
11
|
|
11
12
|
def jsonapi_resource
|
data/lib/graphiti/schema.rb
CHANGED
@@ -84,10 +84,19 @@ module Graphiti
|
|
84
84
|
type: r.type.to_s,
|
85
85
|
attributes: attributes(r),
|
86
86
|
extra_attributes: extra_attributes(r),
|
87
|
+
sorts: sorts(r),
|
87
88
|
filters: filters(r),
|
88
89
|
relationships: relationships(r)
|
89
90
|
}
|
90
91
|
|
92
|
+
if r.default_sort
|
93
|
+
config[:default_sort] = r.default_sort
|
94
|
+
end
|
95
|
+
|
96
|
+
if r.default_page_size
|
97
|
+
config[:default_page_size] = r.default_page_size
|
98
|
+
end
|
99
|
+
|
91
100
|
if r.polymorphic?
|
92
101
|
config.merge!(polymorphic: true, children: r.children.map(&:name))
|
93
102
|
end
|
@@ -99,12 +108,11 @@ module Graphiti
|
|
99
108
|
def attributes(resource)
|
100
109
|
{}.tap do |attrs|
|
101
110
|
resource.attributes.each_pair do |name, config|
|
102
|
-
if config.values_at(:readable, :writable
|
111
|
+
if config.values_at(:readable, :writable).any?
|
103
112
|
attrs[name] = {
|
104
113
|
type: config[:type].to_s,
|
105
114
|
readable: flag(config[:readable]),
|
106
|
-
writable: flag(config[:writable])
|
107
|
-
sortable: flag(config[:sortable])
|
115
|
+
writable: flag(config[:writable])
|
108
116
|
}
|
109
117
|
end
|
110
118
|
end
|
@@ -130,6 +138,20 @@ module Graphiti
|
|
130
138
|
end
|
131
139
|
end
|
132
140
|
|
141
|
+
def sorts(resource)
|
142
|
+
{}.tap do |s|
|
143
|
+
resource.sorts.each_pair do |name, sort|
|
144
|
+
config = {}
|
145
|
+
config[:only] = sort[:only] if sort[:only]
|
146
|
+
attr = resource.attributes[name]
|
147
|
+
if attr[:sortable].is_a?(Symbol)
|
148
|
+
config[:guard] = true
|
149
|
+
end
|
150
|
+
s[name] = config
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
133
155
|
def filters(resource)
|
134
156
|
{}.tap do |f|
|
135
157
|
resource.filters.each_pair do |name, filter|
|
@@ -137,9 +159,11 @@ module Graphiti
|
|
137
159
|
type: filter[:type].to_s,
|
138
160
|
operators: filter[:operators].keys.map(&:to_s)
|
139
161
|
}
|
162
|
+
|
140
163
|
config[:single] = true if filter[:single]
|
141
|
-
config[:allow] = filter[:allow] if filter[:allow]
|
142
|
-
config[:reject] = filter[:reject] if filter[:reject]
|
164
|
+
config[:allow] = filter[:allow].map(&:to_s) if filter[:allow]
|
165
|
+
config[:reject] = filter[:reject].map(&:to_s) if filter[:reject]
|
166
|
+
config[:dependencies] = filter[:dependencies].map(&:to_s) if filter[:dependencies]
|
143
167
|
|
144
168
|
attr = resource.attributes[name]
|
145
169
|
if attr[:filterable].is_a?(Symbol)
|
@@ -165,6 +189,10 @@ module Graphiti
|
|
165
189
|
schema[:resource] = config.resource.class.name
|
166
190
|
end
|
167
191
|
|
192
|
+
if config.single?
|
193
|
+
schema[:single] = true
|
194
|
+
end
|
195
|
+
|
168
196
|
r[name] = schema
|
169
197
|
end
|
170
198
|
end
|
data/lib/graphiti/schema_diff.rb
CHANGED
@@ -24,7 +24,9 @@ module Graphiti
|
|
24
24
|
new_resource = @new[:resources].find { |n| n[:name] == r[:name] }
|
25
25
|
compare_resource(r, new_resource) do
|
26
26
|
compare_attributes(r, new_resource)
|
27
|
+
compare_defaults(r, new_resource)
|
27
28
|
compare_extra_attributes(r, new_resource)
|
29
|
+
compare_sorts(r, new_resource)
|
28
30
|
compare_filters(r, new_resource)
|
29
31
|
compare_relationships(r, new_resource)
|
30
32
|
end
|
@@ -54,6 +56,36 @@ module Graphiti
|
|
54
56
|
end
|
55
57
|
end
|
56
58
|
|
59
|
+
def compare_defaults(old_resource, new_resource)
|
60
|
+
if new_resource[:default_sort] && !old_resource[:default_sort]
|
61
|
+
@errors << "#{old_resource[:name]}: default sort added."
|
62
|
+
end
|
63
|
+
|
64
|
+
if old_resource[:default_sort] && !new_resource[:default_sort]
|
65
|
+
@errors << "#{old_resource[:name]}: default sort removed."
|
66
|
+
end
|
67
|
+
|
68
|
+
if new_resource[:default_sort] && old_resource[:default_sort]
|
69
|
+
if new_resource[:default_sort] != old_resource[:default_sort]
|
70
|
+
@errors << "#{old_resource[:name]}: default sort changed from #{old_resource[:default_sort].inspect} to #{new_resource[:default_sort].inspect}."
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
if new_resource[:default_page_size] && !old_resource[:default_page_size]
|
75
|
+
@errors << "#{old_resource[:name]}: default page size added."
|
76
|
+
end
|
77
|
+
|
78
|
+
if old_resource[:default_page_size] && !new_resource[:default_page_size]
|
79
|
+
@errors << "#{old_resource[:name]}: default page size removed."
|
80
|
+
end
|
81
|
+
|
82
|
+
if old_resource[:default_page_size] && new_resource[:default_page_size]
|
83
|
+
if old_resource[:default_page_size] != new_resource[:default_page_size]
|
84
|
+
@errors << "#{old_resource[:name]}: default page size changed from #{old_resource[:default_page_size]} to #{new_resource[:default_page_size]}."
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
57
89
|
def compare_relationships(old_resource, new_resource)
|
58
90
|
old_resource[:relationships].each_pair do |name, old_rel|
|
59
91
|
unless new_rel = new_resource[:relationships][name]
|
@@ -61,6 +93,11 @@ module Graphiti
|
|
61
93
|
next
|
62
94
|
end
|
63
95
|
|
96
|
+
if new_rel[:single] && !old_rel[:single]
|
97
|
+
@errors << "#{old_resource[:name]}: relationship #{name.inspect} became single: true."
|
98
|
+
next
|
99
|
+
end
|
100
|
+
|
64
101
|
if new_rel[:resource] != old_rel[:resource]
|
65
102
|
@errors << "#{old_resource[:name]}: relationship #{name.inspect} changed resource from #{old_rel[:resource]} to #{new_rel[:resource]}."
|
66
103
|
end
|
@@ -82,6 +119,29 @@ module Graphiti
|
|
82
119
|
end
|
83
120
|
end
|
84
121
|
|
122
|
+
def compare_sorts(old_resource, new_resource)
|
123
|
+
old_resource[:sorts].each_pair do |name, old_sort|
|
124
|
+
unless new_sort = new_resource[:sorts][name]
|
125
|
+
@errors << "#{old_resource[:name]}: sort #{name.inspect} was removed."
|
126
|
+
next
|
127
|
+
end
|
128
|
+
|
129
|
+
if new_sort[:guard] && !old_sort[:guard]
|
130
|
+
@errors << "#{old_resource[:name]}: sort #{name.inspect} became guarded."
|
131
|
+
end
|
132
|
+
|
133
|
+
if new_sort[:only] && !old_sort[:only]
|
134
|
+
@errors << "#{old_resource[:name]}: sort #{name.inspect} now limited to only #{new_sort[:only].inspect}."
|
135
|
+
end
|
136
|
+
|
137
|
+
if new_sort[:only] && old_sort[:only]
|
138
|
+
if new_sort[:only] != old_sort[:only]
|
139
|
+
@errors << "#{old_resource[:name]}: sort #{name.inspect} was limited to only #{old_sort[:only].inspect}, now limited to only #{new_sort[:only].inspect}."
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
85
145
|
def compare_filters(old_resource, new_resource)
|
86
146
|
old_resource[:filters].each_pair do |name, old_filter|
|
87
147
|
unless new_filter = new_resource[:filters][name]
|
@@ -98,6 +158,16 @@ module Graphiti
|
|
98
158
|
@errors << "#{old_resource[:name]}: filter #{name.inspect} became singular."
|
99
159
|
end
|
100
160
|
|
161
|
+
if new_filter[:dependencies] && !old_filter[:dependencies]
|
162
|
+
@errors << "#{old_resource[:name]}: filter #{name.inspect} added dependencies #{new_filter[:dependencies].inspect}."
|
163
|
+
end
|
164
|
+
|
165
|
+
if new_filter[:dependencies] && old_filter[:dependencies]
|
166
|
+
if new_filter[:dependencies] != old_filter[:dependenices]
|
167
|
+
@errors << "#{old_resource[:name]}: filter #{name.inspect} changed dependencies from #{old_filter[:dependencies].inspect} to #{new_filter[:dependencies].inspect}."
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
101
171
|
if new_filter[:allow] != old_filter[:allow]
|
102
172
|
new = new_filter[:allow] || []
|
103
173
|
old = old_filter[:allow] || []
|
@@ -182,7 +252,7 @@ module Graphiti
|
|
182
252
|
@errors << "#{resource_name}: #{prefix} #{att_name.inspect} changed type from #{old_att[:type].inspect} to #{new_att[:type].inspect}."
|
183
253
|
end
|
184
254
|
|
185
|
-
[:readable, :writable
|
255
|
+
[:readable, :writable].each do |flag|
|
186
256
|
if [true, 'guarded'].include?(old_att[flag]) && new_att[flag] == false
|
187
257
|
@errors << "#{resource_name}: #{prefix} #{att_name.inspect} changed flag #{flag.inspect} from #{old_att[flag].inspect} to #{new_att[flag].inspect}."
|
188
258
|
end
|
data/lib/graphiti/scope.rb
CHANGED
@@ -8,13 +8,13 @@ module Graphiti
|
|
8
8
|
@query = query
|
9
9
|
@opts = opts
|
10
10
|
|
11
|
-
@object = @resource.around_scoping(@object,
|
11
|
+
@object = @resource.around_scoping(@object, @query.hash) do |scope|
|
12
12
|
apply_scoping(scope, opts)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
def resolve_stats
|
17
|
-
if
|
17
|
+
if @query.hash[:stats]
|
18
18
|
Stats::Payload.new(@resource, @query, @unpaginated_object).generate
|
19
19
|
else
|
20
20
|
{}
|
@@ -36,18 +36,13 @@ module Graphiti
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
def query_hash
|
40
|
-
@query_hash ||= @query.to_hash
|
41
|
-
end
|
42
|
-
|
43
39
|
private
|
44
40
|
|
45
41
|
# Used to ensure the resource's serializer is used
|
46
42
|
# Not one derived through the usual jsonapi-rb logic
|
47
43
|
def assign_serializer(records)
|
48
44
|
records.each do |r|
|
49
|
-
|
50
|
-
r.instance_variable_set(:@__serializer_klass, serializer)
|
45
|
+
@resource.decorate_record(r)
|
51
46
|
end
|
52
47
|
end
|
53
48
|
|
@@ -91,7 +86,7 @@ module Graphiti
|
|
91
86
|
end
|
92
87
|
|
93
88
|
def add_scoping(key, scoping_class, opts, default = {})
|
94
|
-
@object = scoping_class.new(@resource,
|
89
|
+
@object = scoping_class.new(@resource, @query.hash, @object, opts).apply
|
95
90
|
@unpaginated_object = @object unless key == :paginate
|
96
91
|
end
|
97
92
|
end
|
@@ -6,7 +6,7 @@ module Graphiti
|
|
6
6
|
# a default if not part of the user request.
|
7
7
|
#
|
8
8
|
# @attr_reader [Resource] resource The corresponding Resource instance
|
9
|
-
# @attr_reader [Hash] query_hash the Query#
|
9
|
+
# @attr_reader [Hash] query_hash the Query#hash node relevant to the current resource
|
10
10
|
#
|
11
11
|
# @see Scoping::DefaultFilter
|
12
12
|
# @see Scoping::ExtraFields
|
@@ -15,7 +15,7 @@ module Graphiti
|
|
15
15
|
# @see Scoping::Sort
|
16
16
|
# @see Scope#initialize
|
17
17
|
# @see Scope#query_hash
|
18
|
-
# @see Query#
|
18
|
+
# @see Query#hash
|
19
19
|
class Base
|
20
20
|
attr_reader :resource, :query_hash
|
21
21
|
|
@@ -32,6 +32,11 @@ module Graphiti
|
|
32
32
|
raise Errors::RequiredFilter.new(resource, missing_required_filters)
|
33
33
|
end
|
34
34
|
|
35
|
+
if missing_dependent_filters.any?
|
36
|
+
raise Errors::MissingDependentFilter.new \
|
37
|
+
resource, missing_dependent_filters
|
38
|
+
end
|
39
|
+
|
35
40
|
each_filter do |filter, operator, value|
|
36
41
|
@scope = filter_scope(filter, operator, value)
|
37
42
|
end
|
@@ -20,17 +20,32 @@ module Graphiti
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def missing_required_filters
|
23
|
-
|
23
|
+
required_filters - filter_param.keys
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
27
|
-
resource.
|
28
|
-
k if v[:
|
26
|
+
def required_filters
|
27
|
+
resource.filters.map do |k, v|
|
28
|
+
k if v[:required]
|
29
29
|
end.compact
|
30
30
|
end
|
31
31
|
|
32
|
-
def
|
33
|
-
|
32
|
+
def missing_dependent_filters
|
33
|
+
[].tap do |arr|
|
34
|
+
filter_param.each_pair do |key, value|
|
35
|
+
if df = dependent_filters[key]
|
36
|
+
missing = df[:dependencies] - filter_param.keys
|
37
|
+
unless missing.length.zero?
|
38
|
+
arr << { filter: df, missing: missing }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def dependent_filters
|
46
|
+
resource.filters.select do |k, v|
|
47
|
+
v[:dependencies].present?
|
48
|
+
end
|
34
49
|
end
|
35
50
|
end
|
36
51
|
end
|
@@ -23,14 +23,14 @@ module Graphiti
|
|
23
23
|
# We should use the default unless the user has customized.
|
24
24
|
# @see Resource.paginate
|
25
25
|
class Scoping::Paginate < Scoping::Base
|
26
|
-
|
26
|
+
DEFAULT_PAGE_SIZE = 20
|
27
27
|
|
28
28
|
# Apply the pagination logic. Raise error if over the max page size.
|
29
29
|
# @return the scope object we are chaining/modifying
|
30
30
|
def apply
|
31
|
-
if size >
|
31
|
+
if size > resource.max_page_size
|
32
32
|
raise Graphiti::Errors::UnsupportedPageSize
|
33
|
-
.new(size,
|
33
|
+
.new(size, resource.max_page_size)
|
34
34
|
elsif requested? && @opts[:sideload_parent_length].to_i > 1
|
35
35
|
raise Graphiti::Errors::UnsupportedPagination
|
36
36
|
else
|
@@ -81,7 +81,7 @@ module Graphiti
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def size
|
84
|
-
(page_param[:size] || resource.default_page_size).to_i
|
84
|
+
(page_param[:size] || resource.default_page_size || DEFAULT_PAGE_SIZE).to_i
|
85
85
|
end
|
86
86
|
end
|
87
87
|
end
|
@@ -22,10 +22,16 @@ module Graphiti
|
|
22
22
|
# @return the scope we are chaining/modifying
|
23
23
|
def apply_standard_scope
|
24
24
|
each_sort do |attribute, direction|
|
25
|
-
|
26
|
-
|
25
|
+
sort = resource.sorts[attribute]
|
26
|
+
if sort[:only] && sort[:only] != direction
|
27
|
+
raise Errors::UnsupportedSort.new resource,
|
28
|
+
attribute, sort[:only], direction
|
27
29
|
else
|
28
|
-
|
30
|
+
if sort[:proc]
|
31
|
+
@scope = sort[:proc].call(@scope, direction)
|
32
|
+
else
|
33
|
+
@scope = resource.adapter.order(@scope, attribute, direction)
|
34
|
+
end
|
29
35
|
end
|
30
36
|
end
|
31
37
|
@scope
|
@@ -54,11 +60,26 @@ module Graphiti
|
|
54
60
|
def sort_param
|
55
61
|
@sort_param ||= begin
|
56
62
|
if query_hash[:sort].blank?
|
57
|
-
resource.default_sort
|
63
|
+
resource.default_sort || []
|
58
64
|
else
|
59
|
-
query_hash[:sort]
|
65
|
+
normalize(query_hash[:sort])
|
60
66
|
end
|
61
67
|
end
|
62
68
|
end
|
69
|
+
|
70
|
+
def normalize(sort)
|
71
|
+
return sort if sort.is_a?(Array)
|
72
|
+
sorts = sort.split(',')
|
73
|
+
sorts.map do |s|
|
74
|
+
sort_hash(s)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def sort_hash(attr)
|
79
|
+
value = attr[0] == '-' ? :desc : :asc
|
80
|
+
key = attr.sub('-', '').to_sym
|
81
|
+
|
82
|
+
{ key => value }
|
83
|
+
end
|
63
84
|
end
|
64
85
|
end
|