restpack-resource 0.0.9 → 0.1.0
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/Gemfile +0 -1
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +2 -0
- data/lib/restpack-resource/resource.rb +30 -1
- data/lib/restpack-resource/resource/pageable.rb +38 -37
- data/lib/restpack-resource/resource/single.rb +15 -27
- data/lib/restpack-resource/version.rb +1 -1
- data/restpack-resource.gemspec +1 -0
- metadata +19 -9
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# restpack-resource [](https://travis-ci.org/RestPack/restpack-resource) [](https://travis-ci.org/RestPack/restpack-resource) [](https://codeclimate.com/github/RestPack/restpack-resource) [](https://gemnasium.com/RestPack/restpack-resource)
|
2
2
|
|
3
3
|
RESTful resource paging, side-loading, filtering and sorting.
|
4
4
|
|
data/Rakefile
CHANGED
@@ -23,7 +23,36 @@ module RestPack
|
|
23
23
|
|
24
24
|
module ClassMethods
|
25
25
|
def model_as_resource(model)
|
26
|
-
model.as_resource()
|
26
|
+
model ? model.as_resource() : nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def association_relationships(association)
|
30
|
+
target_model_name = association.to_s.singularize.capitalize
|
31
|
+
relationships = self.relationships.select {|r| r.target_model.to_s == target_model_name }
|
32
|
+
raise InvalidInclude if relationships.empty?
|
33
|
+
relationships
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_child_models(relationship, foreign_keys, page_size)
|
37
|
+
child_key_name = relationship.child_key.first.name
|
38
|
+
relationship.child_model.all(child_key_name.to_sym => foreign_keys).page({ per_page: page_size })
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_one_to_many_side_loads(relationship, foreign_keys)
|
42
|
+
result = {}
|
43
|
+
children = get_child_models(relationship, foreign_keys, 100) #TODO: GJ: configurable side-load page size
|
44
|
+
result[:resources] = children.map {|c| c.as_resource() }
|
45
|
+
result[:count_key] = "#{relationship.child_model_name.downcase}_count".to_sym
|
46
|
+
result[:count] = children.pager.total
|
47
|
+
result
|
48
|
+
end
|
49
|
+
|
50
|
+
def extract_includes_array_from_params(params)
|
51
|
+
params[:includes].nil? ? [] : params[:includes].split(',')
|
52
|
+
end
|
53
|
+
|
54
|
+
def invalid_include(relationship)
|
55
|
+
raise InvalidInclude, "#{self.name}.#{relationship.name} can't be included when paging #{self.name.pluralize.downcase}"
|
27
56
|
end
|
28
57
|
end
|
29
58
|
end
|
@@ -11,66 +11,67 @@ module RestPack
|
|
11
11
|
protected
|
12
12
|
|
13
13
|
def get_paged_resource(options)
|
14
|
-
|
14
|
+
paged_models = get_paged_models(options)
|
15
15
|
|
16
16
|
paged_resource = {
|
17
|
-
:page =>
|
18
|
-
:page_count =>
|
19
|
-
:count =>
|
20
|
-
:previous_page =>
|
21
|
-
:next_page =>
|
17
|
+
:page => paged_models.pager.current_page,
|
18
|
+
:page_count => paged_models.pager.total_pages,
|
19
|
+
:count => paged_models.pager.total,
|
20
|
+
:previous_page => paged_models.pager.previous_page,
|
21
|
+
:next_page => paged_models.pager.next_page
|
22
22
|
}
|
23
23
|
|
24
|
-
paged_resource[self.resource_collection_name] =
|
24
|
+
paged_resource[self.resource_collection_name] = paged_models.map { |model| model_as_resource(model) }
|
25
25
|
|
26
|
-
unless
|
26
|
+
unless paged_models.empty?
|
27
27
|
options[:includes].each do |association|
|
28
|
-
|
28
|
+
paged_resource.merge! get_side_loads(paged_models, association)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
paged_resource
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
35
|
+
def get_paged_models(options)
|
36
36
|
order = options[:sort_by]
|
37
37
|
order = order.desc if order && options[:sort_direction] == :descending
|
38
38
|
|
39
39
|
options[:scope].all(:conditions => options[:filters]).page(options[:page], :order => order)
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
raise InvalidInclude if relationships.empty?
|
42
|
+
def get_side_loads(paged_models, association)
|
43
|
+
side_loads = {}
|
44
|
+
resources = []
|
46
45
|
|
47
|
-
|
48
|
-
|
49
|
-
relationships.each do |relationship|
|
46
|
+
association_relationships(association).each do |relationship|
|
50
47
|
if relationship.is_a? DataMapper::Associations::ManyToOne::Relationship
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
child_key_name = relationship.child_key.first.name
|
58
|
-
foreign_keys = page.map {|e| e.send(parent_key_name)}.uniq
|
59
|
-
|
60
|
-
#TODO: GJ: configurable side-load page size
|
61
|
-
children = relationship.child_model.all(child_key_name.to_sym => foreign_keys).page({ per_page: 100 })
|
62
|
-
side_loaded_entities += children.map { |c| model_as_resource(c) }
|
63
|
-
|
64
|
-
count_key = "#{relationship.child_model_name.downcase}_count".to_sym
|
65
|
-
paged_resource[count_key] = children.pager.total
|
48
|
+
resources += get_many_to_one_side_loads(paged_models, relationship)
|
49
|
+
elsif relationship.is_a? DataMapper::Associations::OneToMany::Relationship
|
50
|
+
foreign_keys = get_foreign_keys(paged_models, relationship)
|
51
|
+
side_load = get_one_to_many_side_loads(relationship, foreign_keys)
|
52
|
+
resources += side_load[:resources]
|
53
|
+
side_loads[side_load[:count_key]] = side_load[:count]
|
66
54
|
else
|
67
|
-
|
55
|
+
invalid_include relationship
|
68
56
|
end
|
69
57
|
end
|
70
58
|
|
71
|
-
|
72
|
-
|
73
|
-
|
59
|
+
side_loads[association] = resources.uniq.compact
|
60
|
+
side_loads
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_many_to_one_side_loads(paged_models, relationship)
|
64
|
+
paged_models.map do |model|
|
65
|
+
relation = model.send(relationship.name.to_sym)
|
66
|
+
model_as_resource(relation)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
def get_foreign_keys(paged_models, relationship)
|
73
|
+
parent_key_name = relationship.parent_key.first.name
|
74
|
+
paged_models.map {|e| e.send(parent_key_name)}.uniq
|
74
75
|
end
|
75
76
|
|
76
77
|
def resource_collection_name
|
@@ -80,7 +81,7 @@ module RestPack
|
|
80
81
|
def build_paged_options(params, overrides)
|
81
82
|
options = overrides.reverse_merge( #overrides take precedence over params
|
82
83
|
:page => params[:page],
|
83
|
-
:includes => params
|
84
|
+
:includes => extract_includes_array_from_params(params),
|
84
85
|
:filters => self.extract_filters_from_params(params),
|
85
86
|
:sort_by => params[:sort_by],
|
86
87
|
:sort_direction => params[:sort_direction]
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module RestPack
|
2
2
|
module Resource
|
3
|
-
#NOTE: GJ: this need a great big refactor
|
4
3
|
module Single
|
5
4
|
def single_resource(params = {}, overrides = {})
|
6
5
|
options = build_single_options(params, overrides)
|
@@ -16,51 +15,40 @@ module RestPack
|
|
16
15
|
resource = model_as_resource(model)
|
17
16
|
|
18
17
|
options[:includes].each do |association|
|
19
|
-
|
18
|
+
resource.merge! get_single_side_loads(model, association)
|
20
19
|
end
|
21
20
|
|
22
21
|
resource
|
23
22
|
end
|
24
23
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
relationships = self.relationships.select {|r| r.target_model.to_s == target_model_name }
|
29
|
-
raise InvalidInclude if relationships.empty?
|
24
|
+
def get_single_side_loads(model, association)
|
25
|
+
side_loads = {}
|
26
|
+
resources = []
|
30
27
|
|
31
|
-
|
32
|
-
|
33
|
-
relationships.each do |relationship|
|
28
|
+
association_relationships(association).each do |relationship|
|
34
29
|
if relationship.is_a? DataMapper::Associations::ManyToOne::Relationship
|
35
30
|
relation = model.send(relationship.name.to_sym)
|
36
|
-
|
37
|
-
side_loaded_entities << (relation ? model_as_resource(relation) : nil)
|
31
|
+
resources << model_as_resource(relation)
|
38
32
|
elsif relationship.is_a? DataMapper::Associations::OneToMany::Relationship
|
39
33
|
parent_key_name = relationship.parent_key.first.name
|
40
|
-
|
41
|
-
foreign_key = model.send(parent_key_name)
|
42
|
-
|
43
|
-
#TODO: GJ: configurable side-load page size
|
44
|
-
children = relationship.child_model.all(child_key_name.to_sym => foreign_key).page({ per_page: 100 })
|
45
|
-
side_loaded_entities += children.map { |c| model_as_resource(c) }
|
34
|
+
foreign_keys = [model.send(parent_key_name)]
|
46
35
|
|
47
|
-
|
48
|
-
|
36
|
+
side_load = get_one_to_many_side_loads(relationship, foreign_keys)
|
37
|
+
resources += side_load[:resources]
|
38
|
+
side_loads[side_load[:count_key]] = side_load[:count]
|
49
39
|
else
|
50
|
-
|
40
|
+
invalid_include relationship
|
51
41
|
end
|
52
42
|
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
resource[association] = side_loaded_entities
|
43
|
+
|
44
|
+
side_loads[association] = resources.uniq.compact
|
45
|
+
side_loads
|
58
46
|
end
|
59
47
|
|
60
48
|
def build_single_options(params, overrides)
|
61
49
|
options = overrides.reverse_merge( #overrides take precedence over params
|
62
50
|
:id => params[:id],
|
63
|
-
:includes => params
|
51
|
+
:includes => extract_includes_array_from_params(params)
|
64
52
|
)
|
65
53
|
|
66
54
|
options.reverse_merge!( #defaults
|
data/restpack-resource.gemspec
CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.require_paths = ["lib"]
|
19
19
|
|
20
20
|
gem.add_dependency 'activesupport', '~> 3.2.11'
|
21
|
+
gem.add_dependency 'datamapper', '~> 1.2.0'
|
21
22
|
gem.add_dependency 'dm-pager', '~> 1.1.0'
|
22
23
|
|
23
24
|
gem.add_development_dependency 'rspec', '~> 2.12'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: restpack-resource
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 3.2.11
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: datamapper
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.2.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.2.0
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
47
|
name: dm-pager
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -102,21 +118,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
102
118
|
- - ! '>='
|
103
119
|
- !ruby/object:Gem::Version
|
104
120
|
version: '0'
|
105
|
-
segments:
|
106
|
-
- 0
|
107
|
-
hash: 1427584335195097292
|
108
121
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
122
|
none: false
|
110
123
|
requirements:
|
111
124
|
- - ! '>='
|
112
125
|
- !ruby/object:Gem::Version
|
113
126
|
version: '0'
|
114
|
-
segments:
|
115
|
-
- 0
|
116
|
-
hash: 1427584335195097292
|
117
127
|
requirements: []
|
118
128
|
rubyforge_project:
|
119
|
-
rubygems_version: 1.8.
|
129
|
+
rubygems_version: 1.8.24
|
120
130
|
signing_key:
|
121
131
|
specification_version: 3
|
122
132
|
summary: ! '...'
|