praxis 0.13.0 → 0.14.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.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +15 -2
  4. data/CHANGELOG.md +54 -1
  5. data/bin/praxis +49 -2
  6. data/lib/api_browser/Gruntfile.js +247 -90
  7. data/lib/api_browser/app/bower_components/angular-mocks/.bower.json +19 -0
  8. data/lib/api_browser/app/bower_components/angular-mocks/README.md +57 -0
  9. data/lib/api_browser/app/bower_components/angular-mocks/angular-mocks.js +2193 -0
  10. data/lib/api_browser/app/bower_components/angular-mocks/bower.json +9 -0
  11. data/lib/api_browser/app/bower_components/angular-mocks/package.json +27 -0
  12. data/lib/api_browser/app/bower_components/angular/.bower.json +6 -5
  13. data/lib/api_browser/app/bower_components/angular/README.md +23 -4
  14. data/lib/api_browser/app/bower_components/angular/angular-csp.css +6 -0
  15. data/lib/api_browser/app/bower_components/angular/angular.js +2287 -1597
  16. data/lib/api_browser/app/bower_components/angular/angular.min.js +212 -205
  17. data/lib/api_browser/app/bower_components/angular/angular.min.js.gzip +0 -0
  18. data/lib/api_browser/app/bower_components/angular/angular.min.js.map +3 -3
  19. data/lib/api_browser/app/bower_components/angular/bower.json +2 -1
  20. data/lib/api_browser/app/bower_components/angular/package.json +25 -0
  21. data/lib/api_browser/app/bower_components/showdown/.bower.json +39 -0
  22. data/lib/api_browser/app/bower_components/showdown/.jshintignore +2 -0
  23. data/lib/api_browser/app/bower_components/showdown/.travis.yml +8 -0
  24. data/lib/api_browser/app/bower_components/showdown/Gruntfile.js +100 -0
  25. data/lib/api_browser/app/bower_components/showdown/README.md +317 -0
  26. data/lib/api_browser/app/bower_components/showdown/bower.json +26 -0
  27. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js +1606 -0
  28. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js.map +1 -0
  29. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.min.js +2 -0
  30. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js +2 -0
  31. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js.map +1 -0
  32. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js +2 -0
  33. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js.map +1 -0
  34. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js +2 -0
  35. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js.map +1 -0
  36. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js +2 -0
  37. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js.map +1 -0
  38. data/lib/api_browser/app/bower_components/showdown/license.txt +34 -0
  39. data/lib/api_browser/app/bower_components/showdown/package.json +47 -0
  40. data/lib/api_browser/app/bower_components/showdown/src/extensions/github.js +25 -0
  41. data/lib/api_browser/app/bower_components/showdown/src/extensions/prettify.js +29 -0
  42. data/lib/api_browser/app/bower_components/showdown/src/extensions/table.js +106 -0
  43. data/lib/api_browser/app/bower_components/showdown/src/extensions/twitter.js +42 -0
  44. data/lib/api_browser/app/bower_components/showdown/src/ng-showdown.js +150 -0
  45. data/lib/api_browser/app/bower_components/showdown/src/showdown.js +1454 -0
  46. data/lib/api_browser/app/index.html +6 -4
  47. data/lib/api_browser/app/js/app.js +1 -2
  48. data/lib/api_browser/app/js/controllers/action.js +4 -4
  49. data/lib/api_browser/app/js/controllers/controller.js +1 -1
  50. data/lib/api_browser/app/js/controllers/menu.js +5 -3
  51. data/lib/api_browser/app/js/controllers/type.js +5 -5
  52. data/lib/api_browser/app/js/directives/attribute_description.js +5 -5
  53. data/lib/api_browser/app/js/directives/attribute_table.js +1 -1
  54. data/lib/api_browser/app/js/directives/attribute_table_row.js +2 -2
  55. data/lib/api_browser/app/js/directives/no_container.js +1 -1
  56. data/lib/api_browser/app/js/directives/request_body.js +5 -5
  57. data/lib/api_browser/app/js/directives/request_headers.js +3 -6
  58. data/lib/api_browser/app/js/directives/request_parameters.js +3 -6
  59. data/lib/api_browser/app/js/directives/type_label.js +4 -5
  60. data/lib/api_browser/app/js/factories/Documentation.js +4 -4
  61. data/lib/api_browser/app/js/factories/PayloadTemplates.js +2 -2
  62. data/lib/api_browser/app/js/factories/TypeTemplates.js +3 -3
  63. data/lib/api_browser/app/js/filters/markdown.js +6 -0
  64. data/lib/api_browser/app/js/filters/resource_name.js +2 -2
  65. data/lib/api_browser/app/sass/modules/_header.scss +2 -7
  66. data/lib/api_browser/app/sass/{main.scss → praxis.scss} +0 -0
  67. data/lib/api_browser/app/sass/variables/_bootstrap-variables.scss +370 -367
  68. data/lib/api_browser/app/views/action.html +2 -2
  69. data/lib/api_browser/app/views/controller.html +2 -2
  70. data/lib/api_browser/app/views/directives/attribute_description.html +1 -1
  71. data/lib/api_browser/app/views/layout.html +2 -11
  72. data/lib/api_browser/app/views/navbar.html +9 -0
  73. data/lib/api_browser/app/views/resource/_actions.html +1 -1
  74. data/lib/api_browser/app/views/type.html +2 -2
  75. data/lib/api_browser/app/views/type/_details.html +2 -1
  76. data/lib/api_browser/bower.json +5 -0
  77. data/lib/api_browser/package.json +18 -7
  78. data/lib/praxis.rb +8 -3
  79. data/lib/praxis/action_definition.rb +28 -6
  80. data/lib/praxis/api_definition.rb +30 -2
  81. data/lib/praxis/api_general_info.rb +36 -0
  82. data/lib/praxis/bootloader.rb +1 -0
  83. data/lib/praxis/collection.rb +34 -0
  84. data/lib/praxis/controller.rb +7 -0
  85. data/lib/praxis/dispatcher.rb +3 -0
  86. data/lib/praxis/links.rb +2 -8
  87. data/lib/praxis/media_type.rb +6 -24
  88. data/lib/praxis/media_type_collection.rb +6 -2
  89. data/lib/praxis/plugin_concern.rb +2 -1
  90. data/lib/praxis/request.rb +24 -15
  91. data/lib/praxis/request_stages/request_stage.rb +19 -4
  92. data/lib/praxis/request_stages/validate_params_and_headers.rb +1 -1
  93. data/lib/praxis/request_stages/validate_payload.rb +1 -1
  94. data/lib/praxis/resource_definition.rb +45 -10
  95. data/lib/praxis/response_definition.rb +46 -27
  96. data/lib/praxis/restful_doc_generator.rb +94 -7
  97. data/lib/praxis/simple_media_type.rb +2 -9
  98. data/lib/praxis/stage.rb +1 -4
  99. data/lib/praxis/tasks/api_docs.rb +51 -19
  100. data/lib/praxis/tasks/routes.rb +19 -15
  101. data/lib/praxis/types/media_type_common.rb +31 -0
  102. data/lib/praxis/types/multipart.rb +4 -4
  103. data/lib/praxis/version.rb +1 -1
  104. data/praxis.gemspec +2 -2
  105. data/spec/api_browser/factories/documentation_spec.js +50 -0
  106. data/spec/api_browser/filters/attribute_name_spec.js +23 -0
  107. data/spec/functional_spec.rb +62 -10
  108. data/spec/praxis/action_definition_spec.rb +12 -4
  109. data/spec/praxis/api_definition_spec.rb +159 -0
  110. data/spec/praxis/api_general_info_spec.rb +36 -0
  111. data/spec/praxis/bootloader_spec.rb +10 -1
  112. data/spec/praxis/media_type_collection_spec.rb +46 -53
  113. data/spec/praxis/media_type_spec.rb +6 -6
  114. data/spec/praxis/request_stage_spec.rb +7 -2
  115. data/spec/praxis/request_stages_validate_spec.rb +12 -7
  116. data/spec/praxis/resource_definition_spec.rb +62 -0
  117. data/spec/praxis/response_definition_spec.rb +26 -16
  118. data/spec/praxis/stage_spec.rb +4 -8
  119. data/spec/praxis/types/collection_spec.rb +144 -0
  120. data/spec/spec_app/app/controllers/instances.rb +8 -2
  121. data/spec/spec_app/design/api.rb +11 -0
  122. data/spec/spec_app/design/media_types/instance.rb +12 -0
  123. data/spec/spec_app/design/media_types/volume.rb +9 -2
  124. data/spec/spec_app/design/media_types/volume_snapshot.rb +9 -6
  125. data/spec/spec_app/design/resources/instances.rb +25 -10
  126. data/spec/support/spec_media_types.rb +1 -1
  127. data/spec/support/spec_resource_definitions.rb +2 -0
  128. data/tasks/thor/app.rb +15 -10
  129. data/tasks/thor/example.rb +115 -115
  130. data/tasks/thor/templates/generator/empty_app/.gitignore +2 -0
  131. data/tasks/thor/templates/generator/empty_app/docs/app.js +1 -0
  132. data/tasks/thor/templates/generator/empty_app/docs/styles.scss +3 -0
  133. metadata +50 -9
  134. data/lib/api_browser/app/css/main.css +0 -4511
  135. data/lib/praxis/types/collection.rb +0 -17
@@ -0,0 +1,23 @@
1
+ describe('attributeName filter', function() {
2
+ var filter;
3
+ beforeEach(angular.mock.module('PraxisDocBrowser'));
4
+
5
+ beforeEach(inject(function(attributeNameFilter, $sce) {
6
+ filter = function(input) {
7
+ return $sce.getTrusted('html', attributeNameFilter(input));
8
+ };
9
+ }));
10
+
11
+ it('only modifies input with one dot', function() {
12
+ expect(function(str) {
13
+ var dotsCount = str.split('.').length - 1;
14
+ if (dotsCount !== 1) {
15
+ return str === filter(str);
16
+ }
17
+ }).forAll(qc.string);
18
+ });
19
+
20
+ it('adds a tag to the prefix', function() {
21
+ expect(filter('ns.test')).toEqual('<span class="attribute-prefix">ns.</span>test');
22
+ });
23
+ });
@@ -10,6 +10,14 @@ describe 'Functional specs' do
10
10
 
11
11
  context 'index' do
12
12
 
13
+ context 'with a valid request' do
14
+ it 'is successful' do
15
+ get '/clouds/1/instances?api_version=1.0', nil, 'global_session' => session
16
+ expect(last_response.headers['Content-Type']).to(
17
+ eq("application/vnd.acme.instance;type=collection"))
18
+ end
19
+ end
20
+
13
21
  context 'with an incorrect response_content_type param' do
14
22
  around do |example|
15
23
  logger = app.logger
@@ -52,15 +60,25 @@ describe 'Functional specs' do
52
60
  it 'works' do
53
61
  get '/clouds/1/instances/2?junk=foo&api_version=1.0', nil, 'global_session' => session
54
62
  expect(last_response.status).to eq(200)
55
- expect(JSON.parse(last_response.body)).to eq({"cloud_id" => 1, "id"=>2, "junk"=>"foo",
56
- "other_params"=>{
57
- "some_date"=>"2012-12-21T00:00:00+00:00",
58
- "fail_filter"=>false},
59
- "payload"=>{"something"=>nil, "optional"=>"not given"}})
60
- expect(last_response.headers).
61
- to eq({"Content-Type"=>"application/vnd.acme.instance",
62
- "Content-Length"=>"214", 'Spec-Middleware' => 'used'})
63
- end
63
+ expected = {
64
+ "cloud_id" => 1,
65
+ "id"=>2,
66
+ "junk"=>"foo",
67
+ "other_params"=>{
68
+ "some_date"=>"2012-12-21T00:00:00+00:00",
69
+ "fail_filter"=>false
70
+ },
71
+ "payload"=>{
72
+ "optional"=>"not given"}
73
+ }
74
+
75
+ expect(JSON.parse(last_response.body)).to eq(expected)
76
+
77
+ headers = last_response.headers
78
+ expect(headers['Content-Type']).to eq('application/vnd.acme.instance')
79
+ expect(headers['Spec-Middleware']).to eq('used')
80
+ expect(headers['Content-Length']).to eq(last_response.body.size.to_s)
81
+ end
64
82
 
65
83
  it 'returns early when making the before filter break' do
66
84
  get '/clouds/1/instances/2?junk=foo&api_version=1.0&fail_filter=true', nil, 'global_session' => session
@@ -267,7 +285,7 @@ describe 'Functional specs' do
267
285
  end
268
286
 
269
287
  end
270
-
288
+
271
289
  context 'auth_plugin' do
272
290
  it 'can terminate' do
273
291
  post '/clouds/23/instances/1/terminate?api_version=1.0', nil, 'global_session' => session
@@ -295,4 +313,38 @@ describe 'Functional specs' do
295
313
  end
296
314
  end
297
315
 
316
+ context 'update' do
317
+
318
+
319
+ let(:body) { JSON.pretty_generate(request_payload) }
320
+ let(:content_type) { 'application/json' }
321
+
322
+ before do
323
+ patch '/clouds/1/instances/3?api_version=1.0', body, 'CONTENT_TYPE' => content_type, 'global_session' => session
324
+ end
325
+
326
+ subject(:response_body) { JSON.parse(last_response.body) }
327
+
328
+ context 'with an empty payload' do
329
+ let(:request_payload) { {} }
330
+ it { should be_empty }
331
+ it { should_not have_key('name') }
332
+ it { should_not have_key('root_volume') }
333
+ end
334
+
335
+ context 'with a provided name' do
336
+ let(:request_payload) { {name: 'My Instance'} }
337
+ its(['name']) { should eq('My Instance') }
338
+ it { should_not have_key('root_volume') }
339
+ end
340
+
341
+ context 'with an explicitly-nil root_volme' do
342
+ let(:request_payload) { {name: 'My Instance', root_volume: nil} }
343
+ its(['name']) { should eq('My Instance') }
344
+ its(['root_volume']) { should be(nil) }
345
+ end
346
+
347
+
348
+ end
349
+
298
350
  end
@@ -21,7 +21,7 @@ describe Praxis::ActionDefinition do
21
21
  end
22
22
 
23
23
  subject(:action) do
24
- Praxis::ActionDefinition.new('foo', resource_definition) do
24
+ Praxis::ActionDefinition.new(:foo, resource_definition) do
25
25
  routing { get '/:one' }
26
26
  payload { attribute :two, String }
27
27
  headers { header "X_REQUESTED_WITH", 'XMLHttpRequest' }
@@ -30,7 +30,7 @@ describe Praxis::ActionDefinition do
30
30
  end
31
31
 
32
32
  context '#initialize' do
33
- its('name') { should eq 'foo' }
33
+ its('name') { should eq :foo }
34
34
  its('resource_definition') { should be resource_definition }
35
35
  its('params.attributes') { should have_key :one }
36
36
  its('params.attributes') { should have_key :inherited }
@@ -116,7 +116,16 @@ describe Praxis::ActionDefinition do
116
116
  end
117
117
 
118
118
  context '#describe' do
119
- it 'has some tests when Skeletor::RestfulRoutingConfig disappears'
119
+ subject(:describe) { action.describe }
120
+
121
+ context 'params' do
122
+ subject(:param_description) { describe[:params] }
123
+ it 'includes attribute sources' do
124
+ attributes = param_description[:type][:attributes]
125
+ expect(attributes[:inherited][:source]).to eq('query')
126
+ expect(attributes[:one][:source]).to eq('url')
127
+ end
128
+ end
120
129
  end
121
130
 
122
131
  context 'href generation' do
@@ -156,5 +165,4 @@ describe Praxis::ActionDefinition do
156
165
  end
157
166
 
158
167
  end
159
-
160
168
  end
@@ -0,0 +1,159 @@
1
+ require 'spec_helper'
2
+
3
+ describe Praxis::ApiDefinition do
4
+
5
+ subject(:api){ Praxis::ApiDefinition.instance }
6
+
7
+ # Without getting a fresh new ApiDefinition it is very difficult to test stuff using the Singleton
8
+ # So for some tests we're gonna create a new instance and work with it to avoid the singleton issues
9
+ let(:non_singleton_api) do
10
+ api_def=Praxis::ApiDefinition.__send__(:new)
11
+ api_def.instance_eval do |api|
12
+ api.response_template :template1, &Proc.new {}
13
+ api.trait :trait1, &Proc.new {}
14
+ end
15
+ api_def
16
+ end
17
+
18
+ let(:info_block) do
19
+ Proc.new do
20
+ name "Name"
21
+ title "Title"
22
+ end
23
+ end
24
+
25
+ context 'singleton' do
26
+ it 'should be a Singleton' do
27
+ expect(Praxis::ApiDefinition.ancestors).to include( Singleton )
28
+ expect(subject).to eq(Praxis::ApiDefinition.instance )
29
+ end
30
+
31
+ it 'has the :ok and :created response templates registered' do
32
+ expect(api.responses.keys).to include(:ok)
33
+ expect(api.responses.keys).to include(:created)
34
+ end
35
+ end
36
+
37
+ context '.response_template' do
38
+ let(:response_template){ Proc.new {} }
39
+ let(:api){ non_singleton_api }
40
+
41
+ it 'has the defined template1 response_template' do
42
+ expect(api.responses.keys).to include(:template1)
43
+ expect(api.response(:template1)).to be_kind_of(Praxis::ResponseTemplate)
44
+ end
45
+ it 'also works outside a .define block' do
46
+ api.response_template :foobar, &response_template
47
+ expect(api.responses.keys).to include(:foobar)
48
+ expect(api.response(:foobar)).to be_kind_of(Praxis::ResponseTemplate)
49
+ end
50
+ end
51
+
52
+ context '.response' do
53
+ let(:api){ non_singleton_api }
54
+
55
+ it 'returns a registered response by name' do
56
+ expect(api.response(:template1)).to be_kind_of(Praxis::ResponseTemplate)
57
+ end
58
+ end
59
+
60
+ context '.trait' do
61
+ let(:api){ non_singleton_api }
62
+
63
+ let(:trait2){ Proc.new{} }
64
+ it 'has the defined trait1 ' do
65
+ expect(api.traits.keys).to include(:trait1)
66
+ expect(api.traits[:trait1]).to be_kind_of(Proc)
67
+ end
68
+
69
+ it 'saves it verbatim' do
70
+ api.trait :trait2, &trait2
71
+ expect(api.traits[:trait2]).to be(trait2)
72
+ end
73
+
74
+ it 'complains trying to register traits with same name' do
75
+ api.trait :trait2, &trait2
76
+ expect{
77
+ api.trait :trait2, &trait2
78
+ }.to raise_error(Praxis::Exceptions::InvalidTrait, /Overwriting a previous trait with the same name/)
79
+ end
80
+ end
81
+
82
+ context '.info' do
83
+
84
+ let(:api){ non_singleton_api }
85
+
86
+ context 'with a version' do
87
+ it 'saves the data into the correct version hash' do
88
+ expect(api.infos.keys).to_not include("9.0")
89
+ api.info("9.0", &info_block)
90
+ expect(api.infos.keys).to include("9.0")
91
+ end
92
+ it 'immediate invokes the block' do
93
+ expect(api.infos["9.0"]).to receive(:instance_eval)
94
+ api.info("9.0", &info_block)
95
+ end
96
+ end
97
+ context 'without a version' do
98
+ it 'saves it into nil if no version is passed' do
99
+ expect(api.infos.keys).to_not include(nil)
100
+ api.info do
101
+ description "Global Description"
102
+ end
103
+ expect(api.infos.keys).to include(nil)
104
+ end
105
+ end
106
+ end
107
+
108
+ context '.describe' do
109
+ subject(:output){ api.describe }
110
+
111
+ context 'using the spec_app definitions' do
112
+ subject(:version_output){ output["1.0"][:info] }
113
+ it 'saves the data into the correct version hash' do
114
+ expect(api.infos.keys).to include(nil)
115
+ expect(api.infos.keys).to include("1.0")
116
+ end
117
+
118
+ it 'outputs data for 1.0 info' do
119
+ expect(output.keys).to include("1.0")
120
+ expect(output["1.0"]).to include(:info)
121
+ end
122
+ it 'describes 1.0 Api info properly' do
123
+ info = output["1.0"][:info]
124
+ expect(info).to include(:schema_version, :name, :title, :description, :base_path)
125
+ expect(info[:schema_version]).to eq("1.0")
126
+ expect(info[:name]).to eq("Spec App")
127
+ expect(info[:title]).to eq("A simple App to do some simple integration testing")
128
+ expect(info[:description]).to eq("A simple 1.0 App")
129
+ expect(info[:base_path]).to eq("/")
130
+ end
131
+ end
132
+
133
+ context 'using a non-singleton object' do
134
+ let(:api){ non_singleton_api }
135
+
136
+ before do
137
+ api.info("9.0", &info_block)
138
+ api.info do
139
+ description "Global Description"
140
+ end
141
+ end
142
+ its(:keys){ should include("9.0") }
143
+
144
+ context 'for v9.0 info' do
145
+ subject(:v9_info){ output["9.0"][:info] }
146
+
147
+ it 'has the info it was set in the call' do
148
+ expect(v9_info).to include({schema_version: "1.0"})
149
+ expect(v9_info).to include({name: "Name"})
150
+ expect(v9_info).to include({title: "Title"})
151
+ end
152
+ it 'inherited the description from the nil(global) one' do
153
+ expect(v9_info).to include({description: "Global Description"})
154
+ end
155
+
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe Praxis::ApiGeneralInfo do
4
+
5
+ subject(:info){ Praxis::ApiGeneralInfo.new }
6
+
7
+ let(:info_block) do
8
+ Proc.new do
9
+ name "Name"
10
+ title "Title"
11
+ description "Description"
12
+ base_path "/base"
13
+ end
14
+ end
15
+
16
+ context 'DSLs' do
17
+ it 'accepts the appropriate DSLs' do
18
+ expect{
19
+ info.instance_exec &info_block
20
+ }.to_not raise_error
21
+ end
22
+
23
+ end
24
+
25
+ context '.describe' do
26
+ subject(:output){ info.describe }
27
+ it 'works' do
28
+ info.instance_exec &info_block
29
+ expect(output).to eq(
30
+ {:schema_version=>"1.0", :name=>"Name", :title=>"Title",
31
+ :description=>"Description", :base_path=>"/base"
32
+ })
33
+ end
34
+
35
+ end
36
+ end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Praxis::Bootloader do
4
4
  let(:application) do
5
- instance_double("Praxis::Application", config: "config", root: "root", plugins: {})
5
+ instance_double("Praxis::Application", config: "config", root: "root", plugins: {}, file_layout: {} )
6
6
  end
7
7
 
8
8
  subject(:bootloader) {Praxis::Bootloader.new(application)}
@@ -17,6 +17,15 @@ describe Praxis::Bootloader do
17
17
  its(:root) {should be(application.root)}
18
18
  end
19
19
 
20
+ context ".run" do
21
+ it "should call setup and run for all the stages" do
22
+ bootloader.stages.each do |s|
23
+ expect(s).to receive(:setup!).once
24
+ expect(s).to receive(:run).once
25
+ end
26
+ bootloader.run
27
+ end
28
+ end
20
29
  context ".delete_stage" do
21
30
  it "delete valid stage" do
22
31
  bootloader.delete_stage(:app)
@@ -2,49 +2,49 @@ require "spec_helper"
2
2
 
3
3
  describe Praxis::MediaTypeCollection do
4
4
 
5
- let(:type) { Volume }
6
- let(:example) { Volume.example('example-volume') }
5
+ subject!(:media_type_collection) do
6
+ silence_warnings do
7
+ klass = Class.new(Praxis::MediaTypeCollection) do
8
+ member_type VolumeSnapshot
9
+
10
+ attributes do
11
+ attribute :name, String, regexp: /snapshots-(\w+)/
12
+ attribute :size, Integer
13
+ attribute :href, String
14
+ end
15
+
16
+ view :link do
17
+ attribute :name
18
+ attribute :size
19
+ attribute :href
20
+ end
7
21
 
8
- let(:snapshots) { example.snapshots }
22
+ member_view :default, using: :default
23
+ end
9
24
 
10
- subject(:media_type_collection) do
11
- Class.new(Praxis::MediaTypeCollection) do
12
- member_type Volume
25
+ klass.finalize!
26
+ klass
13
27
  end
14
28
  end
15
29
 
16
30
  context '.member_type' do
17
- its(:member_type){ should be(Volume) }
31
+ its(:member_type){ should be(VolumeSnapshot) }
18
32
  its(:member_attribute){ should be_kind_of(Attributor::Attribute) }
19
- its('member_attribute.type'){ should be(Volume) }
33
+ its('member_attribute.type'){ should be(VolumeSnapshot) }
20
34
  end
21
35
 
22
36
  context '.load' do
23
- let(:volume_data) do
24
- {
25
- id: 1,
26
- name: 'bob',
27
- snapshots: snapshots_data
28
- }
29
- end
30
-
31
- let(:snapshots_data) {
32
- nil
33
- }
34
-
35
37
  context 'with a hash' do
36
38
  let(:snapshots_data) { {name: 'snapshots', href: '/bob/snapshots' } }
39
+ subject(:snapshots) { media_type_collection.load(snapshots_data) }
37
40
 
38
- let(:volume) { Volume.load(volume_data) }
39
- subject(:snapshots) { volume.snapshots }
41
+ its(:name) { should eq(snapshots_data[:name]) }
42
+ its(:href) { should eq(snapshots_data[:href]) }
40
43
 
41
- its(:name) { should eq(snapshots_data[:name]) }
42
- its(:href) { should eq(snapshots_data[:href]) }
43
-
44
- it 'has no members set' do
45
- expect(snapshots.to_a).to eq([])
46
- end
47
- end
44
+ it 'has no members set' do
45
+ expect(snapshots.to_a).to eq([])
46
+ end
47
+ end
48
48
 
49
49
 
50
50
  context 'loading an array' do
@@ -52,9 +52,8 @@ describe Praxis::MediaTypeCollection do
52
52
  [{id: 1, name: 'snapshot-1'},
53
53
  {id: 2, name: 'snapshot-2'}]
54
54
  end
55
-
56
- let(:volume) { Volume.load(volume_data) }
57
- let(:snapshots) { volume.snapshots }
55
+
56
+ let(:snapshots) { media_type_collection.load(snapshots_data) }
58
57
  subject(:members) { snapshots.to_a }
59
58
 
60
59
  it 'sets the collection members' do
@@ -75,10 +74,10 @@ describe Praxis::MediaTypeCollection do
75
74
  end
76
75
  end
77
76
 
78
-
79
77
  context '#render' do
80
-
81
78
  context 'for standard views' do
79
+ let(:snapshots_data) { {name: 'snapshots', href: '/bob/snapshots' } }
80
+ let(:snapshots) { media_type_collection.load(snapshots_data) }
82
81
  subject(:output) { snapshots.render(:link) }
83
82
 
84
83
  its([:name]) { should eq(snapshots.name)}
@@ -86,8 +85,15 @@ describe Praxis::MediaTypeCollection do
86
85
  its([:href]) { should eq(snapshots.href)}
87
86
  end
88
87
 
89
- context 'for member views' do
90
- subject(:output) { snapshots.render(:default) }
88
+ context 'for members' do
89
+ let(:snapshots_data) do
90
+ [{id: 1, name: 'snapshot-1'},
91
+ {id: 2, name: 'snapshot-2'}]
92
+ end
93
+
94
+ let(:snapshots) { media_type_collection.load(snapshots_data) }
95
+
96
+ subject(:output) { media_type_collection.dump(snapshots, view: :default) }
91
97
 
92
98
  it { should eq(snapshots.collect(&:render)) }
93
99
  end
@@ -96,23 +102,12 @@ describe Praxis::MediaTypeCollection do
96
102
  end
97
103
 
98
104
  context '#validate' do
99
- let(:volume_data) do
100
- {
101
- id: 1,
102
- name: 'bob',
103
- snapshots: snapshots_data
104
- }
105
- end
106
-
107
- let(:snapshots_data) {
108
- nil
109
- }
105
+
110
106
 
111
107
  context 'with a hash' do
112
108
  let(:snapshots_data) { {name: 'snapshots-1', href: '/bob/snapshots' } }
109
+ subject(:snapshots) { media_type_collection.load(snapshots_data) }
113
110
 
114
- let(:volume) { Volume.load(volume_data) }
115
- subject(:snapshots) { volume.snapshots }
116
111
 
117
112
  it 'validates' do
118
113
  expect(snapshots.validate).to be_empty
@@ -133,10 +128,8 @@ describe Praxis::MediaTypeCollection do
133
128
  {id: 2, name: 'snapshot-2'}]
134
129
  end
135
130
 
136
- let(:volume) { Volume.load(volume_data) }
137
- let(:snapshots) { volume.snapshots }
138
- subject(:members) { snapshots.to_a }
139
-
131
+ subject(:snapshots) { media_type_collection.load(snapshots_data) }
132
+
140
133
  it 'validates' do
141
134
  expect(snapshots.validate).to be_empty
142
135
  end