praxis 0.15.0 → 0.16.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 +4 -4
- data/CHANGELOG.md +28 -2
- data/lib/api_browser/Gruntfile.js +6 -1
- data/lib/praxis.rb +2 -14
- data/lib/praxis/action_definition.rb +45 -16
- data/lib/praxis/api_definition.rb +36 -17
- data/lib/praxis/api_general_info.rb +59 -16
- data/lib/praxis/request_stages/request_stage.rb +2 -6
- data/lib/praxis/resource_definition.rb +50 -17
- data/lib/praxis/router.rb +10 -11
- data/lib/praxis/routing_config.rb +65 -0
- data/lib/praxis/tasks/routes.rb +8 -4
- data/lib/praxis/trait.rb +107 -0
- data/lib/praxis/types/media_type_common.rb +1 -1
- data/lib/praxis/version.rb +1 -1
- data/praxis.gemspec +1 -1
- data/spec/functional_spec.rb +16 -3
- data/spec/praxis/action_definition_spec.rb +107 -5
- data/spec/praxis/api_definition_spec.rb +67 -44
- data/spec/praxis/api_general_info_spec.rb +28 -14
- data/spec/praxis/media_type_spec.rb +33 -6
- data/spec/praxis/resource_definition_spec.rb +46 -12
- data/spec/praxis/router_spec.rb +9 -15
- data/spec/praxis/routing_config_spec.rb +89 -0
- data/spec/praxis/trait_spec.rb +38 -0
- data/spec/spec_app/app/controllers/instances.rb +6 -2
- data/spec/spec_app/design/resources/instances.rb +19 -10
- data/spec/spec_app/design/resources/volumes.rb +3 -3
- data/spec/support/spec_resource_definitions.rb +11 -6
- data/tasks/thor/example.rb +43 -35
- metadata +8 -6
- data/lib/praxis/skeletor/restful_routing_config.rb +0 -55
- data/spec/praxis/restful_routing_config_spec.rb +0 -101
@@ -11,7 +11,7 @@ describe Praxis::ActionDefinition do
|
|
11
11
|
|
12
12
|
media_type 'application/json'
|
13
13
|
version '1.0'
|
14
|
-
|
14
|
+
prefix '/api/hello_world'
|
15
15
|
action_defaults do
|
16
16
|
payload { attribute :inherited, String }
|
17
17
|
headers { header "Inherited", String }
|
@@ -61,6 +61,44 @@ describe Praxis::ActionDefinition do
|
|
61
61
|
it 'has some tests after we stop using ApiDefinition.instance'
|
62
62
|
end
|
63
63
|
|
64
|
+
describe 'when a trait is used' do
|
65
|
+
subject(:action) do
|
66
|
+
Praxis::ActionDefinition.new(:bar, resource_definition) do
|
67
|
+
trait :test
|
68
|
+
routing { get '/:one' }
|
69
|
+
params { attribute :one, String }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
let(:trait) do
|
74
|
+
Praxis::Trait.new do
|
75
|
+
routing do
|
76
|
+
prefix '/test_trait/:app_name'
|
77
|
+
end
|
78
|
+
|
79
|
+
params do
|
80
|
+
attribute :app_name, String
|
81
|
+
attribute :name, String
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
let(:traits) { {test: trait} }
|
87
|
+
|
88
|
+
before do
|
89
|
+
allow(Praxis::ApiDefinition.instance).to receive(:traits).and_return(traits)
|
90
|
+
end
|
91
|
+
|
92
|
+
its('params.attributes.keys') { should eq [:inherited, :app_name, :name, :one]}
|
93
|
+
its('routes.first.path.to_s') { should eq '/api/hello_world/test_trait/:app_name/:one' }
|
94
|
+
its(:traits) { should eq [:test] }
|
95
|
+
|
96
|
+
it 'is reflected in the describe output' do
|
97
|
+
expect(action.describe[:traits]).to eq [:test]
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
64
102
|
describe '#params' do
|
65
103
|
it 'merges in more params' do
|
66
104
|
subject.params do
|
@@ -98,9 +136,8 @@ describe Praxis::ActionDefinition do
|
|
98
136
|
header "more"
|
99
137
|
end
|
100
138
|
|
101
|
-
|
102
|
-
|
103
|
-
])
|
139
|
+
expected_array = ["X_REQUESTED_WITH", "Inherited", "more"]
|
140
|
+
expect(subject.headers.attributes.keys).to match_array(expected_array)
|
104
141
|
end
|
105
142
|
end
|
106
143
|
|
@@ -153,7 +190,7 @@ describe Praxis::ActionDefinition do
|
|
153
190
|
|
154
191
|
context 'with nodoc!' do
|
155
192
|
before do
|
156
|
-
action.nodoc!
|
193
|
+
action.nodoc!
|
157
194
|
end
|
158
195
|
|
159
196
|
it 'has :doc_visibility set in metadata' do
|
@@ -165,4 +202,69 @@ describe Praxis::ActionDefinition do
|
|
165
202
|
end
|
166
203
|
|
167
204
|
end
|
205
|
+
|
206
|
+
context 'with a base_path and base_params on ApiDefinition' do
|
207
|
+
# Without getting a fresh new ApiDefinition it is very difficult to test stuff using the Singleton
|
208
|
+
# So for some tests we're gonna create a new instance and work with it to avoid the singleton issues
|
209
|
+
let(:non_singleton_api) do
|
210
|
+
api_def=Praxis::ApiDefinition.__send__(:new)
|
211
|
+
api_def.instance_eval do |api|
|
212
|
+
|
213
|
+
api.info do
|
214
|
+
base_path '/apps/:app_name'
|
215
|
+
end
|
216
|
+
|
217
|
+
api.info '1.0' do
|
218
|
+
base_params do
|
219
|
+
attribute :app_name, String
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
api_def
|
225
|
+
end
|
226
|
+
|
227
|
+
before do
|
228
|
+
allow(Praxis::ApiDefinition).to receive(:instance).and_return(non_singleton_api)
|
229
|
+
end
|
230
|
+
|
231
|
+
its('routes.first.path.to_s') { should eq '/apps/:app_name/api/hello_world/:one' }
|
232
|
+
its('params.attributes.keys') { should eq [:inherited, :app_name, :one]}
|
233
|
+
|
234
|
+
context 'where the action overrides a base_param' do
|
235
|
+
|
236
|
+
let(:resource_definition) do
|
237
|
+
Class.new do
|
238
|
+
include Praxis::ResourceDefinition
|
239
|
+
|
240
|
+
def self.name
|
241
|
+
'FooBar'
|
242
|
+
end
|
243
|
+
version '1.0'
|
244
|
+
prefix '/api/hello_world'
|
245
|
+
action_defaults do
|
246
|
+
payload { attribute :inherited, String }
|
247
|
+
headers { header "Inherited", String }
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
let(:action) do
|
253
|
+
Praxis::ActionDefinition.new(:foo, resource_definition) do
|
254
|
+
routing { get '' }
|
255
|
+
params { attribute :app_name, Integer }
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
subject(:attributes) { action.params.attributes }
|
260
|
+
|
261
|
+
its(:keys) { should eq [:app_name]}
|
262
|
+
|
263
|
+
it 'overrides the base param' do
|
264
|
+
expect(attributes[:app_name].type).to eq(Attributor::Integer)
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
168
270
|
end
|
@@ -1,43 +1,54 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Praxis::ApiDefinition do
|
4
|
-
|
5
|
-
subject(:api){ Praxis::ApiDefinition.instance }
|
6
|
-
|
4
|
+
|
5
|
+
subject(:api){ Praxis::ApiDefinition.instance }
|
6
|
+
|
7
7
|
# Without getting a fresh new ApiDefinition it is very difficult to test stuff using the Singleton
|
8
8
|
# So for some tests we're gonna create a new instance and work with it to avoid the singleton issues
|
9
9
|
let(:non_singleton_api) do
|
10
10
|
api_def=Praxis::ApiDefinition.__send__(:new)
|
11
11
|
api_def.instance_eval do |api|
|
12
|
-
api.response_template :template1, &Proc.new {}
|
13
|
-
api.trait :trait1, &Proc.new {}
|
12
|
+
api.response_template :template1, &Proc.new {}
|
13
|
+
api.trait :trait1, &Proc.new {}
|
14
|
+
api.trait :trait_2 do
|
15
|
+
description 'the second testing trait'
|
16
|
+
end
|
17
|
+
|
18
|
+
api.info '1.0' do
|
19
|
+
base_path '/apps/:app_name'
|
20
|
+
base_params do
|
21
|
+
attribute :app_name, String
|
22
|
+
end
|
23
|
+
end
|
14
24
|
end
|
15
25
|
api_def
|
16
26
|
end
|
17
|
-
|
27
|
+
|
18
28
|
let(:info_block) do
|
19
29
|
Proc.new do
|
20
30
|
name "Name"
|
21
31
|
title "Title"
|
22
32
|
end
|
23
|
-
end
|
24
|
-
|
33
|
+
end
|
34
|
+
|
25
35
|
context 'singleton' do
|
26
36
|
it 'should be a Singleton' do
|
27
37
|
expect(Praxis::ApiDefinition.ancestors).to include( Singleton )
|
28
38
|
expect(subject).to eq(Praxis::ApiDefinition.instance )
|
29
39
|
end
|
30
|
-
|
40
|
+
|
31
41
|
it 'has the :ok and :created response templates registered' do
|
32
42
|
expect(api.responses.keys).to include(:ok)
|
33
43
|
expect(api.responses.keys).to include(:created)
|
34
|
-
end
|
44
|
+
end
|
35
45
|
end
|
36
|
-
|
46
|
+
|
47
|
+
|
37
48
|
context '.response_template' do
|
38
49
|
let(:response_template){ Proc.new {} }
|
39
50
|
let(:api){ non_singleton_api }
|
40
|
-
|
51
|
+
|
41
52
|
it 'has the defined template1 response_template' do
|
42
53
|
expect(api.responses.keys).to include(:template1)
|
43
54
|
expect(api.response(:template1)).to be_kind_of(Praxis::ResponseTemplate)
|
@@ -48,7 +59,7 @@ describe Praxis::ApiDefinition do
|
|
48
59
|
expect(api.response(:foobar)).to be_kind_of(Praxis::ResponseTemplate)
|
49
60
|
end
|
50
61
|
end
|
51
|
-
|
62
|
+
|
52
63
|
context '.response' do
|
53
64
|
let(:api){ non_singleton_api }
|
54
65
|
|
@@ -56,32 +67,39 @@ describe Praxis::ApiDefinition do
|
|
56
67
|
expect(api.response(:template1)).to be_kind_of(Praxis::ResponseTemplate)
|
57
68
|
end
|
58
69
|
end
|
59
|
-
|
70
|
+
|
60
71
|
context '.trait' do
|
61
72
|
let(:api){ non_singleton_api }
|
62
|
-
|
73
|
+
|
63
74
|
let(:trait2){ Proc.new{} }
|
75
|
+
|
64
76
|
it 'has the defined trait1 ' do
|
65
77
|
expect(api.traits.keys).to include(:trait1)
|
66
|
-
expect(api.traits[:trait1]).to be_kind_of(
|
78
|
+
expect(api.traits[:trait1]).to be_kind_of(Praxis::Trait)
|
67
79
|
end
|
68
|
-
|
69
|
-
it 'saves it verbatim' do
|
70
|
-
api.trait :trait2, &trait2
|
71
|
-
expect(api.traits[:trait2]).to be(trait2)
|
72
|
-
end
|
73
|
-
|
80
|
+
|
74
81
|
it 'complains trying to register traits with same name' do
|
75
82
|
api.trait :trait2, &trait2
|
76
|
-
expect{
|
83
|
+
expect{
|
77
84
|
api.trait :trait2, &trait2
|
78
85
|
}.to raise_error(Praxis::Exceptions::InvalidTrait, /Overwriting a previous trait with the same name/)
|
79
86
|
end
|
80
87
|
end
|
81
|
-
|
88
|
+
|
82
89
|
context '.info' do
|
83
|
-
|
90
|
+
|
84
91
|
let(:api){ non_singleton_api }
|
92
|
+
subject(:info) { api.info('1.0') }
|
93
|
+
|
94
|
+
context '.base_path' do
|
95
|
+
its(:base_path) { should eq '/apps/:app_name'}
|
96
|
+
end
|
97
|
+
|
98
|
+
context '.base_params' do
|
99
|
+
subject(:base_params) { info.base_params }
|
100
|
+
it { should be_kind_of(Attributor::Attribute) }
|
101
|
+
its(:attributes) { should include :app_name }
|
102
|
+
end
|
85
103
|
|
86
104
|
context 'with a version' do
|
87
105
|
it 'saves the data into the correct version hash' do
|
@@ -89,36 +107,39 @@ describe Praxis::ApiDefinition do
|
|
89
107
|
api.info("9.0", &info_block)
|
90
108
|
expect(api.infos.keys).to include("9.0")
|
91
109
|
end
|
92
|
-
|
93
|
-
|
94
|
-
api.info("9.0", &info_block)
|
95
|
-
end
|
96
|
-
end
|
110
|
+
end
|
111
|
+
|
97
112
|
context 'without a version' do
|
98
|
-
it 'saves it into
|
113
|
+
it 'saves it into global_info' do
|
99
114
|
expect(api.infos.keys).to_not include(nil)
|
100
115
|
api.info do
|
101
116
|
description "Global Description"
|
102
117
|
end
|
103
|
-
expect(api.infos.keys).
|
118
|
+
expect(api.infos.keys).to_not include(nil)
|
119
|
+
expect(api.global_info.description).to eq 'Global Description'
|
104
120
|
end
|
105
121
|
end
|
106
122
|
end
|
107
|
-
|
123
|
+
|
108
124
|
context '.describe' do
|
109
125
|
subject(:output){ api.describe }
|
110
126
|
|
111
127
|
context 'using the spec_app definitions' do
|
112
128
|
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
129
|
|
130
|
+
context 'for the global_info data' do
|
131
|
+
subject(:info) { output[:global][:info] }
|
132
|
+
it { should include(:name, :title, :description) }
|
133
|
+
its([:name]) { should eq 'Spec App' }
|
134
|
+
its([:title]) { should eq 'A simple App to do some simple integration testing' }
|
135
|
+
its([:description]) { should eq 'This example API should really be replaced by a set of more full-fledged example apps in the future' }
|
136
|
+
end
|
137
|
+
|
118
138
|
it 'outputs data for 1.0 info' do
|
119
139
|
expect(output.keys).to include("1.0")
|
120
140
|
expect(output["1.0"]).to include(:info)
|
121
141
|
end
|
142
|
+
|
122
143
|
it 'describes 1.0 Api info properly' do
|
123
144
|
info = output["1.0"][:info]
|
124
145
|
expect(info).to include(:schema_version, :name, :title, :description, :base_path)
|
@@ -129,21 +150,23 @@ describe Praxis::ApiDefinition do
|
|
129
150
|
expect(info[:base_path]).to eq("/")
|
130
151
|
end
|
131
152
|
end
|
132
|
-
|
153
|
+
|
133
154
|
context 'using a non-singleton object' do
|
134
155
|
let(:api){ non_singleton_api }
|
135
|
-
|
156
|
+
|
136
157
|
before do
|
137
158
|
api.info("9.0", &info_block)
|
138
159
|
api.info do
|
139
160
|
description "Global Description"
|
140
161
|
end
|
141
162
|
end
|
142
|
-
its(:keys){ should include("9.0") }
|
143
|
-
|
163
|
+
its(:keys) { should include("9.0") }
|
164
|
+
its(:keys) { should include :traits }
|
165
|
+
its([:traits, :trait_2]) { should eq api.traits[:trait_2].describe }
|
166
|
+
|
144
167
|
context 'for v9.0 info' do
|
145
168
|
subject(:v9_info){ output["9.0"][:info] }
|
146
|
-
|
169
|
+
|
147
170
|
it 'has the info it was set in the call' do
|
148
171
|
expect(v9_info).to include({schema_version: "1.0"})
|
149
172
|
expect(v9_info).to include({name: "Name"})
|
@@ -152,8 +175,8 @@ describe Praxis::ApiDefinition do
|
|
152
175
|
it 'inherited the description from the nil(global) one' do
|
153
176
|
expect(v9_info).to include({description: "Global Description"})
|
154
177
|
end
|
155
|
-
|
178
|
+
|
156
179
|
end
|
157
180
|
end
|
158
181
|
end
|
159
|
-
end
|
182
|
+
end
|
@@ -1,36 +1,50 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Praxis::ApiGeneralInfo do
|
4
|
-
|
4
|
+
|
5
5
|
subject(:info){ Praxis::ApiGeneralInfo.new }
|
6
|
-
|
6
|
+
|
7
7
|
let(:info_block) do
|
8
8
|
Proc.new do
|
9
9
|
name "Name"
|
10
10
|
title "Title"
|
11
11
|
description "Description"
|
12
12
|
base_path "/base"
|
13
|
+
base_params do
|
14
|
+
attribute :name, String
|
15
|
+
end
|
13
16
|
end
|
14
17
|
end
|
15
|
-
|
16
|
-
context '
|
18
|
+
|
19
|
+
context 'setting' do
|
17
20
|
it 'accepts the appropriate DSLs' do
|
18
|
-
expect{
|
21
|
+
expect{
|
19
22
|
info.instance_exec &info_block
|
20
23
|
}.to_not raise_error
|
21
24
|
end
|
22
|
-
|
23
25
|
end
|
24
26
|
|
27
|
+
context 'getting values' do
|
28
|
+
before do
|
29
|
+
info.instance_exec &info_block
|
30
|
+
end
|
31
|
+
|
32
|
+
its(:name) { should eq 'Name' }
|
33
|
+
|
34
|
+
end
|
25
35
|
context '.describe' do
|
26
|
-
|
27
|
-
it 'works' do
|
36
|
+
before do
|
28
37
|
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
38
|
end
|
34
|
-
|
39
|
+
|
40
|
+
subject(:output){ info.describe }
|
41
|
+
its([:schema_version]) {should eq '1.0' }
|
42
|
+
its([:name]) {should eq 'Name' }
|
43
|
+
its([:title]) {should eq 'Title' }
|
44
|
+
its([:description]) {should eq 'Description' }
|
45
|
+
its([:base_path]) {should eq '/base' }
|
46
|
+
its([:base_params]) { should have_key :name }
|
47
|
+
its([:base_params, :name, :type, :name]) { should eq 'String' }
|
48
|
+
|
35
49
|
end
|
36
|
-
end
|
50
|
+
end
|
@@ -9,8 +9,8 @@ describe Praxis::MediaType do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
let(:resource) do
|
12
|
-
double('address',
|
13
|
-
id: 1,
|
12
|
+
double('address',
|
13
|
+
id: 1,
|
14
14
|
name: 'Home',
|
15
15
|
owner: owner_resource,
|
16
16
|
manager: manager_resource,
|
@@ -38,7 +38,7 @@ describe Praxis::MediaType do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
|
41
|
-
|
41
|
+
|
42
42
|
context 'accessor methods' do
|
43
43
|
subject(:address_klass) { address.class }
|
44
44
|
|
@@ -62,7 +62,7 @@ describe Praxis::MediaType do
|
|
62
62
|
end
|
63
63
|
|
64
64
|
its(:description) { should be_kind_of(String) }
|
65
|
-
|
65
|
+
|
66
66
|
context 'links' do
|
67
67
|
context 'with a custom links attribute' do
|
68
68
|
subject(:person) { Person.new(owner_resource) }
|
@@ -70,7 +70,7 @@ describe Praxis::MediaType do
|
|
70
70
|
its(:links) { should be_kind_of(Array) }
|
71
71
|
its(:links) { should eq(owner_resource.links) }
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
context 'using the links DSL' do
|
75
75
|
subject(:address) { Address.new(resource) }
|
76
76
|
its(:links) { should be_kind_of(Address::Links) }
|
@@ -114,7 +114,7 @@ describe Praxis::MediaType do
|
|
114
114
|
let(:snapshots_summary) { volume.snapshots_summary }
|
115
115
|
let(:output) { volume.render(:default) }
|
116
116
|
subject { links[:snapshots] }
|
117
|
-
|
117
|
+
|
118
118
|
its([:name]) { should eq(snapshots_summary.name) }
|
119
119
|
its([:size]) { should eq(snapshots_summary.size) }
|
120
120
|
its([:href]) { should eq(snapshots_summary.href) }
|
@@ -140,6 +140,33 @@ describe Praxis::MediaType do
|
|
140
140
|
end
|
141
141
|
|
142
142
|
|
143
|
+
context 'describing' do
|
144
|
+
|
145
|
+
subject(:described){ Address.describe }
|
146
|
+
|
147
|
+
its(:keys) { should match_array( [:attributes, :description, :family, :id, :identifier, :key, :name, :views] ) }
|
148
|
+
its([:attributes]) { should be_kind_of(::Hash) }
|
149
|
+
its([:description]) { should be_kind_of(::String) }
|
150
|
+
its([:family]) { should be_kind_of(::String) }
|
151
|
+
its([:id]) { should be_kind_of(::String) }
|
152
|
+
its([:name]) { should be_kind_of(::String) }
|
153
|
+
its([:identifier]) { should be_kind_of(::String) }
|
154
|
+
its([:key]) { should be_kind_of(::Hash) }
|
155
|
+
its([:views]) { should be_kind_of(::Hash) }
|
156
|
+
|
157
|
+
its([:description]) { should eq(Address.description) }
|
158
|
+
its([:family]) { should eq(Address.family) }
|
159
|
+
its([:id]) { should eq(Address.id) }
|
160
|
+
its([:name]) { should eq(Address.name) }
|
161
|
+
its([:identifier]) { should eq(Address.identifier.to_s) }
|
162
|
+
it 'should include the defined views' do
|
163
|
+
expect( subject[:views].keys ).to match( [:default, :master] )
|
164
|
+
end
|
165
|
+
it 'should include the defined attributes' do
|
166
|
+
expect( subject[:attributes].keys ).to match( [:id, :name, :owner, :custodian, :residents, :residents_summary, :links] )
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
143
170
|
context 'using blueprint caching' do
|
144
171
|
it 'has specs'
|
145
172
|
end
|