roaster 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/Gemfile +5 -0
- data/Gemfile.lock +67 -0
- data/Rakefile +5 -0
- data/STUFF +105 -0
- data/lib/roaster.rb +12 -0
- data/lib/roaster/adapters/active_record.rb +116 -0
- data/lib/roaster/decorator.rb +65 -0
- data/lib/roaster/json_api.rb +13 -0
- data/lib/roaster/query.rb +144 -0
- data/lib/roaster/request.rb +73 -0
- data/lib/roaster/resource.rb +41 -0
- data/lib/roaster/version.rb +3 -0
- data/roaster.gemspec +30 -0
- data/test/activerecord_adapter_test.rb +124 -0
- data/test/decorator_test.rb +10 -0
- data/test/factories/album.rb +25 -0
- data/test/factories/band.rb +9 -0
- data/test/factories/track.rb +5 -0
- data/test/mappings/album.rb +19 -0
- data/test/mappings/band.rb +6 -0
- data/test/mappings/track.rb +8 -0
- data/test/models/album.rb +14 -0
- data/test/models/band.rb +11 -0
- data/test/models/namespaced/model.rb +4 -0
- data/test/models/track.rb +13 -0
- data/test/query_target_test.rb +29 -0
- data/test/query_test.rb +103 -0
- data/test/request_test.rb +16 -0
- data/test/resource_test.rb +11 -0
- data/test/support/active_record.rb +11 -0
- data/test/test_helper.rb +27 -0
- data/test/wut_test.rb +205 -0
- metadata +183 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 55772cb368935448bd887e970762e0fe89537916
|
4
|
+
data.tar.gz: 9ced0c2bae78911a674888ccdbf985631d7778fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86eb522d00e4912a98a1c59d2a897fb16cd4d28379c617a8271d969b037b9ad1514669930ce2d30493244cfc06e41019313898e9f399ed3c7ae748afd4129e4f
|
7
|
+
data.tar.gz: cdf43f96e7f5d40707961f97953a878a153e147c7baae2363fdc2464dc39a39b832c0f4598f50c40947a905556229ab67ab23bf68972f77e7208292759073a71
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
roaster (0.0.1)
|
5
|
+
activerecord (~> 4.1.0)
|
6
|
+
activesupport (~> 4.1.0)
|
7
|
+
representable (~> 2.0.0)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
activemodel (4.1.5)
|
13
|
+
activesupport (= 4.1.5)
|
14
|
+
builder (~> 3.1)
|
15
|
+
activerecord (4.1.5)
|
16
|
+
activemodel (= 4.1.5)
|
17
|
+
activesupport (= 4.1.5)
|
18
|
+
arel (~> 5.0.0)
|
19
|
+
activesupport (4.1.5)
|
20
|
+
i18n (~> 0.6, >= 0.6.9)
|
21
|
+
json (~> 1.7, >= 1.7.7)
|
22
|
+
minitest (~> 5.1)
|
23
|
+
thread_safe (~> 0.1)
|
24
|
+
tzinfo (~> 1.1)
|
25
|
+
arel (5.0.1.20140414130214)
|
26
|
+
awesome_print (1.2.0)
|
27
|
+
builder (3.2.2)
|
28
|
+
byebug (3.4.0)
|
29
|
+
columnize (~> 0.8)
|
30
|
+
debugger-linecache (~> 1.2)
|
31
|
+
slop (~> 3.6)
|
32
|
+
columnize (0.8.9)
|
33
|
+
database_cleaner (1.3.0)
|
34
|
+
debugger-linecache (1.2.0)
|
35
|
+
factory_girl (4.4.0)
|
36
|
+
activesupport (>= 3.0.0)
|
37
|
+
i18n (0.6.11)
|
38
|
+
json (1.8.1)
|
39
|
+
mini_portile (0.6.0)
|
40
|
+
minitest (5.4.1)
|
41
|
+
multi_json (1.10.1)
|
42
|
+
nokogiri (1.6.3.1)
|
43
|
+
mini_portile (= 0.6.0)
|
44
|
+
rake (10.3.2)
|
45
|
+
representable (2.0.4)
|
46
|
+
multi_json
|
47
|
+
nokogiri
|
48
|
+
uber (~> 0.0.7)
|
49
|
+
slop (3.6.0)
|
50
|
+
sqlite3 (1.3.9)
|
51
|
+
thread_safe (0.3.4)
|
52
|
+
tzinfo (1.2.2)
|
53
|
+
thread_safe (~> 0.1)
|
54
|
+
uber (0.0.8)
|
55
|
+
|
56
|
+
PLATFORMS
|
57
|
+
ruby
|
58
|
+
|
59
|
+
DEPENDENCIES
|
60
|
+
awesome_print
|
61
|
+
byebug
|
62
|
+
database_cleaner (~> 1.3)
|
63
|
+
factory_girl (~> 4.4)
|
64
|
+
minitest (~> 5.1)
|
65
|
+
rake (~> 10.3)
|
66
|
+
roaster!
|
67
|
+
sqlite3 (~> 1.3)
|
data/Rakefile
ADDED
data/STUFF
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
WOOT:
|
2
|
+
- How to handle this case (different relationship resource type/name (author != people)):
|
3
|
+
POST /articles/1/links/author
|
4
|
+
{
|
5
|
+
"people": "12"
|
6
|
+
}
|
7
|
+
=> Resource type and name are two different things !
|
8
|
+
|
9
|
+
- How to handle HABTM vs regular to_many ?
|
10
|
+
|
11
|
+
WARNING: Ruby code for illustration purposes, NOT CORRECTNESS
|
12
|
+
|
13
|
+
CREATE RESOURCE:
|
14
|
+
POST /albums
|
15
|
+
{
|
16
|
+
albums: {
|
17
|
+
title: "Animals"
|
18
|
+
}
|
19
|
+
}
|
20
|
+
Album.new(title: 'Animals').save!
|
21
|
+
INSERT INTO `albums` (`title`) VALUES ('Animals');
|
22
|
+
|
23
|
+
DELETE RESOURCE:
|
24
|
+
DELETE /albums/1
|
25
|
+
album = Album.find(1)
|
26
|
+
album.destroy
|
27
|
+
DELETE FROM `album` WHERE `album`.`id` = 1
|
28
|
+
|
29
|
+
GET TO_ONE RELATIONSHIP:
|
30
|
+
GET /albums/1/links/band
|
31
|
+
album = Album.find(1)
|
32
|
+
album.band
|
33
|
+
SELECT `albums`.* FROM `albums` WHERE `albums`.`id` = 1 LIMIT 1;
|
34
|
+
SELECT `bands`.* FROM `bands` WHERE `bands`.`id` = #{album.band_id} LIMIT 1;
|
35
|
+
|
36
|
+
CREATE/REPLACE TO_ONE RELATIONSHIP:
|
37
|
+
POST /albums/1/links/band
|
38
|
+
{
|
39
|
+
bands: "2"
|
40
|
+
}
|
41
|
+
album = Album.find(1)
|
42
|
+
album.band = Band.find(2)
|
43
|
+
UPDATE `albums` SET `band_id` = 2 WHERE `albums`.`id` = 1
|
44
|
+
|
45
|
+
REPLACE TO_ONE RELATIONSHIP (equivalent to ADD/REPLACE above):
|
46
|
+
PUT /albums/1
|
47
|
+
{
|
48
|
+
"articles": {
|
49
|
+
"can_update_other_properties_too": true,
|
50
|
+
"links": {
|
51
|
+
"band": "42"
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
55
|
+
album = Album.find(1)
|
56
|
+
album.update_attributes(attr)
|
57
|
+
album.band = Band.find(42)
|
58
|
+
UPDATE `albums` SET `attr` = attr WHERE `albums`.`id` = 1
|
59
|
+
UPDATE `albums` SET `band_id` = 42 WHERE `albums`.`id` = 1
|
60
|
+
|
61
|
+
DELETE TO_ONE RELATIONSHIP:
|
62
|
+
DELETE /albums/1/links/band
|
63
|
+
album = Album.find(1)
|
64
|
+
album.band = nil (.clear instead ?)
|
65
|
+
UPDATE `albums` SET `band_id` = NULL WHERE `albums`.`id` = 1
|
66
|
+
|
67
|
+
GET TO_MANY RELATIONSHIP:
|
68
|
+
GET /albums/1/links/tracks
|
69
|
+
album = Album.find(1)
|
70
|
+
album.tracks
|
71
|
+
SELECT `tracks`.* FROM `tracks` WHERE `tracks`.`album_id` = 1;
|
72
|
+
|
73
|
+
CREATE/ADD TO_MANY RELATIONSHIPS:
|
74
|
+
POST /album/1/links/tracks
|
75
|
+
{
|
76
|
+
tracks: ['1', '2']
|
77
|
+
}
|
78
|
+
album = Album.find(1)
|
79
|
+
album.tracks << tracks
|
80
|
+
UPDATE `tracks` SET `album_id` = 1 WHERE `tracks`.`id` = 1
|
81
|
+
UPDATE `tracks` SET `album_id` = 1 WHERE `tracks`.`id` = 2
|
82
|
+
|
83
|
+
REPLACE TO_MANY RELATIONSHIP (equivalent to ADD/REPLACE above):
|
84
|
+
PUT /albums/1
|
85
|
+
{
|
86
|
+
"articles": {
|
87
|
+
"can_update_other_properties_too": true,
|
88
|
+
"links": {
|
89
|
+
// This can be an empty array (remove all)
|
90
|
+
"tracks": ["3", "5"]
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}
|
94
|
+
album = Album.find(1)
|
95
|
+
album.update_attributes(attr)
|
96
|
+
album.tracks = tracks
|
97
|
+
UPDATE `tracks` SET `tracks`.`album_id` = NULL WHERE `tracks`.`album_id` = 1 AND `tracks`.`id` IN (#{previous_track_ids})
|
98
|
+
UPDATE `tracks` SET `album_id` = 1 WHERE `tracks`.`id` = 3
|
99
|
+
UPDATE `tracks` SET `album_id` = 1 WHERE `tracks`.`id` = 5
|
100
|
+
|
101
|
+
DELETE TO_MANY RELATIONSHIPS:
|
102
|
+
DELETE /albums/1/links/tracks/3,4
|
103
|
+
album = Album.find(1)
|
104
|
+
album.tracks.where(id: [3, 4]).destroy_all
|
105
|
+
UPDATE `blog_post_pictures` SET `blog_post_pictures`.`post_id` = NULL WHERE `blog_post_pictures`.`id` IN (3, 4)
|
data/lib/roaster.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'representable'
|
2
|
+
require 'representable/json'
|
3
|
+
|
4
|
+
require 'roaster/adapters/active_record'
|
5
|
+
require 'roaster/decorator'
|
6
|
+
require 'roaster/json_api'
|
7
|
+
require 'roaster/query'
|
8
|
+
require 'roaster/request'
|
9
|
+
require 'roaster/version'
|
10
|
+
|
11
|
+
module Roaster
|
12
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module Roaster
|
4
|
+
module Adapters
|
5
|
+
|
6
|
+
class ActiveRecord
|
7
|
+
|
8
|
+
def self.model_class_from_resource_name(resource_name)
|
9
|
+
"#{resource_name.to_s.singularize}".classify.constantize
|
10
|
+
end
|
11
|
+
|
12
|
+
def new(query, model_class: nil)
|
13
|
+
model = model_for(model_class || query.target.resource_name)
|
14
|
+
model.new
|
15
|
+
end
|
16
|
+
|
17
|
+
#TODO: HAX, rethink data path for POST/PUT requests from the start
|
18
|
+
#TODO: #save! not good if we want to delay adapter request execution
|
19
|
+
def save(model)
|
20
|
+
model.save!
|
21
|
+
end
|
22
|
+
|
23
|
+
#TODO: Please refactor me, i'm ugly
|
24
|
+
def _change_relationship(query, rel_name, rel_ids, replace: false)
|
25
|
+
model = model_for(query.target.resource_name)
|
26
|
+
object = model.find(query.target.resource_ids.first)
|
27
|
+
rel_model = rel_name.to_s.classify.constantize
|
28
|
+
rel_meta = model.reflect_on_association(rel_name)
|
29
|
+
rel_object = rel_model.find(rel_ids)
|
30
|
+
case rel_meta.macro
|
31
|
+
when :has_many
|
32
|
+
object.send(rel_name).clear if replace === true
|
33
|
+
object.send(rel_name).push(rel_object)
|
34
|
+
when :belongs_to
|
35
|
+
object.send("#{rel_name}=", rel_object)
|
36
|
+
else
|
37
|
+
raise "#{rel_meta.macro} relationship not implemented"
|
38
|
+
end
|
39
|
+
self.save(object)
|
40
|
+
end
|
41
|
+
|
42
|
+
#TODO:
|
43
|
+
# Document key isn't always rel_name, it's rel_name's resource type
|
44
|
+
# ( not accessible here right now :-( )
|
45
|
+
def create_relationship(query, document)
|
46
|
+
rel_name = query.target.relationship_name
|
47
|
+
rel_ids = document[rel_name.to_s.pluralize]
|
48
|
+
_change_relationship(query, rel_name, rel_ids)
|
49
|
+
end
|
50
|
+
|
51
|
+
def update_relationship(query, document)
|
52
|
+
document.each do |rel_name, rel_ids|
|
53
|
+
_change_relationship(query, rel_name.to_sym, rel_ids, replace: true)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def find(query, model_class: nil)
|
58
|
+
raise 'No ID provided' if query.target.resource_ids.empty?
|
59
|
+
scope_for(query.target, model_class).first
|
60
|
+
end
|
61
|
+
|
62
|
+
def read(query, model_class: nil)
|
63
|
+
q = scope_for(query.target, model_class)
|
64
|
+
query.includes.each do |i|
|
65
|
+
q = q.include(i)
|
66
|
+
end
|
67
|
+
query.filters.each_pair do |k, v|
|
68
|
+
q = q.where(k => v)
|
69
|
+
end
|
70
|
+
query.sorting.each do |resource_name, criteria|
|
71
|
+
criteria.each do |field, direction|
|
72
|
+
q = q.order(model_for(resource_name).arel_table[field].send(direction))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
q
|
76
|
+
end
|
77
|
+
|
78
|
+
def delete(query)
|
79
|
+
q = scope_for(query.target)
|
80
|
+
q.destroy_all
|
81
|
+
q
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def resource_for(resource_name, id = nil)
|
87
|
+
end
|
88
|
+
|
89
|
+
def model_for(model_class_or_name)
|
90
|
+
if model_class_or_name.kind_of?(::ActiveRecord::Base)
|
91
|
+
model_class_or_name
|
92
|
+
else
|
93
|
+
self.class.model_class_from_resource_name(model_class_or_name)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
#TODO: Handle ALL, none should be the default: maybe not ?
|
98
|
+
# Move resource stuff into resource_for
|
99
|
+
def scope_for(target, model_class_or_name = nil)
|
100
|
+
model_class = model_for(model_class_or_name || target.resource_name)
|
101
|
+
scope = model_class.all
|
102
|
+
unless target.resource_ids.empty?
|
103
|
+
scope = scope.where(id: target.resource_ids)
|
104
|
+
end
|
105
|
+
if target.relationship_name
|
106
|
+
raise "Cannot apply relationship #{target.relationship_name} to nil object" if scope.count == 0
|
107
|
+
raise "Cannot apply relationship #{target.relationship_name} to more than one object" if scope.count > 1
|
108
|
+
scope = scope.first.send(target.relationship_name)
|
109
|
+
end
|
110
|
+
scope
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'representable/decorator'
|
2
|
+
|
3
|
+
|
4
|
+
module Roaster
|
5
|
+
|
6
|
+
class Decorator < Representable::Decorator
|
7
|
+
|
8
|
+
def resource_name
|
9
|
+
if defined? @@overloaded_resource_name
|
10
|
+
@@overloaded_resource_name
|
11
|
+
else
|
12
|
+
self.class.to_s.gsub(/Mapping\Z/, '').underscore.pluralize
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
class << self
|
18
|
+
|
19
|
+
def can_filter_by(*attrs)
|
20
|
+
representable_attrs[:_filterable_attributes] ||= []
|
21
|
+
representable_attrs[:_filterable_attributes].push(*attrs.map(&:to_sym)).uniq!
|
22
|
+
end
|
23
|
+
|
24
|
+
def filterable_attributes
|
25
|
+
representable_attrs[:_filterable_attributes] ||= []
|
26
|
+
representable_attrs[:_filterable_attributes]
|
27
|
+
end
|
28
|
+
|
29
|
+
#TODO: Disallow sorting directly by relationship (need relationship field)
|
30
|
+
def can_sort_by(*attrs)
|
31
|
+
representable_attrs[:_sortable_attributes] ||= []
|
32
|
+
sort_keys = attrs.map do |option|
|
33
|
+
if option.class == Hash
|
34
|
+
{ option.first[0].to_sym => option.first[1].map(&:to_sym) }
|
35
|
+
else
|
36
|
+
option.to_sym
|
37
|
+
end
|
38
|
+
end
|
39
|
+
representable_attrs[:_sortable_attributes].push(*sort_keys).uniq!
|
40
|
+
end
|
41
|
+
|
42
|
+
def sortable_attributes
|
43
|
+
representable_attrs[:_sortable_attributes] ||= []
|
44
|
+
representable_attrs[:_sortable_attributes]
|
45
|
+
end
|
46
|
+
|
47
|
+
def can_include(*attrs)
|
48
|
+
representable_attrs[:_includeable_attributes] ||= []
|
49
|
+
representable_attrs[:_includeable_attributes].push(*attrs.map(&:to_sym)).uniq!
|
50
|
+
end
|
51
|
+
|
52
|
+
def includeable_attributes
|
53
|
+
representable_attrs[:_includeable_attributes] ||= []
|
54
|
+
representable_attrs[:_includeable_attributes]
|
55
|
+
end
|
56
|
+
|
57
|
+
def resource_name(name)
|
58
|
+
@@overloaded_resource_name = name
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module Roaster
|
2
|
+
|
3
|
+
# Query represents the operation performed on the target, and its parameters
|
4
|
+
class Query
|
5
|
+
|
6
|
+
# Target represents the resource(s) scope on which is executed the query
|
7
|
+
class Target
|
8
|
+
|
9
|
+
attr_accessor :resource_name, :resource_ids, :relationship_name, :relationship_ids
|
10
|
+
|
11
|
+
def initialize(resource_name,
|
12
|
+
resource_ids = [],
|
13
|
+
relationship_name = nil,
|
14
|
+
relationship_ids = [])
|
15
|
+
@resource_name = resource_name
|
16
|
+
@resource_ids = Array(resource_ids)
|
17
|
+
@relationship_name = relationship_name
|
18
|
+
@relationship_ids = Array(relationship_ids)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
#TODO: This is not validating includes it seems (HARD VALIDATE EVERYTHING, raise is your FRIEND)
|
24
|
+
attr_accessor :page, :page_size, :includes, :fields, :filters,
|
25
|
+
:target,
|
26
|
+
:sorting,
|
27
|
+
:operation
|
28
|
+
|
29
|
+
#TODO: Move in config class
|
30
|
+
DEFAULT_PAGE_SIZE = 10
|
31
|
+
OPERATIONS = [:create, :read, :update, :delete]
|
32
|
+
|
33
|
+
def initialize(operation, target, mapping, params = {})
|
34
|
+
raise "Invalid operation: #{operation}" unless OPERATIONS.include?(operation)
|
35
|
+
params.symbolize_keys! if params.respond_to?(:symbolize_keys!)
|
36
|
+
|
37
|
+
@operation = operation
|
38
|
+
@target = target
|
39
|
+
@page = params[:page] ? params[:page].to_i : 1
|
40
|
+
@page_size = params[:page_size] ? params[:page_size].to_i : DEFAULT_PAGE_SIZE
|
41
|
+
@includes = includes_from_params(params, mapping)
|
42
|
+
@fields = fields_from_params(params, mapping)
|
43
|
+
@filters = filters_from_params(params, mapping)
|
44
|
+
@sorting = sorting_from_params(params, mapping)
|
45
|
+
#VALIDATE THIS (TARGET) ! omgz =D
|
46
|
+
@mapping = mapping
|
47
|
+
end
|
48
|
+
|
49
|
+
def default_page_size?
|
50
|
+
@page_size == DEFAULT_PAGE_SIZE
|
51
|
+
end
|
52
|
+
|
53
|
+
def filters_as_url_params
|
54
|
+
@filters.sort.map { |k,v| map_filter_ids(k,v) }.join('&')
|
55
|
+
end
|
56
|
+
|
57
|
+
def sorting_as_url_params
|
58
|
+
sorting_values = sorting.map { |k, v| v == :asc ? k : "-#{k}" }.join(',')
|
59
|
+
"sort=#{sorting_values}"
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def includes_from_params(params, mapping)
|
65
|
+
return [] unless params[:include] && params[:include].respond_to?(:split)
|
66
|
+
includes = params[:include].split(',').map(&:to_sym)
|
67
|
+
includes.select do |i|
|
68
|
+
mapping.includeable_attributes.include?(i)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_fieldset(fields)
|
73
|
+
fields.to_s.split(',').collect do |field|
|
74
|
+
field.downcase.to_sym
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def fields_from_params(params, mapping)
|
79
|
+
return {} if params[:fields].blank?
|
80
|
+
if params[:fields].class == Hash
|
81
|
+
Hash[params[:fields].collect do |resource_name, fieldset|
|
82
|
+
[resource_name.downcase.to_sym, parse_fieldset(fieldset)]
|
83
|
+
end
|
84
|
+
]
|
85
|
+
else
|
86
|
+
{@target.resource_name => parse_fieldset(params[:fields])}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def filters_from_params(params, mapping)
|
91
|
+
filters = {}
|
92
|
+
mapping.filterable_attributes.each do |filter|
|
93
|
+
filters[filter] = params[filter] if params[filter]
|
94
|
+
end
|
95
|
+
filters
|
96
|
+
end
|
97
|
+
|
98
|
+
def parse_sort_criteria(criteria)
|
99
|
+
sorting_parameters = {}
|
100
|
+
criteria.to_s.split(',').each do |sort_value|
|
101
|
+
sort_order = sort_value[0] == '-' ? :desc : :asc
|
102
|
+
sort_value = sort_value.gsub(/\A\-/, '').downcase.to_sym
|
103
|
+
sorting_parameters[sort_value] = sort_order
|
104
|
+
end
|
105
|
+
sorting_parameters
|
106
|
+
end
|
107
|
+
|
108
|
+
def validate_sorting_parameters(sort, mapping)
|
109
|
+
end
|
110
|
+
|
111
|
+
def sorting_from_params(params, mapping)
|
112
|
+
return {} if params[:sort].blank? || mapping.sortable_attributes.blank?
|
113
|
+
if params[:sort].class == Hash
|
114
|
+
sorting_parameters = {}
|
115
|
+
params[:sort].each do |sorting_resource|
|
116
|
+
sorting_parameters[sorting_resource[0].to_sym] = parse_sort_criteria sorting_resource[1]
|
117
|
+
end
|
118
|
+
sorting_parameters
|
119
|
+
else
|
120
|
+
{@target.resource_name => parse_sort_criteria(params[:sort])}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def map_filter_ids(key,value)
|
125
|
+
case value
|
126
|
+
when Hash
|
127
|
+
value.map { |k,v| map_filter_ids(k,v) }
|
128
|
+
else
|
129
|
+
"#{key}=#{value.join(',')}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def query_to_array(value)
|
134
|
+
case value
|
135
|
+
when String
|
136
|
+
value.split(',')
|
137
|
+
when Hash
|
138
|
+
value.each { |k, v| value[k] = query_to_array(v) }
|
139
|
+
else
|
140
|
+
value
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|