apitizer 0.0.1 → 0.0.2

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