haveapi 0.20.0 → 0.21.1
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/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)
|