intermodal 0.0.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +1 -1
  5. data/README +6 -4
  6. data/Rakefile +6 -0
  7. data/intermodal.gemspec +51 -0
  8. data/lib/generators/intermodal_generator.rb +8 -0
  9. data/lib/intermodal.rb +122 -0
  10. data/lib/intermodal/api.rb +246 -0
  11. data/lib/intermodal/api/configuration.rb +40 -0
  12. data/lib/intermodal/api/railties.rb +21 -0
  13. data/lib/intermodal/concerns/acceptors/named_resource.rb +12 -0
  14. data/lib/intermodal/concerns/acceptors/resource.rb +12 -0
  15. data/lib/intermodal/concerns/controllers/accountability.rb +17 -0
  16. data/lib/intermodal/concerns/controllers/anonymous.rb +24 -0
  17. data/lib/intermodal/concerns/controllers/authenticatable.rb +24 -0
  18. data/lib/intermodal/concerns/controllers/paginated_collection.rb +25 -0
  19. data/lib/intermodal/concerns/controllers/presentation.rb +17 -0
  20. data/lib/intermodal/concerns/controllers/resource.rb +51 -0
  21. data/lib/intermodal/concerns/controllers/resource_linking.rb +58 -0
  22. data/lib/intermodal/concerns/let.rb +21 -0
  23. data/lib/intermodal/concerns/models/access_credential.rb +28 -0
  24. data/lib/intermodal/concerns/models/account.rb +16 -0
  25. data/lib/intermodal/concerns/models/accountability.rb +47 -0
  26. data/lib/intermodal/concerns/models/db_access_token.rb +29 -0
  27. data/lib/intermodal/concerns/models/has_parent_resource.rb +45 -0
  28. data/lib/intermodal/concerns/models/presentation.rb +25 -0
  29. data/lib/intermodal/concerns/models/redis_access_token.rb +60 -0
  30. data/lib/intermodal/concerns/models/resource_linking.rb +126 -0
  31. data/lib/intermodal/concerns/models/sanitize_html.rb +35 -0
  32. data/lib/intermodal/concerns/presenters/named_resource.rb +12 -0
  33. data/lib/intermodal/concerns/presenters/resource.rb +14 -0
  34. data/lib/intermodal/concerns/rails/rails_3_stack.rb +42 -0
  35. data/lib/intermodal/concerns/rails/rails_4_stack.rb +17 -0
  36. data/lib/intermodal/concerns/rails/use_warden.rb +21 -0
  37. data/lib/intermodal/config.rb +15 -0
  38. data/lib/intermodal/configuration.rb +11 -0
  39. data/lib/intermodal/controllers/api_controller.rb +26 -0
  40. data/lib/intermodal/controllers/linking_resource_controller.rb +8 -0
  41. data/lib/intermodal/controllers/nested_resource_controller.rb +18 -0
  42. data/lib/intermodal/controllers/resource_controller.rb +11 -0
  43. data/lib/intermodal/dsl/controllers.rb +125 -0
  44. data/lib/intermodal/dsl/mapping.rb +79 -0
  45. data/lib/intermodal/dsl/presentation_helpers.rb +107 -0
  46. data/lib/intermodal/mapping/acceptor.rb +2 -2
  47. data/lib/intermodal/mapping/mapper.rb +39 -13
  48. data/lib/intermodal/mapping/presenter.rb +12 -6
  49. data/lib/intermodal/proxies/linking_resources.rb +58 -0
  50. data/lib/intermodal/proxies/will_paginate.rb +85 -0
  51. data/lib/intermodal/rack/auth.rb +29 -0
  52. data/lib/intermodal/rack/dummy_store.rb +24 -0
  53. data/lib/intermodal/rack/rescue.rb +82 -0
  54. data/lib/intermodal/responders/linking_resource_responder.rb +21 -0
  55. data/lib/intermodal/responders/resource_responder.rb +64 -0
  56. data/lib/intermodal/rspec/acceptors.rb +79 -0
  57. data/lib/intermodal/rspec/models/accountability.rb +114 -0
  58. data/lib/intermodal/rspec/models/has_parent_resource.rb +132 -0
  59. data/lib/intermodal/rspec/models/resource_linking.rb +234 -0
  60. data/lib/intermodal/rspec/models/sanitization.rb +84 -0
  61. data/lib/intermodal/rspec/presenters.rb +92 -0
  62. data/lib/intermodal/rspec/requests/authenticated_requests.rb +17 -0
  63. data/lib/intermodal/rspec/requests/linked_resources.rb +180 -0
  64. data/lib/intermodal/rspec/requests/paginated_collection.rb +60 -0
  65. data/lib/intermodal/rspec/requests/rack.rb +142 -0
  66. data/lib/intermodal/rspec/requests/request_validations.rb +36 -0
  67. data/lib/intermodal/rspec/requests/resources.rb +275 -0
  68. data/lib/intermodal/rspec/requests/rfc2616_status_codes.rb +51 -0
  69. data/lib/intermodal/rspec/validators.rb +86 -0
  70. data/lib/intermodal/validators/account_validator.rb +27 -0
  71. data/lib/intermodal/validators/different_account_validator.rb +27 -0
  72. data/lib/intermodal/version.rb +3 -0
  73. data/spec/mapping/acceptors_spec.rb +142 -0
  74. data/spec/mapping/presenters_spec.rb +186 -0
  75. data/spec/models/accountability_spec.rb +13 -0
  76. data/spec/models/has_parent_resource_spec.rb +18 -0
  77. data/spec/models/resource_linking_spec.rb +21 -0
  78. data/spec/proxies/will_paginate_spec.rb +163 -0
  79. data/spec/rack/auth_spec.rb +51 -0
  80. data/spec/requests/linked_resources.rb +37 -0
  81. data/spec/requests/nested_resources_spec.rb +54 -0
  82. data/spec/requests/resources_spec.rb +50 -0
  83. data/spec/spec_helper.rb +53 -0
  84. data/spec/support/api.rb +50 -0
  85. data/spec/support/app/class_builder.rb +41 -0
  86. data/spec/support/app/db/adapter_helper.rb +53 -0
  87. data/spec/support/app/db/authentication_schema_helper.rb +62 -0
  88. data/spec/support/app/db/migration_helper.rb +44 -0
  89. data/spec/support/app/schema.rb +101 -0
  90. data/spec/support/application.rb +23 -0
  91. data/spec/support/blueprints.rb +41 -0
  92. data/spec/support/epiphyte.rb +29 -0
  93. metadata +393 -52
  94. data/lib/intermodal/base.rb +0 -13
  95. data/lib/intermodal/declare_controllers.rb +0 -102
  96. data/lib/intermodal/mapping.rb +0 -4
  97. data/lib/intermodal/mapping/dsl.rb +0 -76
@@ -0,0 +1,27 @@
1
+ require 'active_model/validator'
2
+
3
+ module Intermodal
4
+ module Validators
5
+ # Validates the associated resource is owned by the same account
6
+ # Examples:
7
+ #
8
+ # validates parent_id, account: true
9
+ # validates parent_id, account: { through: association }
10
+ #
11
+ class AccountValidator < ActiveModel::EachValidator
12
+ def validate_each(record, attribute, value)
13
+ return if value.nil?
14
+ association_name = options[:through] || attribute.to_s.gsub(/_id/, '')
15
+ return if record.send(association_name).account_id == record.account_id
16
+ record.errors[attribute] << message
17
+ end
18
+
19
+ def message
20
+ options[:message] || 'must belong to the same account'
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+
27
+ AccountValidator = Intermodal::Validators::AccountValidator unless defined? AccountValidator
@@ -0,0 +1,27 @@
1
+ require 'active_model/validator'
2
+
3
+ module Intermodal
4
+ module Validators
5
+ # Validates the associated resource is owned by a different account
6
+ # Examples:
7
+ #
8
+ # validates parent_id, different_account: true
9
+ # validates parent_id, different_account: { through: association }
10
+ #
11
+ class DifferentAccountValidator < ActiveModel::EachValidator
12
+ def validate_each(record, attribute, value)
13
+ return if value.nil?
14
+ association_name = options[:through] || attribute.to_s.gsub(/_id/, '')
15
+ return if record.send(association_name).account_id != record.account_id
16
+ record.errors[attribute] << message
17
+ end
18
+
19
+ def message
20
+ options[:message] || 'must belong to a different account'
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+
27
+ DifferentAccountValidator = Intermodal::Validators::DifferentAccountValidator unless defined? DifferentAccountValidator
@@ -0,0 +1,3 @@
1
+ module Intermodal
2
+ VERSION = "0.4.0"
3
+ end
@@ -0,0 +1,142 @@
1
+ require 'spec_helper'
2
+
3
+ describe Intermodal::Mapping::Acceptor do
4
+ include SpecHelpers::ClassBuilder
5
+
6
+ describe '.call' do
7
+ subject { acceptor.call(params) }
8
+
9
+ context 'with pass-through mappings' do
10
+ let(:acceptor) do
11
+ define_class :TestAcceptor, Intermodal::Mapping::Acceptor do
12
+ accepts :acceptable_field
13
+ accepts :optional_field
14
+ end
15
+ end
16
+
17
+ let(:value) { 'Accepted Field' }
18
+ let(:params) { { :acceptable_field => value, :unacceptable_field => true } }
19
+
20
+ it 'should accept explicitly whitelisted fields' do
21
+ should include(:acceptable_field)
22
+ end
23
+
24
+ it 'should pass through value without transformation' do
25
+ subject[:acceptable_field].should eql(value)
26
+ end
27
+
28
+ it 'should not accept undeclared fields' do
29
+ should_not include(:unacceptable_field)
30
+ end
31
+
32
+ context 'without optional field in params' do
33
+ # Since we pass the output of an acceptor directly into
34
+ # the active record #create or #update_attributes, we want
35
+ # the hash to look like:
36
+ #
37
+ # { :acceptable_fields => 'Something' }
38
+ #
39
+ # rather than
40
+ #
41
+ # { :acceptable_field => 'Something', :optional_field => nil }
42
+ #
43
+ # Doing so will update the resource and null out the fields that
44
+ # have not been passed through params.
45
+ it 'should not include the optional field' do
46
+ should_not include(:optional_field)
47
+ end
48
+ end
49
+
50
+ context 'with optional field in params' do
51
+ let(:params) { { :acceptable_field => value, :optional_field => value } }
52
+
53
+ it 'should include the optional field' do
54
+ should include(:optional_field)
55
+ end
56
+
57
+ it 'should pass through value without transformation' do
58
+ subject[:optional_field].should eql(value)
59
+ end
60
+ end
61
+ end
62
+
63
+ context 'with pass-through alias mappings' do
64
+ let(:acceptor) do
65
+ define_class :TestAcceptor, Intermodal::Mapping::Acceptor do
66
+ accepts :name, :with => :full_name
67
+ end
68
+ end
69
+
70
+ let(:value) { 'Henry Deacon' }
71
+ let(:params) { { :full_name => value } }
72
+
73
+ it 'should present explicitly whitelisted fields' do
74
+ should include(:name)
75
+ end
76
+
77
+ it 'should pass through value without transformation' do
78
+ subject[:name].should eql(params[:full_name])
79
+ end
80
+
81
+ it 'should not present undeclared fields' do
82
+ should_not include(:full_name)
83
+ end
84
+ end
85
+
86
+ context 'with nested mappings' do
87
+ let(:acceptor) do
88
+ define_class :TestAcceptor, Intermodal::Mapping::Acceptor do
89
+ # Use this to create nested parameters from flattened parameters
90
+ accepts :name, :with => [ :first_name, :last_name ]
91
+ end
92
+ end
93
+
94
+ let(:first_name) { 'Henry' }
95
+ let(:last_name) { 'Deacon' }
96
+ let(:params) { { :first_name => first_name, :last_name => last_name } }
97
+
98
+ it 'should merge fields into a single hash of fields' do
99
+ should include(:name)
100
+ subject[:name].should include(:first_name)
101
+ subject[:name].should include(:last_name)
102
+ end
103
+
104
+ it 'should merge fields without transforming its value' do
105
+ subject[:name][:first_name].should eql(first_name)
106
+ subject[:name][:last_name].should eql(last_name)
107
+ end
108
+
109
+ # Unless you've explicitly declared the merged fields, it should
110
+ # not include it.
111
+ it 'should not accept merged fields' do
112
+ should_not include(:first_name)
113
+ should_not include(:last_name)
114
+ end
115
+ end
116
+
117
+ context 'with arbitrary mappings' do
118
+ let(:acceptor) do
119
+ define_class :TestAcceptor, Intermodal::Mapping::Acceptor do
120
+ # This lets you use aribrary functions to map out accepted fields.
121
+ # Beware! For many use-cases, declaring these in the model works better.
122
+ # Beware! Do not use functions with side-effects
123
+ accepts :phone_number, :with => lambda { |o| o[:phone_number].gsub(/[^0-9]/, '') }
124
+ end
125
+ end
126
+
127
+ let(:value) { '555-555-5555' }
128
+ let(:params) { { :phone_number => value } }
129
+
130
+ it 'should accept an arbitrary mapping' do
131
+ should include(:phone_number)
132
+ end
133
+
134
+ it 'should transform the value' do
135
+ subject[:phone_number].should_not eql(value)
136
+ end
137
+ end
138
+
139
+ pending 'with scoped acceptor'
140
+ end
141
+ end
142
+
@@ -0,0 +1,186 @@
1
+ require 'spec_helper'
2
+
3
+ describe Intermodal::Mapping::Presenter do
4
+ include SpecHelpers::ClassBuilder
5
+ include SpecHelpers::Application
6
+
7
+ describe '.call' do
8
+ subject { presenter.call(resource) }
9
+ let(:resource) { item }
10
+
11
+ context 'with pass-through mappings' do
12
+ let(:presenter) do
13
+ define_class :TestPresenter, Intermodal::Mapping::Presenter do
14
+ presents :name
15
+ presents :description
16
+ end
17
+ end
18
+
19
+ it 'should present explicitly whitelisted fields' do
20
+ should include(:name)
21
+ end
22
+
23
+ it 'should pass through value without transformation' do
24
+ subject[:name].should eql(resource.name)
25
+ end
26
+
27
+ it 'should present all declared fields regardless of value' do
28
+ should include(:description)
29
+ subject[:description].should be_nil
30
+ end
31
+ end
32
+
33
+ context 'with pass-through alias mappings' do
34
+ let(:presenter) do
35
+ define_class :TestPresenter, Intermodal::Mapping::Presenter do
36
+ presents :full_name, :with => :name
37
+ end
38
+ end
39
+
40
+ it 'should present explicitly whitelisted fields' do
41
+ should include(:full_name)
42
+ end
43
+
44
+ it 'should pass through value without transformation' do
45
+ subject[:full_name].should eql(resource.name)
46
+ end
47
+
48
+ it 'should not present undeclared fields' do
49
+ should_not include(:name)
50
+ end
51
+ end
52
+
53
+ context 'with nested mappings' do
54
+ let(:presenter) do
55
+ define_class :TestPresenter, Intermodal::Mapping::Presenter do
56
+
57
+ # Use this to create nested presentation
58
+ presents :metadata, :with => [ :name, :description ]
59
+ end
60
+ end
61
+
62
+ let(:resource) { Item.make(:name => name, :description => description) }
63
+ let(:name) { 'Weird Eureka Thing' }
64
+ let(:description) { 'Makes Eureka Go Boom!' }
65
+
66
+ it 'should merge fields into a single hash of fields' do
67
+ should include(:metadata)
68
+ subject[:metadata].should include(:name)
69
+ subject[:metadata].should include(:description)
70
+ end
71
+
72
+ it 'should merge fields without transforming its value' do
73
+ subject[:metadata][:name].should eql(name)
74
+ subject[:metadata][:description].should eql(description)
75
+ end
76
+
77
+ # Unless you've explicitly declared the merged fields, it should
78
+ # not present it.
79
+ it 'should not present undeclared fields' do
80
+ should_not include(:name)
81
+ should_not include(:description)
82
+ end
83
+ end
84
+
85
+ pending 'with always_nest_collection'
86
+ context 'with arbitrary mappings' do
87
+ let(:presenter) do
88
+ define_class :TestPresenter, Intermodal::Mapping::Presenter do
89
+ # This lets you use aribrary functions to map out accepted fields.
90
+ # Beware! For many use-cases, declaring these in the model works better.
91
+ # Beware! Do not use functions with side-effects
92
+ presents :name, :with => lambda { |o| o[:name].upcase }
93
+ end
94
+ end
95
+
96
+ it 'should accept an arbitrary mapping' do
97
+ should include(:name)
98
+ end
99
+
100
+ it 'should transform the value' do
101
+ subject[:name].should_not eql(resource.name)
102
+ end
103
+ end
104
+
105
+ context 'when scoping' do
106
+ subject { presenter.call(resource, :scope => scope) }
107
+
108
+ context 'without scope declaration' do
109
+ let(:scope) { :default }
110
+ let(:presenter) do
111
+ define_class :TestPresenter, Intermodal::Mapping::Presenter do
112
+ presents :name
113
+ end
114
+ end
115
+
116
+ it 'should default to :default scope' do
117
+ should include(:name)
118
+ subject[:name].should eql(resource.name)
119
+ end
120
+ end
121
+
122
+ context 'with mixed scopes' do
123
+ let(:scope) { :default }
124
+ let(:presenter) do
125
+ define_class :TestPresenter, Intermodal::Mapping::Presenter do
126
+ presents :name
127
+ presents :description, :scope => :details
128
+ end
129
+ end
130
+
131
+ let(:resource) { Item.make(:name => name, :description => description) }
132
+ let(:name) { 'Weird Eureka Thing' }
133
+ let(:description) { 'Makes Eureka Go Boom!' }
134
+
135
+ it 'should default to :default scope' do
136
+ should include(:name)
137
+ subject[:name].should eql(resource.name)
138
+ end
139
+
140
+ it 'should not present attributes outside of scope' do
141
+ should_not include(:description)
142
+ end
143
+
144
+ context 'when called with non-default scope' do
145
+ let(:scope) { :details }
146
+
147
+ it 'should include non-overlapping attributes from default scope' do
148
+ should include(:name)
149
+ subject[:name].should eql(resource.name)
150
+ end
151
+
152
+ it 'should present attributes from non-default scope' do
153
+ should include(:description)
154
+ subject[:description].should eql(resource.description)
155
+ end
156
+ end
157
+ end
158
+
159
+ context 'with overlapping scopes' do
160
+ let(:scope) { :default }
161
+ let(:presenter) do
162
+ define_class :TestPresenter, Intermodal::Mapping::Presenter do
163
+ presents :name
164
+ presents :name, :with => lambda { |o| 'boo!' }, :scope => :details
165
+ end
166
+ end
167
+
168
+ it 'should default to :default scope' do
169
+ should include(:name)
170
+ subject[:name].should eql(resource.name)
171
+ end
172
+
173
+ context 'when called with non-default scope' do
174
+ let(:scope) { :details }
175
+
176
+ it 'should present attributes from non-default scope' do
177
+ should include(:name)
178
+ subject[:name].should_not eql(resource.name)
179
+ subject[:name].should eql('boo!')
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
186
+
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Intermodal::Models::Accountability do
4
+ context 'when implemented as a resource' do
5
+ include SpecHelpers::Application
6
+ include Intermodal::RSpec::Accountability
7
+
8
+ subject { item }
9
+ implements_get_interface
10
+
11
+ pending 'should belong to account model'
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Intermodal::Models::HasParentResource do
4
+ context 'when implemented as a resource' do
5
+ include SpecHelpers::Application
6
+ include Intermodal::RSpec::HasParentResource
7
+
8
+ subject { part }
9
+ let(:parent_resource_name) { :item }
10
+ let(:different_parent) { Item.make!(:account => account) }
11
+ let(:parent_with_different_account) { Item.make!(:account => different_account) }
12
+ let(:different_account) { Account.make! }
13
+
14
+ implements_get_interface_for_nested_resource
15
+
16
+ pending 'should belong to parent model'
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Intermodal::Models::ResourceLinking do
4
+ context 'when implemented as a resource' do
5
+ include SpecHelpers::Application
6
+ include Intermodal::RSpec::ResourceLinking
7
+
8
+ subject { sku }
9
+
10
+ let(:model) { Sku }
11
+ let(:skus) { item.vendors << vendors }
12
+
13
+ # Protip: sku, item, and vendors can be defined in a global helper, such as SpecHelpers::Application
14
+ let(:sku) { Sku.create(:item => item, :vendor => vendor) }
15
+ let(:item) { Item.make!(:account => account) }
16
+ let(:vendor) { Vendor.make!(:account => account) }
17
+ let(:vendors) { (1..3).map { Vendor.make!(:account => account) } }
18
+
19
+ concerned_with_resource_linking :item, :vendor, :skip_association_examples => true, :skip_validation_examples => true
20
+ end
21
+ end
@@ -0,0 +1,163 @@
1
+ require 'spec_helper'
2
+
3
+ describe Intermodal::Proxies::WillPaginate::Collection do
4
+ include Intermodal::RSpec::Resources
5
+ include SpecHelpers::Application
6
+ include SpecHelpers::API
7
+
8
+ let(:api_class) do
9
+ define_class :Api, Rails::Engine do
10
+ include Intermodal::API
11
+ include SpecHelpers::Epiphyte
12
+
13
+ map_data do
14
+ presentation_for :item do
15
+ presents :id
16
+ presents :name
17
+ presents :description
18
+ end
19
+ end
20
+
21
+ controllers do
22
+ end
23
+
24
+ routes.draw do
25
+ end
26
+ end
27
+ end
28
+
29
+ describe '#as_json' do
30
+ before(:each) { api_class.load_presentations! }
31
+ subject { presented_collection }
32
+
33
+ let(:presenter) { api_class.presenters[:item] }
34
+ let(:collection) { Item.make!(3, :account => account) }
35
+ let(:paginated_collection) { collection; Item.paginate :page => 1 }
36
+ let(:presented_collection) { paginated_collection.as_json presenter_options }
37
+
38
+ let(:collection_ids) { collection.map(&:id) }
39
+
40
+ let(:_ids) { ->(r) { r[:id] } }
41
+ let(:presented_collection_ids) { presented_collection[:collection].map(&_ids) }
42
+
43
+ context 'with presenter default options' do
44
+ let(:presenter_options) { { :presenter => presenter } }
45
+
46
+ it 'should present page' do
47
+ subject[:page].should_not be_nil
48
+ end
49
+
50
+ it 'should present per_page' do
51
+ subject[:per_page].should_not be_nil
52
+ end
53
+
54
+ it 'should present total_pages' do
55
+ subject[:total_pages].should_not be_nil
56
+ end
57
+
58
+ it 'should present total_entries' do
59
+ subject[:total_entries].should_not be_nil
60
+ end
61
+
62
+ it 'should present collection' do
63
+ subject[:collection].should_not be_nil
64
+ end
65
+
66
+ it 'should present collection of resources' do
67
+ presented_collection_ids.should eql(collection_ids)
68
+ end
69
+ end
70
+
71
+ context 'with root option' do
72
+ let(:presenter_options) { { :presenter => presenter, :root => root } }
73
+ let(:root) { :node }
74
+ let(:presented_collection_ids) { presented_collection[root].map(&_ids) }
75
+
76
+ it 'should present page' do
77
+ subject[:page].should_not be_nil
78
+ end
79
+
80
+ it 'should present per_page' do
81
+ subject[:per_page].should_not be_nil
82
+ end
83
+
84
+ it 'should present total_pages' do
85
+ subject[:total_pages].should_not be_nil
86
+ end
87
+
88
+ it 'should present total_entries' do
89
+ subject[:total_entries].should_not be_nil
90
+ end
91
+
92
+ it 'should present collection' do
93
+ subject[root].should_not be_nil
94
+ end
95
+
96
+ it 'should present collection of resources' do
97
+ presented_collection_ids.should eql(collection_ids)
98
+ end
99
+ end
100
+
101
+ context 'with always_nest_collections option' do
102
+ let(:presenter_options) { { :presenter => presenter, :always_nest_collections => true } }
103
+ let(:presented_collection_ids) { presented_collection[:collection].map(&_ids) }
104
+ let(:_ids) { ->(r) { r['item'][:id] } }
105
+
106
+ it 'should present page' do
107
+ subject[:page].should_not be_nil
108
+ end
109
+
110
+ it 'should present per_page' do
111
+ subject[:per_page].should_not be_nil
112
+ end
113
+
114
+ it 'should present total_pages' do
115
+ subject[:total_pages].should_not be_nil
116
+ end
117
+
118
+ it 'should present total_entries' do
119
+ subject[:total_entries].should_not be_nil
120
+ end
121
+
122
+ it 'should present collection' do
123
+ subject[:collection].should_not be_nil
124
+ end
125
+
126
+ it 'should present collection of resources' do
127
+ presented_collection_ids.should eql(collection_ids)
128
+ end
129
+ end
130
+
131
+ context 'with always_nest_collections and root option' do
132
+ let(:presenter_options) { { :presenter => presenter, :root => root, :always_nest_collections => true } }
133
+ let(:root) { :nodes }
134
+ let(:element_name) { root.to_s.singularize }
135
+ let(:presented_collection_ids) { presented_collection[root].map(&_ids) }
136
+ let(:_ids) { ->(r) { r[element_name][:id] } }
137
+
138
+ it 'should present page' do
139
+ subject[:page].should_not be_nil
140
+ end
141
+
142
+ it 'should present per_page' do
143
+ subject[:per_page].should_not be_nil
144
+ end
145
+
146
+ it 'should present total_pages' do
147
+ subject[:total_pages].should_not be_nil
148
+ end
149
+
150
+ it 'should present total_entries' do
151
+ subject[:total_entries].should_not be_nil
152
+ end
153
+
154
+ it 'should present collection' do
155
+ subject[root].should_not be_nil
156
+ end
157
+
158
+ it 'should present collection of resources' do
159
+ presented_collection_ids.should eql(collection_ids)
160
+ end
161
+ end
162
+ end
163
+ end