praxis 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
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