haveapi 0.4.2 → 0.5.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 +11 -0
- data/Rakefile +1 -0
- data/haveapi.gemspec +1 -1
- data/lib/haveapi/authentication/chain.rb +4 -0
- data/lib/haveapi/hooks.rb +68 -11
- data/lib/haveapi/model_adapters/active_record.rb +1 -1
- data/lib/haveapi/{params/param.rb → parameters/typed.rb} +4 -1
- data/lib/haveapi/params.rb +2 -2
- data/lib/haveapi/spec/api_builder.rb +75 -0
- data/lib/haveapi/spec/api_response.rb +41 -0
- data/lib/haveapi/spec/helpers.rb +5 -99
- data/lib/haveapi/spec/mock_action.rb +32 -0
- data/lib/haveapi/spec/spec_methods.rb +121 -0
- data/lib/haveapi/version.rb +1 -1
- data/lib/haveapi/views/main_layout.erb +3 -1
- data/lib/haveapi.rb +1 -1
- data/spec/action/dsl_spec.rb +199 -0
- data/spec/authorization_spec.rb +113 -0
- data/spec/common_spec.rb +47 -0
- data/spec/documentation_spec.rb +29 -0
- data/spec/envelope_spec.rb +33 -0
- data/spec/hooks_spec.rb +146 -0
- data/spec/parameters/typed_spec.rb +93 -0
- data/spec/params_spec.rb +190 -0
- data/spec/resource_spec.rb +63 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/validators/acceptance_spec.rb +29 -0
- data/spec/validators/confirmation_spec.rb +46 -0
- data/spec/validators/custom_spec.rb +6 -0
- data/spec/validators/exclusion_spec.rb +32 -0
- data/spec/validators/format_spec.rb +54 -0
- data/spec/validators/inclusion_spec.rb +43 -0
- data/spec/validators/length_spec.rb +45 -0
- data/spec/validators/numericality_spec.rb +70 -0
- data/spec/validators/presence_spec.rb +47 -0
- metadata +27 -4
- /data/lib/haveapi/{params → parameters}/resource.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00a5f09608d0287b5187d60b5e5f8528b8077c33
|
4
|
+
data.tar.gz: 728e896638ef103f0d62db8aea3d0a26cae3e818
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3519a72a83f31a146da97ffb2422439231afa6a00b55eb55f3ceb1f77d8766f1afb76fe60454bc403fccd7fe2689d2c76fe2970b2557b594258c28f834f478e
|
7
|
+
data.tar.gz: 5296734002aca4221526d0bbd98ff2927bb61b6b74781f46d2d79c5bf01d29d5d0a16bfe59aa0b313b9819fe823150324f247387a7ed310d6fe747e9f8cb4a02
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
* Tue Feb 9 2016 - version 0.5.0
|
2
|
+
- Helper methods for testing APIs
|
3
|
+
- Added tests covering the basic functionality
|
4
|
+
- Fixed overflow in table of contents in online API doc
|
5
|
+
- Authentication chain can be empty
|
6
|
+
- Fixed Params.optional
|
7
|
+
- Fixed coercion of Float input parameters
|
8
|
+
- Renamed Params::Param to Parameters::Typed and Params::Resource to
|
9
|
+
Parameters::Resource
|
10
|
+
- Instance-level hooks are stored in the object itself
|
11
|
+
|
1
12
|
* Sun Jan 24 2016 - version 0.4.2
|
2
13
|
- Inclusion validator: handle hash correctly, don't overwrite given values
|
3
14
|
|
data/Rakefile
CHANGED
data/haveapi.gemspec
CHANGED
@@ -5,7 +5,7 @@ require 'haveapi/version'
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = 'haveapi'
|
7
7
|
s.version = HaveAPI::VERSION
|
8
|
-
s.date = '2016-
|
8
|
+
s.date = '2016-02-09'
|
9
9
|
s.summary =
|
10
10
|
s.description = 'Framework for creating self-describing APIs'
|
11
11
|
s.authors = 'Jakub Skokan'
|
@@ -37,6 +37,8 @@ module HaveAPI::Authentication
|
|
37
37
|
# Authentication provider can deny the user access by calling Base#deny.
|
38
38
|
def authenticate(v, *args)
|
39
39
|
catch(:return) do
|
40
|
+
return unless @instances[v]
|
41
|
+
|
40
42
|
@instances[v].each do |provider|
|
41
43
|
u = provider.authenticate(*args)
|
42
44
|
return u if u
|
@@ -48,6 +50,8 @@ module HaveAPI::Authentication
|
|
48
50
|
|
49
51
|
def describe(context)
|
50
52
|
ret = {}
|
53
|
+
|
54
|
+
return ret unless @instances[context.version]
|
51
55
|
|
52
56
|
@instances[context.version].each do |provider|
|
53
57
|
ret[provider.name] = provider.describe
|
data/lib/haveapi/hooks.rb
CHANGED
@@ -7,14 +7,36 @@ module HaveAPI
|
|
7
7
|
# it is possible to connect to a specific instance and not for
|
8
8
|
# all instances of a class.
|
9
9
|
#
|
10
|
+
# Hook definition contains additional information for as a documentation:
|
11
|
+
# description, context, arguments, return value.
|
12
|
+
#
|
13
|
+
# Every hook can have multiple listeners. They are invoked in the order of
|
14
|
+
# registration. Instance-level listeners first, then class-level. Hooks are
|
15
|
+
# chained using the block's first argument and return value. The first block
|
16
|
+
# to be executed gets the initial value, may make changes and returns it.
|
17
|
+
# The next block gets the return value of the previous block as its first
|
18
|
+
# argument, may make changes and returns it. Return value of the last block
|
19
|
+
# is returned to the caller of the hook.
|
20
|
+
#
|
10
21
|
# === \Usage
|
11
22
|
# ==== \Register hooks
|
12
23
|
# class MyClass
|
13
24
|
# include Hookable
|
14
25
|
#
|
15
|
-
# has_hook :myhook
|
26
|
+
# has_hook :myhook,
|
27
|
+
# desc: 'Called when I want to',
|
28
|
+
# context: 'current',
|
29
|
+
# args: {
|
30
|
+
# a: 'integer',
|
31
|
+
# b: 'integer',
|
32
|
+
# c: 'integer',
|
33
|
+
# }
|
16
34
|
# end
|
17
35
|
#
|
36
|
+
# Not that the additional information is just optional. A list of defined
|
37
|
+
# hooks and their description is a part of the reference documentation
|
38
|
+
# generated by yard.
|
39
|
+
#
|
18
40
|
# ==== \Class level hooks
|
19
41
|
# # Connect hook
|
20
42
|
# MyClass.connect_hook(:myhook) do |ret, a, b, c|
|
@@ -43,7 +65,20 @@ module HaveAPI
|
|
43
65
|
# my.call_class_hooks_for(:myhook, args: [1, 2, 3])
|
44
66
|
# # Call both instance and class hooks at once
|
45
67
|
# my.call_hooks_for(:myhook, args: [1, 2, 3])
|
68
|
+
#
|
69
|
+
# ==== \Chaining
|
70
|
+
# 5.times do |i|
|
71
|
+
# MyClass.connect_hook(:myhook) do |ret, a, b, c|
|
72
|
+
# ret[:counter] += i
|
73
|
+
# ret
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# p MyClass.call_hooks(:myhook, args: [1, 2, 3], initial: {counter: 0})
|
78
|
+
# => {:counter=>5}
|
46
79
|
module Hooks
|
80
|
+
INSTANCE_VARIABLE = '@_haveapi_hooks'
|
81
|
+
|
47
82
|
# Register a hook defined by +klass+ with +name+.
|
48
83
|
# +klass+ is an instance of Class, that is class name, not it's instance.
|
49
84
|
# +opts+ is a hash and can have following keys:
|
@@ -72,16 +107,16 @@ module HaveAPI
|
|
72
107
|
end
|
73
108
|
|
74
109
|
# Connect instance hook from instance +klass+ with +name+ to +block+.
|
75
|
-
def self.connect_instance_hook(
|
76
|
-
|
77
|
-
@hooks[klass] = {}
|
110
|
+
def self.connect_instance_hook(instance, name, &block)
|
111
|
+
hooks = instance.instance_variable_get(INSTANCE_VARIABLE)
|
78
112
|
|
79
|
-
|
80
|
-
|
81
|
-
|
113
|
+
unless hooks
|
114
|
+
hooks = {}
|
115
|
+
instance.instance_variable_set(INSTANCE_VARIABLE, hooks)
|
82
116
|
end
|
83
117
|
|
84
|
-
|
118
|
+
hooks[name] ||= {listeners: []}
|
119
|
+
hooks[name][:listeners] << block
|
85
120
|
end
|
86
121
|
|
87
122
|
# Call all blocks that are connected to hook in +klass+ with +name+.
|
@@ -98,17 +133,39 @@ module HaveAPI
|
|
98
133
|
# A block may decide that no further blocks should be executed.
|
99
134
|
# In such a case it calls Hooks.stop with the return value. It is then
|
100
135
|
# returned to the caller immediately.
|
101
|
-
|
136
|
+
#
|
137
|
+
# @param klass [Class instance, instance]
|
138
|
+
# @param name [Symbol] hook name
|
139
|
+
# @param where [Class instance] class in whose context hooks are executed
|
140
|
+
# @param args [Array] an array of arguments passed to hooks
|
141
|
+
# @param initial [Hash] initial return value
|
142
|
+
# @param instance [Boolean] call instance hooks or not; nil means auto-detect
|
143
|
+
def self.call_for(
|
144
|
+
klass,
|
145
|
+
name,
|
146
|
+
where = nil,
|
147
|
+
args: [],
|
148
|
+
initial: {},
|
149
|
+
instance: nil
|
150
|
+
)
|
102
151
|
classified = hook_classify(klass)
|
103
152
|
|
153
|
+
if (instance.nil? && !classified.is_a?(Class)) || instance
|
154
|
+
all_hooks = klass.instance_variable_get(INSTANCE_VARIABLE)
|
155
|
+
|
156
|
+
else
|
157
|
+
all_hooks = @hooks[classified]
|
158
|
+
end
|
159
|
+
|
104
160
|
catch(:stop) do
|
105
|
-
return initial unless
|
106
|
-
hooks =
|
161
|
+
return initial unless all_hooks
|
162
|
+
hooks = all_hooks[name][:listeners]
|
107
163
|
return initial unless hooks
|
108
164
|
|
109
165
|
hooks.each do |hook|
|
110
166
|
if where
|
111
167
|
ret = where.instance_exec(initial, *args, &hook)
|
168
|
+
|
112
169
|
else
|
113
170
|
ret = hook.call(initial, *args)
|
114
171
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module HaveAPI::Parameters
|
2
|
-
class
|
2
|
+
class Typed
|
3
3
|
ATTRIBUTES = %i(label desc type db_name default fill clean)
|
4
4
|
|
5
5
|
attr_reader :name, :label, :desc, :type, :default
|
@@ -76,6 +76,9 @@ module HaveAPI::Parameters
|
|
76
76
|
|
77
77
|
elsif @type == Integer
|
78
78
|
raw.to_i
|
79
|
+
|
80
|
+
elsif @type == Float
|
81
|
+
raw.to_f
|
79
82
|
|
80
83
|
elsif @type == Boolean
|
81
84
|
Boolean.to_b(raw)
|
data/lib/haveapi/params.rb
CHANGED
@@ -83,7 +83,7 @@ module HaveAPI
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def optional(*args)
|
86
|
-
add_param(*apply(args, required:
|
86
|
+
add_param(*apply(args, required: false))
|
87
87
|
end
|
88
88
|
|
89
89
|
def string(*args)
|
@@ -268,7 +268,7 @@ module HaveAPI
|
|
268
268
|
|
269
269
|
private
|
270
270
|
def add_param(*args)
|
271
|
-
p = Parameters::
|
271
|
+
p = Parameters::Typed.new(*args)
|
272
272
|
|
273
273
|
return if @include && !@include.include?(p.name)
|
274
274
|
return if @exclude && @exclude.include?(p.name)
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module HaveAPI::Spec
|
2
|
+
# Contains methods for specification of API to be used in `description` block.
|
3
|
+
module ApiBuilder
|
4
|
+
# Uses an empty module as the source for the API. It will have no resources,
|
5
|
+
# no actions. Version default to `1`.
|
6
|
+
def empty_api
|
7
|
+
api(Module.new)
|
8
|
+
default_version(1)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Set an API module or create the API using DSL.
|
12
|
+
# @param mod [Module] module name or nil
|
13
|
+
# @yield block is executed in a dynamically created module
|
14
|
+
def api(mod = nil, &block)
|
15
|
+
unless mod
|
16
|
+
mod = Module.new do
|
17
|
+
def self.define_resource(name, superclass: Resource, &block)
|
18
|
+
return false if const_defined?(name)
|
19
|
+
|
20
|
+
cls = Class.new(superclass)
|
21
|
+
const_set(name, cls)
|
22
|
+
cls.class_exec(&block) if block
|
23
|
+
cls
|
24
|
+
end
|
25
|
+
|
26
|
+
module_eval(&block)
|
27
|
+
end
|
28
|
+
|
29
|
+
const_set(:ApiModule, mod)
|
30
|
+
end
|
31
|
+
|
32
|
+
opt(:api_module, mod)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Set authentication chain.
|
36
|
+
def auth_chain(chain)
|
37
|
+
opt(:auth_chain, chain)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Select API versions to be used.
|
41
|
+
def use_version(v)
|
42
|
+
before(:each) do
|
43
|
+
opts(:versions, v)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Set default API version.
|
48
|
+
def default_version(v)
|
49
|
+
opt(:default_version, v)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Set a custom mount path.
|
53
|
+
def mount_to(path)
|
54
|
+
opts(:mount, path)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Login using HTTP basic.
|
58
|
+
def login(*credentials)
|
59
|
+
before(:each) do
|
60
|
+
basic_authorize(*credentials)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @private
|
65
|
+
def opts
|
66
|
+
@opts
|
67
|
+
end
|
68
|
+
|
69
|
+
# @private
|
70
|
+
def opt(name, v)
|
71
|
+
@opts ||= {}
|
72
|
+
@opts[name] = v
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module HaveAPI::Spec
|
2
|
+
# This class wraps raw reply from the API and provides a more friendly
|
3
|
+
# interface.
|
4
|
+
class ApiResponse
|
5
|
+
def initialize(body)
|
6
|
+
@data = JSON.parse(body, symbolize_names: true)
|
7
|
+
end
|
8
|
+
|
9
|
+
def envelope
|
10
|
+
@data
|
11
|
+
end
|
12
|
+
|
13
|
+
def status
|
14
|
+
@data[:status]
|
15
|
+
end
|
16
|
+
|
17
|
+
def ok?
|
18
|
+
@data[:status]
|
19
|
+
end
|
20
|
+
|
21
|
+
def failed?
|
22
|
+
!ok?
|
23
|
+
end
|
24
|
+
|
25
|
+
def response
|
26
|
+
@data[:response]
|
27
|
+
end
|
28
|
+
|
29
|
+
def message
|
30
|
+
@data[:message]
|
31
|
+
end
|
32
|
+
|
33
|
+
def errors
|
34
|
+
@data[:errors]
|
35
|
+
end
|
36
|
+
|
37
|
+
def [](k)
|
38
|
+
@data[:response][k]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/haveapi/spec/helpers.rb
CHANGED
@@ -1,103 +1,9 @@
|
|
1
1
|
require 'rack/test'
|
2
2
|
|
3
3
|
module HaveAPI
|
4
|
-
|
5
|
-
module ApiBuilder
|
6
|
-
def auth_chain(chain)
|
7
|
-
@auth_chain = chain
|
8
|
-
end
|
9
|
-
|
10
|
-
def use_version(v)
|
11
|
-
before(:each) do
|
12
|
-
@versions = v
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def default_version(v)
|
17
|
-
@default_version = v
|
18
|
-
end
|
19
|
-
|
20
|
-
def mount_to(path)
|
21
|
-
@mount = path
|
22
|
-
end
|
23
|
-
|
24
|
-
def login(*credentials)
|
25
|
-
@username, @password = credentials
|
26
|
-
|
27
|
-
before(:each) do
|
28
|
-
basic_authorize(*credentials)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# Helper methods for specs.
|
34
|
-
module SpecMethods
|
35
|
-
include Rack::Test::Methods
|
36
|
-
|
37
|
-
# This class wraps raw reply from the API and provides more friendly
|
38
|
-
# interface.
|
39
|
-
class ApiResponse
|
40
|
-
def initialize(body)
|
41
|
-
@data = JSON.parse(body, symbolize_names: true)
|
42
|
-
end
|
43
|
-
|
44
|
-
def status
|
45
|
-
@data[:status]
|
46
|
-
end
|
47
|
-
|
48
|
-
def ok?
|
49
|
-
@data[:status]
|
50
|
-
end
|
51
|
-
|
52
|
-
def failed?
|
53
|
-
!ok?
|
54
|
-
end
|
55
|
-
|
56
|
-
def response
|
57
|
-
@data[:response]
|
58
|
-
end
|
59
|
-
|
60
|
-
def message
|
61
|
-
@data[:message]
|
62
|
-
end
|
63
|
-
|
64
|
-
def errors
|
65
|
-
@data[:errors]
|
66
|
-
end
|
67
|
-
|
68
|
-
def [](k)
|
69
|
-
@data[:response][k]
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def app
|
74
|
-
api = HaveAPI::Server.new
|
75
|
-
api.auth_chain << @auth_chain if @auth_chain
|
76
|
-
api.use_version(@versions || :all)
|
77
|
-
api.set_default_version(@default_version) if @default_version
|
78
|
-
api.mount(@mount || '/')
|
79
|
-
api.app
|
80
|
-
end
|
81
|
-
|
82
|
-
# Login with HTTP basic auth.
|
83
|
-
def login(*credentials)
|
84
|
-
basic_authorize(*credentials)
|
85
|
-
end
|
86
|
-
|
87
|
-
# Make API request.
|
88
|
-
# This method is a wrapper for Rack::Test::Methods. Input parameters
|
89
|
-
# are encoded into JSON and sent with correct Content-Type.
|
90
|
-
def api(http_method, url, params={})
|
91
|
-
method(http_method).call(
|
92
|
-
url,
|
93
|
-
params.to_json,
|
94
|
-
{'Content-Type' => 'application/json'}
|
95
|
-
)
|
96
|
-
end
|
97
|
-
|
98
|
-
# Return parsed API response.
|
99
|
-
def api_response
|
100
|
-
@api_response ||= ApiResponse.new(last_response.body)
|
101
|
-
end
|
102
|
-
end
|
4
|
+
module Spec ; end
|
103
5
|
end
|
6
|
+
|
7
|
+
require_relative 'api_builder'
|
8
|
+
require_relative 'api_response'
|
9
|
+
require_relative 'spec_methods'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module HaveAPI::Spec
|
2
|
+
class MockAction
|
3
|
+
def initialize(test, server, action, url, v)
|
4
|
+
@test = test
|
5
|
+
@server = server
|
6
|
+
@action = action
|
7
|
+
@url = url
|
8
|
+
@v = v
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(input, user: nil, &block)
|
12
|
+
action = @action.new(nil, @v, input, nil, HaveAPI::Context.new(
|
13
|
+
@server,
|
14
|
+
version: @v,
|
15
|
+
action: @action,
|
16
|
+
url: @url,
|
17
|
+
params: input,
|
18
|
+
user: user,
|
19
|
+
endpoint: true
|
20
|
+
))
|
21
|
+
|
22
|
+
unless action.authorized?(user)
|
23
|
+
fail 'Access denied. Insufficient permissions.'
|
24
|
+
end
|
25
|
+
|
26
|
+
status, data, errors = action.safe_exec
|
27
|
+
fail (data || 'action failed') unless status
|
28
|
+
action.instance_exec(@test, &block)
|
29
|
+
data
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module HaveAPI::Spec
|
2
|
+
# Helper methods for specs.
|
3
|
+
module SpecMethods
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def app
|
7
|
+
return @api.app if @api
|
8
|
+
|
9
|
+
auth = get_opt(:auth_chain)
|
10
|
+
default = get_opt(:default_version)
|
11
|
+
|
12
|
+
@api = HaveAPI::Server.new(get_opt(:api_module))
|
13
|
+
@api.auth_chain << auth if auth
|
14
|
+
@api.use_version(get_opt(:versions) || :all)
|
15
|
+
@api.default_version = default if default
|
16
|
+
@api.mount(get_opt(:mount) || '/')
|
17
|
+
@api.app
|
18
|
+
end
|
19
|
+
|
20
|
+
# Login with HTTP basic auth.
|
21
|
+
def login(*credentials)
|
22
|
+
basic_authorize(*credentials)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Make API request.
|
26
|
+
# This method is a wrapper for Rack::Test::Methods. Input parameters
|
27
|
+
# are encoded into JSON and sent with a correct Content-Type.
|
28
|
+
# Two modes:
|
29
|
+
# http_method, url, params = {}
|
30
|
+
# [resource], action, params, &block
|
31
|
+
def call_api(*args, &block)
|
32
|
+
if args[0].is_a?(::Array) || args[1].is_a?(::Symbol)
|
33
|
+
r_name, a_name, params = args
|
34
|
+
|
35
|
+
app
|
36
|
+
|
37
|
+
action, url = find_action(
|
38
|
+
(params && params[:version]) || @api.default_version,
|
39
|
+
r_name, a_name
|
40
|
+
)
|
41
|
+
|
42
|
+
method(action.http_method).call(
|
43
|
+
url,
|
44
|
+
params && params.to_json,
|
45
|
+
{'Content-Type' => 'application/json'}
|
46
|
+
)
|
47
|
+
|
48
|
+
else
|
49
|
+
http_method, url, params = args
|
50
|
+
|
51
|
+
method(http_method).call(
|
52
|
+
url,
|
53
|
+
params && params.to_json,
|
54
|
+
{'Content-Type' => 'application/json'}
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Mock action call. Note that this method does not involve rack request/response
|
60
|
+
# in any way. It simply creates an instance of specified action and executes it.
|
61
|
+
# Provided block is executed in the context of the action instance after `exec()`
|
62
|
+
# has been called.
|
63
|
+
#
|
64
|
+
# If `exec()` signals error, the block is not called at all, but `RuntimeError`
|
65
|
+
# is raised instead.
|
66
|
+
#
|
67
|
+
# Authentication does not take place. Argument `user` may be used to provide
|
68
|
+
# user object. That will signify that the user is authenticated and it will be passed
|
69
|
+
# to Action.authorize.
|
70
|
+
#
|
71
|
+
# @param r_name [Array, Symbol] path to resource in the API
|
72
|
+
# @param a_name [Symbol] name of wanted action
|
73
|
+
# @param params [Hash] a hash of parameters, must contain correct namespace
|
74
|
+
# @param version [any] API version, if not specified, the default version is used
|
75
|
+
# @param user [any] object representing authenticated user
|
76
|
+
# @yield [self] the block is executed in the action instance
|
77
|
+
def mock_action(r_name, a_name, params, version: nil, user: nil, &block)
|
78
|
+
app
|
79
|
+
v = version || @api.default_version
|
80
|
+
action, url = find_action(v, r_name, a_name)
|
81
|
+
m = MockAction.new(self, @api, action, url, v)
|
82
|
+
m.call(params, user: user, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Return parsed API response.
|
86
|
+
# @return [HaveAPI::Spec::ApiResponse]
|
87
|
+
def api_response
|
88
|
+
if last_response != @last_response
|
89
|
+
@last_response = last_response
|
90
|
+
@api_response = ApiResponse.new(last_response.body)
|
91
|
+
|
92
|
+
else
|
93
|
+
@api_response ||= ApiResponse.new(last_response.body)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
protected
|
98
|
+
def get_opt(name)
|
99
|
+
self.class.opts && self.class.opts[name]
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_action(v, r_name, a_name)
|
103
|
+
# Make sure the API is built
|
104
|
+
app
|
105
|
+
|
106
|
+
resources = r_name.is_a?(::Array) ? r_name : [r_name]
|
107
|
+
|
108
|
+
top = @api.routes[v]
|
109
|
+
|
110
|
+
resources.each do |r|
|
111
|
+
top = top[:resources].detect do |k, _|
|
112
|
+
k.resource_name.to_sym == r
|
113
|
+
end.second
|
114
|
+
end
|
115
|
+
|
116
|
+
top[:actions].detect do |k, v|
|
117
|
+
k.to_s.demodulize.underscore.to_sym == a_name
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/lib/haveapi/version.rb
CHANGED
data/lib/haveapi.rb
CHANGED
@@ -13,7 +13,7 @@ module HaveAPI
|
|
13
13
|
end
|
14
14
|
|
15
15
|
require_relative 'haveapi/params'
|
16
|
-
require_rel 'haveapi/
|
16
|
+
require_rel 'haveapi/parameters/'
|
17
17
|
require_rel 'haveapi/*.rb'
|
18
18
|
require_rel 'haveapi/model_adapters/hash'
|
19
19
|
require_rel 'haveapi/model_adapters/active_record' if ar
|