apitizer 0.0.1 → 0.0.2

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.travis.yml +6 -0
  4. data/.yardopts +6 -0
  5. data/CHANGELOG.md +10 -0
  6. data/Guardfile +11 -4
  7. data/README.md +74 -7
  8. data/apitizer.gemspec +1 -2
  9. data/lib/apitizer.rb +0 -1
  10. data/lib/apitizer/base.rb +16 -27
  11. data/lib/apitizer/connection.rb +3 -1
  12. data/lib/apitizer/connection/adaptor.rb +1 -1
  13. data/lib/apitizer/connection/adaptor/standard.rb +24 -7
  14. data/lib/apitizer/connection/dispatcher.rb +5 -12
  15. data/lib/apitizer/connection/format.rb +14 -0
  16. data/lib/apitizer/{processing/parser → connection/format}/json.rb +6 -2
  17. data/lib/apitizer/{processing/parser → connection/format}/yaml.rb +6 -2
  18. data/lib/apitizer/connection/request.rb +3 -3
  19. data/lib/apitizer/connection/response.rb +3 -3
  20. data/lib/apitizer/core.rb +4 -4
  21. data/lib/apitizer/helper.rb +38 -14
  22. data/lib/apitizer/result.rb +2 -2
  23. data/lib/apitizer/routing.rb +1 -1
  24. data/lib/apitizer/routing/{mapper.rb → map.rb} +3 -10
  25. data/lib/apitizer/routing/node.rb +0 -1
  26. data/lib/apitizer/routing/node/base.rb +15 -17
  27. data/lib/apitizer/routing/node/collection.rb +17 -16
  28. data/lib/apitizer/routing/node/operation.rb +14 -15
  29. data/lib/apitizer/routing/node/root.rb +8 -2
  30. data/lib/apitizer/routing/path.rb +16 -8
  31. data/lib/apitizer/version.rb +1 -1
  32. data/spec/apitizer/base_spec.rb +36 -28
  33. data/spec/apitizer/connection/adaptor_spec.rb +87 -11
  34. data/spec/apitizer/connection/dispatcher_spec.rb +21 -23
  35. data/spec/apitizer/connection/format_spec.rb +15 -0
  36. data/spec/apitizer/helper_spec.rb +53 -24
  37. data/spec/apitizer/result_spec.rb +5 -7
  38. data/spec/apitizer/routing/map_spec.rb +71 -0
  39. data/spec/apitizer/routing/node_spec.rb +108 -36
  40. data/spec/apitizer/routing/path_spec.rb +12 -92
  41. data/spec/spec_helper.rb +4 -6
  42. data/spec/support/factory_helper.rb +25 -5
  43. data/spec/support/resource_helper.rb +8 -0
  44. metadata +14 -15
  45. data/lib/apitizer/processing.rb +0 -8
  46. data/lib/apitizer/processing/parser.rb +0 -14
  47. data/lib/apitizer/processing/translator.rb +0 -13
  48. data/lib/apitizer/routing/node/scope.rb +0 -19
  49. data/spec/apitizer/processing/parser_spec.rb +0 -23
  50. data/spec/apitizer/routing/mapper_spec.rb +0 -80
@@ -1,21 +1,97 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Apitizer::Connection::Adaptor do
3
+ RSpec.describe Apitizer::Connection::Adaptor do
4
4
  let(:parent_module) { Apitizer::Connection }
5
- let(:address) { 'https://service.com/api/v1/json/articles' }
5
+ let(:address) { 'https://service.com/api/articles' }
6
6
 
7
- [ 'Standard' ].each do |adaptor|
8
- subject { parent_module::Adaptor.const_get(adaptor).new }
7
+ shared_examples 'a proper postman' do |method:|
8
+ it 'takes into account headers' do
9
+ headers = { 'Secret-Token' => 'arbitrary' }
10
+ stub = stub_http_request(method, address).with(headers: headers)
11
+ subject.process(method, address, {}, headers)
12
+ expect(stub).to have_been_requested
13
+ end
14
+ end
15
+
16
+ shared_examples '#call of a Rack app' do |method:|
17
+ let(:code) { 200 }
18
+ let(:headers) { { 'a' => [ 'b' ] } }
19
+ let(:body) { 'Hej!' }
20
+
21
+ before(:each) do
22
+ stub_http_request(method, address).to_return(
23
+ code: code, headers: headers, body: body)
24
+ end
25
+
26
+ let(:response) { subject.process(method, address) }
27
+
28
+ it 'returns an array with three elements' do
29
+ expect(response.length).to eq(3)
30
+ end
31
+
32
+ it 'returns the code as the first element' do
33
+ expect(response[0]).to eq(code)
34
+ end
35
+
36
+ it 'returns the code as an integer' do
37
+ expect(response[0]).to be_kind_of(Integer)
38
+ end
39
+
40
+ it 'returns the headers as the second element' do
41
+ expect(response[1]).to eq(headers)
42
+ end
43
+
44
+ it 'returns the headers as a hash' do
45
+ expect(response[1]).to be_kind_of(Hash)
46
+ end
47
+
48
+ it 'returns the body as the third element' do
49
+ expect(response[2]).to eq([ body ])
50
+ end
51
+
52
+ it 'returns the body as an object responding to #each' do
53
+ expect(response[2]).to respond_to(:each)
54
+ end
55
+ end
56
+
57
+ describe '::Standard' do
58
+ subject { parent_module::Adaptor::Standard.new }
59
+
60
+ describe '#process' do
61
+ [ :get ].each do |method|
62
+ context "when sending #{ method } requests" do
63
+ it_behaves_like '#call of a Rack app', method: method
64
+ it_behaves_like 'a proper postman', method: method
65
+
66
+ it 'encodes parameters into the URI' do
67
+ stub = stub_http_request(method, address).with(query: { life: 42 })
68
+ subject.process(method, address, life: 42)
69
+ expect(stub).to have_been_requested
70
+ end
71
+ end
72
+ end
73
+
74
+ [ :post, :put, :patch, :delete ].each do |method|
75
+ context "when sending #{ method } requests" do
76
+ it_behaves_like '#call of a Rack app', method: method
77
+ it_behaves_like 'a proper postman', method: method
78
+
79
+ it 'encodes parameters into the body' do
80
+ stub = stub_http_request(method, address).with(body: 'life=42')
81
+ response = subject.process(method, address, life: 42)
82
+ expect(stub).to have_been_requested
83
+ end
9
84
 
10
- describe "#{ adaptor }.process" do
11
- it 'returns the code, headers, and body of the response' do
12
- stub_http_request(:get, address).to_return(
13
- code: '200', body: 'Hej!', headers: { 'a' => 'b' } )
14
- response = subject.process(:get, address)
15
- expect(response).to eq([ '200', { 'a' => [ 'b' ] }, 'Hej!' ])
85
+ it 'sets the charset parameter to UTF-8' do
86
+ stub = stub_http_request(method, address).with(
87
+ headers: { 'Content-Type' => /charset=UTF-8/ })
88
+ response = subject.process(method, address, life: 42)
89
+ expect(stub).to have_been_requested
90
+ end
91
+ end
16
92
  end
17
93
 
18
- it 'raises exceptions when encounters unknown methods' do
94
+ it 'raises exceptions for unknown methods' do
19
95
  expect { subject.process(:smile, address) }.to \
20
96
  raise_error(parent_module::Error, /Invalid method/i)
21
97
  end
@@ -1,37 +1,35 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Apitizer::Connection::Dispatcher do
3
+ RSpec.describe Apitizer::Connection::Dispatcher do
4
4
  extend ResourceHelper
5
5
  include ResourceHelper
6
6
 
7
- let(:headers) { { 'Secret-Token' => 'arbitrary' } }
8
- let(:address) { 'https://service.com/api/v1/json/articles' }
9
- let(:subject) do
10
- Apitizer::Connection::Dispatcher.new(
11
- dictionary: rest_http_dictionary, headers: headers)
12
- end
7
+ let(:address) { 'https://service.com/api/articles' }
13
8
 
14
- def create_request(action, address)
15
- double(action: action, address: address, parameters: {})
9
+ def create_request(method)
10
+ double(method: method, address: address, parameters: {})
16
11
  end
17
12
 
18
13
  describe '#process' do
19
- restful_actions.each do |action|
20
- method = rest_http_dictionary[action]
14
+ { :json => '{}', :yaml => '---' }.each do |format, sample|
15
+ context "when interacting in #{ format }" do
16
+ let(:subject) { Apitizer::Connection::Dispatcher.new(format: format) }
21
17
 
22
- context "when sending #{ action } Requests" do
23
- it 'sets the token header' do
24
- stub = stub_http_request(method, address)
25
- response = subject.process(create_request(action, address))
26
- expect(stub).to \
27
- have_requested(method, address).with(headers: headers)
28
- end
18
+ http_methods.each do |method|
19
+ context "when performing #{ method } operations" do
20
+ it 'uses propoer HTTP methods' do
21
+ stub = stub_http_request(method, address).to_return(body: sample)
22
+ response = subject.process(create_request(method))
23
+ expect(stub).to have_been_requested
24
+ end
29
25
 
30
- it 'returns Responses' do
31
- stub_http_request(method, address).
32
- to_return(code: '200', body: 'Hej!')
33
- response = subject.process(create_request(action, address))
34
- expect([ response.code, response.body ]).to eq([ 200, 'Hej!' ])
26
+ it 'sets proper headers' do
27
+ stub = stub_http_request(method, address).to_return(body: sample).
28
+ with(headers: { 'Accept' => mime_type_dictionary[format] })
29
+ response = subject.process(create_request(method))
30
+ expect(stub).to have_been_requested
31
+ end
32
+ end
35
33
  end
36
34
  end
37
35
  end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Apitizer::Connection::Format do
4
+ let(:subject_class) { Apitizer::Connection::Format }
5
+
6
+ { :json => '{ "articles": [] }',
7
+ :yaml => "---\narticles: []" }.each do |format, sample|
8
+
9
+ it "supports #{ format }" do
10
+ subject = subject_class.build(format)
11
+ result = subject.process(sample)
12
+ expect(result).to eq('articles' => [])
13
+ end
14
+ end
15
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Apitizer::Helper do
3
+ RSpec.describe Apitizer::Helper do
4
4
  extend ResourceHelper
5
5
 
6
6
  let(:subject_module) { Apitizer::Helper }
@@ -8,13 +8,13 @@ describe Apitizer::Helper do
8
8
  describe '.member_action?' do
9
9
  restful_member_actions.each do |action|
10
10
  it "returns true for the #{ action } member action" do
11
- expect(subject_module.member_action?(action)).to be_true
11
+ expect(subject_module.member_action?(action)).to be true
12
12
  end
13
13
  end
14
14
 
15
15
  restful_collection_actions.each do |action|
16
16
  it "returns false for the #{ action } collection action" do
17
- expect(subject_module.member_action?(action)).to be_false
17
+ expect(subject_module.member_action?(action)).to be false
18
18
  end
19
19
  end
20
20
 
@@ -34,29 +34,39 @@ describe Apitizer::Helper do
34
34
  end
35
35
 
36
36
  describe '.build_query' do
37
- it 'handels ordinary parameters' do
38
- queries = [
39
- 'title=Meaning+of+Life&author=Random+Number+Generator',
40
- 'author=Random+Number+Generator&title=Meaning+of+Life'
41
- ]
37
+ it 'handles ordinary parameters' do
42
38
  query = subject_module.build_query(
43
39
  title: 'Meaning of Life', author: 'Random Number Generator')
44
- expect(queries).to include(query)
40
+ expect(query).to \
41
+ eq('title=Meaning+of+Life&author=Random+Number+Generator')
45
42
  end
46
43
 
47
- it 'handles parameters whose values are ordinary lists' do
44
+ it 'handles parameters whose values are ordinary arrays' do
48
45
  query = subject_module.build_query(keywords: [ 'hitchhiker', 'galaxy' ])
49
46
  expect(query).to eq('keywords[]=hitchhiker&keywords[]=galaxy')
50
47
  end
51
48
 
52
- it 'handles parameters whose values are object lists' do
53
- queries = [
54
- 'genres[0][name]=Comedy&genres[1][name]=Fiction',
55
- 'genres[1][name]=Fiction&genres[0][name]=Comedy'
56
- ]
49
+ it 'handles parameters whose values are object arrays' do
57
50
  query = subject_module.build_query(
58
- genres: { 0 => { name: 'Comedy' }, 1 => { name: 'Fiction' } })
59
- expect(queries).to include(query)
51
+ genres: [ { name: 'Comedy' }, { name: 'Fiction' } ])
52
+ expect(query).to \
53
+ eq('genres[0][name]=Comedy&genres[1][name]=Fiction')
54
+ end
55
+
56
+ it 'ignores parameters whose values are empty ordinary arrays' do
57
+ query = subject_module.build_query(title: 'Pulp Fiction', keywords: [])
58
+ expect(query).to eq('title=Pulp+Fiction')
59
+ end
60
+
61
+ it 'ignores parameters whose values are empty object arrays' do
62
+ query = subject_module.build_query(title: 'Pulp Fiction', genres: {})
63
+ expect(query).to eq('title=Pulp+Fiction')
64
+ end
65
+
66
+ it 'ignores deeply nested empty structures' do
67
+ query = subject_module.build_query(title: 'Pulp Fiction',
68
+ genres: [ { :name => { :language => [ nil, nil, [], {} ] } } ])
69
+ expect(query).to eq('title=Pulp+Fiction')
60
70
  end
61
71
 
62
72
  it 'converts integers to decimal strings' do
@@ -64,14 +74,11 @@ describe Apitizer::Helper do
64
74
  expect(query).to eq('page=42')
65
75
  end
66
76
 
67
- it 'converts integers in object lists to decimal strings' do
68
- queries = [
69
- 'primes[0][value]=2&primes[1][value]=3',
70
- 'primes[1][value]=3&primes[0][value]=2'
71
- ]
77
+ it 'converts integers in object arrays to decimal strings' do
72
78
  query = subject_module.build_query(
73
- primes: { 0 => { value: 2 }, 1 => { value: 3 } })
74
- expect(queries).to include(query)
79
+ primes: [ { value: 2 }, { value: 3 } ])
80
+ expect(query).to \
81
+ eq('primes[0][value]=2&primes[1][value]=3')
75
82
  end
76
83
 
77
84
  it 'converts the logical true to the string true' do
@@ -83,5 +90,27 @@ describe Apitizer::Helper do
83
90
  query = subject_module.build_query(published: false)
84
91
  expect(query).to eq('published=false')
85
92
  end
93
+
94
+ it 'handles an arbitrary object that responds to #to_a' do
95
+ object = double(to_a: [ 'one', 'two' ])
96
+ query = subject_module.build_query(digits: object)
97
+ expect(query).to eq('digits[]=one&digits[]=two')
98
+ end
99
+
100
+ it 'handles an arbitrary object that responds to #to_h' do
101
+ object = double(to_h: { one: 1, two: 2 })
102
+ query = subject_module.build_query(digits: object)
103
+ expect(query).to eq('digits[one]=1&digits[two]=2')
104
+ end
105
+
106
+ it 'raises an exception for an unknown objects without #to_a and #to_h' do
107
+ expect { subject_module.build_query(a: { b: Class.new.new }) }.to \
108
+ raise_error(ArgumentError)
109
+ end
110
+
111
+ it 'returns a string encoded in UTF-8' do
112
+ query = subject_module.build_query(alpha: 'omega')
113
+ expect(query.encoding.to_s).to match(/^UTF-8$/i)
114
+ end
86
115
  end
87
116
  end
@@ -1,19 +1,17 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Apitizer::Result do
3
+ RSpec.describe Apitizer::Result do
4
4
  let(:path) { double('Path') }
5
5
  let(:request) { double('Request', path: path) }
6
- let(:response) { double('Response', code: 200) }
7
6
  let(:content) { double('Content') }
7
+ let(:response) { double('Response', code: 200, content: content) }
8
8
 
9
- subject do
10
- Apitizer::Result.new(request: request, response: response, content: content)
11
- end
9
+ subject { Apitizer::Result.new(request: request, response: response) }
12
10
 
13
11
  it { should == content }
14
12
  it { should be_a(content.class) }
15
13
  it { should be_kind_of(content.class) }
16
14
  it { should be_instance_of(content.class) }
17
- its(:path) { should == path }
18
- its(:code) { should == 200 }
15
+ it { expect(subject.path).to eq(path) }
16
+ it { expect(subject.code).to eq(200) }
19
17
  end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Apitizer::Routing::Map do
4
+ extend ResourceHelper
5
+
6
+ describe '#define' do
7
+ it 'declares plain resources' do
8
+ subject.define { resources(:articles) }
9
+ path = subject.trace(:index, [ :articles ])
10
+ expect(path.address).to eq('articles')
11
+ end
12
+
13
+ it 'declares nested resources' do
14
+ subject.define { resources(:articles) { resources(:sections) } }
15
+ path = subject.trace(:show, [ :articles, 'xxx', :sections, 'yyy' ])
16
+ expect(path.address).to eq('articles/xxx/sections/yyy')
17
+ end
18
+
19
+ it 'declares the root address' do
20
+ subject.define do
21
+ address('https://service.com/api')
22
+ resources(:articles)
23
+ end
24
+ path = subject.trace(:show, [ :articles, 'xxx' ])
25
+ expect(path.address).to eq('https://service.com/api/articles/xxx')
26
+ end
27
+
28
+ restful_actions.each do |action|
29
+ it "declares #{ action } operations on members" do
30
+ subject.define do
31
+ resources(:articles) { send(action, :shred, on: :member) }
32
+ end
33
+ path = subject.trace(action, [ :articles, 'xxx', :shred ])
34
+ expect(path.address).to eq('articles/xxx/shred')
35
+ end
36
+
37
+ it "declares #{ action } operations on members with variable names" do
38
+ subject.define do
39
+ resources(:articles) { send(action, ':paragraph', on: :member) }
40
+ end
41
+ path = subject.trace(action, [ :articles, 'xxx', 'zzz' ])
42
+ expect(path.address).to eq('articles/xxx/zzz')
43
+ end
44
+
45
+ it "declares #{ action } operations on collections" do
46
+ subject.define do
47
+ resources(:articles) { send(action, :shred, on: :collection) }
48
+ end
49
+ path = subject.trace(action, [ :articles, :shred ])
50
+ expect(path.address).to eq('articles/shred')
51
+ end
52
+
53
+ it "declares #{ action } operations on collections with variable names" do
54
+ subject.define do
55
+ resources(:articles) { send(action, ':paragraph', on: :collection) }
56
+ end
57
+ path = subject.trace(action, [ :articles, 'zzz' ])
58
+ expect(path.address).to eq('articles/zzz')
59
+ end
60
+ end
61
+
62
+ it 'supports reopening of resource declarations' do
63
+ subject.define do
64
+ resources(:articles)
65
+ resources(:articles) { resources(:sections) }
66
+ end
67
+ path = subject.trace(:show, [ :articles, 'xxx', :sections, 'yyy' ])
68
+ expect(path.address).to eq('articles/xxx/sections/yyy')
69
+ end
70
+ end
71
+ end
@@ -1,63 +1,135 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Apitizer::Routing::Node do
3
+ RSpec.describe Apitizer::Routing::Node do
4
4
  extend ResourceHelper
5
5
  include FactoryHelper
6
6
 
7
7
  shared_examples 'an adequate pathfinder' do
8
- let(:path) { double(:<< => nil, :advance => nil) }
8
+ it 'finds final destinations' do
9
+ expect(root.trace(steps)).to be_kind_of(Apitizer::Routing::Path)
10
+ end
11
+ end
12
+
13
+ shared_examples 'an adequate collection guard' do |only: restful_actions|
14
+ (restful_collection_actions & only).each do |action|
15
+ it "permits #{ action } actions" do
16
+ path = root.trace(steps)
17
+ expect(path.permit?(action)).to be_truthy
18
+ end
19
+ end
9
20
 
10
- it 'gradually builds up Paths' do
11
- steps.each do |step|
12
- expect(path).to receive(:<<).once.ordered.with(step)
21
+ (restful_collection_actions - only).each do |action|
22
+ it "does not permit #{ action } actions" do
23
+ path = root.trace(steps)
24
+ expect(path.permit?(action)).to be_falsy
13
25
  end
14
- root.trace(steps, path)
15
26
  end
16
27
 
17
- it 'gradually advances Paths' do
18
- steps.select { |step| step.is_a?(Symbol) }.each do |name|
19
- expect(path).to receive(:advance).once.ordered.
20
- with { |n| n.match(name) }
28
+ restful_member_actions.each do |action|
29
+ it "does not permit #{ action } actions" do
30
+ path = root.trace(steps)
31
+ expect(path.permit?(action)).to be_falsy
21
32
  end
22
- root.trace(steps, path)
23
33
  end
24
34
  end
25
35
 
26
- describe '::Base#trace' do
27
- context 'when working with plain collections' do
28
- let(:root) { create_tree(:articles) }
36
+ shared_examples 'an adequate member guard' do |only: restful_actions|
37
+ (restful_member_actions & only).each do |action|
38
+ it "permites #{ action } actions" do
39
+ path = root.trace(steps)
40
+ expect(path.permit?(action)).to be_truthy
41
+ end
42
+ end
29
43
 
30
- context 'when looking for collections' do
31
- let(:steps) { [ :articles ] }
32
- it_behaves_like 'an adequate pathfinder'
44
+ (restful_member_actions - only).each do |action|
45
+ it "does not permit #{ action } actions" do
46
+ path = root.trace(steps)
47
+ expect(path.permit?(action)).to be_falsy
33
48
  end
49
+ end
34
50
 
35
- context 'when looking for members' do
36
- let(:steps) { [ :articles, 'xxx' ] }
37
- it_behaves_like 'an adequate pathfinder'
51
+ restful_collection_actions.each do |action|
52
+ it "does not permit #{ action } actions" do
53
+ path = root.trace(steps)
54
+ expect(path.permit?(action)).to be_falsy
38
55
  end
39
56
  end
57
+ end
58
+
59
+ context 'when defining plain collections' do
60
+ let(:root) { create_tree(:articles) }
61
+
62
+ context 'when looking for collections' do
63
+ let(:steps) { [ :articles ] }
64
+ it_behaves_like 'an adequate pathfinder'
65
+ it_behaves_like 'an adequate collection guard'
66
+ end
40
67
 
41
- context 'when working with nested collections' do
42
- let(:root) { create_tree(:articles, :sections) }
68
+ context 'when looking for members' do
69
+ let(:steps) { [ :articles, 'xxx' ] }
70
+ it_behaves_like 'an adequate pathfinder'
71
+ it_behaves_like 'an adequate member guard'
72
+ end
73
+ end
43
74
 
44
- context 'when looking for collections' do
45
- let(:steps) { [ :articles, 'xxx', :sections ] }
46
- it_behaves_like 'an adequate pathfinder'
47
- end
75
+ context 'when defining nested collections' do
76
+ let(:root) { create_tree(:articles, :sections) }
77
+ let(:steps) { [ :articles, 'yyy', :sections ] }
48
78
 
49
- context 'when looking for members' do
50
- let(:steps) { [ :articles, 'xxx', :sections, 'yyy' ] }
51
- it_behaves_like 'an adequate pathfinder'
52
- end
79
+ context 'when looking for collections' do
80
+ let(:steps) { [ :articles, 'xxx', :sections ] }
81
+ it_behaves_like 'an adequate pathfinder'
82
+ it_behaves_like 'an adequate collection guard'
53
83
  end
54
84
 
55
- restful_actions.each do |action|
56
- context "when working with custom #{ action } actions" do
57
- let(:root) { create_tree(:articles, shred: action) }
58
- let(:steps) { [ :articles, 'xxx', :shred ] }
59
- it_behaves_like 'an adequate pathfinder'
60
- end
85
+ context 'when looking for members' do
86
+ let(:steps) { [ :articles, 'xxx', :sections, 'yyy' ] }
87
+ it_behaves_like 'an adequate pathfinder'
88
+ it_behaves_like 'an adequate member guard'
89
+ end
90
+ end
91
+
92
+ only = [ :index, :show ]
93
+ context "when defining collections restricted to #{ only.join(', ') }" do
94
+ let(:root) { create_tree([ :articles, only ]) }
95
+
96
+ context 'when looking for collections' do
97
+ let(:steps) { [ :articles ] }
98
+ it_behaves_like 'an adequate pathfinder'
99
+ it_behaves_like 'an adequate collection guard', only: only
100
+ end
101
+
102
+ context 'when looking for members' do
103
+ let(:steps) { [ :articles, 'xxx' ] }
104
+ it_behaves_like 'an adequate pathfinder'
105
+ it_behaves_like 'an adequate member guard', only: only
106
+ end
107
+ end
108
+
109
+ restful_actions.each do |action|
110
+ context "when defining #{ action } operations" do
111
+ let(:root) { create_tree(:articles, shred: action) }
112
+ let(:steps) { [ :articles, 'xxx', :shred ] }
113
+
114
+ it_behaves_like 'an adequate pathfinder'
115
+ end
116
+ end
117
+
118
+ restful_collection_actions.each do |action|
119
+ context "when defining #{ action } operations with varible names" do
120
+ let(:root) { create_tree(:articles, ':paragraph' => action) }
121
+ let(:steps) { [ :articles, 'zzz' ] }
122
+
123
+ it_behaves_like 'an adequate pathfinder'
124
+ end
125
+ end
126
+
127
+ restful_member_actions.each do |action|
128
+ context "when defining #{ action } operations with varible names" do
129
+ let(:root) { create_tree(:articles, ':paragraph' => action) }
130
+ let(:steps) { [ :articles, 'xxx', 'zzz' ] }
131
+
132
+ it_behaves_like 'an adequate pathfinder'
61
133
  end
62
134
  end
63
135
  end