paradocs 1.0.24 → 1.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.readthedocs.yml +5 -0
- data/README.md +1 -11
- data/docs/changelog.md +17 -0
- data/docs/custom_configuration.md +10 -0
- data/docs/documentation_generation.md +304 -0
- data/docs/faq.md +21 -0
- data/docs/form_objects_dsl.md +90 -0
- data/docs/index.md +106 -0
- data/docs/payload_builder.md +105 -0
- data/docs/policies.md +309 -0
- data/docs/schema.md +294 -0
- data/docs/struct.md +135 -0
- data/docs/subschema.md +29 -0
- data/lib/paradocs/extensions/payload_builder.rb +45 -0
- data/lib/paradocs/extensions/structure.rb +119 -0
- data/lib/paradocs/field_dsl.rb +12 -0
- data/lib/paradocs/schema.rb +32 -10
- data/lib/paradocs/struct.rb +1 -0
- data/lib/paradocs/version.rb +1 -1
- data/mkdocs.yml +17 -0
- data/paradocs.gemspec +3 -3
- data/requirements.txt +1 -0
- data/spec/extensions/payload_builder_spec.rb +70 -0
- data/spec/extensions/structures_spec.rb +250 -0
- data/spec/field_spec.rb +1 -1
- data/spec/schema_spec.rb +7 -7
- data/spec/struct_spec.rb +8 -8
- data/spec/subschema_spec.rb +4 -4
- metadata +29 -12
- data/lib/paradocs/extensions/insides.rb +0 -77
- data/spec/schema_structures_spec.rb +0 -169
@@ -0,0 +1,45 @@
|
|
1
|
+
module Paradocs
|
2
|
+
module Extensions
|
3
|
+
class PayloadBuilder
|
4
|
+
attr_reader :structure, :result
|
5
|
+
attr_accessor :skip_word
|
6
|
+
def initialize(schema, skip_word: :skip)
|
7
|
+
@structure = schema.structure
|
8
|
+
@skip_word = skip_word
|
9
|
+
end
|
10
|
+
|
11
|
+
def build!(sort_by_schema: false, &block)
|
12
|
+
result = structure.all_nested.map { |name, struct| [name, build_simple_structure(struct, &block)] }.to_h
|
13
|
+
sort_by_schema ? schema.resolve(result).output : result
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def build_simple_structure(struct, &block)
|
19
|
+
struct.map do |key, value|
|
20
|
+
key = key.to_s
|
21
|
+
next if key.start_with?(Paradocs.config.meta_prefix) # skip all the meta fields
|
22
|
+
ex_value = restore_one(key, value, &block)
|
23
|
+
next if ex_value == @skip_word
|
24
|
+
key = value[:alias] || key
|
25
|
+
[key.to_s, ex_value]
|
26
|
+
end.compact.to_h
|
27
|
+
end
|
28
|
+
|
29
|
+
def restore_one(key, value, &block)
|
30
|
+
default = value[:default]
|
31
|
+
ex_value = if value[:structure]
|
32
|
+
data = build_simple_structure(value[:structure], &block)
|
33
|
+
value[:type] == :array ? [data] : data
|
34
|
+
elsif default
|
35
|
+
default.is_a?(Proc) ? default.call : default
|
36
|
+
elsif value[:options] && !value[:options].empty?
|
37
|
+
options = value[:options]
|
38
|
+
value[:type] == :array ? options : options.sample
|
39
|
+
end
|
40
|
+
return ex_value unless block_given?
|
41
|
+
yield(key, value, ex_value, @skip_word)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Paradocs
|
2
|
+
module Extensions
|
3
|
+
class Structure
|
4
|
+
DEFAULT = :generic
|
5
|
+
%w(errors subschemes).each do |key|
|
6
|
+
define_method(key) { "#{Paradocs.config.meta_prefix}#{key}".to_sym }
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :schema, :ignore_transparent, :root
|
10
|
+
attr_accessor :ignore_transparent
|
11
|
+
def initialize(schema, ignore_transparent=true, root="")
|
12
|
+
@schema = schema
|
13
|
+
@ignore_transparent = ignore_transparent
|
14
|
+
@root = root
|
15
|
+
end
|
16
|
+
|
17
|
+
def nested(&block)
|
18
|
+
schema.fields.each_with_object({errors => [], subschemes => {}}) do |(_, field), result|
|
19
|
+
meta, sc = collect_meta(field, root)
|
20
|
+
if sc
|
21
|
+
meta[:structure] = self.class.new(sc, ignore_transparent, meta[:json_path]).nested(&block)
|
22
|
+
result[errors] += meta[:structure].delete(errors)
|
23
|
+
else
|
24
|
+
result[errors] += field.possible_errors
|
25
|
+
end
|
26
|
+
|
27
|
+
field_key = field.meta_data[:alias] || field.key
|
28
|
+
result[field_key] = meta unless ignore_transparent && field.transparent?
|
29
|
+
yield(field_key, meta) if block_given?
|
30
|
+
|
31
|
+
next unless field.mutates_schema?
|
32
|
+
schema.subschemes.each do |name, subschema|
|
33
|
+
result[subschemes][name] = self.class.new(subschema, ignore_transparent, root).nested(&block)
|
34
|
+
result[errors] += result[subschemes][name][errors]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def all_nested(&block)
|
40
|
+
all_flatten(&block).each_with_object({}) do |(name, struct), obj|
|
41
|
+
obj[name] = {}
|
42
|
+
# sort the flatten struct to have iterated 1lvl keys before 2lvl and so on...
|
43
|
+
struct.sort_by { |k, v| k.to_s.count(".") }.each do |key, value|
|
44
|
+
target = obj[name]
|
45
|
+
key, value = key.to_s, value.clone # clone the values, because we do mutation below
|
46
|
+
value.merge!(nested_name: key) if value.respond_to?(:merge) # it can be array (_errors)
|
47
|
+
next target[key.to_sym] = value if key.start_with?(Paradocs.config.meta_prefix) # copy meta fields
|
48
|
+
|
49
|
+
parts = key.split(".")
|
50
|
+
next target[key] ||= value if parts.size == 1 # copy 1lvl key
|
51
|
+
parts.each.with_index do |subkey, index|
|
52
|
+
target[subkey] ||= value
|
53
|
+
next if parts.size == index + 1
|
54
|
+
target[subkey][:structure] ||= {}
|
55
|
+
target = target[subkey][:structure] # target goes deeper for each part
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def all_flatten(schema_structure=nil, &block)
|
62
|
+
schema_structure ||= flatten(&block)
|
63
|
+
if schema_structure[subschemes].empty?
|
64
|
+
schema_structure.delete(subschemes) # don't include redundant key
|
65
|
+
return {DEFAULT => schema_structure}
|
66
|
+
end
|
67
|
+
schema_structure[subschemes].each_with_object({}) do |(name, subschema), result|
|
68
|
+
if subschema[subschemes].empty?
|
69
|
+
result[name] = schema_structure.merge(subschema)
|
70
|
+
result[name][errors] += schema_structure[errors]
|
71
|
+
result[name][errors].uniq!
|
72
|
+
result[name].delete(subschemes)
|
73
|
+
next result[name]
|
74
|
+
end
|
75
|
+
|
76
|
+
all_flatten(subschema).each do |sub_name, schema|
|
77
|
+
result["#{name}_#{sub_name}".to_sym] = schema_structure.merge(schema)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def flatten(&block)
|
83
|
+
schema.fields.each_with_object({errors => [], subschemes => {}}) do |(_, field), obj|
|
84
|
+
meta, sc = collect_meta(field, root)
|
85
|
+
humanized_name = meta.delete(:nested_name)
|
86
|
+
obj[humanized_name] = meta unless ignore_transparent && field.transparent?
|
87
|
+
|
88
|
+
if sc
|
89
|
+
deep_result = self.class.new(sc, ignore_transparent, meta[:json_path]).flatten(&block)
|
90
|
+
obj[errors] += deep_result.delete(errors)
|
91
|
+
obj[subschemes].merge!(deep_result.delete(subschemes))
|
92
|
+
obj.merge!(deep_result)
|
93
|
+
else
|
94
|
+
obj[errors] += field.possible_errors
|
95
|
+
end
|
96
|
+
yield(humanized_name, meta) if block_given?
|
97
|
+
next unless field.mutates_schema?
|
98
|
+
schema.subschemes.each do |name, subschema|
|
99
|
+
obj[subschemes][name] ||= self.class.new(subschema, ignore_transparent, root).flatten(&block)
|
100
|
+
obj[errors] += obj[subschemes][name][errors]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def collect_meta(field, root)
|
108
|
+
field_key = field.meta_data[:alias] || field.key
|
109
|
+
json_path = root.empty? ? "$.#{field_key}" : "#{root}.#{field_key}"
|
110
|
+
meta = field.meta_data.merge(json_path: json_path)
|
111
|
+
sc = meta.delete(:schema)
|
112
|
+
meta[:mutates_schema] = true if meta.delete(:mutates_schema)
|
113
|
+
json_path << "[]" if meta[:type] == :array
|
114
|
+
meta[:nested_name] = json_path.gsub("[]", "")[2..-1]
|
115
|
+
[meta, sc]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/lib/paradocs/field_dsl.rb
CHANGED
@@ -32,5 +32,17 @@ module Paradocs
|
|
32
32
|
def length(opts)
|
33
33
|
policy :length, opts
|
34
34
|
end
|
35
|
+
|
36
|
+
def description(text)
|
37
|
+
meta description: text
|
38
|
+
end
|
39
|
+
|
40
|
+
def as(identifier)
|
41
|
+
meta alias: identifier
|
42
|
+
end
|
43
|
+
|
44
|
+
def example(value)
|
45
|
+
meta example: value
|
46
|
+
end
|
35
47
|
end
|
36
48
|
end
|
data/lib/paradocs/schema.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
require "paradocs/context"
|
2
2
|
require "paradocs/results"
|
3
3
|
require "paradocs/field"
|
4
|
-
require "paradocs/extensions/
|
4
|
+
require "paradocs/extensions/structure"
|
5
|
+
require "paradocs/extensions/payload_builder"
|
5
6
|
|
6
7
|
module Paradocs
|
7
8
|
class Schema
|
8
|
-
include Extensions::Insides
|
9
|
-
|
10
9
|
attr_accessor :environment
|
11
|
-
attr_reader :subschemes
|
10
|
+
attr_reader :subschemes, :structure_builder
|
12
11
|
def initialize(options={}, &block)
|
13
12
|
@options = options
|
14
13
|
@fields = {}
|
@@ -18,6 +17,7 @@ module Paradocs
|
|
18
17
|
@default_field_policies = []
|
19
18
|
@ignored_field_keys = []
|
20
19
|
@expansions = {}
|
20
|
+
@structure_builder = Paradocs::Extensions::Structure.new(self)
|
21
21
|
end
|
22
22
|
|
23
23
|
def schema
|
@@ -29,6 +29,27 @@ module Paradocs
|
|
29
29
|
f.mutates_schema!(&block)
|
30
30
|
end
|
31
31
|
|
32
|
+
def structure(ignore_transparent: true)
|
33
|
+
flush!
|
34
|
+
structure_builder.ignore_transparent = ignore_transparent
|
35
|
+
structure_builder
|
36
|
+
end
|
37
|
+
|
38
|
+
def example_payloads(&block)
|
39
|
+
@example_payloads ||= Paradocs::Extensions::PayloadBuilder.new(self).build!(&block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def walk(meta_key = nil, &visitor)
|
43
|
+
r = visit(meta_key, &visitor)
|
44
|
+
Results.new(r, {}, {})
|
45
|
+
end
|
46
|
+
|
47
|
+
def visit(meta_key = nil, &visitor)
|
48
|
+
fields.each_with_object({}) do |(_, field), m|
|
49
|
+
m[field.key] = field.visit(meta_key, &visitor)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
32
53
|
def subschema(*args, &block)
|
33
54
|
options = args.last.is_a?(Hash) ? args.last : {}
|
34
55
|
name = args.first.is_a?(Symbol) ? args.shift : Paradocs.config.default_schema_name
|
@@ -143,6 +164,11 @@ module Paradocs
|
|
143
164
|
end
|
144
165
|
end
|
145
166
|
|
167
|
+
def flush!
|
168
|
+
@fields = {}
|
169
|
+
@applied = false
|
170
|
+
end
|
171
|
+
|
146
172
|
protected
|
147
173
|
|
148
174
|
attr_reader :definitions, :options
|
@@ -155,7 +181,8 @@ module Paradocs
|
|
155
181
|
invoke_subschemes!(val, context, flds: flds)
|
156
182
|
flds.each_with_object({}) do |(_, field), m|
|
157
183
|
r = field.resolve(val, context.sub(field.key))
|
158
|
-
|
184
|
+
key = field.meta_data[:alias] || field.key
|
185
|
+
m[key] = r.value if r.eligible?
|
159
186
|
end
|
160
187
|
end
|
161
188
|
|
@@ -205,10 +232,5 @@ module Paradocs
|
|
205
232
|
end
|
206
233
|
@applied = true
|
207
234
|
end
|
208
|
-
|
209
|
-
def flush!
|
210
|
-
@fields = {}
|
211
|
-
@applied = false
|
212
|
-
end
|
213
235
|
end
|
214
236
|
end
|
data/lib/paradocs/struct.rb
CHANGED
data/lib/paradocs/version.rb
CHANGED
data/mkdocs.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
site_name: Paradocs
|
2
|
+
theme:
|
3
|
+
name: readthedocs
|
4
|
+
nav_style: dark
|
5
|
+
nav:
|
6
|
+
- 'index.md'
|
7
|
+
- 'policies.md'
|
8
|
+
- 'schema.md'
|
9
|
+
- 'struct.md'
|
10
|
+
- 'form_objects_dsl.md'
|
11
|
+
- 'subschema.md'
|
12
|
+
- 'documentation_generation.md'
|
13
|
+
- 'payload_builder.md'
|
14
|
+
- 'custom_configuration.md'
|
15
|
+
- 'faq.md'
|
16
|
+
- 'changelog.md'
|
17
|
+
|
data/paradocs.gemspec
CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Paradocs::VERSION
|
9
9
|
spec.authors = ["Maxim Tkachenko", "Ismael Celis"]
|
10
10
|
spec.email = ["tkachenko.maxim.w@gmail.com", "ismaelct@gmail.com"]
|
11
|
-
spec.description = %q{Flexible
|
11
|
+
spec.description = %q{Flexible DRY validations with API docs generation done right TLDR; parametrics on steroids.}
|
12
12
|
spec.summary = %q{A huge add-on for original gem mostly focused on retrieving the more metadata from declared schemas as possible.}
|
13
|
-
spec.homepage = "https://
|
13
|
+
spec.homepage = "https://paradocs.readthedocs.io/en/latest"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 2.1"
|
22
|
-
spec.add_development_dependency "rake",
|
22
|
+
spec.add_development_dependency "rake", '~> 12.3'
|
23
23
|
spec.add_development_dependency "rspec", '3.4.0'
|
24
24
|
spec.add_development_dependency "pry", "~> 0"
|
25
25
|
end
|
data/requirements.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
mkdocs==1.1.2
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Paradocs::Extensions::PayloadBuilder do
|
4
|
+
let(:schema) do
|
5
|
+
Paradocs::Schema.new do
|
6
|
+
field(:test).present.type(:string).mutates_schema! { :subschema1 }
|
7
|
+
subschema(:subschema1) do
|
8
|
+
field(:subtest1).declared.type(:number)
|
9
|
+
end
|
10
|
+
subschema(:subschema2) do
|
11
|
+
field(:subtest2).required.type(:array).schema do
|
12
|
+
field(:hello).type(:string).mutates_schema! { |*| :deep_schema }
|
13
|
+
subschema(:deep_schema) { field(:deep_field).type(:boolean) }
|
14
|
+
subschema(:empty) { }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
# 2 mutation fields and more than 1 subschema pack work good in validation but docs
|
18
|
+
# will contain only 1 subschema at once: foo subschemes will never be mixed with test subschemes
|
19
|
+
field(:foo).required.type(:object).schema do
|
20
|
+
field(:bar).present.type(:string).options(["foo", "bar"]).mutates_schema! do |value, *|
|
21
|
+
value == "foo" ? :fooschema : :barschema
|
22
|
+
end
|
23
|
+
subschema(:fooschema) { }
|
24
|
+
subschema(:barschema) do
|
25
|
+
field(:barfield).present.type(:boolean).as(:bar_field)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "gives an example payload and takes into account the subschemes" do
|
32
|
+
allow_any_instance_of(Array).to receive(:sample) { "bar" }
|
33
|
+
payloads = described_class.new(schema).build!
|
34
|
+
expect(payloads.keys.sort).to eq([:barschema, :fooschema, :subschema1, :subschema2_deep_schema, :subschema2_empty])
|
35
|
+
expect(payloads[:barschema]).to eq({"test" => nil, "foo" => {"bar" => "bar", "bar_field" => nil}})
|
36
|
+
expect(payloads[:fooschema]).to eq({"test" => nil, "foo" => {"bar" => "bar"}})
|
37
|
+
expect(payloads[:subschema1]).to eq({"test" => nil, "foo" => {"bar" => "bar"}, "subtest1" => nil})
|
38
|
+
expect(payloads[:subschema2_deep_schema]).to eq({
|
39
|
+
"subtest2" => [{"deep_field" => nil, "hello" => nil}], "test" => nil, "foo" => {"bar" => "bar"}
|
40
|
+
})
|
41
|
+
expect(payloads[:subschema2_empty]).to eq({
|
42
|
+
"test" => nil, "foo" => {"bar" => "bar"}, "subtest2" => [{"hello" => nil}]
|
43
|
+
})
|
44
|
+
end
|
45
|
+
|
46
|
+
it "yields a usefull block that changes the result" do
|
47
|
+
payloads = described_class.new(schema).build! do |key, meta, example, skip_word|
|
48
|
+
if key == "bar"
|
49
|
+
nil
|
50
|
+
elsif meta[:type] == :boolean
|
51
|
+
true
|
52
|
+
elsif key == "subtest1"
|
53
|
+
skip_word # this key value pair will be ommited
|
54
|
+
else
|
55
|
+
example # return suggested value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
expect(payloads.keys.sort).to eq([:barschema, :fooschema, :subschema1, :subschema2_deep_schema, :subschema2_empty])
|
60
|
+
expect(payloads[:barschema]).to eq({"test" => nil, "foo" => {"bar" => nil, "bar_field" => true}}) # barfield is change to true and bar is nil
|
61
|
+
expect(payloads[:fooschema]).to eq({"test" => nil, "foo" => {"bar" => nil}}) # bar is nil
|
62
|
+
expect(payloads[:subschema1]).to eq({"test" => nil, "foo" => {"bar" => nil}}) # subtest is missing, bar is nil
|
63
|
+
expect(payloads[:subschema2_deep_schema]).to eq({
|
64
|
+
"subtest2" => [{"deep_field" => true, "hello" => nil}], "test" => nil, "foo" => {"bar" => nil}
|
65
|
+
})
|
66
|
+
expect(payloads[:subschema2_empty]).to eq({
|
67
|
+
"test" => nil, "foo" => {"bar" => nil}, "subtest2" => [{"hello" => nil}]
|
68
|
+
})
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Paradocs::Extensions::Structure do
|
4
|
+
Paradocs.policy :policy_with_error do
|
5
|
+
register_error ArgumentError
|
6
|
+
|
7
|
+
validate do |*|
|
8
|
+
raise ArgumentError
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
Paradocs.policy :policy_with_silent_error do
|
13
|
+
register_silent_error RuntimeError
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:schema) do
|
17
|
+
Paradocs::Schema.new do
|
18
|
+
subschema(:highest_level) { field(:test).present } # no mutations on this level -> subschema ignored
|
19
|
+
|
20
|
+
field(:data).type(:object).present.schema do
|
21
|
+
field(:id).type(:integer).present.policy(:policy_with_error).as(:user_id)
|
22
|
+
field(:name).type(:string).meta(label: "very important staff").description("Example description").example("John")
|
23
|
+
field(:role).type(:string).declared.options(["admin", "user"]).default("user").mutates_schema! do |*|
|
24
|
+
:test_subschema
|
25
|
+
end
|
26
|
+
field(:extra).type(:array).required.schema do
|
27
|
+
field(:extra).declared.default(false).policy(:policy_with_silent_error)
|
28
|
+
end
|
29
|
+
|
30
|
+
mutation_by!(:name) { :subschema }
|
31
|
+
|
32
|
+
subschema(:subschema) do
|
33
|
+
field(:test_field).present
|
34
|
+
end
|
35
|
+
subschema(:test_subschema) do
|
36
|
+
field(:test1).present
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#nested" do
|
43
|
+
it "generates nested data for documentation generation" do
|
44
|
+
result = schema.structure.nested { |k, meta| meta[:block_works] = true unless meta[:present] }
|
45
|
+
expect(result[:_subschemes]).to eq({})
|
46
|
+
expect(result[:_errors]).to eq([ArgumentError])
|
47
|
+
data_structure = result[:data].delete(:structure)
|
48
|
+
expect(result[:data]).to eq({
|
49
|
+
type: :object,
|
50
|
+
required: true,
|
51
|
+
present: true,
|
52
|
+
json_path: "$.data",
|
53
|
+
nested_name: "data",
|
54
|
+
})
|
55
|
+
expect(data_structure[:_subschemes]).to eq({
|
56
|
+
test_subschema: {
|
57
|
+
_errors: [],
|
58
|
+
_subschemes: {},
|
59
|
+
test1: {required: true, present: true, json_path: "$.data.test1", nested_name: "data.test1"}
|
60
|
+
},
|
61
|
+
subschema: {
|
62
|
+
_errors: [],
|
63
|
+
_subschemes: {},
|
64
|
+
test_field: {required: true, present: true, json_path: "$.data.test_field", nested_name: "data.test_field"}
|
65
|
+
}
|
66
|
+
})
|
67
|
+
expect(data_structure[:user_id]).to eq({
|
68
|
+
type: :integer,
|
69
|
+
required: true,
|
70
|
+
present: true,
|
71
|
+
policy_with_error: {errors: [ArgumentError]},
|
72
|
+
alias: :user_id,
|
73
|
+
json_path: "$.data.user_id",
|
74
|
+
nested_name: "data.user_id"
|
75
|
+
})
|
76
|
+
expect(data_structure[:name]).to eq({
|
77
|
+
type: :string,
|
78
|
+
label: "very important staff",
|
79
|
+
description: "Example description",
|
80
|
+
example: "John",
|
81
|
+
mutates_schema: true,
|
82
|
+
block_works: true,
|
83
|
+
json_path: "$.data.name",
|
84
|
+
nested_name: "data.name"
|
85
|
+
})
|
86
|
+
expect(data_structure[:role]).to eq({
|
87
|
+
type: :string,
|
88
|
+
options: ["admin", "user"],
|
89
|
+
default: "user",
|
90
|
+
mutates_schema: true,
|
91
|
+
block_works: true,
|
92
|
+
json_path: "$.data.role",
|
93
|
+
nested_name: "data.role"
|
94
|
+
})
|
95
|
+
expect(data_structure[:extra]).to eq({
|
96
|
+
type: :array,
|
97
|
+
required: true,
|
98
|
+
block_works: true,
|
99
|
+
json_path: "$.data.extra[]",
|
100
|
+
nested_name: "data.extra",
|
101
|
+
structure: {
|
102
|
+
extra: {
|
103
|
+
default: false,
|
104
|
+
block_works: true,
|
105
|
+
json_path: "$.data.extra[].extra",
|
106
|
+
nested_name: "data.extra.extra",
|
107
|
+
policy_with_silent_error: {errors: []}
|
108
|
+
},
|
109
|
+
_subschemes: {}
|
110
|
+
}
|
111
|
+
})
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "#flatten" do
|
116
|
+
it "generates flatten data for documentation generation" do
|
117
|
+
expect(schema.structure.flatten { |key, meta| meta[:block_works] = true if key.split(".").size == 1 }).to eq({
|
118
|
+
"data" => {
|
119
|
+
type: :object,
|
120
|
+
required: true,
|
121
|
+
present: true,
|
122
|
+
block_works: true,
|
123
|
+
json_path: "$.data"
|
124
|
+
},
|
125
|
+
"data.extra" => {
|
126
|
+
type: :array,
|
127
|
+
required: true,
|
128
|
+
json_path: "$.data.extra[]"
|
129
|
+
},
|
130
|
+
"data.extra.extra" => {
|
131
|
+
default: false,
|
132
|
+
json_path: "$.data.extra[].extra",
|
133
|
+
policy_with_silent_error: {errors: []}
|
134
|
+
},
|
135
|
+
"data.user_id" => {
|
136
|
+
type: :integer,
|
137
|
+
required: true,
|
138
|
+
present: true,
|
139
|
+
alias: :user_id,
|
140
|
+
json_path: "$.data.user_id",
|
141
|
+
policy_with_error: {errors: [ArgumentError]}
|
142
|
+
},
|
143
|
+
"data.name" => {
|
144
|
+
type: :string,
|
145
|
+
json_path: "$.data.name",
|
146
|
+
label: "very important staff",
|
147
|
+
description: "Example description",
|
148
|
+
example: "John",
|
149
|
+
mutates_schema: true
|
150
|
+
},
|
151
|
+
"data.role" => {
|
152
|
+
type: :string,
|
153
|
+
options: ["admin", "user"],
|
154
|
+
default: "user",
|
155
|
+
json_path: "$.data.role",
|
156
|
+
mutates_schema: true
|
157
|
+
},
|
158
|
+
_errors: [ArgumentError],
|
159
|
+
_subschemes: {
|
160
|
+
test_subschema: {
|
161
|
+
_errors: [],
|
162
|
+
_subschemes: {},
|
163
|
+
"data.test1"=>{
|
164
|
+
required: true,
|
165
|
+
present: true,
|
166
|
+
json_path: "$.data.test1"
|
167
|
+
}
|
168
|
+
},
|
169
|
+
subschema: {
|
170
|
+
_errors: [],
|
171
|
+
_subschemes: {},
|
172
|
+
"data.test_field" => {required: true, present: true, json_path: "$.data.test_field"}
|
173
|
+
}
|
174
|
+
}
|
175
|
+
})
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "#all_flatten" do
|
180
|
+
it "generates N structures, where N = number of unique combinations of applied subschemas" do
|
181
|
+
expect(schema.structure.all_flatten).to eq({
|
182
|
+
subschema: {
|
183
|
+
_errors: [ArgumentError],
|
184
|
+
"data" => {type: :object, required: true, present: true, json_path: "$.data"},
|
185
|
+
"data.user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id"},
|
186
|
+
"data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, description: "Example description", example: "John"},
|
187
|
+
"data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
|
188
|
+
"data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"},
|
189
|
+
"data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"},
|
190
|
+
"data.test_field" => {required: true, present: true, json_path: "$.data.test_field"}
|
191
|
+
},
|
192
|
+
test_subschema: {
|
193
|
+
_errors: [ArgumentError],
|
194
|
+
"data" => {type: :object, required: true, present: true, json_path: "$.data"},
|
195
|
+
"data.user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id"},
|
196
|
+
"data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, description: "Example description", example: "John"},
|
197
|
+
"data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
|
198
|
+
"data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"},
|
199
|
+
"data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"},
|
200
|
+
"data.test1" => {required: true, present: true, json_path: "$.data.test1"}
|
201
|
+
}
|
202
|
+
})
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe "#all_nested" do
|
207
|
+
it "generates N structures, where N = number of unique combinations of applied subschemas" do
|
208
|
+
result = schema.structure.all_nested
|
209
|
+
expect(result[:subschema]).to eq({
|
210
|
+
_errors: [ArgumentError],
|
211
|
+
"data" => {
|
212
|
+
type: :object,
|
213
|
+
required: true,
|
214
|
+
present: true,
|
215
|
+
json_path: "$.data",
|
216
|
+
nested_name: "data",
|
217
|
+
structure: {
|
218
|
+
"role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", nested_name: "data.role", mutates_schema: true},
|
219
|
+
"test_field" => {required: true, present: true, json_path: "$.data.test_field", nested_name: "data.test_field"},
|
220
|
+
"user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id", nested_name: "data.user_id"},
|
221
|
+
"name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name", description: "Example description", example: "John"},
|
222
|
+
"extra" => {
|
223
|
+
type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra",
|
224
|
+
structure: {"extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"}}
|
225
|
+
}
|
226
|
+
}
|
227
|
+
}
|
228
|
+
})
|
229
|
+
expect(result[:test_subschema]).to eq({
|
230
|
+
_errors: [ArgumentError],
|
231
|
+
"data" => {
|
232
|
+
type: :object,
|
233
|
+
required: true,
|
234
|
+
present: true,
|
235
|
+
json_path: "$.data",
|
236
|
+
nested_name: "data",
|
237
|
+
structure: {
|
238
|
+
"role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", nested_name: "data.role", mutates_schema: true},
|
239
|
+
"test1" => {required: true, present: true, json_path: "$.data.test1", nested_name: "data.test1"},
|
240
|
+
"user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id", nested_name: "data.user_id"},
|
241
|
+
"name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name", description: "Example description", example: "John"},
|
242
|
+
"extra" => {
|
243
|
+
type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra",
|
244
|
+
structure: {"extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"}}}
|
245
|
+
}
|
246
|
+
}
|
247
|
+
})
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|