intermodal 0.0.1 → 0.4.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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/LICENSE +1 -1
- data/README +6 -4
- data/Rakefile +6 -0
- data/intermodal.gemspec +51 -0
- data/lib/generators/intermodal_generator.rb +8 -0
- data/lib/intermodal.rb +122 -0
- data/lib/intermodal/api.rb +246 -0
- data/lib/intermodal/api/configuration.rb +40 -0
- data/lib/intermodal/api/railties.rb +21 -0
- data/lib/intermodal/concerns/acceptors/named_resource.rb +12 -0
- data/lib/intermodal/concerns/acceptors/resource.rb +12 -0
- data/lib/intermodal/concerns/controllers/accountability.rb +17 -0
- data/lib/intermodal/concerns/controllers/anonymous.rb +24 -0
- data/lib/intermodal/concerns/controllers/authenticatable.rb +24 -0
- data/lib/intermodal/concerns/controllers/paginated_collection.rb +25 -0
- data/lib/intermodal/concerns/controllers/presentation.rb +17 -0
- data/lib/intermodal/concerns/controllers/resource.rb +51 -0
- data/lib/intermodal/concerns/controllers/resource_linking.rb +58 -0
- data/lib/intermodal/concerns/let.rb +21 -0
- data/lib/intermodal/concerns/models/access_credential.rb +28 -0
- data/lib/intermodal/concerns/models/account.rb +16 -0
- data/lib/intermodal/concerns/models/accountability.rb +47 -0
- data/lib/intermodal/concerns/models/db_access_token.rb +29 -0
- data/lib/intermodal/concerns/models/has_parent_resource.rb +45 -0
- data/lib/intermodal/concerns/models/presentation.rb +25 -0
- data/lib/intermodal/concerns/models/redis_access_token.rb +60 -0
- data/lib/intermodal/concerns/models/resource_linking.rb +126 -0
- data/lib/intermodal/concerns/models/sanitize_html.rb +35 -0
- data/lib/intermodal/concerns/presenters/named_resource.rb +12 -0
- data/lib/intermodal/concerns/presenters/resource.rb +14 -0
- data/lib/intermodal/concerns/rails/rails_3_stack.rb +42 -0
- data/lib/intermodal/concerns/rails/rails_4_stack.rb +17 -0
- data/lib/intermodal/concerns/rails/use_warden.rb +21 -0
- data/lib/intermodal/config.rb +15 -0
- data/lib/intermodal/configuration.rb +11 -0
- data/lib/intermodal/controllers/api_controller.rb +26 -0
- data/lib/intermodal/controllers/linking_resource_controller.rb +8 -0
- data/lib/intermodal/controllers/nested_resource_controller.rb +18 -0
- data/lib/intermodal/controllers/resource_controller.rb +11 -0
- data/lib/intermodal/dsl/controllers.rb +125 -0
- data/lib/intermodal/dsl/mapping.rb +79 -0
- data/lib/intermodal/dsl/presentation_helpers.rb +107 -0
- data/lib/intermodal/mapping/acceptor.rb +2 -2
- data/lib/intermodal/mapping/mapper.rb +39 -13
- data/lib/intermodal/mapping/presenter.rb +12 -6
- data/lib/intermodal/proxies/linking_resources.rb +58 -0
- data/lib/intermodal/proxies/will_paginate.rb +85 -0
- data/lib/intermodal/rack/auth.rb +29 -0
- data/lib/intermodal/rack/dummy_store.rb +24 -0
- data/lib/intermodal/rack/rescue.rb +82 -0
- data/lib/intermodal/responders/linking_resource_responder.rb +21 -0
- data/lib/intermodal/responders/resource_responder.rb +64 -0
- data/lib/intermodal/rspec/acceptors.rb +79 -0
- data/lib/intermodal/rspec/models/accountability.rb +114 -0
- data/lib/intermodal/rspec/models/has_parent_resource.rb +132 -0
- data/lib/intermodal/rspec/models/resource_linking.rb +234 -0
- data/lib/intermodal/rspec/models/sanitization.rb +84 -0
- data/lib/intermodal/rspec/presenters.rb +92 -0
- data/lib/intermodal/rspec/requests/authenticated_requests.rb +17 -0
- data/lib/intermodal/rspec/requests/linked_resources.rb +180 -0
- data/lib/intermodal/rspec/requests/paginated_collection.rb +60 -0
- data/lib/intermodal/rspec/requests/rack.rb +142 -0
- data/lib/intermodal/rspec/requests/request_validations.rb +36 -0
- data/lib/intermodal/rspec/requests/resources.rb +275 -0
- data/lib/intermodal/rspec/requests/rfc2616_status_codes.rb +51 -0
- data/lib/intermodal/rspec/validators.rb +86 -0
- data/lib/intermodal/validators/account_validator.rb +27 -0
- data/lib/intermodal/validators/different_account_validator.rb +27 -0
- data/lib/intermodal/version.rb +3 -0
- data/spec/mapping/acceptors_spec.rb +142 -0
- data/spec/mapping/presenters_spec.rb +186 -0
- data/spec/models/accountability_spec.rb +13 -0
- data/spec/models/has_parent_resource_spec.rb +18 -0
- data/spec/models/resource_linking_spec.rb +21 -0
- data/spec/proxies/will_paginate_spec.rb +163 -0
- data/spec/rack/auth_spec.rb +51 -0
- data/spec/requests/linked_resources.rb +37 -0
- data/spec/requests/nested_resources_spec.rb +54 -0
- data/spec/requests/resources_spec.rb +50 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/support/api.rb +50 -0
- data/spec/support/app/class_builder.rb +41 -0
- data/spec/support/app/db/adapter_helper.rb +53 -0
- data/spec/support/app/db/authentication_schema_helper.rb +62 -0
- data/spec/support/app/db/migration_helper.rb +44 -0
- data/spec/support/app/schema.rb +101 -0
- data/spec/support/application.rb +23 -0
- data/spec/support/blueprints.rb +41 -0
- data/spec/support/epiphyte.rb +29 -0
- metadata +393 -52
- data/lib/intermodal/base.rb +0 -13
- data/lib/intermodal/declare_controllers.rb +0 -102
- data/lib/intermodal/mapping.rb +0 -4
- data/lib/intermodal/mapping/dsl.rb +0 -76
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module RSpec
|
|
3
|
+
module RequestValidations
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
module ClassMethods
|
|
7
|
+
# This macro expects the following to be defined:
|
|
8
|
+
# let(:valid_payload) { request_payload that would succeed }
|
|
9
|
+
#
|
|
10
|
+
# You then pass a override hash and the expected response code,
|
|
11
|
+
# usually a 400 Bad Request or 422 Unprocessible Entity
|
|
12
|
+
#
|
|
13
|
+
# In overrides, if you pass a callable object such as a lambda or a proc,
|
|
14
|
+
# then it will be called at test time before merging into the request payload.
|
|
15
|
+
#
|
|
16
|
+
# Example:
|
|
17
|
+
#
|
|
18
|
+
# overrides: { apple: -> { tree.apples.sample } }
|
|
19
|
+
def expect_request_invalid(message, opts={}, &additional_examples)
|
|
20
|
+
_status = opts[:status] || 422
|
|
21
|
+
_overrides = opts[:overrides] or raise 'Must pass overrides: parameter'
|
|
22
|
+
|
|
23
|
+
context "when #{message}" do
|
|
24
|
+
let(:request_payload) { attributes.merge(overrides) }
|
|
25
|
+
let(:overrides) { Hash[_overrides.map(&eval_hash)] }
|
|
26
|
+
let(:eval_hash) { ->(x) { [x[0].to_s, maybe_call.(x[1]) ] } }
|
|
27
|
+
let(:maybe_call) { ->(x) { x.respond_to?(:call) ? x.call : x } }
|
|
28
|
+
|
|
29
|
+
expects_status _status
|
|
30
|
+
instance_eval(&additional_examples) if additional_examples
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module RSpec
|
|
3
|
+
module Resources
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
include Intermodal::RSpec::Rack
|
|
8
|
+
include Intermodal::RSpec::AuthenticatedRequests
|
|
9
|
+
include Intermodal::RSpec::PaginatedCollection
|
|
10
|
+
|
|
11
|
+
let(:api) { raise "Must define let(:api)" }
|
|
12
|
+
|
|
13
|
+
let(:resource_collection_name) { resource_name.pluralize }
|
|
14
|
+
let(:model) { resource_name.camelize.constantize }
|
|
15
|
+
let(:parent_models) { parent_names.map { |p| p.to_s.singularize.camelize.constantize } }
|
|
16
|
+
|
|
17
|
+
let(:model_collection) do
|
|
18
|
+
3.times { model.make!(model_factory_options) }
|
|
19
|
+
(model_parent ? model.by_parent(model_parent) : model.all )
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
let(:model_resource) { model.make!(model_factory_options) }
|
|
23
|
+
let(:model_factory_options) { (model_parent ? { parent_names.last.to_s.singularize => model_parent } : {} ) }
|
|
24
|
+
let(:model_parents) { parent_models.map { |p| p.make! } }
|
|
25
|
+
let(:model_parent) { model_parents.last }
|
|
26
|
+
let(:presenter) { api.presenter_for model }
|
|
27
|
+
|
|
28
|
+
let(:collection) { model_collection }
|
|
29
|
+
let(:paginated_collection) { model_collection.paginate(:page => page, :per_page => per_page) }
|
|
30
|
+
let(:presented_collection) { parser.decode(paginated_collection.send("to_#{format}", :presenter => presenter, :root => collection_element_name, :scope => presenter_scope_for_index)) }
|
|
31
|
+
let(:page) { 1 }
|
|
32
|
+
let(:per_page) { api.default_per_page }
|
|
33
|
+
|
|
34
|
+
let(:resource_element_name) { model.name.demodulize.underscore }
|
|
35
|
+
let(:collection_element_name) { resource_element_name.pluralize }
|
|
36
|
+
let(:expected_resource) { model.find(model_resource.id) }
|
|
37
|
+
let(:persisted_resource_id) { body['id'] }
|
|
38
|
+
let(:resource_after_create) { model.find(persisted_resource_id) }
|
|
39
|
+
let(:resource_after_update) { model.find(resource_id) }
|
|
40
|
+
let(:resource_after_destroy) { resource_after_update }
|
|
41
|
+
let(:resource) { parser.decode(expected_resource.send("to_#{format}", :presenter => presenter, :scope => presenter_scope)) }
|
|
42
|
+
let(:presenter_scope) { nil }
|
|
43
|
+
let(:presenter_scope_for_index) { presenter_scope }
|
|
44
|
+
let(:resource_id) { resource['id'] }
|
|
45
|
+
let(:parent_ids) { parent_names.zip(model_parents.map { |m| m.id }) }
|
|
46
|
+
|
|
47
|
+
let(:namespace) { nil }
|
|
48
|
+
let(:namespace_path) { ( namespace ? "/#{namespace}" : nil ) }
|
|
49
|
+
let(:parent_path) { parent_ids.map { |p, p_id| "/#{p}/#{p_id}" }.join }
|
|
50
|
+
let(:collection_url) { [ namespace_path, parent_path, "/#{resource_collection_name}.#{format}" ].join }
|
|
51
|
+
let(:resource_url) { [ namespace_path, parent_path, "/#{resource_collection_name}/#{resource_id}.#{format}" ].join }
|
|
52
|
+
|
|
53
|
+
let(:request_json_payload) { request_payload.to_json }
|
|
54
|
+
let(:request_xml_payload) { request_payload.to_xml(:root => resource_element_name) }
|
|
55
|
+
|
|
56
|
+
let(:malformed_json_payload) { '{ "bad": [ "data": ] }' }
|
|
57
|
+
let(:malformed_xml_payload) { '<bad><data></bad></data>' }
|
|
58
|
+
|
|
59
|
+
# DELETE specs expect record to be deleted.
|
|
60
|
+
# Override this if you are using something such as ActiveResource:
|
|
61
|
+
# let(:record_not_found_error) { ActiveResource::ResourceNotFound }
|
|
62
|
+
let(:record_not_found_error) { ActiveRecord::RecordNotFound }
|
|
63
|
+
|
|
64
|
+
# Some of the test examples assume that the database is blank.
|
|
65
|
+
# For example, index tests require injecting 3 resources and expects
|
|
66
|
+
# the index endpoint to return exactly 3 resources.
|
|
67
|
+
let(:reset_datastore!) { } # Do nothing by default
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
module ClassMethods
|
|
71
|
+
def given_create_attributes(values = {})
|
|
72
|
+
let(:valid_create_attributes) { values }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def given_update_attributes(values = {})
|
|
76
|
+
let(:valid_update_attributes) { values }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def metadata_for_resources(resource_name, options = {})
|
|
80
|
+
if resource_name.is_a?(Array)
|
|
81
|
+
parents = resource_name
|
|
82
|
+
resource_name = parents.pop
|
|
83
|
+
end
|
|
84
|
+
resource_name = resource_name.to_s if resource_name.is_a?(Symbol)
|
|
85
|
+
|
|
86
|
+
{ :formats => [ :json ],
|
|
87
|
+
:resource_name => resource_name,
|
|
88
|
+
:model_name => resource_name.singularize,
|
|
89
|
+
:encoding => 'utf-8',
|
|
90
|
+
:parents => parents || [],
|
|
91
|
+
:namespace_path => ( options[:namespace] ? "/#{options[:namespace]}" : nil )
|
|
92
|
+
}.merge(options)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def metadata_for_formatted_resource(format, options = {})
|
|
96
|
+
{ :format => format,
|
|
97
|
+
:collection_url => collection_url_for_resource(options[:namespace_path], options[:parents], options[:resource_name], format),
|
|
98
|
+
:resource_url => resource_url_for_resource(options[:namespace_path], options[:parents], options[:resource_name], format),
|
|
99
|
+
:mime_type => Mime::Type.lookup_by_extension(format).to_s
|
|
100
|
+
}.merge(options)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def parent_path_for_resource(parents)
|
|
104
|
+
parents.map { |p| "/#{p}/:id" }.join
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def collection_url_for_resource(namespace, parents, resource_name, format)
|
|
108
|
+
[ namespace, parent_path_for_resource(parents), "/#{resource_name}.#{format}" ].join
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def resource_url_for_resource(namespace, parents, resource_name, format)
|
|
112
|
+
[ namespace, parent_path_for_resource(parents), "/#{resource_name}/:id.#{format}" ].join
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def resources(resource_name, options = {}, &blk)
|
|
116
|
+
options = metadata_for_resources(resource_name, options)
|
|
117
|
+
_resource_name = options[:resource_name].singularize
|
|
118
|
+
|
|
119
|
+
# If you want xml, pass it as formats: [ :json, :xml ]
|
|
120
|
+
options[:formats].each do |format|
|
|
121
|
+
format_options = metadata_for_formatted_resource(format, options)
|
|
122
|
+
context format_options[:collection_url], format_options do
|
|
123
|
+
let(:namespace) { options[:namespace] }
|
|
124
|
+
let(:resource_name) { _resource_name }
|
|
125
|
+
let(:parent_names) { options[:parents] }
|
|
126
|
+
let(:format) { format }
|
|
127
|
+
let(options[:parents].last) { model_parent } if options[:parents].last
|
|
128
|
+
instance_eval(&blk) if blk
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def expects_resource_crud(options = {}, &additional_examples)
|
|
134
|
+
expected_actions = options[:only] || [ :index, :show, :create, :update, :destroy ]
|
|
135
|
+
expected_actions -= options[:except] if options[:except]
|
|
136
|
+
|
|
137
|
+
expected_actions.each do |action|
|
|
138
|
+
send("expects_#{action}")
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
instance_eval(&additional_examples) if additional_examples
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
STANDARD_SUCCESSFUL_STATUS_FOR = {
|
|
145
|
+
:index => 200,
|
|
146
|
+
:show => 200,
|
|
147
|
+
:create => 201,
|
|
148
|
+
:update => 200,
|
|
149
|
+
:destroy => 204 }
|
|
150
|
+
|
|
151
|
+
STANDARD_REQUEST_FOR = {
|
|
152
|
+
:index => { :method => :get, :end_point => :collection_url },
|
|
153
|
+
:show => { :method => :get, :end_point => :resource_url },
|
|
154
|
+
:create => { :method => :post, :end_point => :collection_url, :payload => proc do valid_create_attributes end },
|
|
155
|
+
:update => { :method => :put, :end_point => :resource_url, :payload => proc do valid_update_attributes end },
|
|
156
|
+
:destroy => { :method => :delete, :end_point => :resource_url } }
|
|
157
|
+
|
|
158
|
+
def request_resource_action(action, options = {}, &blk)
|
|
159
|
+
options = {
|
|
160
|
+
:mime_type => metadata[:mime_type],
|
|
161
|
+
:encoding => metadata[:encoding],
|
|
162
|
+
:status => STANDARD_SUCCESSFUL_STATUS_FOR[action],
|
|
163
|
+
:collection_url => metadata[:collection_url],
|
|
164
|
+
:resource_url => metadata[:resource_url]
|
|
165
|
+
}.merge(STANDARD_REQUEST_FOR[action]).merge(options)
|
|
166
|
+
|
|
167
|
+
request options[:method], options[options[:end_point]], options[:payload] do
|
|
168
|
+
let(:request_url) { send(options[:end_point]) }
|
|
169
|
+
expects_status(options[:status])
|
|
170
|
+
expects_content_type(options[:mime_type], options[:encoding])
|
|
171
|
+
instance_eval(&blk) if blk
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def expects_index(options = {}, &additional_examples)
|
|
176
|
+
request_resource_action(:index, options) do
|
|
177
|
+
unless options[:skip_presentation_test]
|
|
178
|
+
it "should return a list of all #{metadata[:resource_name]}" do
|
|
179
|
+
reset_datastore!
|
|
180
|
+
collection.should_not be_empty
|
|
181
|
+
body.should eql(presented_collection)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
expects_unauthorized_access_to_respond_with_401
|
|
186
|
+
unless options[:skip_pagination_examples]
|
|
187
|
+
expects_paginated_resource do
|
|
188
|
+
before(:each) { reset_datastore! }
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
instance_eval(&additional_examples) if additional_examples
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def expects_show(options = {}, &additional_examples)
|
|
196
|
+
request_resource_action(:show, options) do
|
|
197
|
+
it "should return a #{metadata[:resource_name]} of id 1" do
|
|
198
|
+
resource.should_not be_nil
|
|
199
|
+
body.should eql(resource)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
with_non_existent_resource_should_respond_with_404
|
|
203
|
+
expects_unauthorized_access_to_respond_with_401
|
|
204
|
+
instance_eval(&additional_examples) if additional_examples
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def expects_create(options = {}, &additional_examples)
|
|
209
|
+
request_resource_action(:create, options) do
|
|
210
|
+
it "should return the newly created #{metadata[:resource_name]}" do
|
|
211
|
+
body.should eql(parser.decode(resource_after_create.send("to_#{format}", { :presenter => presenter, :scope => presenter_scope})))
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
with_malformed_data_should_respond_with_400
|
|
215
|
+
expects_unauthorized_access_to_respond_with_401
|
|
216
|
+
instance_eval(&additional_examples) if additional_examples
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def expects_update(options = {}, &additional_examples)
|
|
221
|
+
request_resource_action(:update, options) do
|
|
222
|
+
it "should update #{metadata[:resource_name]}" do
|
|
223
|
+
response.should_not be(nil)
|
|
224
|
+
valid_update_attributes.each do |updated_attribute, updated_value|
|
|
225
|
+
resource_after_update[updated_attribute].should eql(updated_value)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
with_malformed_data_should_respond_with_400
|
|
230
|
+
with_non_existent_resource_should_respond_with_404
|
|
231
|
+
expects_unauthorized_access_to_respond_with_401
|
|
232
|
+
instance_eval(&additional_examples) if additional_examples
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def expects_destroy(options = {}, &additional_examples)
|
|
237
|
+
request_resource_action(:destroy, {mime_type: nil, encoding: nil}.merge(options)) do
|
|
238
|
+
it "should delete #{metadata[:resource_name]}" do
|
|
239
|
+
response.should_not be(nil)
|
|
240
|
+
lambda { resource_after_destroy }.should raise_error(record_not_found_error)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
with_non_existent_resource_should_respond_with_404
|
|
244
|
+
expects_unauthorized_access_to_respond_with_401
|
|
245
|
+
instance_eval(&additional_examples) if additional_examples
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def with_malformed_data_should_respond_with_400
|
|
250
|
+
context "with malformed #{metadata[:format]} payload" do
|
|
251
|
+
let(:request_raw_payload) { send("malformed_#{format}_payload") }
|
|
252
|
+
|
|
253
|
+
expects_status(400)
|
|
254
|
+
expects_content_type(metadata[:mime_type], metadata[:encoding])
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def with_non_existent_resource_should_respond_with_404
|
|
259
|
+
context 'with non-existent resource' do
|
|
260
|
+
let(:resource_id) { 0 } # Assumes that persisted datastore never uses id of 0
|
|
261
|
+
expects_status 404
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def expects_json_presentation(_presenter_scope = nil)
|
|
266
|
+
let(:presenter_scope) { _presenter_scope } if _presenter_scope
|
|
267
|
+
|
|
268
|
+
it 'should present a JSON object' do
|
|
269
|
+
body.should eql(presented_resource)
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module RSpec
|
|
3
|
+
module HTTP
|
|
4
|
+
RFC2616_HTTP_STATUS_CODES =
|
|
5
|
+
[[100, "Continue"],
|
|
6
|
+
[101, "Switching Protocols"],
|
|
7
|
+
[200, "OK"],
|
|
8
|
+
[201, "Created"],
|
|
9
|
+
[202, "Accepted"],
|
|
10
|
+
[203, "Non-Authoritative Information"],
|
|
11
|
+
[204, "No Content"],
|
|
12
|
+
[205, "Reset Content"],
|
|
13
|
+
[206, "Partial Content"],
|
|
14
|
+
[300, "Multiple Choices"],
|
|
15
|
+
[301, "Moved Permanently"],
|
|
16
|
+
[302, "Found"],
|
|
17
|
+
[303, "See Other"],
|
|
18
|
+
[304, "Not Modified"],
|
|
19
|
+
[305, "Use Proxy"],
|
|
20
|
+
[306, "(Unused)"],
|
|
21
|
+
[307, "Temporary Redirect"],
|
|
22
|
+
[400, "Bad Request"],
|
|
23
|
+
[401, "Unauthorized"],
|
|
24
|
+
[402, "Payment Required"],
|
|
25
|
+
[403, "Forbidden"],
|
|
26
|
+
[404, "Not Found"],
|
|
27
|
+
[405, "Method Not Allowed"],
|
|
28
|
+
[406, "Not Acceptable"],
|
|
29
|
+
[407, "Proxy Authentication Required"],
|
|
30
|
+
[408, "Request Timeout"],
|
|
31
|
+
[409, "Conflict"],
|
|
32
|
+
[410, "Gone"],
|
|
33
|
+
[411, "Length Required"],
|
|
34
|
+
[412, "Precondition Failed"],
|
|
35
|
+
[413, "Request Entity Too Large"],
|
|
36
|
+
[414, "Request-URI Too Long"],
|
|
37
|
+
[415, "Unsupported Media Type"],
|
|
38
|
+
[416, "Requested Range Not Satisfiable"],
|
|
39
|
+
[417, "Expectation Failed"],
|
|
40
|
+
[422, "Unprocessable Entity"],
|
|
41
|
+
[500, "Internal Server Error"],
|
|
42
|
+
[501, "Not Implemented"],
|
|
43
|
+
[502, "Bad Gateway"],
|
|
44
|
+
[503, "Service Unavailable"],
|
|
45
|
+
[504, "Gateway Timeout"],
|
|
46
|
+
[505, "HTTP Version Not Supported"]]
|
|
47
|
+
|
|
48
|
+
STATUS_CODES = RFC2616_HTTP_STATUS_CODES.inject({}) { |h, e| h[e[0].to_s]=e[1]; h }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
require 'active_support/concern'
|
|
2
|
+
|
|
3
|
+
module Intermodal
|
|
4
|
+
module RSpec
|
|
5
|
+
module Validators
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
# Make sure to define:
|
|
10
|
+
# let(:resource) # resource to be tested
|
|
11
|
+
# let(:associated_resource) # association and resource both owned by same account
|
|
12
|
+
# let(:invalid_associated_resource) # association and resource owned by different account
|
|
13
|
+
#
|
|
14
|
+
# Optional
|
|
15
|
+
# let(:attribute) # Attribute being validated
|
|
16
|
+
# let(:message) # Error message
|
|
17
|
+
#
|
|
18
|
+
def self.validates_same_account(association, &additional_examples)
|
|
19
|
+
context "validates #{association} is owned by same account" do
|
|
20
|
+
let(:attribute) { association }
|
|
21
|
+
let(:message) { 'must belong to the same account' }
|
|
22
|
+
|
|
23
|
+
context 'when association belongs to same account' do
|
|
24
|
+
it 'should be_valid' do
|
|
25
|
+
expect(resource.account_id).to eql(associated_resource.account_id)
|
|
26
|
+
expect(resource).to be_valid
|
|
27
|
+
expect(resource.errors).not_to have_key(attribute)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context 'when association belongs to a different account' do
|
|
32
|
+
before(:each) { resource.send("#{association}=", invalid_associated_resource) }
|
|
33
|
+
|
|
34
|
+
it 'should not be valid and report the error' do
|
|
35
|
+
expect(resource.account_id).not_to eql(invalid_associated_resource.account_id)
|
|
36
|
+
expect(resource).not_to be_valid
|
|
37
|
+
expect(resource.errors).to have_key(attribute)
|
|
38
|
+
expect(resource.errors[attribute]).to include(message)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
instance_eval(&additional_examples) if additional_examples
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Make sure to define:
|
|
47
|
+
# let(:resource) # resource to be tested
|
|
48
|
+
# let(:associated_resource) # association and resource owned by different accounts
|
|
49
|
+
# let(:invalid_associated_resource) # association and resource both owned by same account
|
|
50
|
+
#
|
|
51
|
+
# Optional
|
|
52
|
+
# let(:attribute) # Attribute being validated
|
|
53
|
+
# let(:message) # Error message
|
|
54
|
+
#
|
|
55
|
+
def self.validates_different_account(association, &additional_examples)
|
|
56
|
+
context "validates #{association} is owned by a different account" do
|
|
57
|
+
let(:attribute) { association }
|
|
58
|
+
let(:message) { 'must belong to a different account' }
|
|
59
|
+
|
|
60
|
+
context 'when association belongs to same account' do
|
|
61
|
+
before(:each) { resource.send("#{association}=", invalid_associated_resource) }
|
|
62
|
+
|
|
63
|
+
it 'should not be valid' do
|
|
64
|
+
expect(resource.account_id).to eql(invalid_associated_resource.account_id)
|
|
65
|
+
expect(resource).not_to be_valid
|
|
66
|
+
expect(resource.errors).to have_key(attribute)
|
|
67
|
+
expect(resource.errors[attribute]).to include(message)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
context 'when association belongs to a different account' do
|
|
72
|
+
|
|
73
|
+
it 'should not be valid and report the error' do
|
|
74
|
+
expect(resource.account_id).not_to eql(associated_resource.account_id)
|
|
75
|
+
expect(resource).to be_valid
|
|
76
|
+
expect(resource.errors).not_to have_key(attribute)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
instance_eval(&additional_examples) if additional_examples
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|