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 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: ! '...'