restpack-resource 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -3,7 +3,6 @@ source 'https://rubygems.org'
3
3
  group :test do
4
4
  gem 'rake', '~> 10.0.3'
5
5
  gem 'rspec', :require => 'spec'
6
- gem 'datamapper', '~> 1.2.0'
7
6
  gem 'dm-sqlite-adapter', '~> 1.2.0'
8
7
  gem 'factory_girl', '~> 4.2.0'
9
8
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- restpack-resource (0.0.9)
4
+ restpack-resource (0.0.8)
5
5
  activesupport (~> 3.2.11)
6
6
  dm-pager (~> 1.1.0)
7
7
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # restpack-resource [![Build Status](https://api.travis-ci.org/RestPack/restpack-resource.png?branch=master)](https://travis-ci.org/RestPack/restpack-resource) [![Code Climate](https://codeclimate.com/github/RESTpack/restpack-resource.png)](https://codeclimate.com/github/RESTpack/restpack-resource) [![Dependency Status](https://gemnasium.com/RestPack/restpack-resource.png)](https://gemnasium.com/RestPack/restpack-resource)
1
+ # restpack-resource [![Build Status](https://api.travis-ci.org/RestPack/restpack-resource.png?branch=master)](https://travis-ci.org/RestPack/restpack-resource) [![Code Climate](https://codeclimate.com/github/RestPack/restpack-resource.png)](https://codeclimate.com/github/RestPack/restpack-resource) [![Dependency Status](https://gemnasium.com/RestPack/restpack-resource.png)](https://gemnasium.com/RestPack/restpack-resource)
2
2
 
3
3
  RESTful resource paging, side-loading, filtering and sorting.
4
4
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ require_relative 'lib/restpack-resource/version'
2
+
1
3
  task :default => :test
2
4
  task :test => :spec
3
5
 
@@ -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
- page = get_page(options)
14
+ paged_models = get_paged_models(options)
15
15
 
16
16
  paged_resource = {
17
- :page => page.pager.current_page,
18
- :page_count => page.pager.total_pages,
19
- :count => page.pager.total,
20
- :previous_page => page.pager.previous_page,
21
- :next_page => page.pager.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] = page.map { |item| model_as_resource(item) }
24
+ paged_resource[self.resource_collection_name] = paged_models.map { |model| model_as_resource(model) }
25
25
 
26
- unless page.empty?
26
+ unless paged_models.empty?
27
27
  options[:includes].each do |association|
28
- add_side_loads(paged_resource, page, association)
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 get_page(options)
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 add_side_loads(paged_resource, page, association)
43
- target_model_name = association.to_s.singularize.capitalize
44
- relationships = self.relationships.select {|r| r.target_model.to_s == target_model_name }
45
- raise InvalidInclude if relationships.empty?
42
+ def get_side_loads(paged_models, association)
43
+ side_loads = {}
44
+ resources = []
46
45
 
47
- side_loaded_entities = []
48
-
49
- relationships.each do |relationship|
46
+ association_relationships(association).each do |relationship|
50
47
  if relationship.is_a? DataMapper::Associations::ManyToOne::Relationship
51
- side_loaded_entities += page.map do |entity| #TODO: GJ: PERF: we can bypass datamapper associations and get by a list of ids instead
52
- relation = entity.send(relationship.name.to_sym)
53
- relation ? model_as_resource(relation) : nil
54
- end
55
- elsif relationship.is_a? DataMapper::Associations::OneToMany::Relationship
56
- parent_key_name = relationship.parent_key.first.name
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
- raise InvalidInclude, "#{self.name}.#{relationship.name} can't be included when paging #{self.name.pluralize.downcase}"
55
+ invalid_include relationship
68
56
  end
69
57
  end
70
58
 
71
- side_loaded_entities.uniq!
72
- side_loaded_entities.compact!
73
- paged_resource[association] = side_loaded_entities
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[:includes].nil? ? [] : params[:includes].split(','),
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
- add_single_side_loads(resource, model, association)
18
+ resource.merge! get_single_side_loads(model, association)
20
19
  end
21
20
 
22
21
  resource
23
22
  end
24
23
 
25
- def add_single_side_loads(resource, model, association)
26
- target_model_name = association.to_s.singularize.capitalize
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
- side_loaded_entities = []
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
- child_key_name = relationship.child_key.first.name
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
- count_key = "#{relationship.child_model_name.downcase}_count".to_sym
48
- resource[count_key] = children.pager.total
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
- raise InvalidInclude, "#{self.name}.#{relationship.name} can't be included when paging #{self.name.pluralize.downcase}"
40
+ invalid_include relationship
51
41
  end
52
42
  end
53
-
54
- side_loaded_entities.uniq!
55
- side_loaded_entities.compact!
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[:includes].nil? ? [] : params[:includes].split(',') #TODO: GJ: refactor, repeated in pageable
51
+ :includes => extract_includes_array_from_params(params)
64
52
  )
65
53
 
66
54
  options.reverse_merge!( #defaults
@@ -1,5 +1,5 @@
1
1
  module RestPack
2
2
  module Resource
3
- VERSION = "0.0.9"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
@@ -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.9
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-10 00:00:00.000000000 Z
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.25
129
+ rubygems_version: 1.8.24
120
130
  signing_key:
121
131
  specification_version: 3
122
132
  summary: ! '...'