haveapi 0.20.0 → 0.21.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Rakefile +6 -6
- data/haveapi.gemspec +13 -13
- data/lib/haveapi/action.rb +153 -167
- data/lib/haveapi/action_state.rb +2 -6
- data/lib/haveapi/actions/default.rb +8 -10
- data/lib/haveapi/api.rb +2 -1
- data/lib/haveapi/authentication/base.rb +5 -8
- data/lib/haveapi/authentication/basic/provider.rb +4 -5
- data/lib/haveapi/authentication/chain.rb +19 -17
- data/lib/haveapi/authentication/oauth2/config.rb +12 -32
- data/lib/haveapi/authentication/oauth2/provider.rb +20 -30
- data/lib/haveapi/authentication/oauth2/revoke_endpoint.rb +1 -2
- data/lib/haveapi/authentication/token/action_config.rb +5 -3
- data/lib/haveapi/authentication/token/config.rb +5 -5
- data/lib/haveapi/authentication/token/provider.rb +33 -37
- data/lib/haveapi/authorization.rb +10 -4
- data/lib/haveapi/client_example.rb +11 -14
- data/lib/haveapi/client_examples/curl.rb +37 -37
- data/lib/haveapi/client_examples/fs_client.rb +29 -31
- data/lib/haveapi/client_examples/http.rb +35 -36
- data/lib/haveapi/client_examples/js_client.rb +62 -63
- data/lib/haveapi/client_examples/php_client.rb +77 -76
- data/lib/haveapi/client_examples/ruby_cli.rb +30 -30
- data/lib/haveapi/client_examples/ruby_client.rb +26 -26
- data/lib/haveapi/common.rb +3 -4
- data/lib/haveapi/context.rb +11 -10
- data/lib/haveapi/example.rb +9 -4
- data/lib/haveapi/example_list.rb +2 -2
- data/lib/haveapi/exceptions.rb +1 -1
- data/lib/haveapi/extensions/action_exceptions.rb +2 -2
- data/lib/haveapi/extensions/base.rb +1 -3
- data/lib/haveapi/extensions/exception_mailer.rb +260 -257
- data/lib/haveapi/hooks.rb +40 -39
- data/lib/haveapi/metadata.rb +1 -1
- data/lib/haveapi/model_adapter.rb +16 -27
- data/lib/haveapi/model_adapters/active_record.rb +59 -69
- data/lib/haveapi/output_formatter.rb +7 -7
- data/lib/haveapi/output_formatters/base.rb +2 -4
- data/lib/haveapi/parameters/resource.rb +7 -7
- data/lib/haveapi/parameters/typed.rb +6 -9
- data/lib/haveapi/params.rb +38 -45
- data/lib/haveapi/resource.rb +8 -8
- data/lib/haveapi/resources/action_state.rb +11 -19
- data/lib/haveapi/server.rb +102 -107
- data/lib/haveapi/spec/api_response.rb +1 -1
- data/lib/haveapi/spec/helpers.rb +1 -1
- data/lib/haveapi/spec/mock_action.rb +11 -10
- data/lib/haveapi/spec/spec_methods.rb +9 -8
- data/lib/haveapi/tasks/yard.rb +2 -2
- data/lib/haveapi/types.rb +0 -3
- data/lib/haveapi/validator.rb +6 -3
- data/lib/haveapi/validator_chain.rb +9 -8
- data/lib/haveapi/validators/acceptance.rb +6 -6
- data/lib/haveapi/validators/confirmation.rb +2 -3
- data/lib/haveapi/validators/exclusion.rb +1 -1
- data/lib/haveapi/validators/format.rb +1 -1
- data/lib/haveapi/validators/inclusion.rb +1 -1
- data/lib/haveapi/validators/length.rb +12 -11
- data/lib/haveapi/validators/numericality.rb +14 -13
- data/lib/haveapi/validators/presence.rb +4 -3
- data/lib/haveapi/version.rb +2 -2
- data/lib/haveapi.rb +2 -3
- data/spec/.rubocop.yml +4 -0
- data/spec/action/dsl_spec.rb +18 -18
- data/spec/authorization_spec.rb +8 -8
- data/spec/common_spec.rb +2 -1
- data/spec/documentation_spec.rb +2 -9
- data/spec/envelope_spec.rb +2 -2
- data/spec/hooks_spec.rb +12 -12
- data/spec/parameters/typed_spec.rb +6 -6
- data/spec/params_spec.rb +22 -24
- data/spec/resource_spec.rb +5 -7
- data/spec/spec_helper.rb +0 -1
- data/spec/validators/acceptance_spec.rb +1 -1
- data/spec/validators/confirmation_spec.rb +5 -5
- data/spec/validators/exclusion_spec.rb +3 -3
- data/spec/validators/format_spec.rb +2 -2
- data/spec/validators/inclusion_spec.rb +4 -4
- data/spec/validators/length_spec.rb +23 -23
- data/spec/validators/numericality_spec.rb +13 -13
- data/spec/validators/presence_spec.rb +3 -3
- metadata +49 -48
data/lib/haveapi/hooks.rb
CHANGED
@@ -77,7 +77,7 @@ module HaveAPI
|
|
77
77
|
# p MyClass.call_hooks(:myhook, args: [1, 2, 3], initial: {counter: 0})
|
78
78
|
# => {:counter=>5}
|
79
79
|
module Hooks
|
80
|
-
INSTANCE_VARIABLE = '@_haveapi_hooks'
|
80
|
+
INSTANCE_VARIABLE = '@_haveapi_hooks'.freeze
|
81
81
|
|
82
82
|
# Register a hook defined by `klass` with `name`.
|
83
83
|
# @param klass [Class] an instance of Class, that is class name, not it's instance
|
@@ -116,7 +116,7 @@ module HaveAPI
|
|
116
116
|
instance.instance_variable_set(INSTANCE_VARIABLE, hooks)
|
117
117
|
end
|
118
118
|
|
119
|
-
hooks[name] ||= {listeners: []}
|
119
|
+
hooks[name] ||= { listeners: [] }
|
120
120
|
hooks[name][:listeners] << block
|
121
121
|
end
|
122
122
|
|
@@ -143,36 +143,37 @@ module HaveAPI
|
|
143
143
|
# @param initial [Hash] initial return value
|
144
144
|
# @param instance [Boolean] call instance hooks or not; nil means auto-detect
|
145
145
|
def self.call_for(
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
146
|
+
klass,
|
147
|
+
name,
|
148
|
+
where = nil,
|
149
|
+
args: [],
|
150
|
+
kwargs: {},
|
151
|
+
initial: {},
|
152
|
+
instance: nil
|
153
153
|
)
|
154
154
|
classified = hook_classify(klass)
|
155
155
|
|
156
|
-
if (instance.nil? && !classified.is_a?(Class)) || instance
|
157
|
-
|
156
|
+
all_hooks = if (instance.nil? && !classified.is_a?(Class)) || instance
|
157
|
+
klass.instance_variable_get(INSTANCE_VARIABLE)
|
158
158
|
|
159
|
-
|
160
|
-
|
161
|
-
|
159
|
+
else
|
160
|
+
@hooks[classified]
|
161
|
+
end
|
162
162
|
|
163
163
|
catch(:stop) do
|
164
164
|
return initial unless all_hooks
|
165
165
|
return initial unless all_hooks[name]
|
166
|
+
|
166
167
|
hooks = all_hooks[name][:listeners]
|
167
168
|
return initial unless hooks
|
168
169
|
|
169
170
|
hooks.each do |hook|
|
170
|
-
if where
|
171
|
-
|
171
|
+
ret = if where
|
172
|
+
where.instance_exec(initial, *args, **kwargs, &hook)
|
172
173
|
|
173
|
-
|
174
|
-
|
175
|
-
|
174
|
+
else
|
175
|
+
hook.call(initial, *args, **kwargs)
|
176
|
+
end
|
176
177
|
|
177
178
|
initial.update(ret) if ret
|
178
179
|
end
|
@@ -195,60 +196,60 @@ module HaveAPI
|
|
195
196
|
module ClassMethods
|
196
197
|
# Register a hook named `name`.
|
197
198
|
def has_hook(name, opts = {})
|
198
|
-
Hooks.register_hook(
|
199
|
+
Hooks.register_hook(to_s, name, opts)
|
199
200
|
end
|
200
201
|
|
201
202
|
# Connect `block` to registered hook with `name`.
|
202
|
-
def connect_hook(name, &
|
203
|
-
Hooks.connect_hook(
|
203
|
+
def connect_hook(name, &)
|
204
|
+
Hooks.connect_hook(to_s, name, &)
|
204
205
|
end
|
205
206
|
|
206
207
|
# Call all hooks for `name`. see {Hooks.call_for}.
|
207
|
-
def call_hooks(
|
208
|
-
Hooks.call_for(
|
208
|
+
def call_hooks(*, **)
|
209
|
+
Hooks.call_for(to_s, *, **)
|
209
210
|
end
|
210
211
|
end
|
211
212
|
|
212
213
|
module InstanceMethods
|
213
214
|
# Call all instance and class hooks.
|
214
|
-
def call_hooks_for(
|
215
|
-
ret = call_instance_hooks_for(
|
215
|
+
def call_hooks_for(*, **kwargs)
|
216
|
+
ret = call_instance_hooks_for(*, **kwargs)
|
216
217
|
|
217
218
|
kwargs[:initial] = ret
|
218
|
-
call_class_hooks_for(
|
219
|
+
call_class_hooks_for(*, **kwargs)
|
219
220
|
end
|
220
221
|
|
221
222
|
# Call only instance hooks.
|
222
223
|
def call_instance_hooks_for(name, where = nil, args: [], kwargs: {}, initial: {})
|
223
|
-
Hooks.call_for(self, name, where, args
|
224
|
+
Hooks.call_for(self, name, where, args:, kwargs:, initial:)
|
224
225
|
end
|
225
226
|
|
226
227
|
# Call only class hooks.
|
227
|
-
def call_class_hooks_for(name, where
|
228
|
-
Hooks.call_for(self.class, name, where, args
|
228
|
+
def call_class_hooks_for(name, where = nil, args: [], kwargs: {}, initial: {})
|
229
|
+
Hooks.call_for(self.class, name, where, args:, kwargs:, initial:)
|
229
230
|
end
|
230
231
|
|
231
232
|
# Call hooks for different `klass`.
|
232
|
-
def call_hooks_as_for(klass,
|
233
|
-
ret = call_instance_hooks_as_for(klass,
|
233
|
+
def call_hooks_as_for(klass, *, **kwargs)
|
234
|
+
ret = call_instance_hooks_as_for(klass, *, **kwargs)
|
234
235
|
|
235
236
|
kwargs[:initial] = ret
|
236
|
-
call_class_hooks_as_for(klass.class,
|
237
|
+
call_class_hooks_as_for(klass.class, *, **kwargs)
|
237
238
|
end
|
238
239
|
|
239
240
|
# Call only instance hooks for different `klass`.
|
240
|
-
def call_instance_hooks_as_for(klass,
|
241
|
-
Hooks.call_for(klass,
|
241
|
+
def call_instance_hooks_as_for(klass, *, **)
|
242
|
+
Hooks.call_for(klass, *, **)
|
242
243
|
end
|
243
244
|
|
244
245
|
# Call only class hooks for different `klass`.
|
245
|
-
def call_class_hooks_as_for(klass,
|
246
|
-
Hooks.call_for(klass,
|
246
|
+
def call_class_hooks_as_for(klass, *, **)
|
247
|
+
Hooks.call_for(klass, *, **)
|
247
248
|
end
|
248
249
|
|
249
250
|
# Connect instance level hook `name` to `block`.
|
250
|
-
def connect_hook(name, &
|
251
|
-
Hooks.connect_instance_hook(self, name, &
|
251
|
+
def connect_hook(name, &)
|
252
|
+
Hooks.connect_instance_hook(self, name, &)
|
252
253
|
end
|
253
254
|
end
|
254
255
|
|
data/lib/haveapi/metadata.rb
CHANGED
@@ -14,38 +14,37 @@ module HaveAPI
|
|
14
14
|
# Every model adapter must register itself using this method.
|
15
15
|
def register
|
16
16
|
ModelAdapter.adapters ||= []
|
17
|
-
ModelAdapter.adapters << Kernel.const_get(
|
17
|
+
ModelAdapter.adapters << Kernel.const_get(to_s)
|
18
18
|
end
|
19
19
|
|
20
20
|
# Returns an adapter suitable for `layout` and `obj`.
|
21
21
|
# Adapters are iterated over and the first to return true to handle?()
|
22
22
|
# is returned.
|
23
23
|
def for(layout, obj)
|
24
|
-
return ModelAdapters::Hash if !obj || %i
|
25
|
-
|
24
|
+
return ModelAdapters::Hash if !obj || %i[hash hash_list].include?(layout)
|
25
|
+
|
26
|
+
adapter = @adapters.detect { |a| a.handle?(layout, obj) }
|
26
27
|
adapter || ModelAdapters::Hash
|
27
28
|
end
|
28
29
|
|
29
30
|
# Shortcut to Input::clean.
|
30
|
-
def input_clean(*
|
31
|
-
self::Input.clean(*
|
31
|
+
def input_clean(*)
|
32
|
+
self::Input.clean(*)
|
32
33
|
end
|
33
34
|
|
34
35
|
# Shortcut to get an instance of Input model adapter.
|
35
|
-
def input(*
|
36
|
-
self::Input.new(*
|
36
|
+
def input(*)
|
37
|
+
self::Input.new(*)
|
37
38
|
end
|
38
39
|
|
39
40
|
# Shortcut to get an instance of Output model adapter.
|
40
|
-
def output(*
|
41
|
-
self::Output.new(*
|
41
|
+
def output(*)
|
42
|
+
self::Output.new(*)
|
42
43
|
end
|
43
44
|
|
44
45
|
# Override this method to load validators from `model`
|
45
46
|
# to `params`.
|
46
|
-
def load_validators(model, params)
|
47
|
-
|
48
|
-
end
|
47
|
+
def load_validators(model, params); end
|
49
48
|
|
50
49
|
# Called when mounting the API. Model adapters may use this method
|
51
50
|
# to add custom meta parameters to `action`. `direction` is one of
|
@@ -63,9 +62,7 @@ module HaveAPI
|
|
63
62
|
# Subclass this class in your adapter and reimplement
|
64
63
|
# necessary methods.
|
65
64
|
class Input
|
66
|
-
def self.used_by(action)
|
67
|
-
|
68
|
-
end
|
65
|
+
def self.used_by(action); end
|
69
66
|
|
70
67
|
def initialize(input)
|
71
68
|
@input = input
|
@@ -83,17 +80,13 @@ module HaveAPI
|
|
83
80
|
end
|
84
81
|
|
85
82
|
# Return model instance from a raw input resource parameter.
|
86
|
-
def self.clean(model, raw, extra)
|
87
|
-
|
88
|
-
end
|
83
|
+
def self.clean(model, raw, extra); end
|
89
84
|
end
|
90
85
|
|
91
86
|
# Subclass this class in your adapter and reimplement
|
92
87
|
# necessary methods.
|
93
88
|
class Output
|
94
|
-
def self.used_by(action)
|
95
|
-
|
96
|
-
end
|
89
|
+
def self.used_by(action); end
|
97
90
|
|
98
91
|
def initialize(context, obj)
|
99
92
|
@context = context
|
@@ -102,14 +95,10 @@ module HaveAPI
|
|
102
95
|
|
103
96
|
# Return true if input parameters contain parameter
|
104
97
|
# with `name`.
|
105
|
-
def has_param?(name)
|
106
|
-
|
107
|
-
end
|
98
|
+
def has_param?(name); end
|
108
99
|
|
109
100
|
# Return a parameter in an appropriate format to be sent to a client.
|
110
|
-
def [](name)
|
111
|
-
|
112
|
-
end
|
101
|
+
def [](name); end
|
113
102
|
|
114
103
|
def meta
|
115
104
|
{}
|
@@ -6,7 +6,7 @@ module HaveAPI::ModelAdapters
|
|
6
6
|
register
|
7
7
|
|
8
8
|
def self.handle?(layout, klass)
|
9
|
-
klass < ::ActiveRecord::Base && %i
|
9
|
+
klass < ::ActiveRecord::Base && %i[object object_list].include?(layout)
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.load_validators(model, params)
|
@@ -34,10 +34,10 @@ module HaveAPI::ModelAdapters
|
|
34
34
|
# it well, it is not necessary to fix.
|
35
35
|
args.concat(ar_default_includes).uniq
|
36
36
|
|
37
|
-
if
|
38
|
-
q.includes(*args)
|
39
|
-
else
|
37
|
+
if args.empty?
|
40
38
|
q
|
39
|
+
else
|
40
|
+
q.includes(*args)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -45,6 +45,7 @@ module HaveAPI::ModelAdapters
|
|
45
45
|
# in an array of symbols and hashes.
|
46
46
|
def ar_parse_includes(raw)
|
47
47
|
return @ar_parsed_includes if @ar_parsed_includes
|
48
|
+
|
48
49
|
@ar_parsed_includes = ar_inner_includes(raw).select do |inc|
|
49
50
|
# Drop associations that are not registered in the AR:
|
50
51
|
# The API resource may have associations that are not based on
|
@@ -70,7 +71,7 @@ module HaveAPI::ModelAdapters
|
|
70
71
|
if assoc.index('__')
|
71
72
|
tmp = {}
|
72
73
|
parts = assoc.split('__')
|
73
|
-
tmp[parts.first.to_sym] = ar_inner_includes([parts[1
|
74
|
+
tmp[parts.first.to_sym] = ar_inner_includes([parts[1..].join('__')])
|
74
75
|
|
75
76
|
args << tmp
|
76
77
|
else
|
@@ -115,39 +116,37 @@ module HaveAPI::ModelAdapters
|
|
115
116
|
action.meta(:object) do
|
116
117
|
output do
|
117
118
|
custom :path_params, label: 'URL parameters',
|
118
|
-
|
119
|
+
desc: 'An array of parameters needed to resolve URL to this object'
|
119
120
|
bool :resolved, label: 'Resolved', desc: 'True if the association is resolved'
|
120
121
|
end
|
121
122
|
end
|
122
123
|
|
123
|
-
|
124
|
-
clean = Proc.new do |raw|
|
125
|
-
if raw.is_a?(String)
|
126
|
-
raw.strip.split(',')
|
127
|
-
elsif raw.is_a?(Array)
|
128
|
-
raw
|
129
|
-
else
|
130
|
-
nil
|
131
|
-
end
|
132
|
-
end
|
124
|
+
return unless %i[object object_list].include?(action.input.layout)
|
133
125
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
to go even deeper, use e.g. 'user,node__location__environment'.
|
140
|
-
END
|
141
|
-
|
142
|
-
action.meta(:global) do
|
143
|
-
input do
|
144
|
-
custom :includes, label: 'Included associations',
|
145
|
-
desc: desc, &clean
|
146
|
-
end
|
126
|
+
clean = proc do |raw|
|
127
|
+
if raw.is_a?(String)
|
128
|
+
raw.strip.split(',')
|
129
|
+
elsif raw.is_a?(Array)
|
130
|
+
raw
|
147
131
|
end
|
132
|
+
end
|
148
133
|
|
149
|
-
|
134
|
+
desc = <<~END
|
135
|
+
A list of names of associated resources separated by a comma.
|
136
|
+
Nested associations are declared with '__' between resource names.
|
137
|
+
For example, 'user,node' will resolve the two associations.
|
138
|
+
To resolve further associations of node, use e.g. 'user,node__location',
|
139
|
+
to go even deeper, use e.g. 'user,node__location__environment'.
|
140
|
+
END
|
141
|
+
|
142
|
+
action.meta(:global) do
|
143
|
+
input do
|
144
|
+
custom :includes, label: 'Included associations',
|
145
|
+
desc:, &clean
|
146
|
+
end
|
150
147
|
end
|
148
|
+
|
149
|
+
action.send(:include, Action::InstanceMethods)
|
151
150
|
end
|
152
151
|
|
153
152
|
def has_param?(name)
|
@@ -169,14 +168,14 @@ END
|
|
169
168
|
def meta
|
170
169
|
res = @context.action.resource
|
171
170
|
|
172
|
-
if @context.action.name.demodulize == 'Index' \
|
171
|
+
params = if @context.action.name.demodulize == 'Index' \
|
173
172
|
&& !@context.action.resolve \
|
174
173
|
&& res.const_defined?(:Show)
|
175
|
-
|
174
|
+
res::Show.resolve_path_params(@object)
|
176
175
|
|
177
|
-
|
178
|
-
|
179
|
-
|
176
|
+
else
|
177
|
+
@context.action.resolve_path_params(@object)
|
178
|
+
end
|
180
179
|
|
181
180
|
{
|
182
181
|
path_params: params.is_a?(Array) ? params : [params],
|
@@ -185,6 +184,7 @@ END
|
|
185
184
|
end
|
186
185
|
|
187
186
|
protected
|
187
|
+
|
188
188
|
# Return representation of an associated resource `param`
|
189
189
|
# with its instance in `val`.
|
190
190
|
#
|
@@ -223,7 +223,7 @@ END
|
|
223
223
|
@context.action_instance = push_ins
|
224
224
|
@context.action = push_cls
|
225
225
|
|
226
|
-
|
226
|
+
raise "#{res_show} resolve failed" unless ret[0]
|
227
227
|
|
228
228
|
ret[1][res_show.output.namespace].update({
|
229
229
|
_meta: ret[1][:_meta].update(resolved: true)
|
@@ -234,8 +234,8 @@ END
|
|
234
234
|
param.value_id => val.send(res_output[param.value_id].db_name),
|
235
235
|
param.value_label => val.send(res_output[param.value_label].db_name),
|
236
236
|
_meta: {
|
237
|
-
:
|
238
|
-
:
|
237
|
+
path_params: args.is_a?(Array) ? args : [args],
|
238
|
+
resolved: false
|
239
239
|
}
|
240
240
|
}
|
241
241
|
end
|
@@ -244,49 +244,39 @@ END
|
|
244
244
|
# Should an association with `name` be resolved?
|
245
245
|
def includes_include?(name)
|
246
246
|
includes = @context.action_instance.meta[:includes]
|
247
|
-
return unless includes
|
247
|
+
return false unless includes
|
248
248
|
|
249
249
|
name = name.to_sym
|
250
250
|
|
251
251
|
if @context.action_instance.flags[:inner_assoc]
|
252
252
|
# This action is called as an association of parent resource.
|
253
253
|
# Meta includes are already parsed and can be accessed directly.
|
254
|
-
includes.each do |v|
|
255
|
-
if v.is_a?(::Hash)
|
256
|
-
return true if v.has_key?(name)
|
257
|
-
else
|
258
|
-
return true if v == name
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
false
|
263
|
-
|
264
254
|
else
|
265
255
|
# This action is the one that was called by the user.
|
266
256
|
# Meta includes contains an array of strings as was sent
|
267
257
|
# by the user. The parsed includes must be fetched from
|
268
258
|
# the action itself.
|
269
259
|
includes = @context.action_instance.ar_parse_includes([])
|
260
|
+
end
|
270
261
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
end
|
262
|
+
includes.each do |v|
|
263
|
+
if v.is_a?(::Hash)
|
264
|
+
return true if v.has_key?(name)
|
265
|
+
elsif v == name
|
266
|
+
return true
|
277
267
|
end
|
278
|
-
|
279
|
-
false
|
280
268
|
end
|
269
|
+
|
270
|
+
false
|
281
271
|
end
|
282
272
|
|
283
273
|
# Create an array of includes that is passed to child association.
|
284
274
|
def includes_pass_on_to(assoc)
|
285
275
|
parsed = if @context.action_instance.flags[:inner_assoc]
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
276
|
+
@context.action_instance.meta[:includes]
|
277
|
+
else
|
278
|
+
@context.action_instance.ar_parse_includes([])
|
279
|
+
end
|
290
280
|
|
291
281
|
ret = []
|
292
282
|
|
@@ -324,7 +314,7 @@ END
|
|
324
314
|
|
325
315
|
handle ::ActiveModel::Validations::ExclusionValidator do |v|
|
326
316
|
opts = {
|
327
|
-
values: v.options[:in]
|
317
|
+
values: v.options[:in]
|
328
318
|
}
|
329
319
|
opts[:message] = v.options[:message] if v.options[:message]
|
330
320
|
|
@@ -342,7 +332,7 @@ END
|
|
342
332
|
|
343
333
|
handle ::ActiveModel::Validations::InclusionValidator do |v|
|
344
334
|
opts = {
|
345
|
-
values: v.options[:in]
|
335
|
+
values: v.options[:in]
|
346
336
|
}
|
347
337
|
opts[:message] = v.options[:message] if v.options[:message]
|
348
338
|
|
@@ -402,13 +392,13 @@ END
|
|
402
392
|
|
403
393
|
def translate(v)
|
404
394
|
self.class.handlers.each do |klass, translator|
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
break
|
395
|
+
next unless v.is_a?(klass)
|
396
|
+
|
397
|
+
v.attributes.each do |attr|
|
398
|
+
@attr = attr
|
399
|
+
instance_exec(v, &translator)
|
411
400
|
end
|
401
|
+
break
|
412
402
|
end
|
413
403
|
end
|
414
404
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module HaveAPI
|
2
2
|
module OutputFormatters
|
3
|
-
|
4
3
|
end
|
5
4
|
|
6
5
|
class OutputFormatter
|
@@ -29,7 +28,7 @@ module HaveAPI
|
|
29
28
|
end
|
30
29
|
end
|
31
30
|
|
32
|
-
|
31
|
+
!@formatter.nil?
|
33
32
|
end
|
34
33
|
|
35
34
|
def format(status, response, message = nil, errors = nil, version: true)
|
@@ -45,14 +44,15 @@ module HaveAPI
|
|
45
44
|
end
|
46
45
|
|
47
46
|
protected
|
48
|
-
|
47
|
+
|
48
|
+
def header(status, response, message = nil, errors = nil, version = nil)
|
49
49
|
ret = {}
|
50
50
|
ret[:version] = HaveAPI::PROTOCOL_VERSION if version
|
51
51
|
ret.update({
|
52
|
-
status
|
53
|
-
response
|
54
|
-
message
|
55
|
-
errors:
|
52
|
+
status:,
|
53
|
+
response:,
|
54
|
+
message:,
|
55
|
+
errors:
|
56
56
|
})
|
57
57
|
ret
|
58
58
|
end
|
@@ -7,7 +7,7 @@ module HaveAPI::OutputFormatters
|
|
7
7
|
@types ||= []
|
8
8
|
@types += args
|
9
9
|
|
10
|
-
HaveAPI::OutputFormatter.register(Kernel.const_get(
|
10
|
+
HaveAPI::OutputFormatter.register(Kernel.const_get(to_s)) unless @registered
|
11
11
|
@registered = true
|
12
12
|
end
|
13
13
|
|
@@ -22,8 +22,6 @@ module HaveAPI::OutputFormatters
|
|
22
22
|
self.class.types.first
|
23
23
|
end
|
24
24
|
|
25
|
-
def format(response)
|
26
|
-
|
27
|
-
end
|
25
|
+
def format(response); end
|
28
26
|
end
|
29
27
|
end
|
@@ -4,8 +4,8 @@ module HaveAPI::Parameters
|
|
4
4
|
:choices, :value_params
|
5
5
|
|
6
6
|
def initialize(resource, name: nil, label: nil, desc: nil,
|
7
|
-
|
8
|
-
|
7
|
+
choices: nil, value_id: :id, value_label: :label, required: nil,
|
8
|
+
db_name: nil, fetch: nil)
|
9
9
|
@resource = resource
|
10
10
|
@resource_path = build_resource_path(resource)
|
11
11
|
@name = name || resource.resource_name.underscore.to_sym
|
@@ -17,7 +17,7 @@ module HaveAPI::Parameters
|
|
17
17
|
@required = required
|
18
18
|
@db_name = db_name
|
19
19
|
@extra = {
|
20
|
-
fetch:
|
20
|
+
fetch:
|
21
21
|
}
|
22
22
|
end
|
23
23
|
|
@@ -65,7 +65,7 @@ module HaveAPI::Parameters
|
|
65
65
|
value: context.action_prepare && {
|
66
66
|
path: val_path,
|
67
67
|
method: val_method,
|
68
|
-
help: "#{val_path}?method=#{val_method}"
|
68
|
+
help: "#{val_path}?method=#{val_method}"
|
69
69
|
},
|
70
70
|
choices: {
|
71
71
|
path: choices_path,
|
@@ -76,13 +76,13 @@ module HaveAPI::Parameters
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def validate_build_output
|
79
|
-
%i
|
79
|
+
%i[value_id value_label].each do |name|
|
80
80
|
v = instance_variable_get("@#{name}")
|
81
81
|
|
82
82
|
[show_action, show_index].each do |klass|
|
83
83
|
next unless klass.instance_variable_get('@output')[v].nil?
|
84
84
|
|
85
|
-
|
85
|
+
raise "association to '#{@resource}': value_label '#{v}' is not an output parameter of '#{klass}'"
|
86
86
|
end
|
87
87
|
end
|
88
88
|
end
|
@@ -106,6 +106,7 @@ module HaveAPI::Parameters
|
|
106
106
|
end
|
107
107
|
|
108
108
|
private
|
109
|
+
|
109
110
|
def build_resource_path(r)
|
110
111
|
path = []
|
111
112
|
top_module = Kernel
|
@@ -115,7 +116,6 @@ module HaveAPI::Parameters
|
|
115
116
|
|
116
117
|
begin
|
117
118
|
top_module.obj_type
|
118
|
-
|
119
119
|
rescue NoMethodError
|
120
120
|
next
|
121
121
|
end
|
@@ -2,7 +2,7 @@ require 'date'
|
|
2
2
|
|
3
3
|
module HaveAPI::Parameters
|
4
4
|
class Typed
|
5
|
-
ATTRIBUTES = %i
|
5
|
+
ATTRIBUTES = %i[label desc type db_name default fill clean protected load_validators].freeze
|
6
6
|
|
7
7
|
attr_reader :name, :label, :desc, :type, :default
|
8
8
|
|
@@ -14,14 +14,14 @@ module HaveAPI::Parameters
|
|
14
14
|
@label = myargs.delete(:label) || name.to_s.capitalize
|
15
15
|
@layout = :custom
|
16
16
|
|
17
|
-
(ATTRIBUTES - %i
|
17
|
+
(ATTRIBUTES - %i[label]).each do |attr|
|
18
18
|
instance_variable_set("@#{attr}", myargs.delete(attr))
|
19
19
|
end
|
20
20
|
|
21
21
|
@type ||= String
|
22
22
|
|
23
23
|
@validators = HaveAPI::ValidatorChain.new(myargs) unless myargs.empty?
|
24
|
-
|
24
|
+
raise "unused arguments #{myargs}" unless myargs.empty?
|
25
25
|
end
|
26
26
|
|
27
27
|
def db_name
|
@@ -52,7 +52,7 @@ module HaveAPI::Parameters
|
|
52
52
|
type: @type ? @type.to_s : String.to_s,
|
53
53
|
validators: @validators ? @validators.describe : {},
|
54
54
|
default: @default,
|
55
|
-
protected: @protected || false
|
55
|
+
protected: @protected || false
|
56
56
|
}
|
57
57
|
end
|
58
58
|
|
@@ -75,7 +75,7 @@ module HaveAPI::Parameters
|
|
75
75
|
def clean(raw)
|
76
76
|
return instance_exec(raw, &@clean) if @clean
|
77
77
|
|
78
|
-
|
78
|
+
if raw.nil?
|
79
79
|
@default
|
80
80
|
|
81
81
|
elsif @type.nil?
|
@@ -93,16 +93,13 @@ module HaveAPI::Parameters
|
|
93
93
|
elsif @type == ::Datetime
|
94
94
|
begin
|
95
95
|
DateTime.iso8601(raw).to_time
|
96
|
-
|
97
96
|
rescue ArgumentError
|
98
|
-
raise HaveAPI::ValidationError
|
97
|
+
raise HaveAPI::ValidationError, "not in ISO 8601 format '#{raw}'"
|
99
98
|
end
|
100
99
|
|
101
100
|
else
|
102
101
|
raw
|
103
102
|
end
|
104
|
-
|
105
|
-
val
|
106
103
|
end
|
107
104
|
|
108
105
|
def validate(v, params)
|