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.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.travis.yml +6 -0
- data/.yardopts +6 -0
- data/CHANGELOG.md +10 -0
- data/Guardfile +11 -4
- data/README.md +74 -7
- data/apitizer.gemspec +1 -2
- data/lib/apitizer.rb +0 -1
- data/lib/apitizer/base.rb +16 -27
- data/lib/apitizer/connection.rb +3 -1
- data/lib/apitizer/connection/adaptor.rb +1 -1
- data/lib/apitizer/connection/adaptor/standard.rb +24 -7
- data/lib/apitizer/connection/dispatcher.rb +5 -12
- data/lib/apitizer/connection/format.rb +14 -0
- data/lib/apitizer/{processing/parser → connection/format}/json.rb +6 -2
- data/lib/apitizer/{processing/parser → connection/format}/yaml.rb +6 -2
- data/lib/apitizer/connection/request.rb +3 -3
- data/lib/apitizer/connection/response.rb +3 -3
- data/lib/apitizer/core.rb +4 -4
- data/lib/apitizer/helper.rb +38 -14
- data/lib/apitizer/result.rb +2 -2
- data/lib/apitizer/routing.rb +1 -1
- data/lib/apitizer/routing/{mapper.rb → map.rb} +3 -10
- data/lib/apitizer/routing/node.rb +0 -1
- data/lib/apitizer/routing/node/base.rb +15 -17
- data/lib/apitizer/routing/node/collection.rb +17 -16
- data/lib/apitizer/routing/node/operation.rb +14 -15
- data/lib/apitizer/routing/node/root.rb +8 -2
- data/lib/apitizer/routing/path.rb +16 -8
- data/lib/apitizer/version.rb +1 -1
- data/spec/apitizer/base_spec.rb +36 -28
- data/spec/apitizer/connection/adaptor_spec.rb +87 -11
- data/spec/apitizer/connection/dispatcher_spec.rb +21 -23
- data/spec/apitizer/connection/format_spec.rb +15 -0
- data/spec/apitizer/helper_spec.rb +53 -24
- data/spec/apitizer/result_spec.rb +5 -7
- data/spec/apitizer/routing/map_spec.rb +71 -0
- data/spec/apitizer/routing/node_spec.rb +108 -36
- data/spec/apitizer/routing/path_spec.rb +12 -92
- data/spec/spec_helper.rb +4 -6
- data/spec/support/factory_helper.rb +25 -5
- data/spec/support/resource_helper.rb +8 -0
- metadata +14 -15
- data/lib/apitizer/processing.rb +0 -8
- data/lib/apitizer/processing/parser.rb +0 -14
- data/lib/apitizer/processing/translator.rb +0 -13
- data/lib/apitizer/routing/node/scope.rb +0 -19
- data/spec/apitizer/processing/parser_spec.rb +0 -23
- data/spec/apitizer/routing/mapper_spec.rb +0 -80
@@ -3,11 +3,11 @@ module Apitizer
|
|
3
3
|
class Request
|
4
4
|
extend Forwardable
|
5
5
|
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :method, :path, :parameters
|
7
7
|
def_delegator :path, :address
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
@
|
9
|
+
def initialize(method:, path:, parameters: {})
|
10
|
+
@method = method
|
11
11
|
@path = path
|
12
12
|
@parameters = parameters
|
13
13
|
end
|
data/lib/apitizer/core.rb
CHANGED
@@ -12,11 +12,11 @@ module Apitizer
|
|
12
12
|
:delete => :delete
|
13
13
|
},
|
14
14
|
headers: {}
|
15
|
-
}
|
15
|
+
}
|
16
16
|
|
17
|
-
@actions = [ :index, :show, :create, :update, :delete ]
|
18
|
-
@collection_actions = [ :index, :create ]
|
19
|
-
@member_actions = [ :show, :update, :delete ]
|
17
|
+
@actions = [ :index, :show, :create, :update, :delete ]
|
18
|
+
@collection_actions = [ :index, :create ]
|
19
|
+
@member_actions = [ :show, :update, :delete ]
|
20
20
|
|
21
21
|
singleton_class.class_eval do
|
22
22
|
attr_reader :defaults, :actions, :collection_actions, :member_actions
|
data/lib/apitizer/helper.rb
CHANGED
@@ -14,6 +14,10 @@ module Apitizer
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
def self.action_scope(action)
|
18
|
+
member_action?(action) ? :member : :collection
|
19
|
+
end
|
20
|
+
|
17
21
|
def self.deep_merge(one, two)
|
18
22
|
merger = Proc.new do |key, v1, v2|
|
19
23
|
Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2
|
@@ -22,25 +26,45 @@ module Apitizer
|
|
22
26
|
end
|
23
27
|
|
24
28
|
def self.build_query(parameters)
|
25
|
-
Rack::Utils.build_nested_query(prepare_parameters(parameters))
|
29
|
+
query = Rack::Utils.build_nested_query(prepare_parameters(parameters))
|
30
|
+
query.encode!('UTF-8')
|
26
31
|
end
|
27
32
|
|
28
33
|
private
|
29
34
|
|
30
|
-
def self.prepare_parameters(
|
31
|
-
# PATCH: https://github.com/rack/rack/issues/557
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
35
|
+
def self.prepare_parameters(value)
|
36
|
+
# PATCH 1: https://github.com/rack/rack/issues/557
|
37
|
+
# PATCH 2: https://github.com/rack/rack/pull/698
|
38
|
+
case value
|
39
|
+
when NilClass, String
|
40
|
+
value
|
41
|
+
when Symbol, Integer, TrueClass, FalseClass
|
42
|
+
value.to_s
|
43
|
+
when Array
|
44
|
+
value = value.map { |v| prepare_parameters(v) }.compact
|
45
|
+
if value.any? { |v| v.is_a?(Hash) }
|
46
|
+
value = Hash[(0...value.length).to_a.zip(value)]
|
47
|
+
end
|
48
|
+
value.empty? ? nil : value
|
49
|
+
when Hash
|
50
|
+
value = Hash[
|
51
|
+
value.map do |k, v|
|
52
|
+
v = prepare_parameters(v)
|
53
|
+
v.nil? ? nil : [ k, v ]
|
54
|
+
end.compact
|
55
|
+
]
|
56
|
+
value.empty? ? nil : value
|
57
|
+
else
|
58
|
+
if value.respond_to?(:to_a)
|
59
|
+
prepare_parameters(value.to_a)
|
60
|
+
elsif value.respond_to?(:to_h)
|
61
|
+
prepare_parameters(value.to_h)
|
62
|
+
else
|
63
|
+
raise ArgumentError, 'Unknown parameter class'
|
42
64
|
end
|
43
|
-
|
65
|
+
end
|
44
66
|
end
|
67
|
+
|
68
|
+
private_class_method :prepare_parameters
|
45
69
|
end
|
46
70
|
end
|
data/lib/apitizer/result.rb
CHANGED
@@ -6,8 +6,8 @@ module Apitizer
|
|
6
6
|
def_delegator :@response, :code
|
7
7
|
def_delegators :__getobj__, :is_a?, :kind_of?, :instance_of?
|
8
8
|
|
9
|
-
def initialize(request:, response
|
10
|
-
super(content)
|
9
|
+
def initialize(request:, response:)
|
10
|
+
super(response.content)
|
11
11
|
@request = request
|
12
12
|
@response = response
|
13
13
|
end
|
data/lib/apitizer/routing.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Apitizer
|
2
2
|
module Routing
|
3
|
-
class
|
3
|
+
class Map
|
4
4
|
extend Forwardable
|
5
5
|
|
6
6
|
def_delegator :@root, :define_address
|
@@ -11,8 +11,8 @@ module Apitizer
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def trace(action, *arguments)
|
14
|
-
path = @root.trace(*arguments)
|
15
|
-
raise Error, 'Not permitted' unless path.
|
14
|
+
path = @root.trace(*arguments) or raise Error, 'Not found'
|
15
|
+
raise Error, 'Not permitted' unless path.permit?(action)
|
16
16
|
path
|
17
17
|
end
|
18
18
|
|
@@ -21,13 +21,6 @@ module Apitizer
|
|
21
21
|
proxy.instance_eval(&block)
|
22
22
|
end
|
23
23
|
|
24
|
-
def define_scope(path, parent: @root, &block)
|
25
|
-
child = Node::Scope.new(path)
|
26
|
-
parent.append(child)
|
27
|
-
proxy = Proxy.new(self, parent: child)
|
28
|
-
proxy.instance_eval(&block)
|
29
|
-
end
|
30
|
-
|
31
24
|
def define_resources(name, parent: @root, **options, &block)
|
32
25
|
child = Node::Collection.new(name, **options)
|
33
26
|
parent.append(child)
|
@@ -7,36 +7,34 @@ module Apitizer
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def trace(steps, path = Path.new)
|
10
|
-
|
11
|
-
advance(path)
|
10
|
+
return nil unless recognize?(steps)
|
12
11
|
|
12
|
+
steps, path = steps.clone, path.clone
|
13
|
+
|
14
|
+
walk(steps, path)
|
13
15
|
return path if steps.empty?
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
children.each do |child|
|
18
|
+
branch = child.trace(steps, path)
|
19
|
+
return branch if branch
|
20
|
+
end
|
18
21
|
|
19
|
-
|
22
|
+
nil
|
20
23
|
end
|
21
24
|
|
22
|
-
def
|
25
|
+
def recognize?(steps)
|
23
26
|
end
|
24
27
|
|
25
|
-
def
|
28
|
+
def permit?(action, on:)
|
26
29
|
end
|
27
30
|
|
28
|
-
|
29
|
-
|
30
|
-
def children
|
31
|
-
@children ||= []
|
32
|
-
end
|
31
|
+
private
|
33
32
|
|
34
|
-
def
|
35
|
-
children.find { |c| c.match(name) }
|
33
|
+
def walk(steps, path)
|
36
34
|
end
|
37
35
|
|
38
|
-
def
|
39
|
-
|
36
|
+
def children
|
37
|
+
@children ||= []
|
40
38
|
end
|
41
39
|
end
|
42
40
|
end
|
@@ -2,31 +2,32 @@ module Apitizer
|
|
2
2
|
module Routing
|
3
3
|
module Node
|
4
4
|
class Collection < Base
|
5
|
-
def initialize(name, only: nil)
|
5
|
+
def initialize(name, only: nil, except: [])
|
6
6
|
@name = name
|
7
|
-
@actions = only && Array(only) || Apitizer.actions
|
8
|
-
unless (@actions - Apitizer.actions).empty?
|
9
|
-
raise Error, 'Not supported'
|
10
|
-
end
|
7
|
+
@actions = (only && Array(only) || Apitizer.actions) - Array(except)
|
11
8
|
end
|
12
9
|
|
13
|
-
def
|
14
|
-
@name ==
|
10
|
+
def recognize?(steps)
|
11
|
+
@name == steps.first
|
15
12
|
end
|
16
13
|
|
17
|
-
def
|
18
|
-
|
19
|
-
return path if steps.empty?
|
20
|
-
path << steps.shift # id
|
14
|
+
def permit?(action, on:)
|
15
|
+
@actions.include?(action) && on == Helper.action_scope(action)
|
21
16
|
end
|
22
17
|
|
23
|
-
|
24
|
-
|
18
|
+
private
|
19
|
+
|
20
|
+
def walk(steps, path)
|
21
|
+
path.advance(steps.shift, node: self, on: :collection)
|
25
22
|
|
26
|
-
|
27
|
-
|
23
|
+
return if steps.empty?
|
24
|
+
|
25
|
+
children.each do |child|
|
26
|
+
next unless child.respond_to?(:on?) && child.on?(:collection)
|
27
|
+
return if child.recognize?(steps)
|
28
|
+
end
|
28
29
|
|
29
|
-
|
30
|
+
path.advance(steps.shift, node: self, on: :member)
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
@@ -2,29 +2,28 @@ module Apitizer
|
|
2
2
|
module Routing
|
3
3
|
module Node
|
4
4
|
class Operation < Base
|
5
|
-
def initialize(name, action:, on
|
6
|
-
# TODO: how about on == :collection?
|
7
|
-
unless Apitizer.actions.include?(action) && on == :member
|
8
|
-
raise Error, 'Not supported'
|
9
|
-
end
|
5
|
+
def initialize(name, action:, on:)
|
10
6
|
@name = name
|
11
7
|
@action = action
|
8
|
+
@on = on
|
12
9
|
end
|
13
10
|
|
14
|
-
def
|
15
|
-
|
16
|
-
true
|
17
|
-
else
|
18
|
-
@name == name
|
19
|
-
end
|
11
|
+
def recognize?(steps)
|
12
|
+
@name == steps.first || @name.to_s =~ /^:/
|
20
13
|
end
|
21
14
|
|
22
|
-
def
|
23
|
-
|
15
|
+
def permit?(action, on:)
|
16
|
+
@action == action && @on == on
|
24
17
|
end
|
25
18
|
|
26
|
-
def
|
27
|
-
@
|
19
|
+
def on?(on)
|
20
|
+
@on == on
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def walk(steps, path)
|
26
|
+
path.advance(steps.shift, node: self, on: @on)
|
28
27
|
end
|
29
28
|
end
|
30
29
|
end
|
@@ -2,12 +2,18 @@ module Apitizer
|
|
2
2
|
module Routing
|
3
3
|
module Node
|
4
4
|
class Root < Base
|
5
|
+
def recognize?(steps)
|
6
|
+
true
|
7
|
+
end
|
8
|
+
|
5
9
|
def define_address(address, *_)
|
6
10
|
@address = address
|
7
11
|
end
|
8
12
|
|
9
|
-
|
10
|
-
|
13
|
+
private
|
14
|
+
|
15
|
+
def walk(steps, path)
|
16
|
+
path.advance(@address, node: self) if @address
|
11
17
|
end
|
12
18
|
end
|
13
19
|
end
|
@@ -1,25 +1,33 @@
|
|
1
1
|
module Apitizer
|
2
2
|
module Routing
|
3
3
|
class Path
|
4
|
-
extend Forwardable
|
5
|
-
|
6
4
|
attr_reader :steps, :node
|
7
|
-
def_delegators :steps, :<<
|
8
5
|
|
9
|
-
def initialize
|
10
|
-
@steps =
|
6
|
+
def initialize(steps: [], node: nil)
|
7
|
+
@steps = steps
|
8
|
+
@node = node
|
11
9
|
end
|
12
10
|
|
13
11
|
def address
|
14
12
|
@steps.map(&:to_s).join('/')
|
15
13
|
end
|
16
14
|
|
17
|
-
def advance(node)
|
15
|
+
def advance(step, node:, on: nil)
|
16
|
+
@steps << step
|
18
17
|
@node = node
|
18
|
+
@on = on
|
19
|
+
end
|
20
|
+
|
21
|
+
def permit?(action)
|
22
|
+
@node && @node.permit?(action, on: @on)
|
23
|
+
end
|
24
|
+
|
25
|
+
def on?(on)
|
26
|
+
@on == on
|
19
27
|
end
|
20
28
|
|
21
|
-
def
|
22
|
-
@node
|
29
|
+
def clone
|
30
|
+
self.class.new(steps: @steps.clone, node: @node)
|
23
31
|
end
|
24
32
|
end
|
25
33
|
end
|
data/lib/apitizer/version.rb
CHANGED
data/spec/apitizer/base_spec.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Apitizer::Base do
|
3
|
+
RSpec.describe Apitizer::Base do
|
4
4
|
extend ResourceHelper
|
5
5
|
|
6
6
|
let(:subject_class) { Apitizer::Base }
|
7
7
|
let(:address) { 'https://service.com/api' }
|
8
8
|
|
9
|
-
def stub_request(method,
|
10
|
-
stub_http_request(method, "
|
9
|
+
def stub_request(method, path)
|
10
|
+
stub_http_request(method, "#{ address }/#{ path }").
|
11
11
|
to_return(code: '200', body: '{}')
|
12
12
|
end
|
13
13
|
|
@@ -17,16 +17,20 @@ describe Apitizer::Base do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'draws a routing map when a block is given' do
|
20
|
-
|
21
|
-
subject = subject_class.new
|
20
|
+
prefix = address
|
21
|
+
subject = subject_class.new do
|
22
|
+
address(prefix)
|
23
|
+
resources(:articles)
|
24
|
+
end
|
22
25
|
stub_request(:get, 'articles')
|
23
26
|
expect { subject.process(:index, :articles) }.not_to raise_error
|
24
27
|
end
|
25
28
|
|
26
29
|
it 'customizes the mapping between the REST actions and HTTP verbs' do
|
27
|
-
|
30
|
+
prefix = address
|
28
31
|
subject = subject_class.new(dictionary: { update: :delete }) do
|
29
|
-
|
32
|
+
address(prefix)
|
33
|
+
resources(:articles)
|
30
34
|
end
|
31
35
|
stub = stub_request(:delete, 'articles/xxx')
|
32
36
|
subject.process(:update, :articles, 'xxx')
|
@@ -34,37 +38,41 @@ describe Apitizer::Base do
|
|
34
38
|
end
|
35
39
|
end
|
36
40
|
|
37
|
-
describe '#
|
38
|
-
|
39
|
-
|
40
|
-
subject_class.new
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
method = rest_http_dictionary[action]
|
45
|
-
|
46
|
-
it "is capable of #{ action } actions" do
|
47
|
-
stub_request(method, 'articles')
|
48
|
-
expect { subject.process(action, :articles) }.not_to raise_error
|
41
|
+
describe '#define' do
|
42
|
+
it 'is another way of drawing a routing map' do
|
43
|
+
prefix = address
|
44
|
+
subject = subject_class.new
|
45
|
+
subject.define do
|
46
|
+
address(prefix)
|
47
|
+
resources(:articles)
|
49
48
|
end
|
49
|
+
stub_request(:get, 'articles')
|
50
|
+
expect { subject.process(:index, :articles) }.not_to raise_error
|
51
|
+
end
|
52
|
+
end
|
50
53
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
+
describe '#process' do
|
55
|
+
subject do
|
56
|
+
prefix = address
|
57
|
+
subject_class.new do
|
58
|
+
address(prefix)
|
59
|
+
resources(:articles)
|
54
60
|
end
|
55
61
|
end
|
56
62
|
|
57
|
-
|
63
|
+
restful_actions.each do |action|
|
58
64
|
method = rest_http_dictionary[action]
|
65
|
+
steps = [ :articles ]
|
66
|
+
steps << 'xxx' if restful_member_actions.include?(action)
|
59
67
|
|
60
68
|
it "is capable of #{ action } actions" do
|
61
|
-
stub_request(method, '
|
62
|
-
expect { subject.process(action,
|
69
|
+
stub_request(method, steps.join('/'))
|
70
|
+
expect { subject.process(action, *steps) }.not_to raise_error
|
63
71
|
end
|
64
72
|
|
65
|
-
it "is capable of #{ action } actions via alias" do
|
66
|
-
stub_request(method, '
|
67
|
-
expect { subject.send(action,
|
73
|
+
it "is capable of #{ action } actions via the corresponding alias" do
|
74
|
+
stub_request(method, steps.join('/'))
|
75
|
+
expect { subject.send(action, *steps) }.not_to raise_error
|
68
76
|
end
|
69
77
|
end
|
70
78
|
end
|