praxis 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,7 +9,7 @@ describe Praxis::ResourceDefinition do
9
9
  its(:version) { should eq('1.0') }
10
10
  its(:version_options) { should eq({using: [:header,:params]}) }
11
11
 
12
- its(:routing_config) { should be_kind_of(Proc) }
12
+ its(:prefix) { should eq('/people') }
13
13
 
14
14
  its(:actions) { should have(2).items }
15
15
  its(:metadata) { should_not have_key(:doc_visibility) }
@@ -22,6 +22,7 @@ describe Praxis::ResourceDefinition do
22
22
 
23
23
  its([:actions]) { should have(2).items }
24
24
  its([:metadata]) { should be_kind_of(Hash) }
25
+ its([:traits]) { should eq [:test] }
25
26
  end
26
27
 
27
28
  context '.action' do
@@ -34,7 +35,7 @@ describe Praxis::ResourceDefinition do
34
35
  expect(index).to be_kind_of(Praxis::ActionDefinition)
35
36
  expect(index.description).to eq("index description")
36
37
  end
37
-
38
+
38
39
  it 'complains if action names are not symbols' do
39
40
  expect do
40
41
  Class.new do
@@ -46,7 +47,43 @@ describe Praxis::ResourceDefinition do
46
47
  end
47
48
  end
48
49
 
50
+ context 'action_defaults' do
51
+ let(:resource_definition) do
52
+ Class.new do
53
+ include Praxis::ResourceDefinition
54
+ media_type Person
55
+
56
+ def self.name
57
+ 'FooBar'
58
+ end
49
59
 
60
+ action_defaults do
61
+ routing do
62
+ prefix '/people/:id'
63
+ end
64
+
65
+ params do
66
+ attribute :id
67
+ end
68
+ end
69
+
70
+ action :show do
71
+ routing do
72
+ get ''
73
+ end
74
+ end
75
+
76
+ end
77
+ end
78
+
79
+ it 'are applied to actions' do
80
+ action = resource_definition.actions[:show]
81
+ expect(action.params.attributes).to have_key(:id)
82
+ expect(action.routes.first.path.to_s).to eq '/people/:id'
83
+ end
84
+
85
+ end
86
+
50
87
  context 'setting other values' do
51
88
  subject(:resource_definition) { Class.new {include Praxis::ResourceDefinition } }
52
89
 
@@ -64,7 +101,7 @@ describe Praxis::ResourceDefinition do
64
101
  end
65
102
 
66
103
 
67
- context '.use' do
104
+ context '.trait' do
68
105
  subject(:resource_definition) { Class.new {include Praxis::ResourceDefinition } }
69
106
  it 'raises an error for missing traits' do
70
107
  expect { resource_definition.use(:stuff) }.to raise_error(Praxis::Exceptions::InvalidTrait)
@@ -81,9 +118,10 @@ describe Praxis::ResourceDefinition do
81
118
  def self.name
82
119
  'FooBar'
83
120
  end
121
+
84
122
  silence_warnings do
85
123
  payload { attribute :inherited_payload, String }
86
- headers { header "Inherited-Header", String }
124
+ headers { key "Inherited-Header", String }
87
125
  params { attribute :inherited_params, String }
88
126
  response :not_found
89
127
  end
@@ -95,11 +133,7 @@ describe Praxis::ResourceDefinition do
95
133
 
96
134
  let(:action) { resource_definition.actions[:index] }
97
135
 
98
- it 'defers the values to procs in action_defaults' do
99
- expect(resource_definition.action_defaults).to have(4).items
100
- end
101
-
102
- it 'delegates defaults to the action' do
136
+ it 'are applied to the action' do
103
137
  expect(action.payload.attributes).to have_key(:inherited_payload)
104
138
  expect(action.headers.attributes).to have_key("Inherited-Header")
105
139
  expect(action.params.attributes).to have_key(:inherited_params)
@@ -110,7 +144,7 @@ describe Praxis::ResourceDefinition do
110
144
 
111
145
  context 'with nodoc! called' do
112
146
  before do
113
- resource_definition.nodoc!
147
+ resource_definition.nodoc!
114
148
  end
115
149
 
116
150
  it 'has the :doc_visibility option set' do
@@ -133,7 +167,7 @@ describe Praxis::ResourceDefinition do
133
167
  resource_definition.canonical_path :reset
134
168
  }.to raise_error(/'canonical_path' can only be defined once./)
135
169
  end
136
- end
170
+ end
137
171
  context 'if none specified' do
138
172
  subject(:resource_definition) do
139
173
  Class.new do
@@ -143,7 +177,7 @@ describe Praxis::ResourceDefinition do
143
177
  end
144
178
  end
145
179
  it 'defaults to the :show action' do
146
- expect(subject.canonical_path).to eq(subject.actions[:show])
180
+ expect(subject.canonical_path).to eq(subject.actions[:show])
147
181
  end
148
182
  end
149
183
  context 'with an undefined action' do
@@ -75,9 +75,6 @@ describe Praxis::Router do
75
75
  end
76
76
 
77
77
  context ".add_route" do
78
- before do
79
- expect(router).to receive(:warn).with("other conditions not supported yet")
80
- end
81
78
 
82
79
  let(:route){ double('route', options: [1], version: 1, verb: 'verb', path: 'path')}
83
80
  let(:target){ double('target') }
@@ -93,9 +90,6 @@ describe Praxis::Router do
93
90
  router.add_route(target ,route)
94
91
  end
95
92
 
96
- it "raises warning when options are specified in route" do
97
- expect(router.add_route(proc {'target'},route)).to eq(['path'])
98
- end
99
93
  end
100
94
 
101
95
  context ".call" do
@@ -116,34 +110,34 @@ describe Praxis::Router do
116
110
  router.add_route(double("P"),double("route1", verb: 'POST', path: '/', options: {} , version: request_version))
117
111
  # Hijack the callable block in the routes (since there's no way to point back to the registered route object)
118
112
  router.instance_variable_get( :@routes )['POST'] = post_target_router
119
-
113
+
120
114
  end
121
115
 
122
116
  context 'for routes without wildcards (a single POST route)' do
123
-
117
+
124
118
  context 'and an incoming POST request' do
125
119
  it "finds a match and invokes it the route" do
126
120
  expect(router.call(request)).to eq(router_response_for_post)
127
121
  end
128
122
  end
129
123
  context 'and an incoming PUT request' do
130
- let(:request_verb) { 'PUT' }
124
+ let(:request_verb) { 'PUT' }
131
125
  it "does not find a route" do
132
126
  response_code, _ , _ = router.call(request)
133
127
  expect(response_code).to be(404)
134
128
  end
135
129
  end
136
130
  end
137
-
131
+
138
132
  context 'for routes with wildcards (a POST and a * route)' do
139
133
  before do
140
134
  router.add_route(double("*"),double("route2", verb: 'ANY', path: '/*', options: {} , version: request_version))
141
135
  # Hijack the callable block in the routes (since there's no way to point back to the registered route object)
142
- router.instance_variable_get( :@routes )['ANY'] = any_target_router
136
+ router.instance_variable_get( :@routes )['ANY'] = any_target_router
143
137
  end
144
-
138
+
145
139
  context 'and an incoming PUT request' do
146
- let(:request_verb) { 'PUT' }
140
+ let(:request_verb) { 'PUT' }
147
141
  it "it can successfully find a match using the wildcard target" do
148
142
  expect(router.call(request)).to eq(router_response_for_wildcard)
149
143
  end
@@ -164,7 +158,7 @@ describe Praxis::Router do
164
158
  end
165
159
 
166
160
  context "when not_found is returned" do
167
- let(:request_verb) { 'DELETE' }
161
+ let(:request_verb) { 'DELETE' }
168
162
  let(:router_response){ :not_found }
169
163
 
170
164
 
@@ -175,7 +169,7 @@ describe Praxis::Router do
175
169
  end
176
170
 
177
171
  context 'with X-Cascade disabled' do
178
- let(:config) { Praxis::Application.instance.config.praxis }
172
+ let(:config) { Praxis::Application.instance.config.praxis }
179
173
  before do
180
174
  expect(config).to receive(:x_cascade).and_return(false)
181
175
  end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ describe Praxis::RoutingConfig do
4
+
5
+ let(:resource_definition) do
6
+ Class.new do
7
+ include Praxis::ResourceDefinition
8
+ def self.name; 'MyResource'; end
9
+ end
10
+ end
11
+
12
+ let(:routing_block) { Proc.new{} }
13
+ let(:default_route_prefix) { "/" + resource_definition.name.split("::").last.underscore }
14
+
15
+ subject(:routing_config){ Praxis::RoutingConfig.new(&routing_block) }
16
+
17
+ its(:version) { should eq('n/a') }
18
+ its(:prefix ) { should eq('') }
19
+
20
+ context '#prefix' do
21
+ it 'sets the prefix' do
22
+ routing_config.prefix '/'
23
+ expect(routing_config.prefix).to eq('/')
24
+ end
25
+
26
+ it 'is additive' do
27
+ routing_config.prefix '/people/:id'
28
+ routing_config.prefix '/address'
29
+ expect(routing_config.prefix).to eq('/people/:id/address')
30
+ end
31
+
32
+ it 'strips duplicated /s' do
33
+ routing_config.prefix '/'
34
+ routing_config.prefix '/people'
35
+ expect(routing_config.prefix).to eq('/people')
36
+ end
37
+
38
+ end
39
+
40
+ context '#add_route' do
41
+ let(:path) { '/people' }
42
+ let(:options) { {} }
43
+ let(:route) { routing_config.add_route 'GET', path, **options}
44
+
45
+ it 'returns a corresponding Praxis::Route' do
46
+ expect(route).to be_kind_of(Praxis::Route)
47
+ end
48
+
49
+ it 'appends the Route to the set of routes' do
50
+ expect(routing_config.routes).to include(route)
51
+ end
52
+
53
+ context 'passing options' do
54
+ let(:options){ {name: 'alternative', except: '/special' } }
55
+
56
+ it 'uses :name to name the route' do
57
+ expect(route.name).to eq('alternative')
58
+ end
59
+
60
+ it 'does NOT pass the name option down to mustermann' do
61
+ expect(Mustermann).to receive(:new).with(path, hash_excluding({name: 'alternative'}))
62
+ expect(route.name).to eq('alternative')
63
+ end
64
+
65
+ it 'passes them through the underlying mustermann object (telling it to ignore unknown ones)' do
66
+ expect(Mustermann).to receive(:new).with(path, hash_including(ignore_unknown_options: true, except: '/special'))
67
+ expect(route.options).to eq( { except: '/special' })
68
+ end
69
+ end
70
+
71
+ context 'with prefix defined' do
72
+ before do
73
+ routing_config.prefix '/parents/:parent_id'
74
+ end
75
+
76
+ it 'includes the prefix in the route path' do
77
+ expect(route.path.to_s).to eq '/parents/:parent_id/people'
78
+ end
79
+
80
+ context 'for paths beginning with //' do
81
+ let(:path) { '//people' }
82
+ it 'does not include the prefix in the route path' do
83
+ expect(route.path.to_s).to eq '/people'
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ end
@@ -0,0 +1,38 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Praxis::Trait do
4
+
5
+ subject(:trait) do
6
+ Praxis::Trait.new do
7
+ description 'my awesome trait'
8
+
9
+ routing do
10
+ prefix '/:app_name'
11
+ end
12
+
13
+ response :something
14
+ response :nothing
15
+
16
+ params do
17
+ attribute :app_name, String
18
+ attribute :order, String,
19
+ description: "Field to sort by."
20
+ end
21
+
22
+ end
23
+ end
24
+
25
+ context 'describe' do
26
+ subject(:describe) { trait.describe }
27
+
28
+ its([:description]) { should eq('my awesome trait') }
29
+
30
+ its([:responses, :something]) { should eq Hash.new }
31
+ its([:responses, :nothing]) { should eq Hash.new }
32
+
33
+ its([:params, :app_name, :type, :name]) { should eq 'String' }
34
+ its([:params, :order, :type, :name]) { should eq 'String' }
35
+ its([:routing, :prefix]) { should eq '/:app_name'}
36
+ end
37
+
38
+ end
@@ -3,7 +3,7 @@ class Instances < BaseClass
3
3
 
4
4
  implements ApiResources::Instances
5
5
  include Concerns::BasicApi
6
-
6
+
7
7
  before :validate, actions: [:index] do |controller|
8
8
  #p [:before, :validate, :params_and_headers, controller.request.action.name]
9
9
  end
@@ -32,7 +32,7 @@ class Instances < BaseClass
32
32
  blk.call
33
33
  #puts "Decorator two end"
34
34
  end
35
-
35
+
36
36
  around :action, actions: [:index] do |controller, blk|
37
37
  #puts "Decorator three (index action) start"
38
38
  blk.call
@@ -104,4 +104,8 @@ class Instances < BaseClass
104
104
  response
105
105
  end
106
106
 
107
+ def exceptional(cloud_id:, splat:)
108
+ response.headers['Content-Type'] = 'application/vnd.acme.instance'
109
+ response
110
+ end
107
111
  end
@@ -4,18 +4,16 @@ module ApiResources
4
4
 
5
5
  media_type Instance
6
6
  version '1.0'
7
-
7
+
8
8
  # :show action is the canonical path for this resource.
9
9
  # Note that the following is redundant, since :show is the default canonical path if none is defined.
10
10
  canonical_path :show
11
-
12
- routing do
13
- prefix '/clouds/:cloud_id/instances'
14
- end
15
11
 
16
- action_defaults do
17
- use :authenticated
12
+ prefix '/clouds/:cloud_id/instances'
13
+
14
+ trait :authenticated
18
15
 
16
+ action_defaults do
19
17
  requires_ability :read
20
18
 
21
19
  params do
@@ -31,7 +29,7 @@ module ApiResources
31
29
  params do
32
30
  attribute :response_content_type, String, default: 'application/vnd.acme.instance;type=collection'
33
31
  end
34
-
32
+
35
33
  headers do
36
34
  # BOTH ARE EQUIVALENT
37
35
  #key "FOO", String, required: true
@@ -130,14 +128,14 @@ module ApiResources
130
128
  routing do
131
129
  any '/:id/terminate'
132
130
  end
133
-
131
+
134
132
  requires_ability :terminate
135
133
 
136
134
  params do
137
135
  attribute :id
138
136
  end
139
137
 
140
- payload do
138
+ payload do
141
139
  attribute :when, DateTime
142
140
  end
143
141
 
@@ -175,6 +173,17 @@ module ApiResources
175
173
  response :ok
176
174
  end
177
175
 
176
+
177
+ action :exceptional do
178
+ routing do
179
+ prefix '//clouds/:cloud_id/otherinstances'
180
+ get '/_action/*', except: '*/_action/exceptional'
181
+ end
182
+ params do
183
+ attribute :splat, Attributor::Collection.of(String), required: true
184
+ end
185
+ response :ok
186
+ end
178
187
  # OTHER USAGES:
179
188
  # note: these are all hypothetical, pending, brainstorming usages.
180
189
 
@@ -5,10 +5,10 @@ module ApiResources
5
5
  media_type Volume
6
6
  version '1.0', using: :path
7
7
 
8
- action_defaults do
9
- use :authenticated
10
- end
8
+ prefix '/volumes'
11
9
 
10
+ trait :authenticated
11
+
12
12
  action :index do
13
13
  routing do
14
14
  get ''
@@ -1,5 +1,11 @@
1
1
  require_relative 'spec_media_types'
2
2
 
3
+ Praxis::ApiDefinition.define do
4
+ trait :test do
5
+ description 'testing trait'
6
+ end
7
+ end
8
+
3
9
  class PeopleResource
4
10
  include Praxis::ResourceDefinition
5
11
 
@@ -11,9 +17,9 @@ class PeopleResource
11
17
 
12
18
  canonical_path :show
13
19
 
14
- routing do
15
- prefix "/people"
16
- end
20
+ trait :test
21
+
22
+ prefix '/people'
17
23
 
18
24
  action :index do
19
25
  description 'index description'
@@ -34,6 +40,7 @@ class PeopleResource
34
40
 
35
41
  end
36
42
 
43
+
37
44
  class AddressResource
38
45
  include Praxis::ResourceDefinition
39
46
 
@@ -43,9 +50,7 @@ class AddressResource
43
50
 
44
51
  version '1.0'
45
52
 
46
- routing do
47
- prefix "/addresses"
48
- end
53
+ prefix '/addresses'
49
54
 
50
55
  action :index do
51
56
  description 'index description'